blob: c6252bc0ecf1436e6b5bb20e94243f22b2ee3299 [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
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700161def simple_eth_packet(pktlen=60,
162 dl_dst='00:01:02:03:04:05',
163 dl_src='01:80:c2:00:00:00',
164 dl_type=0x88cc):
165 pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
166
167 pkt = pkt/("0" * (pktlen - len(pkt)))
168
169 return pkt
170
Dan Talayco41eae8b2010-03-10 13:57:06 -0800171def do_barrier(ctrl):
172 b = message.barrier_request()
173 ctrl.transact(b)
Dan Talayco92c99122010-06-03 13:53:18 -0700174
175
176def port_config_get(controller, port_no, logger):
177 """
178 Get a port's configuration
179
180 Gets the switch feature configuration and grabs one port's
181 configuration
182
183 @returns (hwaddr, config, advert) The hwaddress, configuration and
184 advertised values
185 """
186 request = message.features_request()
187 reply, pkt = controller.transact(request, timeout=2)
188 logger.debug(reply.show())
189 if reply is None:
190 logger.warn("Get feature request failed")
191 return None, None, None
192 for idx in range(len(reply.ports)):
193 if reply.ports[idx].port_no == port_no:
194 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
195 reply.ports[idx].advertised)
196
197 logger.warn("Did not find port number for port config")
198 return None, None, None
199
200def port_config_set(controller, port_no, config, mask, logger):
201 """
202 Set the port configuration according the given parameters
203
204 Gets the switch feature configuration and updates one port's
205 configuration value according to config and mask
206 """
207 logger.info("Setting port " + str(port_no) + " to config " + str(config))
208 request = message.features_request()
209 reply, pkt = controller.transact(request, timeout=2)
210 if reply is None:
211 return -1
212 logger.debug(reply.show())
213 for idx in range(len(reply.ports)):
214 if reply.ports[idx].port_no == port_no:
215 break
216 if idx >= len(reply.ports):
217 return -1
218 mod = message.port_mod()
219 mod.port_no = port_no
220 mod.hw_addr = reply.ports[idx].hw_addr
221 mod.config = config
222 mod.mask = mask
223 mod.advertise = reply.ports[idx].advertised
224 rv = controller.message_send(mod)
225 return rv
226
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700227def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger,
228 config):
Dan Talayco92c99122010-06-03 13:53:18 -0700229 """
230 Check for proper receive packets across all ports
231 @param dataplane The dataplane object
232 @param pkt Expected packet; may be None if yes_ports is empty
233 @param yes_ports Set or list of ports that should recieve packet
234 @param no_ports Set or list of ports that should not receive packet
235 @param assert_if Object that implements assertXXX
236 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700237 exp_pkt_arg = None
238 if config and config["relax"]:
239 exp_pkt_arg = pkt
240
Dan Talayco92c99122010-06-03 13:53:18 -0700241 for ofport in yes_ports:
242 logger.debug("Checking 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 not None,
246 "Did not receive pkt on " + str(ofport))
247 assert_if.assertEqual(str(pkt), str(rcv_pkt),
248 "Response packet does not match send packet " +
249 "on port " + str(ofport))
250
251 for ofport in no_ports:
252 logger.debug("Negative check for pkt on port " + str(ofport))
253 (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700254 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700255 assert_if.assertTrue(rcv_pkt is None,
256 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700257
258
259def receive_pkt_verify(parent, egr_port, exp_pkt):
260 """
261 Receive a packet and verify it matches an expected value
262
263 parent must implement dataplane, assertTrue and assertEqual
264 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700265 exp_pkt_arg = None
266 if parent.config["relax"]:
267 exp_pkt_arg = exp_pkt
268
Dan Talayco551befa2010-07-15 17:05:32 -0700269 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(port_number=egr_port,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700270 timeout=1,
271 exp_pkt=exp_pkt_arg)
272
Dan Talayco551befa2010-07-15 17:05:32 -0700273 if rcv_pkt is None:
274 parent.logger.error("ERROR: No packet received from " + str(egr_port))
275
276 parent.assertTrue(rcv_pkt is not None,
277 "Did not receive packet port " + str(egr_port))
278 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
279 str(rcv_port))
280
281 if str(exp_pkt) != str(rcv_pkt):
282 parent.logger.error("ERROR: Packet match failed.")
283 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
284 + str(exp_pkt).encode('hex'))
285 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
286 + str(rcv_pkt).encode('hex'))
287 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
288 "Packet match error on port " + str(egr_port))
289
290def match_verify(parent, req_match, res_match):
291 """
292 Verify flow matches agree; if they disagree, report where
293
294 parent must implement assertEqual
295 Use str() to ensure content is compared and not pointers
296 """
297
298 parent.assertEqual(req_match.wildcards, res_match.wildcards,
299 'Match failed: wildcards: ' + hex(req_match.wildcards) +
300 " != " + hex(res_match.wildcards))
301 parent.assertEqual(req_match.in_port, res_match.in_port,
302 'Match failed: in_port: ' + str(req_match.in_port) +
303 " != " + str(res_match.in_port))
304 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
305 'Match failed: dl_src: ' + str(req_match.dl_src) +
306 " != " + str(res_match.dl_src))
307 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
308 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
309 " != " + str(res_match.dl_dst))
310 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
311 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
312 " != " + str(res_match.dl_vlan))
313 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
314 'Match failed: dl_vlan_pcp: ' +
315 str(req_match.dl_vlan_pcp) + " != " +
316 str(res_match.dl_vlan_pcp))
317 parent.assertEqual(req_match.dl_type, res_match.dl_type,
318 'Match failed: dl_type: ' + str(req_match.dl_type) +
319 " != " + str(res_match.dl_type))
320
321 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
322 and (req_match.dl_type == IP_ETHERTYPE)):
323 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
324 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
325 " != " + str(res_match.nw_tos))
326 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
327 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
328 " != " + str(res_match.nw_proto))
329 parent.assertEqual(req_match.nw_src, res_match.nw_src,
330 'Match failed: nw_src: ' + str(req_match.nw_src) +
331 " != " + str(res_match.nw_src))
332 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
333 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
334 " != " + str(res_match.nw_dst))
335
336 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
337 and ((req_match.nw_proto == TCP_PROTOCOL)
338 or (req_match.nw_proto == UDP_PROTOCOL))):
339 parent.assertEqual(req_match.tp_src, res_match.tp_src,
340 'Match failed: tp_src: ' +
341 str(req_match.tp_src) +
342 " != " + str(res_match.tp_src))
343 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
344 'Match failed: tp_dst: ' +
345 str(req_match.tp_dst) +
346 " != " + str(res_match.tp_dst))
347
348def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
349 """
350 Receive a flow removed msg and verify it matches expected
351
352 @params parent Must implement controller, assertEqual
353 @param pkt_count If >= 0, verify packet count
354 @param byte_count If >= 0, verify byte count
355 """
356 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
357 parent.assertTrue(response is not None, 'No flow removed message received')
358
359 if request is None:
360 return
361
362 parent.assertEqual(request.cookie, response.cookie,
363 "Flow removed cookie error: " +
364 hex(request.cookie) + " != " + hex(response.cookie))
365
366 req_match = request.match
367 res_match = response.match
368 verifyMatchField(req_match, res_match)
369
370 if (req_match.wildcards != 0):
371 parent.assertEqual(request.priority, response.priority,
372 'Flow remove prio mismatch: ' +
373 str(request,priority) + " != " +
374 str(response.priority))
375 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
376 'Flow remove reason is not HARD TIMEOUT:' +
377 str(response.reason))
378 if pkt_count >= 0:
379 parent.assertEqual(response.packet_count, pkt_count,
380 'Flow removed failed, packet count: ' +
381 str(response.packet_count) + " != " +
382 str(pkt_count))
383 if byte_count >= 0:
384 parent.assertEqual(response.byte_count, byte_count,
385 'Flow removed failed, byte count: ' +
386 str(response.byte_count) + " != " +
387 str(byte_count))
388
389def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
Dan Talayco677c0b72011-08-23 22:53:38 -0700390 egr_port=None, egr_queue=None, check_expire=False, in_band=True):
Dan Talayco551befa2010-07-15 17:05:32 -0700391 """
392 Create a flow message
393
394 Match on packet with given wildcards.
395 See flow_match_test for other parameter descriptoins
396 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700397 @param in_band if True, do not wildcard ingress port
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
406 request = message.flow_mod()
407 request.match = match
408 request.buffer_id = 0xffffffff
409 if check_expire:
410 request.flags |= ofp.OFPFF_SEND_FLOW_REM
411 request.hard_timeout = 1
412
413 if action_list is not None:
414 for act in action_list:
415 parent.logger.debug("Adding action " + act.show())
416 rv = request.actions.add(act)
417 parent.assertTrue(rv, "Could not add action" + act.show())
418
419 # Set up output/enqueue action if directed
420 if egr_queue is not None:
421 parent.assertTrue(egr_port is not None, "Egress port not set")
422 act = action.action_enqueue()
423 act.port = egr_port
424 act.queue_id = egr_queue
425 rv = request.actions.add(act)
426 parent.assertTrue(rv, "Could not add enqueue action " +
427 str(egr_port) + " Q: " + str(egr_queue))
428 elif egr_port is not None:
429 act = action.action_output()
430 act.port = egr_port
431 rv = request.actions.add(act)
432 parent.assertTrue(rv, "Could not add output action " + str(egr_port))
433
434 parent.logger.debug(request.show())
435
436 return request
437
438def flow_msg_install(parent, request, clear_table=True):
439 """
440 Install a flow mod message in the switch
441
442 @param parent Must implement controller, assertEqual, assertTrue
443 @param request The request, all set to go
444 @param clear_table If true, clear the flow table before installing
445 """
446 if clear_table:
447 parent.logger.debug("Clear flow table")
448 rc = delete_all_flows(parent.controller, parent.logger)
449 parent.assertEqual(rc, 0, "Failed to delete all flows")
450 do_barrier(parent.controller)
451
452 parent.logger.debug("Insert flow")
453 rv = parent.controller.message_send(request)
454 parent.assertTrue(rv != -1, "Error installing flow mod")
455 do_barrier(parent.controller)
456
457def flow_match_test_port_pair(parent, ing_port, egr_port, wildcards=0,
458 dl_vlan=-1, pkt=None, exp_pkt=None,
459 action_list=None, check_expire=False):
460 """
461 Flow match test on single TCP packet
462
463 Run test with packet through switch from ing_port to egr_port
464 See flow_match_test for parameter descriptions
465 """
466
467 parent.logger.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
468 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700469 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700470 if pkt is None:
471 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
472
473 request = flow_msg_create(parent, pkt, ing_port=ing_port,
474 wildcards=wildcards, egr_port=egr_port,
475 action_list=action_list)
476
477 flow_msg_install(parent, request)
478
479 parent.logger.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
480 parent.dataplane.send(ing_port, str(pkt))
481
482 if exp_pkt is None:
483 exp_pkt = pkt
484 receive_pkt_verify(parent, egr_port, exp_pkt)
485
486 if check_expire:
487 #@todo Not all HW supports both pkt and byte counters
488 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
489
490def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
491 exp_pkt=None, action_list=None, check_expire=False,
492 max_test=0):
493 """
494 Run flow_match_test_port_pair on all port pairs
495
496 @param max_test If > 0 no more than this number of tests are executed.
497 @param parent Must implement controller, dataplane, assertTrue, assertEqual
498 and logger
499 @param pkt If not None, use this packet for ingress
500 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700501 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700502 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
503 @param action_list Additional actions to add to flow mod
504 @param check_expire Check for flow expiration message
505 """
506 of_ports = port_map.keys()
507 of_ports.sort()
508 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
509 test_count = 0
510
511 for ing_idx in range(len(of_ports)):
512 ingress_port = of_ports[ing_idx]
513 for egr_idx in range(len(of_ports)):
514 if egr_idx == ing_idx:
515 continue
516 egress_port = of_ports[egr_idx]
517 flow_match_test_port_pair(parent, ingress_port, egress_port,
Dan Talayco98fada92010-07-17 00:36:21 -0700518 wildcards=wildcards, dl_vlan=dl_vlan,
519 pkt=pkt, exp_pkt=exp_pkt,
520 action_list=action_list,
Dan Talayco551befa2010-07-15 17:05:32 -0700521 check_expire=check_expire)
522 test_count += 1
523 if (max_test > 0) and (test_count > max_test):
524 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
525 return
526
Dan Talayco4b2bee62010-07-20 14:10:05 -0700527def test_param_get(config, key, default=None):
528 """
529 Return value passed via test-params if present
530
531 @param config The configuration structure for OFTest
532 @param key The lookup key
533 @param default Default value to use if not found
534
535 If the pair 'key=val' appeared in the string passed to --test-params
536 on the command line, return val (as interpreted by exec). Otherwise
537 return default value.
538 """
539 try:
540 exec config["test_params"]
541 except:
542 return default
543
544 s = "val = " + str(key)
545 try:
546 exec s
547 return val
548 except:
549 return default
550
551def action_generate(parent, field_to_mod, mod_field_vals):
552 """
553 Create an action to modify the field indicated in field_to_mod
554
555 @param parent Must implement, assertTrue
556 @param field_to_mod The field to modify as a string name
557 @param mod_field_vals Hash of values to use for modified values
558 """
559
560 act = None
561
562 if field_to_mod in ['pktlen']:
563 return None
564
565 if field_to_mod == 'dl_dst':
566 act = action.action_set_dl_dst()
567 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
568 elif field_to_mod == 'dl_src':
569 act = action.action_set_dl_src()
570 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
571 elif field_to_mod == 'dl_vlan_enable':
572 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
573 act = action.action_strip_vlan()
574 # Add VLAN tag is handled by dl_vlan field
575 # Will return None in this case
576 elif field_to_mod == 'dl_vlan':
577 act = action.action_set_vlan_vid()
578 act.vlan_vid = mod_field_vals['dl_vlan']
579 elif field_to_mod == 'dl_vlan_pcp':
580 act = action.action_set_vlan_pcp()
581 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
582 elif field_to_mod == 'ip_src':
583 act = action.action_set_nw_src()
584 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
585 elif field_to_mod == 'ip_dst':
586 act = action.action_set_nw_dst()
587 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
588 elif field_to_mod == 'ip_tos':
589 act = action.action_set_nw_tos()
590 act.nw_tos = mod_field_vals['ip_tos']
591 elif field_to_mod == 'tcp_sport':
592 act = action.action_set_tp_src()
593 act.tp_port = mod_field_vals['tcp_sport']
594 elif field_to_mod == 'tcp_dport':
595 act = action.action_set_tp_dst()
596 act.tp_port = mod_field_vals['tcp_dport']
597 else:
598 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
599
600 return act
601
602def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
603 mod_fields={}, check_test_params=False):
604 """
605 Set up the ingress and expected packet and action list for a test
606
607 @param parent Must implement, assertTrue, config hash and logger
608 @param start_field_values Field values to use for ingress packet (optional)
609 @param mod_field_values Field values to use for modified packet (optional)
610 @param mod_fields The list of fields to be modified by the switch in the test.
611 @params check_test_params If True, will check the parameters vid, add_vlan
612 and strip_vlan from the command line.
613
614 Returns a triple: pkt-to-send, expected-pkt, action-list
615 """
616
617 new_actions = []
618
Dan Talayco4b2bee62010-07-20 14:10:05 -0700619 base_pkt_params = {}
620 base_pkt_params['pktlen'] = 100
621 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
622 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
623 base_pkt_params['dl_vlan_enable'] = False
624 base_pkt_params['dl_vlan'] = 2
625 base_pkt_params['dl_vlan_pcp'] = 0
626 base_pkt_params['ip_src'] = '192.168.0.1'
627 base_pkt_params['ip_dst'] = '192.168.0.2'
628 base_pkt_params['ip_tos'] = 0
629 base_pkt_params['tcp_sport'] = 1234
630 base_pkt_params['tcp_dport'] = 80
631 for keyname in start_field_vals.keys():
632 base_pkt_params[keyname] = start_field_vals[keyname]
633
634 mod_pkt_params = {}
635 mod_pkt_params['pktlen'] = 100
636 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
637 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
638 mod_pkt_params['dl_vlan_enable'] = False
639 mod_pkt_params['dl_vlan'] = 3
640 mod_pkt_params['dl_vlan_pcp'] = 7
641 mod_pkt_params['ip_src'] = '10.20.30.40'
642 mod_pkt_params['ip_dst'] = '50.60.70.80'
643 mod_pkt_params['ip_tos'] = 0xf0
644 mod_pkt_params['tcp_sport'] = 4321
645 mod_pkt_params['tcp_dport'] = 8765
646 for keyname in mod_field_vals.keys():
647 mod_pkt_params[keyname] = mod_field_vals[keyname]
648
649 # Check for test param modifications
650 strip = False
651 if check_test_params:
652 add_vlan = test_param_get(parent.config, 'add_vlan')
653 strip_vlan = test_param_get(parent.config, 'strip_vlan')
654 vid = test_param_get(parent.config, 'vid')
655
656 if add_vlan and strip_vlan:
657 parent.assertTrue(0, "Add and strip VLAN both specified")
658
659 if vid:
660 base_pkt_params['dl_vlan_enable'] = True
661 base_pkt_params['dl_vlan'] = vid
662 if 'dl_vlan' in mod_fields:
663 mod_pkt_params['dl_vlan'] = vid + 1
664
665 if add_vlan:
666 base_pkt_params['dl_vlan_enable'] = False
667 mod_pkt_params['dl_vlan_enable'] = True
668 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
669 mod_fields.append('pktlen')
670 mod_fields.append('dl_vlan_enable')
671 if 'dl_vlan' not in mod_fields:
672 mod_fields.append('dl_vlan')
673 elif strip_vlan:
674 base_pkt_params['dl_vlan_enable'] = True
675 mod_pkt_params['dl_vlan_enable'] = False
676 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
677 mod_fields.append('dl_vlan_enable')
678 mod_fields.append('pktlen')
679
680 # Build the ingress packet
681 ingress_pkt = simple_tcp_packet(**base_pkt_params)
682
683 # Build the expected packet, modifying the indicated fields
684 for item in mod_fields:
685 base_pkt_params[item] = mod_pkt_params[item]
686 act = action_generate(parent, item, mod_pkt_params)
687 if act:
688 new_actions.append(act)
689
690 expected_pkt = simple_tcp_packet(**base_pkt_params)
691
692 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700693
694# Generate a simple "drop" flow mod
695# If in_band is true, then only drop from first test port
696def flow_mod_gen(port_map, in_band):
697 request = message.flow_mod()
698 request.match.wildcards = ofp.OFPFW_ALL
699 if in_band:
700 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
701 for of_port, ifname in port_map.items(): # Grab first port
702 break
703 request.match.in_port = of_port
704 request.buffer_id = 0xffffffff
705 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700706
707def skip_message_emit(parent, s):
708 """
709 Print out a 'skipped' message to stderr
710
711 @param s The string to print out to the log file
712 @param parent Must implement config and logger objects
713 """
714 global skipped_test_count
715
716 skipped_test_count += 1
717 parent.logger.info("Skipping: " + s)
718 if parent.config["dbg_level"] < logging.WARNING:
719 sys.stderr.write("(skipped) ")
720 else:
721 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700722