blob: 5a99a00a589265fe753b37312af0928775322cbf [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
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700259def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700260 """
261 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700262 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700263
264 parent must implement dataplane, assertTrue and assertEqual
265 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700266 exp_pkt_arg = None
267 if parent.config["relax"]:
268 exp_pkt_arg = exp_pkt
269
Dan Talaycof6e76c02012-03-23 10:56:12 -0700270 if type(egr_ports) == type([]):
271 egr_port_list = egr_ports
272 else:
273 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700274
Dan Talaycof6e76c02012-03-23 10:56:12 -0700275 # Expect a packet from each port on egr port list
276 for egr_port in egr_port_list:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700277 check_port = egr_port
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700278 if egr_port == ofp.OFPP_IN_PORT:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700279 check_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700280 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
Dan Talaycod8ae7582012-03-23 12:24:56 -0700281 port_number=check_port, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700282
Dan Talaycof6e76c02012-03-23 10:56:12 -0700283 if rcv_pkt is None:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700284 parent.logger.error("ERROR: No packet received from " +
285 str(check_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700286
Dan Talaycof6e76c02012-03-23 10:56:12 -0700287 parent.assertTrue(rcv_pkt is not None,
Dan Talaycod8ae7582012-03-23 12:24:56 -0700288 "Did not receive packet port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700289 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
290 str(rcv_port))
291
292 if str(exp_pkt) != str(rcv_pkt):
293 parent.logger.error("ERROR: Packet match failed.")
294 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
295 + str(exp_pkt).encode('hex'))
296 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
297 + str(rcv_pkt).encode('hex'))
298 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
Dan Talaycod8ae7582012-03-23 12:24:56 -0700299 "Packet match error on port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700300
Dan Talayco551befa2010-07-15 17:05:32 -0700301def match_verify(parent, req_match, res_match):
302 """
303 Verify flow matches agree; if they disagree, report where
304
305 parent must implement assertEqual
306 Use str() to ensure content is compared and not pointers
307 """
308
309 parent.assertEqual(req_match.wildcards, res_match.wildcards,
310 'Match failed: wildcards: ' + hex(req_match.wildcards) +
311 " != " + hex(res_match.wildcards))
312 parent.assertEqual(req_match.in_port, res_match.in_port,
313 'Match failed: in_port: ' + str(req_match.in_port) +
314 " != " + str(res_match.in_port))
315 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
316 'Match failed: dl_src: ' + str(req_match.dl_src) +
317 " != " + str(res_match.dl_src))
318 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
319 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
320 " != " + str(res_match.dl_dst))
321 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
322 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
323 " != " + str(res_match.dl_vlan))
324 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
325 'Match failed: dl_vlan_pcp: ' +
326 str(req_match.dl_vlan_pcp) + " != " +
327 str(res_match.dl_vlan_pcp))
328 parent.assertEqual(req_match.dl_type, res_match.dl_type,
329 'Match failed: dl_type: ' + str(req_match.dl_type) +
330 " != " + str(res_match.dl_type))
331
332 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
333 and (req_match.dl_type == IP_ETHERTYPE)):
334 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
335 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
336 " != " + str(res_match.nw_tos))
337 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
338 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
339 " != " + str(res_match.nw_proto))
340 parent.assertEqual(req_match.nw_src, res_match.nw_src,
341 'Match failed: nw_src: ' + str(req_match.nw_src) +
342 " != " + str(res_match.nw_src))
343 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
344 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
345 " != " + str(res_match.nw_dst))
346
347 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
348 and ((req_match.nw_proto == TCP_PROTOCOL)
349 or (req_match.nw_proto == UDP_PROTOCOL))):
350 parent.assertEqual(req_match.tp_src, res_match.tp_src,
351 'Match failed: tp_src: ' +
352 str(req_match.tp_src) +
353 " != " + str(res_match.tp_src))
354 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
355 'Match failed: tp_dst: ' +
356 str(req_match.tp_dst) +
357 " != " + str(res_match.tp_dst))
358
359def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
360 """
361 Receive a flow removed msg and verify it matches expected
362
363 @params parent Must implement controller, assertEqual
364 @param pkt_count If >= 0, verify packet count
365 @param byte_count If >= 0, verify byte count
366 """
367 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
368 parent.assertTrue(response is not None, 'No flow removed message received')
369
370 if request is None:
371 return
372
373 parent.assertEqual(request.cookie, response.cookie,
374 "Flow removed cookie error: " +
375 hex(request.cookie) + " != " + hex(response.cookie))
376
377 req_match = request.match
378 res_match = response.match
379 verifyMatchField(req_match, res_match)
380
381 if (req_match.wildcards != 0):
382 parent.assertEqual(request.priority, response.priority,
383 'Flow remove prio mismatch: ' +
384 str(request,priority) + " != " +
385 str(response.priority))
386 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
387 'Flow remove reason is not HARD TIMEOUT:' +
388 str(response.reason))
389 if pkt_count >= 0:
390 parent.assertEqual(response.packet_count, pkt_count,
391 'Flow removed failed, packet count: ' +
392 str(response.packet_count) + " != " +
393 str(pkt_count))
394 if byte_count >= 0:
395 parent.assertEqual(response.byte_count, byte_count,
396 'Flow removed failed, byte count: ' +
397 str(response.byte_count) + " != " +
398 str(byte_count))
399
400def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=0,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700401 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700402 """
403 Create a flow message
404
405 Match on packet with given wildcards.
406 See flow_match_test for other parameter descriptoins
407 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700408 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700409 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700410 """
411 match = parse.packet_to_flow_match(pkt)
412 parent.assertTrue(match is not None, "Flow match from pkt failed")
Dan Talayco677c0b72011-08-23 22:53:38 -0700413 if in_band:
414 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700415 match.wildcards = wildcards
416 match.in_port = ing_port
417
Dan Talaycof6e76c02012-03-23 10:56:12 -0700418 if type(egr_ports) == type([]):
419 egr_port_list = egr_ports
420 else:
421 egr_port_list = [egr_ports]
422
Dan Talayco551befa2010-07-15 17:05:32 -0700423 request = message.flow_mod()
424 request.match = match
425 request.buffer_id = 0xffffffff
426 if check_expire:
427 request.flags |= ofp.OFPFF_SEND_FLOW_REM
428 request.hard_timeout = 1
429
430 if action_list is not None:
431 for act in action_list:
432 parent.logger.debug("Adding action " + act.show())
433 rv = request.actions.add(act)
434 parent.assertTrue(rv, "Could not add action" + act.show())
435
436 # Set up output/enqueue action if directed
437 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700438 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700439 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700440 for egr_port in egr_port_list:
441 act.port = egr_port
442 act.queue_id = egr_queue
443 rv = request.actions.add(act)
444 parent.assertTrue(rv, "Could not add enqueue action " +
445 str(egr_port) + " Q: " + str(egr_queue))
446 elif egr_ports is not None:
447 for egr_port in egr_port_list:
448 act = action.action_output()
449 act.port = egr_port
450 rv = request.actions.add(act)
451 parent.assertTrue(rv, "Could not add output action " +
452 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700453
454 parent.logger.debug(request.show())
455
456 return request
457
458def flow_msg_install(parent, request, clear_table=True):
459 """
460 Install a flow mod message in the switch
461
462 @param parent Must implement controller, assertEqual, assertTrue
463 @param request The request, all set to go
464 @param clear_table If true, clear the flow table before installing
465 """
466 if clear_table:
467 parent.logger.debug("Clear flow table")
468 rc = delete_all_flows(parent.controller, parent.logger)
469 parent.assertEqual(rc, 0, "Failed to delete all flows")
470 do_barrier(parent.controller)
471
472 parent.logger.debug("Insert flow")
473 rv = parent.controller.message_send(request)
474 parent.assertTrue(rv != -1, "Error installing flow mod")
475 do_barrier(parent.controller)
476
Dan Talaycof6e76c02012-03-23 10:56:12 -0700477def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=0,
Dan Talayco551befa2010-07-15 17:05:32 -0700478 dl_vlan=-1, pkt=None, exp_pkt=None,
479 action_list=None, check_expire=False):
480 """
481 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700482 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700483
484 Run test with packet through switch from ing_port to egr_port
485 See flow_match_test for parameter descriptions
486 """
487
Dan Talaycof6e76c02012-03-23 10:56:12 -0700488 parent.logger.info("Pkt match test: " + str(ing_port) + " to " +
489 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700490 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700491 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700492 if pkt is None:
493 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
494
495 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700496 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700497 action_list=action_list)
498
499 flow_msg_install(parent, request)
500
Dan Talaycof6e76c02012-03-23 10:56:12 -0700501 parent.logger.debug("Send packet: " + str(ing_port) + " to " +
502 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700503 parent.dataplane.send(ing_port, str(pkt))
504
505 if exp_pkt is None:
506 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700507 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700508
509 if check_expire:
510 #@todo Not all HW supports both pkt and byte counters
511 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
512
Dan Talaycof6e76c02012-03-23 10:56:12 -0700513def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
514 """
515 Generate a list of ports avoiding those in the exclude list
516 @param parent Supplies logger
517 @param of_ports List of OF port numbers
518 @param how_many Number of ports to be added to the list
519 @param exclude_list List of ports not to be used
520 @returns An empty list if unable to find enough ports
521 """
522
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700523 if how_many == 0:
524 return []
525
Dan Talaycof6e76c02012-03-23 10:56:12 -0700526 count = 0
527 egr_ports = []
528 for egr_idx in range(len(of_ports)):
529 if of_ports[egr_idx] not in exclude_list:
530 egr_ports.append(of_ports[egr_idx])
531 count += 1
532 if count >= how_many:
533 return egr_ports
534 parent.logger.debug("Could not generate enough egress ports for test")
535 return []
536
Dan Talayco551befa2010-07-15 17:05:32 -0700537def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
538 exp_pkt=None, action_list=None, check_expire=False,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700539 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700540 """
541 Run flow_match_test_port_pair on all port pairs
542
543 @param max_test If > 0 no more than this number of tests are executed.
544 @param parent Must implement controller, dataplane, assertTrue, assertEqual
545 and logger
546 @param pkt If not None, use this packet for ingress
547 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700548 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700549 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
550 @param action_list Additional actions to add to flow mod
551 @param check_expire Check for flow expiration message
Dan Talaycocfa172f2012-03-23 12:03:00 -0700552 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700553 """
554 of_ports = port_map.keys()
555 of_ports.sort()
556 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
557 test_count = 0
558
Dan Talaycocfa172f2012-03-23 12:03:00 -0700559 if egr_count == -1:
560 egr_count = test_param_get(parent.config, 'egr_count', default=2)
561
Dan Talayco551befa2010-07-15 17:05:32 -0700562 for ing_idx in range(len(of_ports)):
563 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700564 egr_ports = get_egr_list(parent, of_ports, egr_count,
565 exclude_list=[ingress_port])
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700566 if ing_port:
567 egr_ports.append(ofp.OFPP_IN_PORT)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700568 if len(egr_ports) == 0:
569 parent.assertTrue(0, "Failed to generate egress port list")
570
571 flow_match_test_port_pair(parent, ingress_port, egr_ports,
572 wildcards=wildcards, dl_vlan=dl_vlan,
573 pkt=pkt, exp_pkt=exp_pkt,
574 action_list=action_list,
575 check_expire=check_expire)
576 test_count += 1
577 if (max_test > 0) and (test_count > max_test):
578 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
579 return
Dan Talayco551befa2010-07-15 17:05:32 -0700580
Dan Talayco4b2bee62010-07-20 14:10:05 -0700581def test_param_get(config, key, default=None):
582 """
583 Return value passed via test-params if present
584
585 @param config The configuration structure for OFTest
586 @param key The lookup key
587 @param default Default value to use if not found
588
589 If the pair 'key=val' appeared in the string passed to --test-params
590 on the command line, return val (as interpreted by exec). Otherwise
591 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700592
593 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
594 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700595 """
596 try:
597 exec config["test_params"]
598 except:
599 return default
600
601 s = "val = " + str(key)
602 try:
603 exec s
604 return val
605 except:
606 return default
607
608def action_generate(parent, field_to_mod, mod_field_vals):
609 """
610 Create an action to modify the field indicated in field_to_mod
611
612 @param parent Must implement, assertTrue
613 @param field_to_mod The field to modify as a string name
614 @param mod_field_vals Hash of values to use for modified values
615 """
616
617 act = None
618
619 if field_to_mod in ['pktlen']:
620 return None
621
622 if field_to_mod == 'dl_dst':
623 act = action.action_set_dl_dst()
624 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
625 elif field_to_mod == 'dl_src':
626 act = action.action_set_dl_src()
627 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
628 elif field_to_mod == 'dl_vlan_enable':
629 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
630 act = action.action_strip_vlan()
631 # Add VLAN tag is handled by dl_vlan field
632 # Will return None in this case
633 elif field_to_mod == 'dl_vlan':
634 act = action.action_set_vlan_vid()
635 act.vlan_vid = mod_field_vals['dl_vlan']
636 elif field_to_mod == 'dl_vlan_pcp':
637 act = action.action_set_vlan_pcp()
638 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
639 elif field_to_mod == 'ip_src':
640 act = action.action_set_nw_src()
641 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
642 elif field_to_mod == 'ip_dst':
643 act = action.action_set_nw_dst()
644 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
645 elif field_to_mod == 'ip_tos':
646 act = action.action_set_nw_tos()
647 act.nw_tos = mod_field_vals['ip_tos']
648 elif field_to_mod == 'tcp_sport':
649 act = action.action_set_tp_src()
650 act.tp_port = mod_field_vals['tcp_sport']
651 elif field_to_mod == 'tcp_dport':
652 act = action.action_set_tp_dst()
653 act.tp_port = mod_field_vals['tcp_dport']
654 else:
655 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
656
657 return act
658
659def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
660 mod_fields={}, check_test_params=False):
661 """
662 Set up the ingress and expected packet and action list for a test
663
664 @param parent Must implement, assertTrue, config hash and logger
665 @param start_field_values Field values to use for ingress packet (optional)
666 @param mod_field_values Field values to use for modified packet (optional)
667 @param mod_fields The list of fields to be modified by the switch in the test.
668 @params check_test_params If True, will check the parameters vid, add_vlan
669 and strip_vlan from the command line.
670
671 Returns a triple: pkt-to-send, expected-pkt, action-list
672 """
673
674 new_actions = []
675
Dan Talayco4b2bee62010-07-20 14:10:05 -0700676 base_pkt_params = {}
677 base_pkt_params['pktlen'] = 100
678 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
679 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
680 base_pkt_params['dl_vlan_enable'] = False
681 base_pkt_params['dl_vlan'] = 2
682 base_pkt_params['dl_vlan_pcp'] = 0
683 base_pkt_params['ip_src'] = '192.168.0.1'
684 base_pkt_params['ip_dst'] = '192.168.0.2'
685 base_pkt_params['ip_tos'] = 0
686 base_pkt_params['tcp_sport'] = 1234
687 base_pkt_params['tcp_dport'] = 80
688 for keyname in start_field_vals.keys():
689 base_pkt_params[keyname] = start_field_vals[keyname]
690
691 mod_pkt_params = {}
692 mod_pkt_params['pktlen'] = 100
693 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
694 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
695 mod_pkt_params['dl_vlan_enable'] = False
696 mod_pkt_params['dl_vlan'] = 3
697 mod_pkt_params['dl_vlan_pcp'] = 7
698 mod_pkt_params['ip_src'] = '10.20.30.40'
699 mod_pkt_params['ip_dst'] = '50.60.70.80'
700 mod_pkt_params['ip_tos'] = 0xf0
701 mod_pkt_params['tcp_sport'] = 4321
702 mod_pkt_params['tcp_dport'] = 8765
703 for keyname in mod_field_vals.keys():
704 mod_pkt_params[keyname] = mod_field_vals[keyname]
705
706 # Check for test param modifications
707 strip = False
708 if check_test_params:
709 add_vlan = test_param_get(parent.config, 'add_vlan')
710 strip_vlan = test_param_get(parent.config, 'strip_vlan')
711 vid = test_param_get(parent.config, 'vid')
712
713 if add_vlan and strip_vlan:
714 parent.assertTrue(0, "Add and strip VLAN both specified")
715
716 if vid:
717 base_pkt_params['dl_vlan_enable'] = True
718 base_pkt_params['dl_vlan'] = vid
719 if 'dl_vlan' in mod_fields:
720 mod_pkt_params['dl_vlan'] = vid + 1
721
722 if add_vlan:
723 base_pkt_params['dl_vlan_enable'] = False
724 mod_pkt_params['dl_vlan_enable'] = True
725 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
726 mod_fields.append('pktlen')
727 mod_fields.append('dl_vlan_enable')
728 if 'dl_vlan' not in mod_fields:
729 mod_fields.append('dl_vlan')
730 elif strip_vlan:
731 base_pkt_params['dl_vlan_enable'] = True
732 mod_pkt_params['dl_vlan_enable'] = False
733 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
734 mod_fields.append('dl_vlan_enable')
735 mod_fields.append('pktlen')
736
737 # Build the ingress packet
738 ingress_pkt = simple_tcp_packet(**base_pkt_params)
739
740 # Build the expected packet, modifying the indicated fields
741 for item in mod_fields:
742 base_pkt_params[item] = mod_pkt_params[item]
743 act = action_generate(parent, item, mod_pkt_params)
744 if act:
745 new_actions.append(act)
746
747 expected_pkt = simple_tcp_packet(**base_pkt_params)
748
749 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700750
751# Generate a simple "drop" flow mod
752# If in_band is true, then only drop from first test port
753def flow_mod_gen(port_map, in_band):
754 request = message.flow_mod()
755 request.match.wildcards = ofp.OFPFW_ALL
756 if in_band:
757 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
758 for of_port, ifname in port_map.items(): # Grab first port
759 break
760 request.match.in_port = of_port
761 request.buffer_id = 0xffffffff
762 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700763
764def skip_message_emit(parent, s):
765 """
766 Print out a 'skipped' message to stderr
767
768 @param s The string to print out to the log file
769 @param parent Must implement config and logger objects
770 """
771 global skipped_test_count
772
773 skipped_test_count += 1
774 parent.logger.info("Skipping: " + s)
775 if parent.config["dbg_level"] < logging.WARNING:
776 sys.stderr.write("(skipped) ")
777 else:
778 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700779