blob: fe705f46e0e08286fe342cbb0191ba83eccb2f3d [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
Dan Talaycocfa172f2012-03-23 12:03:00 -0700535 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700536 """
537 of_ports = port_map.keys()
538 of_ports.sort()
539 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
540 test_count = 0
541
Dan Talaycocfa172f2012-03-23 12:03:00 -0700542 if egr_count == -1:
543 egr_count = test_param_get(parent.config, 'egr_count', default=2)
544
Dan Talayco551befa2010-07-15 17:05:32 -0700545 for ing_idx in range(len(of_ports)):
546 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700547 egr_ports = get_egr_list(parent, of_ports, egr_count,
548 exclude_list=[ingress_port])
549 if len(egr_ports) == 0:
550 parent.assertTrue(0, "Failed to generate egress port list")
551
552 flow_match_test_port_pair(parent, ingress_port, egr_ports,
553 wildcards=wildcards, dl_vlan=dl_vlan,
554 pkt=pkt, exp_pkt=exp_pkt,
555 action_list=action_list,
556 check_expire=check_expire)
557 test_count += 1
558 if (max_test > 0) and (test_count > max_test):
559 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
560 return
Dan Talayco551befa2010-07-15 17:05:32 -0700561
Dan Talayco4b2bee62010-07-20 14:10:05 -0700562def test_param_get(config, key, default=None):
563 """
564 Return value passed via test-params if present
565
566 @param config The configuration structure for OFTest
567 @param key The lookup key
568 @param default Default value to use if not found
569
570 If the pair 'key=val' appeared in the string passed to --test-params
571 on the command line, return val (as interpreted by exec). Otherwise
572 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700573
574 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
575 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700576 """
577 try:
578 exec config["test_params"]
579 except:
580 return default
581
582 s = "val = " + str(key)
583 try:
584 exec s
585 return val
586 except:
587 return default
588
589def action_generate(parent, field_to_mod, mod_field_vals):
590 """
591 Create an action to modify the field indicated in field_to_mod
592
593 @param parent Must implement, assertTrue
594 @param field_to_mod The field to modify as a string name
595 @param mod_field_vals Hash of values to use for modified values
596 """
597
598 act = None
599
600 if field_to_mod in ['pktlen']:
601 return None
602
603 if field_to_mod == 'dl_dst':
604 act = action.action_set_dl_dst()
605 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
606 elif field_to_mod == 'dl_src':
607 act = action.action_set_dl_src()
608 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
609 elif field_to_mod == 'dl_vlan_enable':
610 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
611 act = action.action_strip_vlan()
612 # Add VLAN tag is handled by dl_vlan field
613 # Will return None in this case
614 elif field_to_mod == 'dl_vlan':
615 act = action.action_set_vlan_vid()
616 act.vlan_vid = mod_field_vals['dl_vlan']
617 elif field_to_mod == 'dl_vlan_pcp':
618 act = action.action_set_vlan_pcp()
619 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
620 elif field_to_mod == 'ip_src':
621 act = action.action_set_nw_src()
622 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
623 elif field_to_mod == 'ip_dst':
624 act = action.action_set_nw_dst()
625 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
626 elif field_to_mod == 'ip_tos':
627 act = action.action_set_nw_tos()
628 act.nw_tos = mod_field_vals['ip_tos']
629 elif field_to_mod == 'tcp_sport':
630 act = action.action_set_tp_src()
631 act.tp_port = mod_field_vals['tcp_sport']
632 elif field_to_mod == 'tcp_dport':
633 act = action.action_set_tp_dst()
634 act.tp_port = mod_field_vals['tcp_dport']
635 else:
636 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
637
638 return act
639
640def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
641 mod_fields={}, check_test_params=False):
642 """
643 Set up the ingress and expected packet and action list for a test
644
645 @param parent Must implement, assertTrue, config hash and logger
646 @param start_field_values Field values to use for ingress packet (optional)
647 @param mod_field_values Field values to use for modified packet (optional)
648 @param mod_fields The list of fields to be modified by the switch in the test.
649 @params check_test_params If True, will check the parameters vid, add_vlan
650 and strip_vlan from the command line.
651
652 Returns a triple: pkt-to-send, expected-pkt, action-list
653 """
654
655 new_actions = []
656
Dan Talayco4b2bee62010-07-20 14:10:05 -0700657 base_pkt_params = {}
658 base_pkt_params['pktlen'] = 100
659 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
660 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
661 base_pkt_params['dl_vlan_enable'] = False
662 base_pkt_params['dl_vlan'] = 2
663 base_pkt_params['dl_vlan_pcp'] = 0
664 base_pkt_params['ip_src'] = '192.168.0.1'
665 base_pkt_params['ip_dst'] = '192.168.0.2'
666 base_pkt_params['ip_tos'] = 0
667 base_pkt_params['tcp_sport'] = 1234
668 base_pkt_params['tcp_dport'] = 80
669 for keyname in start_field_vals.keys():
670 base_pkt_params[keyname] = start_field_vals[keyname]
671
672 mod_pkt_params = {}
673 mod_pkt_params['pktlen'] = 100
674 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
675 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
676 mod_pkt_params['dl_vlan_enable'] = False
677 mod_pkt_params['dl_vlan'] = 3
678 mod_pkt_params['dl_vlan_pcp'] = 7
679 mod_pkt_params['ip_src'] = '10.20.30.40'
680 mod_pkt_params['ip_dst'] = '50.60.70.80'
681 mod_pkt_params['ip_tos'] = 0xf0
682 mod_pkt_params['tcp_sport'] = 4321
683 mod_pkt_params['tcp_dport'] = 8765
684 for keyname in mod_field_vals.keys():
685 mod_pkt_params[keyname] = mod_field_vals[keyname]
686
687 # Check for test param modifications
688 strip = False
689 if check_test_params:
690 add_vlan = test_param_get(parent.config, 'add_vlan')
691 strip_vlan = test_param_get(parent.config, 'strip_vlan')
692 vid = test_param_get(parent.config, 'vid')
693
694 if add_vlan and strip_vlan:
695 parent.assertTrue(0, "Add and strip VLAN both specified")
696
697 if vid:
698 base_pkt_params['dl_vlan_enable'] = True
699 base_pkt_params['dl_vlan'] = vid
700 if 'dl_vlan' in mod_fields:
701 mod_pkt_params['dl_vlan'] = vid + 1
702
703 if add_vlan:
704 base_pkt_params['dl_vlan_enable'] = False
705 mod_pkt_params['dl_vlan_enable'] = True
706 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
707 mod_fields.append('pktlen')
708 mod_fields.append('dl_vlan_enable')
709 if 'dl_vlan' not in mod_fields:
710 mod_fields.append('dl_vlan')
711 elif strip_vlan:
712 base_pkt_params['dl_vlan_enable'] = True
713 mod_pkt_params['dl_vlan_enable'] = False
714 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
715 mod_fields.append('dl_vlan_enable')
716 mod_fields.append('pktlen')
717
718 # Build the ingress packet
719 ingress_pkt = simple_tcp_packet(**base_pkt_params)
720
721 # Build the expected packet, modifying the indicated fields
722 for item in mod_fields:
723 base_pkt_params[item] = mod_pkt_params[item]
724 act = action_generate(parent, item, mod_pkt_params)
725 if act:
726 new_actions.append(act)
727
728 expected_pkt = simple_tcp_packet(**base_pkt_params)
729
730 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700731
732# Generate a simple "drop" flow mod
733# If in_band is true, then only drop from first test port
734def flow_mod_gen(port_map, in_band):
735 request = message.flow_mod()
736 request.match.wildcards = ofp.OFPFW_ALL
737 if in_band:
738 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
739 for of_port, ifname in port_map.items(): # Grab first port
740 break
741 request.match.in_port = of_port
742 request.buffer_id = 0xffffffff
743 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700744
745def skip_message_emit(parent, s):
746 """
747 Print out a 'skipped' message to stderr
748
749 @param s The string to print out to the log file
750 @param parent Must implement config and logger objects
751 """
752 global skipped_test_count
753
754 skipped_test_count += 1
755 parent.logger.info("Skipping: " + s)
756 if parent.config["dbg_level"] < logging.WARNING:
757 sys.stderr.write("(skipped) ")
758 else:
759 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700760