blob: b9e69d41f258a4ea2d3d1fa27a2cb3c7457d2b83 [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
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000030MINSIZE = 0
31
Dan Talayco98fada92010-07-17 00:36:21 -070032def clear_switch(parent, port_list, logger):
33 """
34 Clear the switch configuration
35
36 @param parent Object implementing controller and assert equal
37 @param logger Logging object
38 """
39 for port in port_list:
40 clear_port_config(parent, port, logger)
41 delete_all_flows(parent.controller, logger)
42
Dan Talayco41eae8b2010-03-10 13:57:06 -080043def delete_all_flows(ctrl, logger):
44 """
45 Delete all flows on the switch
46 @param ctrl The controller object for the test
47 @param logger Logging object
48 """
49
Dan Talaycoc901f4d2010-03-07 21:55:45 -080050 logger.info("Deleting all flows")
51 msg = message.flow_mod()
52 msg.match.wildcards = ofp.OFPFW_ALL
Dan Talayco41eae8b2010-03-10 13:57:06 -080053 msg.out_port = ofp.OFPP_NONE
Dan Talaycoc901f4d2010-03-07 21:55:45 -080054 msg.command = ofp.OFPFC_DELETE
55 msg.buffer_id = 0xffffffff
Dan Talayco41eae8b2010-03-10 13:57:06 -080056 return ctrl.message_send(msg)
57
Dan Talayco98fada92010-07-17 00:36:21 -070058def clear_port_config(parent, port, logger):
59 """
60 Clear the port configuration (currently only no flood setting)
61
62 @param parent Object implementing controller and assert equal
63 @param logger Logging object
64 """
65 rv = port_config_set(parent.controller, port,
66 0, ofp.OFPPC_NO_FLOOD, logger)
67 self.assertEqual(rv, 0, "Failed to reset port config")
68
Ed Swierk99a74de2012-08-22 06:40:54 -070069def required_wildcards(parent):
70 w = test_param_get(parent.config, 'required_wildcards', default='default')
71 if w == 'l3-l4':
72 return (ofp.OFPFW_NW_SRC_ALL | ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS
73 | ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST)
74 else:
75 return 0
76
Dan Talayco41eae8b2010-03-10 13:57:06 -080077def simple_tcp_packet(pktlen=100,
78 dl_dst='00:01:02:03:04:05',
79 dl_src='00:06:07:08:09:0a',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070080 dl_vlan_enable=False,
81 dl_vlan=0,
82 dl_vlan_pcp=0,
Dan Talayco551befa2010-07-15 17:05:32 -070083 dl_vlan_cfi=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080084 ip_src='192.168.0.1',
85 ip_dst='192.168.0.2',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070086 ip_tos=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080087 tcp_sport=1234,
88 tcp_dport=80
89 ):
90 """
91 Return a simple dataplane TCP packet
92
93 Supports a few parameters:
94 @param len Length of packet in bytes w/o CRC
95 @param dl_dst Destinatino MAC
96 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070097 @param dl_vlan_enable True if the packet is with vlan, False otherwise
98 @param dl_vlan VLAN ID
99 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -0800100 @param ip_src IP source
101 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700102 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -0800103 @param tcp_dport TCP destination port
104 @param ip_sport TCP source port
105
106 Generates a simple TCP request. Users
107 shouldn't assume anything about this packet other than that
108 it is a valid ethernet/IP/TCP frame.
109 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000110
111 if MINSIZE > pktlen:
112 pktlen = MINSIZE
113
Dan Talayco551befa2010-07-15 17:05:32 -0700114 # Note Dot1Q.id is really CFI
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700115 if (dl_vlan_enable):
116 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -0700117 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700118 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
119 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
120 else:
121 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
122 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
123 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
124
Dan Talayco41eae8b2010-03-10 13:57:06 -0800125 pkt = pkt/("D" * (pktlen - len(pkt)))
126
127 return pkt
128
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700129def simple_icmp_packet(pktlen=60,
130 dl_dst='00:01:02:03:04:05',
131 dl_src='00:06:07:08:09:0a',
132 dl_vlan_enable=False,
133 dl_vlan=0,
134 dl_vlan_pcp=0,
135 ip_src='192.168.0.1',
136 ip_dst='192.168.0.2',
137 ip_tos=0,
138 icmp_type=8,
139 icmp_code=0
140 ):
141 """
142 Return a simple ICMP packet
143
144 Supports a few parameters:
145 @param len Length of packet in bytes w/o CRC
146 @param dl_dst Destinatino MAC
147 @param dl_src Source MAC
148 @param dl_vlan_enable True if the packet is with vlan, False otherwise
149 @param dl_vlan VLAN ID
150 @param dl_vlan_pcp VLAN priority
151 @param ip_src IP source
152 @param ip_dst IP destination
153 @param ip_tos IP ToS
154 @param icmp_type ICMP type
155 @param icmp_code ICMP code
156
157 Generates a simple ICMP ECHO REQUEST. Users
158 shouldn't assume anything about this packet other than that
159 it is a valid ethernet/ICMP frame.
160 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000161
162 if MINSIZE > pktlen:
163 pktlen = MINSIZE
164
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700165 if (dl_vlan_enable):
166 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
167 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
168 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
169 scapy.ICMP(type=icmp_type, code=icmp_code)
170 else:
171 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
172 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
173 scapy.ICMP(type=icmp_type, code=icmp_code)
174
175 pkt = pkt/("0" * (pktlen - len(pkt)))
176
177 return pkt
178
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700179def simple_eth_packet(pktlen=60,
180 dl_dst='00:01:02:03:04:05',
181 dl_src='01:80:c2:00:00:00',
182 dl_type=0x88cc):
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000183
184 if MINSIZE > pktlen:
185 pktlen = MINSIZE
186
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700187 pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
188
189 pkt = pkt/("0" * (pktlen - len(pkt)))
190
191 return pkt
192
Dan Talayco41eae8b2010-03-10 13:57:06 -0800193def do_barrier(ctrl):
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700194 """
195 Do a barrier command
196 Return 0 on success, -1 on error
197 """
Dan Talayco41eae8b2010-03-10 13:57:06 -0800198 b = message.barrier_request()
Dan Talaycof6b94832012-04-12 21:50:57 -0700199 (resp, pkt) = ctrl.transact(b)
200 # We'll trust the transaction processing in the controller that xid matched
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700201 if not resp:
202 return -1
203 return 0
Dan Talayco92c99122010-06-03 13:53:18 -0700204
205def port_config_get(controller, port_no, logger):
206 """
207 Get a port's configuration
208
209 Gets the switch feature configuration and grabs one port's
210 configuration
211
212 @returns (hwaddr, config, advert) The hwaddress, configuration and
213 advertised values
214 """
215 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700216 reply, pkt = controller.transact(request)
Dan Talayco92c99122010-06-03 13:53:18 -0700217 logger.debug(reply.show())
218 if reply is None:
219 logger.warn("Get feature request failed")
220 return None, None, None
221 for idx in range(len(reply.ports)):
222 if reply.ports[idx].port_no == port_no:
223 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
224 reply.ports[idx].advertised)
225
226 logger.warn("Did not find port number for port config")
227 return None, None, None
228
229def port_config_set(controller, port_no, config, mask, logger):
230 """
231 Set the port configuration according the given parameters
232
233 Gets the switch feature configuration and updates one port's
234 configuration value according to config and mask
235 """
236 logger.info("Setting port " + str(port_no) + " to config " + str(config))
237 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700238 reply, pkt = controller.transact(request)
Dan Talayco92c99122010-06-03 13:53:18 -0700239 if reply is None:
240 return -1
241 logger.debug(reply.show())
242 for idx in range(len(reply.ports)):
243 if reply.ports[idx].port_no == port_no:
244 break
245 if idx >= len(reply.ports):
246 return -1
247 mod = message.port_mod()
248 mod.port_no = port_no
249 mod.hw_addr = reply.ports[idx].hw_addr
250 mod.config = config
251 mod.mask = mask
252 mod.advertise = reply.ports[idx].advertised
253 rv = controller.message_send(mod)
254 return rv
255
Ken Chiang1bf01602012-04-04 10:48:23 -0700256def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if, logger,
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700257 config):
Dan Talayco92c99122010-06-03 13:53:18 -0700258 """
259 Check for proper receive packets across all ports
Ken Chiang1bf01602012-04-04 10:48:23 -0700260 @param dp The dataplane object
Dan Talayco92c99122010-06-03 13:53:18 -0700261 @param pkt Expected packet; may be None if yes_ports is empty
262 @param yes_ports Set or list of ports that should recieve packet
263 @param no_ports Set or list of ports that should not receive packet
264 @param assert_if Object that implements assertXXX
265 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700266 exp_pkt_arg = None
267 if config and config["relax"]:
268 exp_pkt_arg = pkt
269
Dan Talayco92c99122010-06-03 13:53:18 -0700270 for ofport in yes_ports:
271 logger.debug("Checking for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700272 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700273 port_number=ofport, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700274 assert_if.assertTrue(rcv_pkt is not None,
275 "Did not receive pkt on " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700276 if not dataplane.match_exp_pkt(pkt, rcv_pkt):
277 logger.debug("Sent %s" % format_packet(pkt))
278 logger.debug("Resp %s" % format_packet(rcv_pkt))
279 assert_if.assertTrue(dataplane.match_exp_pkt(pkt, rcv_pkt),
280 "Response packet does not match send packet " +
281 "on port " + str(ofport))
Dan Talayco92c99122010-06-03 13:53:18 -0700282
283 for ofport in no_ports:
284 logger.debug("Negative check for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700285 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700286 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700287 assert_if.assertTrue(rcv_pkt is None,
288 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700289
290
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700291def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700292 """
293 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700294 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700295
296 parent must implement dataplane, assertTrue and assertEqual
297 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700298 exp_pkt_arg = None
299 if parent.config["relax"]:
300 exp_pkt_arg = exp_pkt
301
Dan Talaycof6e76c02012-03-23 10:56:12 -0700302 if type(egr_ports) == type([]):
303 egr_port_list = egr_ports
304 else:
305 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700306
Dan Talaycof6e76c02012-03-23 10:56:12 -0700307 # Expect a packet from each port on egr port list
308 for egr_port in egr_port_list:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700309 check_port = egr_port
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700310 if egr_port == ofp.OFPP_IN_PORT:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700311 check_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700312 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700313 port_number=check_port, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700314
Dan Talaycof6e76c02012-03-23 10:56:12 -0700315 if rcv_pkt is None:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700316 parent.logger.error("ERROR: No packet received from " +
317 str(check_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700318
Dan Talaycof6e76c02012-03-23 10:56:12 -0700319 parent.assertTrue(rcv_pkt is not None,
Dan Talaycod8ae7582012-03-23 12:24:56 -0700320 "Did not receive packet port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700321 parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
322 str(rcv_port))
323
324 if str(exp_pkt) != str(rcv_pkt):
325 parent.logger.error("ERROR: Packet match failed.")
326 parent.logger.debug("Expected len " + str(len(exp_pkt)) + ": "
327 + str(exp_pkt).encode('hex'))
328 parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
329 + str(rcv_pkt).encode('hex'))
330 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
Dan Talaycod8ae7582012-03-23 12:24:56 -0700331 "Packet match error on port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700332
Dan Talayco551befa2010-07-15 17:05:32 -0700333def match_verify(parent, req_match, res_match):
334 """
335 Verify flow matches agree; if they disagree, report where
336
337 parent must implement assertEqual
338 Use str() to ensure content is compared and not pointers
339 """
340
341 parent.assertEqual(req_match.wildcards, res_match.wildcards,
342 'Match failed: wildcards: ' + hex(req_match.wildcards) +
343 " != " + hex(res_match.wildcards))
344 parent.assertEqual(req_match.in_port, res_match.in_port,
345 'Match failed: in_port: ' + str(req_match.in_port) +
346 " != " + str(res_match.in_port))
347 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
348 'Match failed: dl_src: ' + str(req_match.dl_src) +
349 " != " + str(res_match.dl_src))
350 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
351 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
352 " != " + str(res_match.dl_dst))
353 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
354 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
355 " != " + str(res_match.dl_vlan))
356 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
357 'Match failed: dl_vlan_pcp: ' +
358 str(req_match.dl_vlan_pcp) + " != " +
359 str(res_match.dl_vlan_pcp))
360 parent.assertEqual(req_match.dl_type, res_match.dl_type,
361 'Match failed: dl_type: ' + str(req_match.dl_type) +
362 " != " + str(res_match.dl_type))
363
364 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
365 and (req_match.dl_type == IP_ETHERTYPE)):
366 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
367 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
368 " != " + str(res_match.nw_tos))
369 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
370 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
371 " != " + str(res_match.nw_proto))
372 parent.assertEqual(req_match.nw_src, res_match.nw_src,
373 'Match failed: nw_src: ' + str(req_match.nw_src) +
374 " != " + str(res_match.nw_src))
375 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
376 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
377 " != " + str(res_match.nw_dst))
378
379 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
380 and ((req_match.nw_proto == TCP_PROTOCOL)
381 or (req_match.nw_proto == UDP_PROTOCOL))):
382 parent.assertEqual(req_match.tp_src, res_match.tp_src,
383 'Match failed: tp_src: ' +
384 str(req_match.tp_src) +
385 " != " + str(res_match.tp_src))
386 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
387 'Match failed: tp_dst: ' +
388 str(req_match.tp_dst) +
389 " != " + str(res_match.tp_dst))
390
391def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
392 """
393 Receive a flow removed msg and verify it matches expected
394
395 @params parent Must implement controller, assertEqual
396 @param pkt_count If >= 0, verify packet count
397 @param byte_count If >= 0, verify byte count
398 """
399 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
400 parent.assertTrue(response is not None, 'No flow removed message received')
401
402 if request is None:
403 return
404
405 parent.assertEqual(request.cookie, response.cookie,
406 "Flow removed cookie error: " +
407 hex(request.cookie) + " != " + hex(response.cookie))
408
409 req_match = request.match
410 res_match = response.match
411 verifyMatchField(req_match, res_match)
412
413 if (req_match.wildcards != 0):
414 parent.assertEqual(request.priority, response.priority,
415 'Flow remove prio mismatch: ' +
416 str(request,priority) + " != " +
417 str(response.priority))
418 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
419 'Flow remove reason is not HARD TIMEOUT:' +
420 str(response.reason))
421 if pkt_count >= 0:
422 parent.assertEqual(response.packet_count, pkt_count,
423 'Flow removed failed, packet count: ' +
424 str(response.packet_count) + " != " +
425 str(pkt_count))
426 if byte_count >= 0:
427 parent.assertEqual(response.byte_count, byte_count,
428 'Flow removed failed, byte count: ' +
429 str(response.byte_count) + " != " +
430 str(byte_count))
431
Ed Swierk99a74de2012-08-22 06:40:54 -0700432def packet_to_flow_match(parent, packet):
433 match = parse.packet_to_flow_match(packet)
434 match.wildcards |= required_wildcards(parent)
435 return match
436
437def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700438 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700439 """
440 Create a flow message
441
442 Match on packet with given wildcards.
443 See flow_match_test for other parameter descriptoins
444 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700445 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700446 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700447 """
448 match = parse.packet_to_flow_match(pkt)
449 parent.assertTrue(match is not None, "Flow match from pkt failed")
Ed Swierk99a74de2012-08-22 06:40:54 -0700450 if wildcards is None:
451 wildcards = required_wildcards(parent)
Dan Talayco677c0b72011-08-23 22:53:38 -0700452 if in_band:
453 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700454 match.wildcards = wildcards
455 match.in_port = ing_port
456
Dan Talaycof6e76c02012-03-23 10:56:12 -0700457 if type(egr_ports) == type([]):
458 egr_port_list = egr_ports
459 else:
460 egr_port_list = [egr_ports]
461
Dan Talayco551befa2010-07-15 17:05:32 -0700462 request = message.flow_mod()
463 request.match = match
464 request.buffer_id = 0xffffffff
465 if check_expire:
466 request.flags |= ofp.OFPFF_SEND_FLOW_REM
467 request.hard_timeout = 1
468
469 if action_list is not None:
470 for act in action_list:
471 parent.logger.debug("Adding action " + act.show())
472 rv = request.actions.add(act)
473 parent.assertTrue(rv, "Could not add action" + act.show())
474
475 # Set up output/enqueue action if directed
476 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700477 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700478 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700479 for egr_port in egr_port_list:
480 act.port = egr_port
481 act.queue_id = egr_queue
482 rv = request.actions.add(act)
483 parent.assertTrue(rv, "Could not add enqueue action " +
484 str(egr_port) + " Q: " + str(egr_queue))
485 elif egr_ports is not None:
486 for egr_port in egr_port_list:
487 act = action.action_output()
488 act.port = egr_port
489 rv = request.actions.add(act)
490 parent.assertTrue(rv, "Could not add output action " +
491 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700492
493 parent.logger.debug(request.show())
494
495 return request
496
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700497def flow_msg_install(parent, request, clear_table_override=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700498 """
499 Install a flow mod message in the switch
500
501 @param parent Must implement controller, assertEqual, assertTrue
502 @param request The request, all set to go
503 @param clear_table If true, clear the flow table before installing
504 """
Dan Talayco8a64e332012-03-28 14:53:20 -0700505
506 clear_table = test_param_get(parent.config, 'clear_table', default=True)
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700507 if(clear_table_override != None):
508 clear_table = clear_table_override
509
510 if clear_table:
Dan Talayco551befa2010-07-15 17:05:32 -0700511 parent.logger.debug("Clear flow table")
512 rc = delete_all_flows(parent.controller, parent.logger)
513 parent.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700514 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700515
516 parent.logger.debug("Insert flow")
517 rv = parent.controller.message_send(request)
518 parent.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700519 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700520
Ed Swierk99a74de2012-08-22 06:40:54 -0700521def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700522 dl_vlan=-1, pkt=None, exp_pkt=None,
523 action_list=None, check_expire=False):
524 """
525 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700526 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700527
528 Run test with packet through switch from ing_port to egr_port
529 See flow_match_test for parameter descriptions
530 """
531
Ed Swierk99a74de2012-08-22 06:40:54 -0700532 if wildcards is None:
533 wildcards = required_wildcards(parent)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700534 parent.logger.info("Pkt match test: " + str(ing_port) + " to " +
535 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700536 parent.logger.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700537 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700538 if pkt is None:
539 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
540
541 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700542 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700543 action_list=action_list)
544
545 flow_msg_install(parent, request)
546
Dan Talaycof6e76c02012-03-23 10:56:12 -0700547 parent.logger.debug("Send packet: " + str(ing_port) + " to " +
548 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700549 parent.dataplane.send(ing_port, str(pkt))
550
551 if exp_pkt is None:
552 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700553 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700554
555 if check_expire:
556 #@todo Not all HW supports both pkt and byte counters
557 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
558
Dan Talaycof6e76c02012-03-23 10:56:12 -0700559def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
560 """
561 Generate a list of ports avoiding those in the exclude list
562 @param parent Supplies logger
563 @param of_ports List of OF port numbers
564 @param how_many Number of ports to be added to the list
565 @param exclude_list List of ports not to be used
566 @returns An empty list if unable to find enough ports
567 """
568
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700569 if how_many == 0:
570 return []
571
Dan Talaycof6e76c02012-03-23 10:56:12 -0700572 count = 0
573 egr_ports = []
574 for egr_idx in range(len(of_ports)):
575 if of_ports[egr_idx] not in exclude_list:
576 egr_ports.append(of_ports[egr_idx])
577 count += 1
578 if count >= how_many:
579 return egr_ports
580 parent.logger.debug("Could not generate enough egress ports for test")
581 return []
582
Ed Swierk99a74de2012-08-22 06:40:54 -0700583def flow_match_test(parent, port_map, wildcards=None, dl_vlan=-1, pkt=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700584 exp_pkt=None, action_list=None, check_expire=False,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700585 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700586 """
587 Run flow_match_test_port_pair on all port pairs
588
589 @param max_test If > 0 no more than this number of tests are executed.
590 @param parent Must implement controller, dataplane, assertTrue, assertEqual
591 and logger
592 @param pkt If not None, use this packet for ingress
593 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700594 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700595 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
596 @param action_list Additional actions to add to flow mod
597 @param check_expire Check for flow expiration message
Dan Talaycocfa172f2012-03-23 12:03:00 -0700598 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700599 """
Ed Swierk99a74de2012-08-22 06:40:54 -0700600 if wildcards is None:
601 wildcards = required_wildcards(parent)
Dan Talayco551befa2010-07-15 17:05:32 -0700602 of_ports = port_map.keys()
603 of_ports.sort()
604 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
605 test_count = 0
606
Dan Talaycocfa172f2012-03-23 12:03:00 -0700607 if egr_count == -1:
608 egr_count = test_param_get(parent.config, 'egr_count', default=2)
609
Dan Talayco551befa2010-07-15 17:05:32 -0700610 for ing_idx in range(len(of_ports)):
611 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700612 egr_ports = get_egr_list(parent, of_ports, egr_count,
613 exclude_list=[ingress_port])
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700614 if ing_port:
615 egr_ports.append(ofp.OFPP_IN_PORT)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700616 if len(egr_ports) == 0:
617 parent.assertTrue(0, "Failed to generate egress port list")
618
619 flow_match_test_port_pair(parent, ingress_port, egr_ports,
620 wildcards=wildcards, dl_vlan=dl_vlan,
621 pkt=pkt, exp_pkt=exp_pkt,
622 action_list=action_list,
623 check_expire=check_expire)
624 test_count += 1
625 if (max_test > 0) and (test_count > max_test):
626 parent.logger.info("Ran " + str(test_count) + " tests; exiting")
627 return
Dan Talayco551befa2010-07-15 17:05:32 -0700628
Dan Talayco4b2bee62010-07-20 14:10:05 -0700629def test_param_get(config, key, default=None):
630 """
631 Return value passed via test-params if present
632
633 @param config The configuration structure for OFTest
634 @param key The lookup key
635 @param default Default value to use if not found
636
637 If the pair 'key=val' appeared in the string passed to --test-params
638 on the command line, return val (as interpreted by exec). Otherwise
639 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700640
641 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
642 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700643 """
644 try:
645 exec config["test_params"]
646 except:
647 return default
648
649 s = "val = " + str(key)
650 try:
651 exec s
652 return val
653 except:
654 return default
655
656def action_generate(parent, field_to_mod, mod_field_vals):
657 """
658 Create an action to modify the field indicated in field_to_mod
659
660 @param parent Must implement, assertTrue
661 @param field_to_mod The field to modify as a string name
662 @param mod_field_vals Hash of values to use for modified values
663 """
664
665 act = None
666
667 if field_to_mod in ['pktlen']:
668 return None
669
670 if field_to_mod == 'dl_dst':
671 act = action.action_set_dl_dst()
672 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
673 elif field_to_mod == 'dl_src':
674 act = action.action_set_dl_src()
675 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
676 elif field_to_mod == 'dl_vlan_enable':
677 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
678 act = action.action_strip_vlan()
679 # Add VLAN tag is handled by dl_vlan field
680 # Will return None in this case
681 elif field_to_mod == 'dl_vlan':
682 act = action.action_set_vlan_vid()
683 act.vlan_vid = mod_field_vals['dl_vlan']
684 elif field_to_mod == 'dl_vlan_pcp':
685 act = action.action_set_vlan_pcp()
686 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
687 elif field_to_mod == 'ip_src':
688 act = action.action_set_nw_src()
689 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
690 elif field_to_mod == 'ip_dst':
691 act = action.action_set_nw_dst()
692 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
693 elif field_to_mod == 'ip_tos':
694 act = action.action_set_nw_tos()
695 act.nw_tos = mod_field_vals['ip_tos']
696 elif field_to_mod == 'tcp_sport':
697 act = action.action_set_tp_src()
698 act.tp_port = mod_field_vals['tcp_sport']
699 elif field_to_mod == 'tcp_dport':
700 act = action.action_set_tp_dst()
701 act.tp_port = mod_field_vals['tcp_dport']
702 else:
703 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
704
705 return act
706
707def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
708 mod_fields={}, check_test_params=False):
709 """
710 Set up the ingress and expected packet and action list for a test
711
712 @param parent Must implement, assertTrue, config hash and logger
713 @param start_field_values Field values to use for ingress packet (optional)
714 @param mod_field_values Field values to use for modified packet (optional)
715 @param mod_fields The list of fields to be modified by the switch in the test.
716 @params check_test_params If True, will check the parameters vid, add_vlan
717 and strip_vlan from the command line.
718
719 Returns a triple: pkt-to-send, expected-pkt, action-list
720 """
721
722 new_actions = []
723
Dan Talayco4b2bee62010-07-20 14:10:05 -0700724 base_pkt_params = {}
725 base_pkt_params['pktlen'] = 100
726 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
727 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
728 base_pkt_params['dl_vlan_enable'] = False
729 base_pkt_params['dl_vlan'] = 2
730 base_pkt_params['dl_vlan_pcp'] = 0
731 base_pkt_params['ip_src'] = '192.168.0.1'
732 base_pkt_params['ip_dst'] = '192.168.0.2'
733 base_pkt_params['ip_tos'] = 0
734 base_pkt_params['tcp_sport'] = 1234
735 base_pkt_params['tcp_dport'] = 80
736 for keyname in start_field_vals.keys():
737 base_pkt_params[keyname] = start_field_vals[keyname]
738
739 mod_pkt_params = {}
740 mod_pkt_params['pktlen'] = 100
741 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
742 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
743 mod_pkt_params['dl_vlan_enable'] = False
744 mod_pkt_params['dl_vlan'] = 3
745 mod_pkt_params['dl_vlan_pcp'] = 7
746 mod_pkt_params['ip_src'] = '10.20.30.40'
747 mod_pkt_params['ip_dst'] = '50.60.70.80'
748 mod_pkt_params['ip_tos'] = 0xf0
749 mod_pkt_params['tcp_sport'] = 4321
750 mod_pkt_params['tcp_dport'] = 8765
751 for keyname in mod_field_vals.keys():
752 mod_pkt_params[keyname] = mod_field_vals[keyname]
753
754 # Check for test param modifications
755 strip = False
756 if check_test_params:
757 add_vlan = test_param_get(parent.config, 'add_vlan')
758 strip_vlan = test_param_get(parent.config, 'strip_vlan')
759 vid = test_param_get(parent.config, 'vid')
760
761 if add_vlan and strip_vlan:
762 parent.assertTrue(0, "Add and strip VLAN both specified")
763
764 if vid:
765 base_pkt_params['dl_vlan_enable'] = True
766 base_pkt_params['dl_vlan'] = vid
767 if 'dl_vlan' in mod_fields:
768 mod_pkt_params['dl_vlan'] = vid + 1
769
770 if add_vlan:
771 base_pkt_params['dl_vlan_enable'] = False
772 mod_pkt_params['dl_vlan_enable'] = True
773 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
774 mod_fields.append('pktlen')
775 mod_fields.append('dl_vlan_enable')
776 if 'dl_vlan' not in mod_fields:
777 mod_fields.append('dl_vlan')
778 elif strip_vlan:
779 base_pkt_params['dl_vlan_enable'] = True
780 mod_pkt_params['dl_vlan_enable'] = False
781 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
782 mod_fields.append('dl_vlan_enable')
783 mod_fields.append('pktlen')
784
785 # Build the ingress packet
786 ingress_pkt = simple_tcp_packet(**base_pkt_params)
787
788 # Build the expected packet, modifying the indicated fields
789 for item in mod_fields:
790 base_pkt_params[item] = mod_pkt_params[item]
791 act = action_generate(parent, item, mod_pkt_params)
792 if act:
793 new_actions.append(act)
794
795 expected_pkt = simple_tcp_packet(**base_pkt_params)
796
797 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700798
799# Generate a simple "drop" flow mod
800# If in_band is true, then only drop from first test port
801def flow_mod_gen(port_map, in_band):
802 request = message.flow_mod()
803 request.match.wildcards = ofp.OFPFW_ALL
804 if in_band:
805 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
806 for of_port, ifname in port_map.items(): # Grab first port
807 break
808 request.match.in_port = of_port
809 request.buffer_id = 0xffffffff
810 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700811
812def skip_message_emit(parent, s):
813 """
814 Print out a 'skipped' message to stderr
815
816 @param s The string to print out to the log file
817 @param parent Must implement config and logger objects
818 """
819 global skipped_test_count
820
821 skipped_test_count += 1
822 parent.logger.info("Skipping: " + s)
823 if parent.config["dbg_level"] < logging.WARNING:
824 sys.stderr.write("(skipped) ")
825 else:
826 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700827
Dan Talayco8a64e332012-03-28 14:53:20 -0700828
829def all_stats_get(parent):
830 """
831 Get the aggregate stats for all flows in the table
832 @param parent Test instance with controller connection and assert
833 @returns dict with keys flows, packets, bytes, active (flows),
834 lookups, matched
835 """
836 stat_req = message.aggregate_stats_request()
837 stat_req.match = ofp.ofp_match()
838 stat_req.match.wildcards = ofp.OFPFW_ALL
839 stat_req.table_id = 0xff
840 stat_req.out_port = ofp.OFPP_NONE
841
842 rv = {}
843
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700844 (reply, pkt) = parent.controller.transact(stat_req)
Dan Talayco8a64e332012-03-28 14:53:20 -0700845 parent.assertTrue(len(reply.stats) == 1, "Did not receive flow stats reply")
846
847 for obj in reply.stats:
848 (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
849 obj.packet_count, obj.byte_count)
850 break
851
852 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700853 (reply , pkt) = parent.controller.transact(request)
Dan Talayco8a64e332012-03-28 14:53:20 -0700854
855
856 (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
857 for obj in reply.stats:
858 rv["active"] += obj.active_count
859 rv["lookups"] += obj.lookup_count
860 rv["matched"] += obj.matched_count
861
862 return rv
Dan Talayco2baf8b52012-03-30 09:55:42 -0700863
864FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
865 for x in range(256)])
866
867def hex_dump_buffer(src, length=16):
868 """
869 Convert src to a hex dump string and return the string
870 @param src The source buffer
871 @param length The number of bytes shown in each line
872 @returns A string showing the hex dump
873 """
Dan Talaycoc516fa02012-04-12 22:28:43 -0700874 result = ["\n"]
Dan Talayco2baf8b52012-03-30 09:55:42 -0700875 for i in xrange(0, len(src), length):
876 chars = src[i:i+length]
877 hex = ' '.join(["%02x" % ord(x) for x in chars])
878 printable = ''.join(["%s" % ((ord(x) <= 127 and
879 FILTER[ord(x)]) or '.') for x in chars])
880 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
881 return ''.join(result)
882
883def format_packet(pkt):
884 return "Packet length %d \n%s" % (len(str(pkt)),
885 hex_dump_buffer(str(pkt)))