blob: 4c7420c27ef55265f6ce1f97b8d9a1dad0ad7371 [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 Talaycof6e76c02012-03-23 10:56:12 -0700249def receive_pkt_verify(parent, egr_ports, exp_pkt):
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:
267 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
268 port_number=egr_port, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700269
Dan Talaycof6e76c02012-03-23 10:56:12 -0700270 if rcv_pkt is None:
271 parent.logger.error("ERROR: No packet received from " + str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700272
Dan Talaycof6e76c02012-03-23 10:56:12 -0700273 parent.assertTrue(rcv_pkt is not None,
274 "Did not receive packet port " + str(egr_port))
275 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
276 str(rcv_port))
277
278 if str(exp_pkt) != str(rcv_pkt):
279 parent.logger.error("ERROR: Packet match failed.")
280 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
281 + str(exp_pkt).encode('hex'))
282 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
283 + str(rcv_pkt).encode('hex'))
284 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
285 "Packet match error on port " + str(egr_port))
286
Dan Talayco551befa2010-07-15 17:05:32 -0700287def match_verify(parent, req_match, res_match):
288 """
289 Verify flow matches agree; if they disagree, report where
290
291 parent must implement assertEqual
292 Use str() to ensure content is compared and not pointers
293 """
294
295 parent.assertEqual(req_match.wildcards, res_match.wildcards,
296 'Match failed: wildcards: ' + hex(req_match.wildcards) +
297 " != " + hex(res_match.wildcards))
298 parent.assertEqual(req_match.in_port, res_match.in_port,
299 'Match failed: in_port: ' + str(req_match.in_port) +
300 " != " + str(res_match.in_port))
301 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
302 'Match failed: dl_src: ' + str(req_match.dl_src) +
303 " != " + str(res_match.dl_src))
304 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
305 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
306 " != " + str(res_match.dl_dst))
307 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
308 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
309 " != " + str(res_match.dl_vlan))
310 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
311 'Match failed: dl_vlan_pcp: ' +
312 str(req_match.dl_vlan_pcp) + " != " +
313 str(res_match.dl_vlan_pcp))
314 parent.assertEqual(req_match.dl_type, res_match.dl_type,
315 'Match failed: dl_type: ' + str(req_match.dl_type) +
316 " != " + str(res_match.dl_type))
317
318 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
319 and (req_match.dl_type == IP_ETHERTYPE)):
320 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
321 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
322 " != " + str(res_match.nw_tos))
323 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
324 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
325 " != " + str(res_match.nw_proto))
326 parent.assertEqual(req_match.nw_src, res_match.nw_src,
327 'Match failed: nw_src: ' + str(req_match.nw_src) +
328 " != " + str(res_match.nw_src))
329 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
330 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
331 " != " + str(res_match.nw_dst))
332
333 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
334 and ((req_match.nw_proto == TCP_PROTOCOL)
335 or (req_match.nw_proto == UDP_PROTOCOL))):
336 parent.assertEqual(req_match.tp_src, res_match.tp_src,
337 'Match failed: tp_src: ' +
338 str(req_match.tp_src) +
339 " != " + str(res_match.tp_src))
340 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
341 'Match failed: tp_dst: ' +
342 str(req_match.tp_dst) +
343 " != " + str(res_match.tp_dst))
344
345def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
346 """
347 Receive a flow removed msg and verify it matches expected
348
349 @params parent Must implement controller, assertEqual
350 @param pkt_count If >= 0, verify packet count
351 @param byte_count If >= 0, verify byte count
352 """
353 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
354 parent.assertTrue(response is not None, 'No flow removed message received')
355
356 if request is None:
357 return
358
359 parent.assertEqual(request.cookie, response.cookie,
360 "Flow removed cookie error: " +
361 hex(request.cookie) + " != " + hex(response.cookie))
362
363 req_match = request.match
364 res_match = response.match
365 verifyMatchField(req_match, res_match)
366
367 if (req_match.wildcards != 0):
368 parent.assertEqual(request.priority, response.priority,
369 'Flow remove prio mismatch: ' +
370 str(request,priority) + " != " +
371 str(response.priority))
372 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
373 'Flow remove reason is not HARD TIMEOUT:' +
374 str(response.reason))
375 if pkt_count >= 0:
376 parent.assertEqual(response.packet_count, pkt_count,
377 'Flow removed failed, packet count: ' +
378 str(response.packet_count) + " != " +
379 str(pkt_count))
380 if byte_count >= 0:
381 parent.assertEqual(response.byte_count, byte_count,
382 'Flow removed failed, byte count: ' +
383 str(response.byte_count) + " != " +
384 str(byte_count))
385
386def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700387 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700388 """
389 Create a flow message
390
391 Match on packet with given wildcards.
392 See flow_match_test for other parameter descriptoins
393 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700394 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700395 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700396 """
397 match = parse.packet_to_flow_match(pkt)
398 parent.assertTrue(match is not None, "Flow match from pkt failed")
Dan Talayco677c0b72011-08-23 22:53:38 -0700399 if in_band:
400 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700401 match.wildcards = wildcards
402 match.in_port = ing_port
403
Dan Talaycof6e76c02012-03-23 10:56:12 -0700404 if type(egr_ports) == type([]):
405 egr_port_list = egr_ports
406 else:
407 egr_port_list = [egr_ports]
408
Dan Talayco551befa2010-07-15 17:05:32 -0700409 request = message.flow_mod()
410 request.match = match
411 request.buffer_id = 0xffffffff
412 if check_expire:
413 request.flags |= ofp.OFPFF_SEND_FLOW_REM
414 request.hard_timeout = 1
415
416 if action_list is not None:
417 for act in action_list:
418 parent.logger.debug("Adding action " + act.show())
419 rv = request.actions.add(act)
420 parent.assertTrue(rv, "Could not add action" + act.show())
421
422 # Set up output/enqueue action if directed
423 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700424 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700425 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700426 for egr_port in egr_port_list:
427 act.port = egr_port
428 act.queue_id = egr_queue
429 rv = request.actions.add(act)
430 parent.assertTrue(rv, "Could not add enqueue action " +
431 str(egr_port) + " Q: " + str(egr_queue))
432 elif egr_ports is not None:
433 for egr_port in egr_port_list:
434 act = action.action_output()
435 act.port = egr_port
436 rv = request.actions.add(act)
437 parent.assertTrue(rv, "Could not add output action " +
438 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700439
440 parent.logger.debug(request.show())
441
442 return request
443
444def flow_msg_install(parent, request, clear_table=True):
445 """
446 Install a flow mod message in the switch
447
448 @param parent Must implement controller, assertEqual, assertTrue
449 @param request The request, all set to go
450 @param clear_table If true, clear the flow table before installing
451 """
452 if clear_table:
453 parent.logger.debug("Clear flow table")
454 rc = delete_all_flows(parent.controller, parent.logger)
455 parent.assertEqual(rc, 0, "Failed to delete all flows")
456 do_barrier(parent.controller)
457
458 parent.logger.debug("Insert flow")
459 rv = parent.controller.message_send(request)
460 parent.assertTrue(rv != -1, "Error installing flow mod")
461 do_barrier(parent.controller)
462
Dan Talaycof6e76c02012-03-23 10:56:12 -0700463def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=0,
Dan Talayco551befa2010-07-15 17:05:32 -0700464 dl_vlan=-1, pkt=None, exp_pkt=None,
465 action_list=None, check_expire=False):
466 """
467 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700468 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700469
470 Run test with packet through switch from ing_port to egr_port
471 See flow_match_test for parameter descriptions
472 """
473
Dan Talaycof6e76c02012-03-23 10:56:12 -0700474 parent.logger.info("Pkt match test: " + str(ing_port) + " to " +
475 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700476 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700477 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700478 if pkt is None:
479 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
480
481 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700482 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700483 action_list=action_list)
484
485 flow_msg_install(parent, request)
486
Dan Talaycof6e76c02012-03-23 10:56:12 -0700487 parent.logger.debug("Send packet: " + str(ing_port) + " to " +
488 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700489 parent.dataplane.send(ing_port, str(pkt))
490
491 if exp_pkt is None:
492 exp_pkt = pkt
Dan Talaycof6e76c02012-03-23 10:56:12 -0700493 receive_pkt_verify(parent, egr_ports, exp_pkt)
Dan Talayco551befa2010-07-15 17:05:32 -0700494
495 if check_expire:
496 #@todo Not all HW supports both pkt and byte counters
497 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
498
Dan Talaycof6e76c02012-03-23 10:56:12 -0700499def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
500 """
501 Generate a list of ports avoiding those in the exclude list
502 @param parent Supplies logger
503 @param of_ports List of OF port numbers
504 @param how_many Number of ports to be added to the list
505 @param exclude_list List of ports not to be used
506 @returns An empty list if unable to find enough ports
507 """
508
509 count = 0
510 egr_ports = []
511 for egr_idx in range(len(of_ports)):
512 if of_ports[egr_idx] not in exclude_list:
513 egr_ports.append(of_ports[egr_idx])
514 count += 1
515 if count >= how_many:
516 return egr_ports
517 parent.logger.debug("Could not generate enough egress ports for test")
518 return []
519
Dan Talayco551befa2010-07-15 17:05:32 -0700520def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
521 exp_pkt=None, action_list=None, check_expire=False,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700522 max_test=0, egr_count=1):
Dan Talayco551befa2010-07-15 17:05:32 -0700523 """
524 Run flow_match_test_port_pair on all port pairs
525
526 @param max_test If > 0 no more than this number of tests are executed.
527 @param parent Must implement controller, dataplane, assertTrue, assertEqual
528 and logger
529 @param pkt If not None, use this packet for ingress
530 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700531 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700532 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
533 @param action_list Additional actions to add to flow mod
534 @param check_expire Check for flow expiration message
535 """
536 of_ports = port_map.keys()
537 of_ports.sort()
538 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
539 test_count = 0
540
541 for ing_idx in range(len(of_ports)):
542 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700543 egr_ports = get_egr_list(parent, of_ports, egr_count,
544 exclude_list=[ingress_port])
545 if len(egr_ports) == 0:
546 parent.assertTrue(0, "Failed to generate egress port list")
547
548 flow_match_test_port_pair(parent, ingress_port, egr_ports,
549 wildcards=wildcards, dl_vlan=dl_vlan,
550 pkt=pkt, exp_pkt=exp_pkt,
551 action_list=action_list,
552 check_expire=check_expire)
553 test_count += 1
554 if (max_test > 0) and (test_count > max_test):
555 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
556 return
Dan Talayco551befa2010-07-15 17:05:32 -0700557
Dan Talayco4b2bee62010-07-20 14:10:05 -0700558def test_param_get(config, key, default=None):
559 """
560 Return value passed via test-params if present
561
562 @param config The configuration structure for OFTest
563 @param key The lookup key
564 @param default Default value to use if not found
565
566 If the pair 'key=val' appeared in the string passed to --test-params
567 on the command line, return val (as interpreted by exec). Otherwise
568 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700569
570 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
571 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700572 """
573 try:
574 exec config["test_params"]
575 except:
576 return default
577
578 s = "val = " + str(key)
579 try:
580 exec s
581 return val
582 except:
583 return default
584
585def action_generate(parent, field_to_mod, mod_field_vals):
586 """
587 Create an action to modify the field indicated in field_to_mod
588
589 @param parent Must implement, assertTrue
590 @param field_to_mod The field to modify as a string name
591 @param mod_field_vals Hash of values to use for modified values
592 """
593
594 act = None
595
596 if field_to_mod in ['pktlen']:
597 return None
598
599 if field_to_mod == 'dl_dst':
600 act = action.action_set_dl_dst()
601 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
602 elif field_to_mod == 'dl_src':
603 act = action.action_set_dl_src()
604 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
605 elif field_to_mod == 'dl_vlan_enable':
606 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
607 act = action.action_strip_vlan()
608 # Add VLAN tag is handled by dl_vlan field
609 # Will return None in this case
610 elif field_to_mod == 'dl_vlan':
611 act = action.action_set_vlan_vid()
612 act.vlan_vid = mod_field_vals['dl_vlan']
613 elif field_to_mod == 'dl_vlan_pcp':
614 act = action.action_set_vlan_pcp()
615 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
616 elif field_to_mod == 'ip_src':
617 act = action.action_set_nw_src()
618 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
619 elif field_to_mod == 'ip_dst':
620 act = action.action_set_nw_dst()
621 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
622 elif field_to_mod == 'ip_tos':
623 act = action.action_set_nw_tos()
624 act.nw_tos = mod_field_vals['ip_tos']
625 elif field_to_mod == 'tcp_sport':
626 act = action.action_set_tp_src()
627 act.tp_port = mod_field_vals['tcp_sport']
628 elif field_to_mod == 'tcp_dport':
629 act = action.action_set_tp_dst()
630 act.tp_port = mod_field_vals['tcp_dport']
631 else:
632 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
633
634 return act
635
636def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
637 mod_fields={}, check_test_params=False):
638 """
639 Set up the ingress and expected packet and action list for a test
640
641 @param parent Must implement, assertTrue, config hash and logger
642 @param start_field_values Field values to use for ingress packet (optional)
643 @param mod_field_values Field values to use for modified packet (optional)
644 @param mod_fields The list of fields to be modified by the switch in the test.
645 @params check_test_params If True, will check the parameters vid, add_vlan
646 and strip_vlan from the command line.
647
648 Returns a triple: pkt-to-send, expected-pkt, action-list
649 """
650
651 new_actions = []
652
Dan Talayco4b2bee62010-07-20 14:10:05 -0700653 base_pkt_params = {}
654 base_pkt_params['pktlen'] = 100
655 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
656 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
657 base_pkt_params['dl_vlan_enable'] = False
658 base_pkt_params['dl_vlan'] = 2
659 base_pkt_params['dl_vlan_pcp'] = 0
660 base_pkt_params['ip_src'] = '192.168.0.1'
661 base_pkt_params['ip_dst'] = '192.168.0.2'
662 base_pkt_params['ip_tos'] = 0
663 base_pkt_params['tcp_sport'] = 1234
664 base_pkt_params['tcp_dport'] = 80
665 for keyname in start_field_vals.keys():
666 base_pkt_params[keyname] = start_field_vals[keyname]
667
668 mod_pkt_params = {}
669 mod_pkt_params['pktlen'] = 100
670 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
671 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
672 mod_pkt_params['dl_vlan_enable'] = False
673 mod_pkt_params['dl_vlan'] = 3
674 mod_pkt_params['dl_vlan_pcp'] = 7
675 mod_pkt_params['ip_src'] = '10.20.30.40'
676 mod_pkt_params['ip_dst'] = '50.60.70.80'
677 mod_pkt_params['ip_tos'] = 0xf0
678 mod_pkt_params['tcp_sport'] = 4321
679 mod_pkt_params['tcp_dport'] = 8765
680 for keyname in mod_field_vals.keys():
681 mod_pkt_params[keyname] = mod_field_vals[keyname]
682
683 # Check for test param modifications
684 strip = False
685 if check_test_params:
686 add_vlan = test_param_get(parent.config, 'add_vlan')
687 strip_vlan = test_param_get(parent.config, 'strip_vlan')
688 vid = test_param_get(parent.config, 'vid')
689
690 if add_vlan and strip_vlan:
691 parent.assertTrue(0, "Add and strip VLAN both specified")
692
693 if vid:
694 base_pkt_params['dl_vlan_enable'] = True
695 base_pkt_params['dl_vlan'] = vid
696 if 'dl_vlan' in mod_fields:
697 mod_pkt_params['dl_vlan'] = vid + 1
698
699 if add_vlan:
700 base_pkt_params['dl_vlan_enable'] = False
701 mod_pkt_params['dl_vlan_enable'] = True
702 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
703 mod_fields.append('pktlen')
704 mod_fields.append('dl_vlan_enable')
705 if 'dl_vlan' not in mod_fields:
706 mod_fields.append('dl_vlan')
707 elif strip_vlan:
708 base_pkt_params['dl_vlan_enable'] = True
709 mod_pkt_params['dl_vlan_enable'] = False
710 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
711 mod_fields.append('dl_vlan_enable')
712 mod_fields.append('pktlen')
713
714 # Build the ingress packet
715 ingress_pkt = simple_tcp_packet(**base_pkt_params)
716
717 # Build the expected packet, modifying the indicated fields
718 for item in mod_fields:
719 base_pkt_params[item] = mod_pkt_params[item]
720 act = action_generate(parent, item, mod_pkt_params)
721 if act:
722 new_actions.append(act)
723
724 expected_pkt = simple_tcp_packet(**base_pkt_params)
725
726 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700727
728# Generate a simple "drop" flow mod
729# If in_band is true, then only drop from first test port
730def flow_mod_gen(port_map, in_band):
731 request = message.flow_mod()
732 request.match.wildcards = ofp.OFPFW_ALL
733 if in_band:
734 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
735 for of_port, ifname in port_map.items(): # Grab first port
736 break
737 request.match.in_port = of_port
738 request.buffer_id = 0xffffffff
739 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700740
741def skip_message_emit(parent, s):
742 """
743 Print out a 'skipped' message to stderr
744
745 @param s The string to print out to the log file
746 @param parent Must implement config and logger objects
747 """
748 global skipped_test_count
749
750 skipped_test_count += 1
751 parent.logger.info("Skipping: " + s)
752 if parent.config["dbg_level"] < logging.WARNING:
753 sys.stderr.write("(skipped) ")
754 else:
755 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700756