blob: c7721fd2954d41c9996801f7d3127ccb8c76629a [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
249def receive_pkt_verify(parent, egr_port, exp_pkt):
250 """
251 Receive a packet and verify it matches an expected value
252
253 parent must implement dataplane, assertTrue and assertEqual
254 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700255 exp_pkt_arg = None
256 if parent.config["relax"]:
257 exp_pkt_arg = exp_pkt
258
Dan Talayco551befa2010-07-15 17:05:32 -0700259 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(port_number=egr_port,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700260 timeout=1,
261 exp_pkt=exp_pkt_arg)
262
Dan Talayco551befa2010-07-15 17:05:32 -0700263 if rcv_pkt is None:
264 parent.logger.error("ERROR: No packet received from " + str(egr_port))
265
266 parent.assertTrue(rcv_pkt is not None,
267 "Did not receive packet port " + str(egr_port))
268 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
269 str(rcv_port))
270
271 if str(exp_pkt) != str(rcv_pkt):
272 parent.logger.error("ERROR: Packet match failed.")
273 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
274 + str(exp_pkt).encode('hex'))
275 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
276 + str(rcv_pkt).encode('hex'))
277 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
278 "Packet match error on port " + str(egr_port))
279
280def match_verify(parent, req_match, res_match):
281 """
282 Verify flow matches agree; if they disagree, report where
283
284 parent must implement assertEqual
285 Use str() to ensure content is compared and not pointers
286 """
287
288 parent.assertEqual(req_match.wildcards, res_match.wildcards,
289 'Match failed: wildcards: ' + hex(req_match.wildcards) +
290 " != " + hex(res_match.wildcards))
291 parent.assertEqual(req_match.in_port, res_match.in_port,
292 'Match failed: in_port: ' + str(req_match.in_port) +
293 " != " + str(res_match.in_port))
294 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
295 'Match failed: dl_src: ' + str(req_match.dl_src) +
296 " != " + str(res_match.dl_src))
297 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
298 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
299 " != " + str(res_match.dl_dst))
300 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
301 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
302 " != " + str(res_match.dl_vlan))
303 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
304 'Match failed: dl_vlan_pcp: ' +
305 str(req_match.dl_vlan_pcp) + " != " +
306 str(res_match.dl_vlan_pcp))
307 parent.assertEqual(req_match.dl_type, res_match.dl_type,
308 'Match failed: dl_type: ' + str(req_match.dl_type) +
309 " != " + str(res_match.dl_type))
310
311 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
312 and (req_match.dl_type == IP_ETHERTYPE)):
313 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
314 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
315 " != " + str(res_match.nw_tos))
316 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
317 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
318 " != " + str(res_match.nw_proto))
319 parent.assertEqual(req_match.nw_src, res_match.nw_src,
320 'Match failed: nw_src: ' + str(req_match.nw_src) +
321 " != " + str(res_match.nw_src))
322 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
323 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
324 " != " + str(res_match.nw_dst))
325
326 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
327 and ((req_match.nw_proto == TCP_PROTOCOL)
328 or (req_match.nw_proto == UDP_PROTOCOL))):
329 parent.assertEqual(req_match.tp_src, res_match.tp_src,
330 'Match failed: tp_src: ' +
331 str(req_match.tp_src) +
332 " != " + str(res_match.tp_src))
333 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
334 'Match failed: tp_dst: ' +
335 str(req_match.tp_dst) +
336 " != " + str(res_match.tp_dst))
337
338def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
339 """
340 Receive a flow removed msg and verify it matches expected
341
342 @params parent Must implement controller, assertEqual
343 @param pkt_count If >= 0, verify packet count
344 @param byte_count If >= 0, verify byte count
345 """
346 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
347 parent.assertTrue(response is not None, 'No flow removed message received')
348
349 if request is None:
350 return
351
352 parent.assertEqual(request.cookie, response.cookie,
353 "Flow removed cookie error: " +
354 hex(request.cookie) + " != " + hex(response.cookie))
355
356 req_match = request.match
357 res_match = response.match
358 verifyMatchField(req_match, res_match)
359
360 if (req_match.wildcards != 0):
361 parent.assertEqual(request.priority, response.priority,
362 'Flow remove prio mismatch: ' +
363 str(request,priority) + " != " +
364 str(response.priority))
365 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
366 'Flow remove reason is not HARD TIMEOUT:' +
367 str(response.reason))
368 if pkt_count >= 0:
369 parent.assertEqual(response.packet_count, pkt_count,
370 'Flow removed failed, packet count: ' +
371 str(response.packet_count) + " != " +
372 str(pkt_count))
373 if byte_count >= 0:
374 parent.assertEqual(response.byte_count, byte_count,
375 'Flow removed failed, byte count: ' +
376 str(response.byte_count) + " != " +
377 str(byte_count))
378
379def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
Dan Talayco677c0b72011-08-23 22:53:38 -0700380 egr_port=None, egr_queue=None, check_expire=False, in_band=True):
Dan Talayco551befa2010-07-15 17:05:32 -0700381 """
382 Create a flow message
383
384 Match on packet with given wildcards.
385 See flow_match_test for other parameter descriptoins
386 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700387 @param in_band if True, do not wildcard ingress port
Dan Talayco551befa2010-07-15 17:05:32 -0700388 """
389 match = parse.packet_to_flow_match(pkt)
390 parent.assertTrue(match is not None, "Flow match from pkt failed")
Dan Talayco677c0b72011-08-23 22:53:38 -0700391 if in_band:
392 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700393 match.wildcards = wildcards
394 match.in_port = ing_port
395
396 request = message.flow_mod()
397 request.match = match
398 request.buffer_id = 0xffffffff
399 if check_expire:
400 request.flags |= ofp.OFPFF_SEND_FLOW_REM
401 request.hard_timeout = 1
402
403 if action_list is not None:
404 for act in action_list:
405 parent.logger.debug("Adding action " + act.show())
406 rv = request.actions.add(act)
407 parent.assertTrue(rv, "Could not add action" + act.show())
408
409 # Set up output/enqueue action if directed
410 if egr_queue is not None:
411 parent.assertTrue(egr_port is not None, "Egress port not set")
412 act = action.action_enqueue()
413 act.port = egr_port
414 act.queue_id = egr_queue
415 rv = request.actions.add(act)
416 parent.assertTrue(rv, "Could not add enqueue action " +
417 str(egr_port) + " Q: " + str(egr_queue))
418 elif egr_port is not None:
419 act = action.action_output()
420 act.port = egr_port
421 rv = request.actions.add(act)
422 parent.assertTrue(rv, "Could not add output action " + str(egr_port))
423
424 parent.logger.debug(request.show())
425
426 return request
427
428def flow_msg_install(parent, request, clear_table=True):
429 """
430 Install a flow mod message in the switch
431
432 @param parent Must implement controller, assertEqual, assertTrue
433 @param request The request, all set to go
434 @param clear_table If true, clear the flow table before installing
435 """
436 if clear_table:
437 parent.logger.debug("Clear flow table")
438 rc = delete_all_flows(parent.controller, parent.logger)
439 parent.assertEqual(rc, 0, "Failed to delete all flows")
440 do_barrier(parent.controller)
441
442 parent.logger.debug("Insert flow")
443 rv = parent.controller.message_send(request)
444 parent.assertTrue(rv != -1, "Error installing flow mod")
445 do_barrier(parent.controller)
446
447def flow_match_test_port_pair(parent, ing_port, egr_port, wildcards=0,
448 dl_vlan=-1, pkt=None, exp_pkt=None,
449 action_list=None, check_expire=False):
450 """
451 Flow match test on single TCP packet
452
453 Run test with packet through switch from ing_port to egr_port
454 See flow_match_test for parameter descriptions
455 """
456
457 parent.logger.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
458 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700459 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700460 if pkt is None:
461 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
462
463 request = flow_msg_create(parent, pkt, ing_port=ing_port,
464 wildcards=wildcards, egr_port=egr_port,
465 action_list=action_list)
466
467 flow_msg_install(parent, request)
468
469 parent.logger.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
470 parent.dataplane.send(ing_port, str(pkt))
471
472 if exp_pkt is None:
473 exp_pkt = pkt
474 receive_pkt_verify(parent, egr_port, exp_pkt)
475
476 if check_expire:
477 #@todo Not all HW supports both pkt and byte counters
478 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
479
480def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
481 exp_pkt=None, action_list=None, check_expire=False,
482 max_test=0):
483 """
484 Run flow_match_test_port_pair on all port pairs
485
486 @param max_test If > 0 no more than this number of tests are executed.
487 @param parent Must implement controller, dataplane, assertTrue, assertEqual
488 and logger
489 @param pkt If not None, use this packet for ingress
490 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700491 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700492 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
493 @param action_list Additional actions to add to flow mod
494 @param check_expire Check for flow expiration message
495 """
496 of_ports = port_map.keys()
497 of_ports.sort()
498 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
499 test_count = 0
500
501 for ing_idx in range(len(of_ports)):
502 ingress_port = of_ports[ing_idx]
503 for egr_idx in range(len(of_ports)):
504 if egr_idx == ing_idx:
505 continue
506 egress_port = of_ports[egr_idx]
507 flow_match_test_port_pair(parent, ingress_port, egress_port,
Dan Talayco98fada92010-07-17 00:36:21 -0700508 wildcards=wildcards, dl_vlan=dl_vlan,
509 pkt=pkt, exp_pkt=exp_pkt,
510 action_list=action_list,
Dan Talayco551befa2010-07-15 17:05:32 -0700511 check_expire=check_expire)
512 test_count += 1
513 if (max_test > 0) and (test_count > max_test):
514 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
515 return
516
Dan Talayco4b2bee62010-07-20 14:10:05 -0700517def test_param_get(config, key, default=None):
518 """
519 Return value passed via test-params if present
520
521 @param config The configuration structure for OFTest
522 @param key The lookup key
523 @param default Default value to use if not found
524
525 If the pair 'key=val' appeared in the string passed to --test-params
526 on the command line, return val (as interpreted by exec). Otherwise
527 return default value.
528 """
529 try:
530 exec config["test_params"]
531 except:
532 return default
533
534 s = "val = " + str(key)
535 try:
536 exec s
537 return val
538 except:
539 return default
540
541def action_generate(parent, field_to_mod, mod_field_vals):
542 """
543 Create an action to modify the field indicated in field_to_mod
544
545 @param parent Must implement, assertTrue
546 @param field_to_mod The field to modify as a string name
547 @param mod_field_vals Hash of values to use for modified values
548 """
549
550 act = None
551
552 if field_to_mod in ['pktlen']:
553 return None
554
555 if field_to_mod == 'dl_dst':
556 act = action.action_set_dl_dst()
557 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
558 elif field_to_mod == 'dl_src':
559 act = action.action_set_dl_src()
560 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
561 elif field_to_mod == 'dl_vlan_enable':
562 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
563 act = action.action_strip_vlan()
564 # Add VLAN tag is handled by dl_vlan field
565 # Will return None in this case
566 elif field_to_mod == 'dl_vlan':
567 act = action.action_set_vlan_vid()
568 act.vlan_vid = mod_field_vals['dl_vlan']
569 elif field_to_mod == 'dl_vlan_pcp':
570 act = action.action_set_vlan_pcp()
571 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
572 elif field_to_mod == 'ip_src':
573 act = action.action_set_nw_src()
574 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
575 elif field_to_mod == 'ip_dst':
576 act = action.action_set_nw_dst()
577 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
578 elif field_to_mod == 'ip_tos':
579 act = action.action_set_nw_tos()
580 act.nw_tos = mod_field_vals['ip_tos']
581 elif field_to_mod == 'tcp_sport':
582 act = action.action_set_tp_src()
583 act.tp_port = mod_field_vals['tcp_sport']
584 elif field_to_mod == 'tcp_dport':
585 act = action.action_set_tp_dst()
586 act.tp_port = mod_field_vals['tcp_dport']
587 else:
588 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
589
590 return act
591
592def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
593 mod_fields={}, check_test_params=False):
594 """
595 Set up the ingress and expected packet and action list for a test
596
597 @param parent Must implement, assertTrue, config hash and logger
598 @param start_field_values Field values to use for ingress packet (optional)
599 @param mod_field_values Field values to use for modified packet (optional)
600 @param mod_fields The list of fields to be modified by the switch in the test.
601 @params check_test_params If True, will check the parameters vid, add_vlan
602 and strip_vlan from the command line.
603
604 Returns a triple: pkt-to-send, expected-pkt, action-list
605 """
606
607 new_actions = []
608
Dan Talayco4b2bee62010-07-20 14:10:05 -0700609 base_pkt_params = {}
610 base_pkt_params['pktlen'] = 100
611 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
612 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
613 base_pkt_params['dl_vlan_enable'] = False
614 base_pkt_params['dl_vlan'] = 2
615 base_pkt_params['dl_vlan_pcp'] = 0
616 base_pkt_params['ip_src'] = '192.168.0.1'
617 base_pkt_params['ip_dst'] = '192.168.0.2'
618 base_pkt_params['ip_tos'] = 0
619 base_pkt_params['tcp_sport'] = 1234
620 base_pkt_params['tcp_dport'] = 80
621 for keyname in start_field_vals.keys():
622 base_pkt_params[keyname] = start_field_vals[keyname]
623
624 mod_pkt_params = {}
625 mod_pkt_params['pktlen'] = 100
626 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
627 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
628 mod_pkt_params['dl_vlan_enable'] = False
629 mod_pkt_params['dl_vlan'] = 3
630 mod_pkt_params['dl_vlan_pcp'] = 7
631 mod_pkt_params['ip_src'] = '10.20.30.40'
632 mod_pkt_params['ip_dst'] = '50.60.70.80'
633 mod_pkt_params['ip_tos'] = 0xf0
634 mod_pkt_params['tcp_sport'] = 4321
635 mod_pkt_params['tcp_dport'] = 8765
636 for keyname in mod_field_vals.keys():
637 mod_pkt_params[keyname] = mod_field_vals[keyname]
638
639 # Check for test param modifications
640 strip = False
641 if check_test_params:
642 add_vlan = test_param_get(parent.config, 'add_vlan')
643 strip_vlan = test_param_get(parent.config, 'strip_vlan')
644 vid = test_param_get(parent.config, 'vid')
645
646 if add_vlan and strip_vlan:
647 parent.assertTrue(0, "Add and strip VLAN both specified")
648
649 if vid:
650 base_pkt_params['dl_vlan_enable'] = True
651 base_pkt_params['dl_vlan'] = vid
652 if 'dl_vlan' in mod_fields:
653 mod_pkt_params['dl_vlan'] = vid + 1
654
655 if add_vlan:
656 base_pkt_params['dl_vlan_enable'] = False
657 mod_pkt_params['dl_vlan_enable'] = True
658 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
659 mod_fields.append('pktlen')
660 mod_fields.append('dl_vlan_enable')
661 if 'dl_vlan' not in mod_fields:
662 mod_fields.append('dl_vlan')
663 elif strip_vlan:
664 base_pkt_params['dl_vlan_enable'] = True
665 mod_pkt_params['dl_vlan_enable'] = False
666 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
667 mod_fields.append('dl_vlan_enable')
668 mod_fields.append('pktlen')
669
670 # Build the ingress packet
671 ingress_pkt = simple_tcp_packet(**base_pkt_params)
672
673 # Build the expected packet, modifying the indicated fields
674 for item in mod_fields:
675 base_pkt_params[item] = mod_pkt_params[item]
676 act = action_generate(parent, item, mod_pkt_params)
677 if act:
678 new_actions.append(act)
679
680 expected_pkt = simple_tcp_packet(**base_pkt_params)
681
682 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700683
684# Generate a simple "drop" flow mod
685# If in_band is true, then only drop from first test port
686def flow_mod_gen(port_map, in_band):
687 request = message.flow_mod()
688 request.match.wildcards = ofp.OFPFW_ALL
689 if in_band:
690 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
691 for of_port, ifname in port_map.items(): # Grab first port
692 break
693 request.match.in_port = of_port
694 request.buffer_id = 0xffffffff
695 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700696
697def skip_message_emit(parent, s):
698 """
699 Print out a 'skipped' message to stderr
700
701 @param s The string to print out to the log file
702 @param parent Must implement config and logger objects
703 """
704 global skipped_test_count
705
706 skipped_test_count += 1
707 parent.logger.info("Skipping: " + s)
708 if parent.config["dbg_level"] < logging.WARNING:
709 sys.stderr.write("(skipped) ")
710 else:
711 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700712