blob: a5214afa9effceacdf0b380b4f4426f54bffa615 [file] [log] [blame]
Dan Talaycoc901f4d2010-03-07 21:55:45 -08001
Dan Talaycod2ca1032010-03-10 14:40:26 -08002import sys
Dan Talayco92c99122010-06-03 13:53:18 -07003import copy
Dan Talaycod2ca1032010-03-10 14:40:26 -08004
5try:
6 import scapy.all as scapy
7except:
8 try:
9 import scapy as scapy
10 except:
11 sys.exit("Need to install scapy for packet parsing")
Dan Talayco41eae8b2010-03-10 13:57:06 -080012
Dan Talaycoc901f4d2010-03-07 21:55:45 -080013import oftest.controller as controller
14import oftest.cstruct as ofp
15import oftest.message as message
16import oftest.dataplane as dataplane
17import oftest.action as action
Dan Talayco41eae8b2010-03-10 13:57:06 -080018import oftest.parse as parse
Dan Talaycoc901f4d2010-03-07 21:55:45 -080019import logging
Dan Talayco4b2bee62010-07-20 14:10:05 -070020import types
Dan Talaycoc901f4d2010-03-07 21:55:45 -080021
Dan Talaycoba3745c2010-07-21 21:51:08 -070022global skipped_test_count
23skipped_test_count = 0
24
Dan Talayco551befa2010-07-15 17:05:32 -070025# Some useful defines
26IP_ETHERTYPE = 0x800
27TCP_PROTOCOL = 0x6
28UDP_PROTOCOL = 0x11
29
Dan Talayco98fada92010-07-17 00:36:21 -070030def clear_switch(parent, port_list, logger):
31 """
32 Clear the switch configuration
33
34 @param parent Object implementing controller and assert equal
35 @param logger Logging object
36 """
37 for port in port_list:
38 clear_port_config(parent, port, logger)
39 delete_all_flows(parent.controller, logger)
40
Dan Talayco41eae8b2010-03-10 13:57:06 -080041def delete_all_flows(ctrl, logger):
42 """
43 Delete all flows on the switch
44 @param ctrl The controller object for the test
45 @param logger Logging object
46 """
47
Dan Talaycoc901f4d2010-03-07 21:55:45 -080048 logger.info("Deleting all flows")
49 msg = message.flow_mod()
50 msg.match.wildcards = ofp.OFPFW_ALL
Dan Talayco41eae8b2010-03-10 13:57:06 -080051 msg.out_port = ofp.OFPP_NONE
Dan Talaycoc901f4d2010-03-07 21:55:45 -080052 msg.command = ofp.OFPFC_DELETE
53 msg.buffer_id = 0xffffffff
Dan Talayco41eae8b2010-03-10 13:57:06 -080054 return ctrl.message_send(msg)
55
Dan Talayco98fada92010-07-17 00:36:21 -070056def clear_port_config(parent, port, logger):
57 """
58 Clear the port configuration (currently only no flood setting)
59
60 @param parent Object implementing controller and assert equal
61 @param logger Logging object
62 """
63 rv = port_config_set(parent.controller, port,
64 0, ofp.OFPPC_NO_FLOOD, logger)
65 self.assertEqual(rv, 0, "Failed to reset port config")
66
Dan Talayco41eae8b2010-03-10 13:57:06 -080067def simple_tcp_packet(pktlen=100,
68 dl_dst='00:01:02:03:04:05',
69 dl_src='00:06:07:08:09:0a',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070070 dl_vlan_enable=False,
71 dl_vlan=0,
72 dl_vlan_pcp=0,
Dan Talayco551befa2010-07-15 17:05:32 -070073 dl_vlan_cfi=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080074 ip_src='192.168.0.1',
75 ip_dst='192.168.0.2',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070076 ip_tos=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080077 tcp_sport=1234,
78 tcp_dport=80
79 ):
80 """
81 Return a simple dataplane TCP packet
82
83 Supports a few parameters:
84 @param len Length of packet in bytes w/o CRC
85 @param dl_dst Destinatino MAC
86 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070087 @param dl_vlan_enable True if the packet is with vlan, False otherwise
88 @param dl_vlan VLAN ID
89 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -080090 @param ip_src IP source
91 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -070092 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -080093 @param tcp_dport TCP destination port
94 @param ip_sport TCP source port
95
96 Generates a simple TCP request. Users
97 shouldn't assume anything about this packet other than that
98 it is a valid ethernet/IP/TCP frame.
99 """
Dan Talayco551befa2010-07-15 17:05:32 -0700100 # Note Dot1Q.id is really CFI
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700101 if (dl_vlan_enable):
102 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -0700103 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700104 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
105 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
106 else:
107 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
108 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
109 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
110
Dan Talayco41eae8b2010-03-10 13:57:06 -0800111 pkt = pkt/("D" * (pktlen - len(pkt)))
112
113 return pkt
114
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700115def simple_icmp_packet(pktlen=60,
116 dl_dst='00:01:02:03:04:05',
117 dl_src='00:06:07:08:09:0a',
118 dl_vlan_enable=False,
119 dl_vlan=0,
120 dl_vlan_pcp=0,
121 ip_src='192.168.0.1',
122 ip_dst='192.168.0.2',
123 ip_tos=0,
124 icmp_type=8,
125 icmp_code=0
126 ):
127 """
128 Return a simple ICMP packet
129
130 Supports a few parameters:
131 @param len Length of packet in bytes w/o CRC
132 @param dl_dst Destinatino MAC
133 @param dl_src Source MAC
134 @param dl_vlan_enable True if the packet is with vlan, False otherwise
135 @param dl_vlan VLAN ID
136 @param dl_vlan_pcp VLAN priority
137 @param ip_src IP source
138 @param ip_dst IP destination
139 @param ip_tos IP ToS
140 @param icmp_type ICMP type
141 @param icmp_code ICMP code
142
143 Generates a simple ICMP ECHO REQUEST. Users
144 shouldn't assume anything about this packet other than that
145 it is a valid ethernet/ICMP frame.
146 """
147 if (dl_vlan_enable):
148 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
149 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
150 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
151 scapy.ICMP(type=icmp_type, code=icmp_code)
152 else:
153 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
154 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
155 scapy.ICMP(type=icmp_type, code=icmp_code)
156
157 pkt = pkt/("0" * (pktlen - len(pkt)))
158
159 return pkt
160
Dan Talayco41eae8b2010-03-10 13:57:06 -0800161def do_barrier(ctrl):
162 b = message.barrier_request()
163 ctrl.transact(b)
Dan Talayco92c99122010-06-03 13:53:18 -0700164
165
166def port_config_get(controller, port_no, logger):
167 """
168 Get a port's configuration
169
170 Gets the switch feature configuration and grabs one port's
171 configuration
172
173 @returns (hwaddr, config, advert) The hwaddress, configuration and
174 advertised values
175 """
176 request = message.features_request()
177 reply, pkt = controller.transact(request, timeout=2)
178 logger.debug(reply.show())
179 if reply is None:
180 logger.warn("Get feature request failed")
181 return None, None, None
182 for idx in range(len(reply.ports)):
183 if reply.ports[idx].port_no == port_no:
184 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
185 reply.ports[idx].advertised)
186
187 logger.warn("Did not find port number for port config")
188 return None, None, None
189
190def port_config_set(controller, port_no, config, mask, logger):
191 """
192 Set the port configuration according the given parameters
193
194 Gets the switch feature configuration and updates one port's
195 configuration value according to config and mask
196 """
197 logger.info("Setting port " + str(port_no) + " to config " + str(config))
198 request = message.features_request()
199 reply, pkt = controller.transact(request, timeout=2)
200 if reply is None:
201 return -1
202 logger.debug(reply.show())
203 for idx in range(len(reply.ports)):
204 if reply.ports[idx].port_no == port_no:
205 break
206 if idx >= len(reply.ports):
207 return -1
208 mod = message.port_mod()
209 mod.port_no = port_no
210 mod.hw_addr = reply.ports[idx].hw_addr
211 mod.config = config
212 mod.mask = mask
213 mod.advertise = reply.ports[idx].advertised
214 rv = controller.message_send(mod)
215 return rv
216
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700217def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger,
218 config):
Dan Talayco92c99122010-06-03 13:53:18 -0700219 """
220 Check for proper receive packets across all ports
221 @param dataplane The dataplane object
222 @param pkt Expected packet; may be None if yes_ports is empty
223 @param yes_ports Set or list of ports that should recieve packet
224 @param no_ports Set or list of ports that should not receive packet
225 @param assert_if Object that implements assertXXX
226 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700227 exp_pkt_arg = None
228 if config and config["relax"]:
229 exp_pkt_arg = pkt
230
Dan Talayco92c99122010-06-03 13:53:18 -0700231 for ofport in yes_ports:
232 logger.debug("Checking for pkt on port " + str(ofport))
233 (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700234 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700235 assert_if.assertTrue(rcv_pkt is not None,
236 "Did not receive pkt on " + str(ofport))
237 assert_if.assertEqual(str(pkt), str(rcv_pkt),
238 "Response packet does not match send packet " +
239 "on port " + str(ofport))
240
241 for ofport in no_ports:
242 logger.debug("Negative check for pkt on port " + str(ofport))
243 (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700244 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700245 assert_if.assertTrue(rcv_pkt is None,
246 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700247
248
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700249def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700250 """
251 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700252 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700253
254 parent must implement dataplane, assertTrue and assertEqual
255 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700256 exp_pkt_arg = None
257 if parent.config["relax"]:
258 exp_pkt_arg = exp_pkt
259
Dan Talaycof6e76c02012-03-23 10:56:12 -0700260 if type(egr_ports) == type([]):
261 egr_port_list = egr_ports
262 else:
263 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700264
Dan Talaycof6e76c02012-03-23 10:56:12 -0700265 # Expect a packet from each port on egr port list
266 for egr_port in egr_port_list:
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700267 if egr_port == ofp.OFPP_IN_PORT:
268 egr_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700269 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
270 port_number=egr_port, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700271
Dan Talaycof6e76c02012-03-23 10:56:12 -0700272 if rcv_pkt is None:
273 parent.logger.error("ERROR: No packet received from " + str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700274
Dan Talaycof6e76c02012-03-23 10:56:12 -0700275 parent.assertTrue(rcv_pkt is not None,
276 "Did not receive packet port " + str(egr_port))
277 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
278 str(rcv_port))
279
280 if str(exp_pkt) != str(rcv_pkt):
281 parent.logger.error("ERROR: Packet match failed.")
282 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
283 + str(exp_pkt).encode('hex'))
284 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
285 + str(rcv_pkt).encode('hex'))
286 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
287 "Packet match error on port " + str(egr_port))
288
Dan Talayco551befa2010-07-15 17:05:32 -0700289def match_verify(parent, req_match, res_match):
290 """
291 Verify flow matches agree; if they disagree, report where
292
293 parent must implement assertEqual
294 Use str() to ensure content is compared and not pointers
295 """
296
297 parent.assertEqual(req_match.wildcards, res_match.wildcards,
298 'Match failed: wildcards: ' + hex(req_match.wildcards) +
299 " != " + hex(res_match.wildcards))
300 parent.assertEqual(req_match.in_port, res_match.in_port,
301 'Match failed: in_port: ' + str(req_match.in_port) +
302 " != " + str(res_match.in_port))
303 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
304 'Match failed: dl_src: ' + str(req_match.dl_src) +
305 " != " + str(res_match.dl_src))
306 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
307 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
308 " != " + str(res_match.dl_dst))
309 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
310 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
311 " != " + str(res_match.dl_vlan))
312 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
313 'Match failed: dl_vlan_pcp: ' +
314 str(req_match.dl_vlan_pcp) + " != " +
315 str(res_match.dl_vlan_pcp))
316 parent.assertEqual(req_match.dl_type, res_match.dl_type,
317 'Match failed: dl_type: ' + str(req_match.dl_type) +
318 " != " + str(res_match.dl_type))
319
320 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
321 and (req_match.dl_type == IP_ETHERTYPE)):
322 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
323 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
324 " != " + str(res_match.nw_tos))
325 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
326 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
327 " != " + str(res_match.nw_proto))
328 parent.assertEqual(req_match.nw_src, res_match.nw_src,
329 'Match failed: nw_src: ' + str(req_match.nw_src) +
330 " != " + str(res_match.nw_src))
331 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
332 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
333 " != " + str(res_match.nw_dst))
334
335 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
336 and ((req_match.nw_proto == TCP_PROTOCOL)
337 or (req_match.nw_proto == UDP_PROTOCOL))):
338 parent.assertEqual(req_match.tp_src, res_match.tp_src,
339 'Match failed: tp_src: ' +
340 str(req_match.tp_src) +
341 " != " + str(res_match.tp_src))
342 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
343 'Match failed: tp_dst: ' +
344 str(req_match.tp_dst) +
345 " != " + str(res_match.tp_dst))
346
347def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
348 """
349 Receive a flow removed msg and verify it matches expected
350
351 @params parent Must implement controller, assertEqual
352 @param pkt_count If >= 0, verify packet count
353 @param byte_count If >= 0, verify byte count
354 """
355 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
356 parent.assertTrue(response is not None, 'No flow removed message received')
357
358 if request is None:
359 return
360
361 parent.assertEqual(request.cookie, response.cookie,
362 "Flow removed cookie error: " +
363 hex(request.cookie) + " != " + hex(response.cookie))
364
365 req_match = request.match
366 res_match = response.match
367 verifyMatchField(req_match, res_match)
368
369 if (req_match.wildcards != 0):
370 parent.assertEqual(request.priority, response.priority,
371 'Flow remove prio mismatch: ' +
372 str(request,priority) + " != " +
373 str(response.priority))
374 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
375 'Flow remove reason is not HARD TIMEOUT:' +
376 str(response.reason))
377 if pkt_count >= 0:
378 parent.assertEqual(response.packet_count, pkt_count,
379 'Flow removed failed, packet count: ' +
380 str(response.packet_count) + " != " +
381 str(pkt_count))
382 if byte_count >= 0:
383 parent.assertEqual(response.byte_count, byte_count,
384 'Flow removed failed, byte count: ' +
385 str(response.byte_count) + " != " +
386 str(byte_count))
387
388def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700389 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700390 """
391 Create a flow message
392
393 Match on packet with given wildcards.
394 See flow_match_test for other parameter descriptoins
395 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700396 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700397 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700398 """
399 match = parse.packet_to_flow_match(pkt)
400 parent.assertTrue(match is not None, "Flow match from pkt failed")
Dan Talayco677c0b72011-08-23 22:53:38 -0700401 if in_band:
402 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700403 match.wildcards = wildcards
404 match.in_port = ing_port
405
Dan Talaycof6e76c02012-03-23 10:56:12 -0700406 if type(egr_ports) == type([]):
407 egr_port_list = egr_ports
408 else:
409 egr_port_list = [egr_ports]
410
Dan Talayco551befa2010-07-15 17:05:32 -0700411 request = message.flow_mod()
412 request.match = match
413 request.buffer_id = 0xffffffff
414 if check_expire:
415 request.flags |= ofp.OFPFF_SEND_FLOW_REM
416 request.hard_timeout = 1
417
418 if action_list is not None:
419 for act in action_list:
420 parent.logger.debug("Adding action " + act.show())
421 rv = request.actions.add(act)
422 parent.assertTrue(rv, "Could not add action" + act.show())
423
424 # Set up output/enqueue action if directed
425 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700426 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700427 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700428 for egr_port in egr_port_list:
429 act.port = egr_port
430 act.queue_id = egr_queue
431 rv = request.actions.add(act)
432 parent.assertTrue(rv, "Could not add enqueue action " +
433 str(egr_port) + " Q: " + str(egr_queue))
434 elif egr_ports is not None:
435 for egr_port in egr_port_list:
436 act = action.action_output()
437 act.port = egr_port
438 rv = request.actions.add(act)
439 parent.assertTrue(rv, "Could not add output action " +
440 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700441
442 parent.logger.debug(request.show())
443
444 return request
445
446def flow_msg_install(parent, request, clear_table=True):
447 """
448 Install a flow mod message in the switch
449
450 @param parent Must implement controller, assertEqual, assertTrue
451 @param request The request, all set to go
452 @param clear_table If true, clear the flow table before installing
453 """
454 if clear_table:
455 parent.logger.debug("Clear flow table")
456 rc = delete_all_flows(parent.controller, parent.logger)
457 parent.assertEqual(rc, 0, "Failed to delete all flows")
458 do_barrier(parent.controller)
459
460 parent.logger.debug("Insert flow")
461 rv = parent.controller.message_send(request)
462 parent.assertTrue(rv != -1, "Error installing flow mod")
463 do_barrier(parent.controller)
464
Dan Talaycof6e76c02012-03-23 10:56:12 -0700465def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=0,
Dan Talayco551befa2010-07-15 17:05:32 -0700466 dl_vlan=-1, pkt=None, exp_pkt=None,
467 action_list=None, check_expire=False):
468 """
469 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700470 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700471
472 Run test with packet through switch from ing_port to egr_port
473 See flow_match_test for parameter descriptions
474 """
475
Dan Talaycof6e76c02012-03-23 10:56:12 -0700476 parent.logger.info("Pkt match test: " + str(ing_port) + " to " +
477 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700478 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700479 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700480 if pkt is None:
481 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
482
483 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700484 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700485 action_list=action_list)
486
487 flow_msg_install(parent, request)
488
Dan Talaycof6e76c02012-03-23 10:56:12 -0700489 parent.logger.debug("Send packet: " + str(ing_port) + " to " +
490 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700491 parent.dataplane.send(ing_port, str(pkt))
492
493 if exp_pkt is None:
494 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700495 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700496
497 if check_expire:
498 #@todo Not all HW supports both pkt and byte counters
499 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
500
Dan Talaycof6e76c02012-03-23 10:56:12 -0700501def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
502 """
503 Generate a list of ports avoiding those in the exclude list
504 @param parent Supplies logger
505 @param of_ports List of OF port numbers
506 @param how_many Number of ports to be added to the list
507 @param exclude_list List of ports not to be used
508 @returns An empty list if unable to find enough ports
509 """
510
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700511 if how_many == 0:
512 return []
513
Dan Talaycof6e76c02012-03-23 10:56:12 -0700514 count = 0
515 egr_ports = []
516 for egr_idx in range(len(of_ports)):
517 if of_ports[egr_idx] not in exclude_list:
518 egr_ports.append(of_ports[egr_idx])
519 count += 1
520 if count >= how_many:
521 return egr_ports
522 parent.logger.debug("Could not generate enough egress ports for test")
523 return []
524
Dan Talayco551befa2010-07-15 17:05:32 -0700525def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
526 exp_pkt=None, action_list=None, check_expire=False,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700527 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700528 """
529 Run flow_match_test_port_pair on all port pairs
530
531 @param max_test If > 0 no more than this number of tests are executed.
532 @param parent Must implement controller, dataplane, assertTrue, assertEqual
533 and logger
534 @param pkt If not None, use this packet for ingress
535 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700536 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700537 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
538 @param action_list Additional actions to add to flow mod
539 @param check_expire Check for flow expiration message
Dan Talaycocfa172f2012-03-23 12:03:00 -0700540 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700541 """
542 of_ports = port_map.keys()
543 of_ports.sort()
544 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
545 test_count = 0
546
Dan Talaycocfa172f2012-03-23 12:03:00 -0700547 if egr_count == -1:
548 egr_count = test_param_get(parent.config, 'egr_count', default=2)
549
Dan Talayco551befa2010-07-15 17:05:32 -0700550 for ing_idx in range(len(of_ports)):
551 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700552 egr_ports = get_egr_list(parent, of_ports, egr_count,
553 exclude_list=[ingress_port])
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700554 if ing_port:
555 egr_ports.append(ofp.OFPP_IN_PORT)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700556 if len(egr_ports) == 0:
557 parent.assertTrue(0, "Failed to generate egress port list")
558
559 flow_match_test_port_pair(parent, ingress_port, egr_ports,
560 wildcards=wildcards, dl_vlan=dl_vlan,
561 pkt=pkt, exp_pkt=exp_pkt,
562 action_list=action_list,
563 check_expire=check_expire)
564 test_count += 1
565 if (max_test > 0) and (test_count > max_test):
566 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
567 return
Dan Talayco551befa2010-07-15 17:05:32 -0700568
Dan Talayco4b2bee62010-07-20 14:10:05 -0700569def test_param_get(config, key, default=None):
570 """
571 Return value passed via test-params if present
572
573 @param config The configuration structure for OFTest
574 @param key The lookup key
575 @param default Default value to use if not found
576
577 If the pair 'key=val' appeared in the string passed to --test-params
578 on the command line, return val (as interpreted by exec). Otherwise
579 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700580
581 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
582 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700583 """
584 try:
585 exec config["test_params"]
586 except:
587 return default
588
589 s = "val = " + str(key)
590 try:
591 exec s
592 return val
593 except:
594 return default
595
596def action_generate(parent, field_to_mod, mod_field_vals):
597 """
598 Create an action to modify the field indicated in field_to_mod
599
600 @param parent Must implement, assertTrue
601 @param field_to_mod The field to modify as a string name
602 @param mod_field_vals Hash of values to use for modified values
603 """
604
605 act = None
606
607 if field_to_mod in ['pktlen']:
608 return None
609
610 if field_to_mod == 'dl_dst':
611 act = action.action_set_dl_dst()
612 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
613 elif field_to_mod == 'dl_src':
614 act = action.action_set_dl_src()
615 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
616 elif field_to_mod == 'dl_vlan_enable':
617 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
618 act = action.action_strip_vlan()
619 # Add VLAN tag is handled by dl_vlan field
620 # Will return None in this case
621 elif field_to_mod == 'dl_vlan':
622 act = action.action_set_vlan_vid()
623 act.vlan_vid = mod_field_vals['dl_vlan']
624 elif field_to_mod == 'dl_vlan_pcp':
625 act = action.action_set_vlan_pcp()
626 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
627 elif field_to_mod == 'ip_src':
628 act = action.action_set_nw_src()
629 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
630 elif field_to_mod == 'ip_dst':
631 act = action.action_set_nw_dst()
632 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
633 elif field_to_mod == 'ip_tos':
634 act = action.action_set_nw_tos()
635 act.nw_tos = mod_field_vals['ip_tos']
636 elif field_to_mod == 'tcp_sport':
637 act = action.action_set_tp_src()
638 act.tp_port = mod_field_vals['tcp_sport']
639 elif field_to_mod == 'tcp_dport':
640 act = action.action_set_tp_dst()
641 act.tp_port = mod_field_vals['tcp_dport']
642 else:
643 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
644
645 return act
646
647def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
648 mod_fields={}, check_test_params=False):
649 """
650 Set up the ingress and expected packet and action list for a test
651
652 @param parent Must implement, assertTrue, config hash and logger
653 @param start_field_values Field values to use for ingress packet (optional)
654 @param mod_field_values Field values to use for modified packet (optional)
655 @param mod_fields The list of fields to be modified by the switch in the test.
656 @params check_test_params If True, will check the parameters vid, add_vlan
657 and strip_vlan from the command line.
658
659 Returns a triple: pkt-to-send, expected-pkt, action-list
660 """
661
662 new_actions = []
663
Dan Talayco4b2bee62010-07-20 14:10:05 -0700664 base_pkt_params = {}
665 base_pkt_params['pktlen'] = 100
666 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
667 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
668 base_pkt_params['dl_vlan_enable'] = False
669 base_pkt_params['dl_vlan'] = 2
670 base_pkt_params['dl_vlan_pcp'] = 0
671 base_pkt_params['ip_src'] = '192.168.0.1'
672 base_pkt_params['ip_dst'] = '192.168.0.2'
673 base_pkt_params['ip_tos'] = 0
674 base_pkt_params['tcp_sport'] = 1234
675 base_pkt_params['tcp_dport'] = 80
676 for keyname in start_field_vals.keys():
677 base_pkt_params[keyname] = start_field_vals[keyname]
678
679 mod_pkt_params = {}
680 mod_pkt_params['pktlen'] = 100
681 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
682 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
683 mod_pkt_params['dl_vlan_enable'] = False
684 mod_pkt_params['dl_vlan'] = 3
685 mod_pkt_params['dl_vlan_pcp'] = 7
686 mod_pkt_params['ip_src'] = '10.20.30.40'
687 mod_pkt_params['ip_dst'] = '50.60.70.80'
688 mod_pkt_params['ip_tos'] = 0xf0
689 mod_pkt_params['tcp_sport'] = 4321
690 mod_pkt_params['tcp_dport'] = 8765
691 for keyname in mod_field_vals.keys():
692 mod_pkt_params[keyname] = mod_field_vals[keyname]
693
694 # Check for test param modifications
695 strip = False
696 if check_test_params:
697 add_vlan = test_param_get(parent.config, 'add_vlan')
698 strip_vlan = test_param_get(parent.config, 'strip_vlan')
699 vid = test_param_get(parent.config, 'vid')
700
701 if add_vlan and strip_vlan:
702 parent.assertTrue(0, "Add and strip VLAN both specified")
703
704 if vid:
705 base_pkt_params['dl_vlan_enable'] = True
706 base_pkt_params['dl_vlan'] = vid
707 if 'dl_vlan' in mod_fields:
708 mod_pkt_params['dl_vlan'] = vid + 1
709
710 if add_vlan:
711 base_pkt_params['dl_vlan_enable'] = False
712 mod_pkt_params['dl_vlan_enable'] = True
713 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
714 mod_fields.append('pktlen')
715 mod_fields.append('dl_vlan_enable')
716 if 'dl_vlan' not in mod_fields:
717 mod_fields.append('dl_vlan')
718 elif strip_vlan:
719 base_pkt_params['dl_vlan_enable'] = True
720 mod_pkt_params['dl_vlan_enable'] = False
721 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
722 mod_fields.append('dl_vlan_enable')
723 mod_fields.append('pktlen')
724
725 # Build the ingress packet
726 ingress_pkt = simple_tcp_packet(**base_pkt_params)
727
728 # Build the expected packet, modifying the indicated fields
729 for item in mod_fields:
730 base_pkt_params[item] = mod_pkt_params[item]
731 act = action_generate(parent, item, mod_pkt_params)
732 if act:
733 new_actions.append(act)
734
735 expected_pkt = simple_tcp_packet(**base_pkt_params)
736
737 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700738
739# Generate a simple "drop" flow mod
740# If in_band is true, then only drop from first test port
741def flow_mod_gen(port_map, in_band):
742 request = message.flow_mod()
743 request.match.wildcards = ofp.OFPFW_ALL
744 if in_band:
745 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
746 for of_port, ifname in port_map.items(): # Grab first port
747 break
748 request.match.in_port = of_port
749 request.buffer_id = 0xffffffff
750 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700751
752def skip_message_emit(parent, s):
753 """
754 Print out a 'skipped' message to stderr
755
756 @param s The string to print out to the log file
757 @param parent Must implement config and logger objects
758 """
759 global skipped_test_count
760
761 skipped_test_count += 1
762 parent.logger.info("Skipping: " + s)
763 if parent.config["dbg_level"] < logging.WARNING:
764 sys.stderr.write("(skipped) ")
765 else:
766 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700767