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