blob: 3be9c32fb3390d6437f72c107e36691226b45841 [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
Rich Lane477f4812012-10-04 22:49:00 -070013from oftest import config
Dan Talaycoc901f4d2010-03-07 21:55:45 -080014import oftest.controller as controller
15import oftest.cstruct as ofp
16import oftest.message as message
17import oftest.dataplane as dataplane
18import oftest.action as action
Dan Talayco41eae8b2010-03-10 13:57:06 -080019import oftest.parse as parse
Dan Talaycoc901f4d2010-03-07 21:55:45 -080020import logging
Dan Talayco4b2bee62010-07-20 14:10:05 -070021import types
Dan Talayco73f84012012-10-02 09:23:18 -070022import time
Dan Talaycoc901f4d2010-03-07 21:55:45 -080023
Dan Talaycoba3745c2010-07-21 21:51:08 -070024global skipped_test_count
25skipped_test_count = 0
26
Dan Talayco551befa2010-07-15 17:05:32 -070027# Some useful defines
28IP_ETHERTYPE = 0x800
29TCP_PROTOCOL = 0x6
30UDP_PROTOCOL = 0x11
31
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000032MINSIZE = 0
33
Rich Lane9a003812012-10-04 17:17:59 -070034def delete_all_flows(ctrl):
Dan Talayco41eae8b2010-03-10 13:57:06 -080035 """
36 Delete all flows on the switch
37 @param ctrl The controller object for the test
Dan Talayco41eae8b2010-03-10 13:57:06 -080038 """
39
Rich Lane9a003812012-10-04 17:17:59 -070040 logging.info("Deleting all flows")
Dan Talaycoc901f4d2010-03-07 21:55:45 -080041 msg = message.flow_mod()
42 msg.match.wildcards = ofp.OFPFW_ALL
Dan Talayco41eae8b2010-03-10 13:57:06 -080043 msg.out_port = ofp.OFPP_NONE
Dan Talaycoc901f4d2010-03-07 21:55:45 -080044 msg.command = ofp.OFPFC_DELETE
45 msg.buffer_id = 0xffffffff
Dan Talayco41eae8b2010-03-10 13:57:06 -080046 return ctrl.message_send(msg)
47
Ed Swierk99a74de2012-08-22 06:40:54 -070048def required_wildcards(parent):
Rich Lane2014f9b2012-10-05 15:29:40 -070049 w = test_param_get('required_wildcards', default='default')
Ed Swierk99a74de2012-08-22 06:40:54 -070050 if w == 'l3-l4':
51 return (ofp.OFPFW_NW_SRC_ALL | ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS
52 | ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST)
53 else:
54 return 0
55
Dan Talayco41eae8b2010-03-10 13:57:06 -080056def simple_tcp_packet(pktlen=100,
57 dl_dst='00:01:02:03:04:05',
58 dl_src='00:06:07:08:09:0a',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070059 dl_vlan_enable=False,
60 dl_vlan=0,
61 dl_vlan_pcp=0,
Dan Talayco551befa2010-07-15 17:05:32 -070062 dl_vlan_cfi=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080063 ip_src='192.168.0.1',
64 ip_dst='192.168.0.2',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070065 ip_tos=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080066 tcp_sport=1234,
67 tcp_dport=80
68 ):
69 """
70 Return a simple dataplane TCP packet
71
72 Supports a few parameters:
73 @param len Length of packet in bytes w/o CRC
74 @param dl_dst Destinatino MAC
75 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070076 @param dl_vlan_enable True if the packet is with vlan, False otherwise
77 @param dl_vlan VLAN ID
78 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -080079 @param ip_src IP source
80 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -070081 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -080082 @param tcp_dport TCP destination port
83 @param ip_sport TCP source port
84
85 Generates a simple TCP request. Users
86 shouldn't assume anything about this packet other than that
87 it is a valid ethernet/IP/TCP frame.
88 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000089
90 if MINSIZE > pktlen:
91 pktlen = MINSIZE
92
Dan Talayco551befa2010-07-15 17:05:32 -070093 # Note Dot1Q.id is really CFI
Tatsuya Yabe460321e2010-05-25 17:50:49 -070094 if (dl_vlan_enable):
95 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -070096 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -070097 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
98 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
99 else:
100 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
101 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
102 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
103
Dan Talayco41eae8b2010-03-10 13:57:06 -0800104 pkt = pkt/("D" * (pktlen - len(pkt)))
105
106 return pkt
107
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700108def simple_icmp_packet(pktlen=60,
109 dl_dst='00:01:02:03:04:05',
110 dl_src='00:06:07:08:09:0a',
111 dl_vlan_enable=False,
112 dl_vlan=0,
113 dl_vlan_pcp=0,
114 ip_src='192.168.0.1',
115 ip_dst='192.168.0.2',
116 ip_tos=0,
117 icmp_type=8,
118 icmp_code=0
119 ):
120 """
121 Return a simple ICMP packet
122
123 Supports a few parameters:
124 @param len Length of packet in bytes w/o CRC
125 @param dl_dst Destinatino MAC
126 @param dl_src Source MAC
127 @param dl_vlan_enable True if the packet is with vlan, False otherwise
128 @param dl_vlan VLAN ID
129 @param dl_vlan_pcp VLAN priority
130 @param ip_src IP source
131 @param ip_dst IP destination
132 @param ip_tos IP ToS
133 @param icmp_type ICMP type
134 @param icmp_code ICMP code
135
136 Generates a simple ICMP ECHO REQUEST. Users
137 shouldn't assume anything about this packet other than that
138 it is a valid ethernet/ICMP frame.
139 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000140
141 if MINSIZE > pktlen:
142 pktlen = MINSIZE
143
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700144 if (dl_vlan_enable):
145 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
146 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
147 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
148 scapy.ICMP(type=icmp_type, code=icmp_code)
149 else:
150 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
151 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
152 scapy.ICMP(type=icmp_type, code=icmp_code)
153
154 pkt = pkt/("0" * (pktlen - len(pkt)))
155
156 return pkt
157
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700158def simple_eth_packet(pktlen=60,
159 dl_dst='00:01:02:03:04:05',
160 dl_src='01:80:c2:00:00:00',
161 dl_type=0x88cc):
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000162
163 if MINSIZE > pktlen:
164 pktlen = MINSIZE
165
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700166 pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
167
168 pkt = pkt/("0" * (pktlen - len(pkt)))
169
170 return pkt
171
Dan Talayco41eae8b2010-03-10 13:57:06 -0800172def do_barrier(ctrl):
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700173 """
174 Do a barrier command
175 Return 0 on success, -1 on error
176 """
Dan Talayco41eae8b2010-03-10 13:57:06 -0800177 b = message.barrier_request()
Dan Talaycof6b94832012-04-12 21:50:57 -0700178 (resp, pkt) = ctrl.transact(b)
179 # We'll trust the transaction processing in the controller that xid matched
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700180 if not resp:
181 return -1
182 return 0
Dan Talayco92c99122010-06-03 13:53:18 -0700183
Rich Lane9a003812012-10-04 17:17:59 -0700184def port_config_get(controller, port_no):
Dan Talayco92c99122010-06-03 13:53:18 -0700185 """
186 Get a port's configuration
187
188 Gets the switch feature configuration and grabs one port's
189 configuration
190
191 @returns (hwaddr, config, advert) The hwaddress, configuration and
192 advertised values
193 """
194 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700195 reply, pkt = controller.transact(request)
Rich Lane9a003812012-10-04 17:17:59 -0700196 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700197 if reply is None:
Rich Lane9a003812012-10-04 17:17:59 -0700198 logging.warn("Get feature request failed")
Dan Talayco92c99122010-06-03 13:53:18 -0700199 return None, None, None
200 for idx in range(len(reply.ports)):
201 if reply.ports[idx].port_no == port_no:
202 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
203 reply.ports[idx].advertised)
204
Rich Lane9a003812012-10-04 17:17:59 -0700205 logging.warn("Did not find port number for port config")
Dan Talayco92c99122010-06-03 13:53:18 -0700206 return None, None, None
207
Rich Lane9a003812012-10-04 17:17:59 -0700208def port_config_set(controller, port_no, config, mask):
Dan Talayco92c99122010-06-03 13:53:18 -0700209 """
210 Set the port configuration according the given parameters
211
212 Gets the switch feature configuration and updates one port's
213 configuration value according to config and mask
214 """
Rich Lane9a003812012-10-04 17:17:59 -0700215 logging.info("Setting port " + str(port_no) + " to config " + str(config))
Dan Talayco92c99122010-06-03 13:53:18 -0700216 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700217 reply, pkt = controller.transact(request)
Dan Talayco92c99122010-06-03 13:53:18 -0700218 if reply is None:
219 return -1
Rich Lane9a003812012-10-04 17:17:59 -0700220 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700221 for idx in range(len(reply.ports)):
222 if reply.ports[idx].port_no == port_no:
223 break
224 if idx >= len(reply.ports):
225 return -1
226 mod = message.port_mod()
227 mod.port_no = port_no
228 mod.hw_addr = reply.ports[idx].hw_addr
229 mod.config = config
230 mod.mask = mask
231 mod.advertise = reply.ports[idx].advertised
232 rv = controller.message_send(mod)
233 return rv
234
Rich Lane2014f9b2012-10-05 15:29:40 -0700235def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if):
Dan Talayco92c99122010-06-03 13:53:18 -0700236 """
237 Check for proper receive packets across all ports
Ken Chiang1bf01602012-04-04 10:48:23 -0700238 @param dp The dataplane object
Dan Talayco92c99122010-06-03 13:53:18 -0700239 @param pkt Expected packet; may be None if yes_ports is empty
240 @param yes_ports Set or list of ports that should recieve packet
241 @param no_ports Set or list of ports that should not receive packet
242 @param assert_if Object that implements assertXXX
243 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700244 exp_pkt_arg = None
Rich Lane2014f9b2012-10-05 15:29:40 -0700245 if config["relax"]:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700246 exp_pkt_arg = pkt
247
Dan Talayco92c99122010-06-03 13:53:18 -0700248 for ofport in yes_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700249 logging.debug("Checking for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700250 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700251 port_number=ofport, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700252 assert_if.assertTrue(rcv_pkt is not None,
253 "Did not receive pkt on " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700254 if not dataplane.match_exp_pkt(pkt, rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700255 logging.debug("Sent %s" % format_packet(pkt))
256 logging.debug("Resp %s" % format_packet(rcv_pkt))
Ken Chiang1bf01602012-04-04 10:48:23 -0700257 assert_if.assertTrue(dataplane.match_exp_pkt(pkt, rcv_pkt),
258 "Response packet does not match send packet " +
259 "on port " + str(ofport))
Dan Talayco73f84012012-10-02 09:23:18 -0700260 if len(no_ports) > 0:
261 time.sleep(1)
Dan Talayco92c99122010-06-03 13:53:18 -0700262 for ofport in no_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700263 logging.debug("Negative check for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700264 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700265 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700266 assert_if.assertTrue(rcv_pkt is None,
267 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700268
269
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700270def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700271 """
272 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700273 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700274
275 parent must implement dataplane, assertTrue and assertEqual
276 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700277 exp_pkt_arg = None
Rich Lane477f4812012-10-04 22:49:00 -0700278 if config["relax"]:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700279 exp_pkt_arg = exp_pkt
280
Dan Talaycof6e76c02012-03-23 10:56:12 -0700281 if type(egr_ports) == type([]):
282 egr_port_list = egr_ports
283 else:
284 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700285
Dan Talaycof6e76c02012-03-23 10:56:12 -0700286 # Expect a packet from each port on egr port list
287 for egr_port in egr_port_list:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700288 check_port = egr_port
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700289 if egr_port == ofp.OFPP_IN_PORT:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700290 check_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700291 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700292 port_number=check_port, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700293
Dan Talaycof6e76c02012-03-23 10:56:12 -0700294 if rcv_pkt is None:
Rich Lane9a003812012-10-04 17:17:59 -0700295 logging.error("ERROR: No packet received from " +
Dan Talaycod8ae7582012-03-23 12:24:56 -0700296 str(check_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700297
Dan Talaycof6e76c02012-03-23 10:56:12 -0700298 parent.assertTrue(rcv_pkt is not None,
Dan Talaycod8ae7582012-03-23 12:24:56 -0700299 "Did not receive packet port " + str(check_port))
Rich Lane9a003812012-10-04 17:17:59 -0700300 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700301 str(rcv_port))
302
303 if str(exp_pkt) != str(rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700304 logging.error("ERROR: Packet match failed.")
305 logging.debug("Expected len " + str(len(exp_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700306 + str(exp_pkt).encode('hex'))
Rich Lane9a003812012-10-04 17:17:59 -0700307 logging.debug("Received len " + str(len(rcv_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700308 + str(rcv_pkt).encode('hex'))
309 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
Dan Talaycod8ae7582012-03-23 12:24:56 -0700310 "Packet match error on port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700311
Dan Talayco551befa2010-07-15 17:05:32 -0700312def match_verify(parent, req_match, res_match):
313 """
314 Verify flow matches agree; if they disagree, report where
315
316 parent must implement assertEqual
317 Use str() to ensure content is compared and not pointers
318 """
319
320 parent.assertEqual(req_match.wildcards, res_match.wildcards,
321 'Match failed: wildcards: ' + hex(req_match.wildcards) +
322 " != " + hex(res_match.wildcards))
323 parent.assertEqual(req_match.in_port, res_match.in_port,
324 'Match failed: in_port: ' + str(req_match.in_port) +
325 " != " + str(res_match.in_port))
326 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
327 'Match failed: dl_src: ' + str(req_match.dl_src) +
328 " != " + str(res_match.dl_src))
329 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
330 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
331 " != " + str(res_match.dl_dst))
332 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
333 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
334 " != " + str(res_match.dl_vlan))
335 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
336 'Match failed: dl_vlan_pcp: ' +
337 str(req_match.dl_vlan_pcp) + " != " +
338 str(res_match.dl_vlan_pcp))
339 parent.assertEqual(req_match.dl_type, res_match.dl_type,
340 'Match failed: dl_type: ' + str(req_match.dl_type) +
341 " != " + str(res_match.dl_type))
342
343 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
344 and (req_match.dl_type == IP_ETHERTYPE)):
345 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
346 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
347 " != " + str(res_match.nw_tos))
348 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
349 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
350 " != " + str(res_match.nw_proto))
351 parent.assertEqual(req_match.nw_src, res_match.nw_src,
352 'Match failed: nw_src: ' + str(req_match.nw_src) +
353 " != " + str(res_match.nw_src))
354 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
355 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
356 " != " + str(res_match.nw_dst))
357
358 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
359 and ((req_match.nw_proto == TCP_PROTOCOL)
360 or (req_match.nw_proto == UDP_PROTOCOL))):
361 parent.assertEqual(req_match.tp_src, res_match.tp_src,
362 'Match failed: tp_src: ' +
363 str(req_match.tp_src) +
364 " != " + str(res_match.tp_src))
365 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
366 'Match failed: tp_dst: ' +
367 str(req_match.tp_dst) +
368 " != " + str(res_match.tp_dst))
369
Ed Swierk99a74de2012-08-22 06:40:54 -0700370def packet_to_flow_match(parent, packet):
371 match = parse.packet_to_flow_match(packet)
372 match.wildcards |= required_wildcards(parent)
373 return match
374
375def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700376 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700377 """
378 Create a flow message
379
380 Match on packet with given wildcards.
381 See flow_match_test for other parameter descriptoins
382 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700383 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700384 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700385 """
386 match = parse.packet_to_flow_match(pkt)
387 parent.assertTrue(match is not None, "Flow match from pkt failed")
Ed Swierk99a74de2012-08-22 06:40:54 -0700388 if wildcards is None:
389 wildcards = required_wildcards(parent)
Dan Talayco677c0b72011-08-23 22:53:38 -0700390 if in_band:
391 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700392 match.wildcards = wildcards
393 match.in_port = ing_port
394
Dan Talaycof6e76c02012-03-23 10:56:12 -0700395 if type(egr_ports) == type([]):
396 egr_port_list = egr_ports
397 else:
398 egr_port_list = [egr_ports]
399
Dan Talayco551befa2010-07-15 17:05:32 -0700400 request = message.flow_mod()
401 request.match = match
402 request.buffer_id = 0xffffffff
403 if check_expire:
404 request.flags |= ofp.OFPFF_SEND_FLOW_REM
405 request.hard_timeout = 1
406
407 if action_list is not None:
408 for act in action_list:
Rich Lane9a003812012-10-04 17:17:59 -0700409 logging.debug("Adding action " + act.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700410 rv = request.actions.add(act)
411 parent.assertTrue(rv, "Could not add action" + act.show())
412
413 # Set up output/enqueue action if directed
414 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700415 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700416 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700417 for egr_port in egr_port_list:
418 act.port = egr_port
419 act.queue_id = egr_queue
420 rv = request.actions.add(act)
421 parent.assertTrue(rv, "Could not add enqueue action " +
422 str(egr_port) + " Q: " + str(egr_queue))
423 elif egr_ports is not None:
424 for egr_port in egr_port_list:
425 act = action.action_output()
426 act.port = egr_port
427 rv = request.actions.add(act)
428 parent.assertTrue(rv, "Could not add output action " +
429 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700430
Rich Lane9a003812012-10-04 17:17:59 -0700431 logging.debug(request.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700432
433 return request
434
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700435def flow_msg_install(parent, request, clear_table_override=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700436 """
437 Install a flow mod message in the switch
438
439 @param parent Must implement controller, assertEqual, assertTrue
440 @param request The request, all set to go
441 @param clear_table If true, clear the flow table before installing
442 """
Dan Talayco8a64e332012-03-28 14:53:20 -0700443
Rich Lane2014f9b2012-10-05 15:29:40 -0700444 clear_table = test_param_get('clear_table', default=True)
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700445 if(clear_table_override != None):
446 clear_table = clear_table_override
447
448 if clear_table:
Rich Lane9a003812012-10-04 17:17:59 -0700449 logging.debug("Clear flow table")
450 rc = delete_all_flows(parent.controller)
Dan Talayco551befa2010-07-15 17:05:32 -0700451 parent.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700452 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700453
Rich Lane9a003812012-10-04 17:17:59 -0700454 logging.debug("Insert flow")
Dan Talayco551befa2010-07-15 17:05:32 -0700455 rv = parent.controller.message_send(request)
456 parent.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700457 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700458
Ed Swierk99a74de2012-08-22 06:40:54 -0700459def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700460 dl_vlan=-1, pkt=None, exp_pkt=None,
Rich Lanee5779d32012-10-05 17:56:04 -0700461 action_list=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700462 """
463 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700464 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700465
466 Run test with packet through switch from ing_port to egr_port
467 See flow_match_test for parameter descriptions
468 """
469
Ed Swierk99a74de2012-08-22 06:40:54 -0700470 if wildcards is None:
471 wildcards = required_wildcards(parent)
Rich Lane9a003812012-10-04 17:17:59 -0700472 logging.info("Pkt match test: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700473 str(egr_ports))
Rich Lanee5779d32012-10-05 17:56:04 -0700474 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan))
Dan Talayco551befa2010-07-15 17:05:32 -0700475 if pkt is None:
476 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
477
478 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700479 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700480 action_list=action_list)
481
482 flow_msg_install(parent, request)
483
Rich Lane9a003812012-10-04 17:17:59 -0700484 logging.debug("Send packet: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700485 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700486 parent.dataplane.send(ing_port, str(pkt))
487
488 if exp_pkt is None:
489 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700490 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700491
Dan Talaycof6e76c02012-03-23 10:56:12 -0700492def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
493 """
494 Generate a list of ports avoiding those in the exclude list
Rich Lane9a003812012-10-04 17:17:59 -0700495 @param parent Supplies logging
Dan Talaycof6e76c02012-03-23 10:56:12 -0700496 @param of_ports List of OF port numbers
497 @param how_many Number of ports to be added to the list
498 @param exclude_list List of ports not to be used
499 @returns An empty list if unable to find enough ports
500 """
501
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700502 if how_many == 0:
503 return []
504
Dan Talaycof6e76c02012-03-23 10:56:12 -0700505 count = 0
506 egr_ports = []
507 for egr_idx in range(len(of_ports)):
508 if of_ports[egr_idx] not in exclude_list:
509 egr_ports.append(of_ports[egr_idx])
510 count += 1
511 if count >= how_many:
512 return egr_ports
Rich Lane9a003812012-10-04 17:17:59 -0700513 logging.debug("Could not generate enough egress ports for test")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700514 return []
515
Ed Swierk99a74de2012-08-22 06:40:54 -0700516def flow_match_test(parent, port_map, wildcards=None, dl_vlan=-1, pkt=None,
Rich Lanee5779d32012-10-05 17:56:04 -0700517 exp_pkt=None, action_list=None,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700518 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700519 """
520 Run flow_match_test_port_pair on all port pairs
521
522 @param max_test If > 0 no more than this number of tests are executed.
523 @param parent Must implement controller, dataplane, assertTrue, assertEqual
Rich Lane9a003812012-10-04 17:17:59 -0700524 and logging
Dan Talayco551befa2010-07-15 17:05:32 -0700525 @param pkt If not None, use this packet for ingress
526 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700527 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700528 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
529 @param action_list Additional actions to add to flow mod
Dan Talaycocfa172f2012-03-23 12:03:00 -0700530 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700531 """
Ed Swierk99a74de2012-08-22 06:40:54 -0700532 if wildcards is None:
533 wildcards = required_wildcards(parent)
Dan Talayco551befa2010-07-15 17:05:32 -0700534 of_ports = port_map.keys()
535 of_ports.sort()
536 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
537 test_count = 0
538
Dan Talaycocfa172f2012-03-23 12:03:00 -0700539 if egr_count == -1:
Rich Lane2014f9b2012-10-05 15:29:40 -0700540 egr_count = test_param_get('egr_count', default=2)
Dan Talaycocfa172f2012-03-23 12:03:00 -0700541
Dan Talayco551befa2010-07-15 17:05:32 -0700542 for ing_idx in range(len(of_ports)):
543 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700544 egr_ports = get_egr_list(parent, of_ports, egr_count,
545 exclude_list=[ingress_port])
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700546 if ing_port:
547 egr_ports.append(ofp.OFPP_IN_PORT)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700548 if len(egr_ports) == 0:
549 parent.assertTrue(0, "Failed to generate egress port list")
550
551 flow_match_test_port_pair(parent, ingress_port, egr_ports,
552 wildcards=wildcards, dl_vlan=dl_vlan,
553 pkt=pkt, exp_pkt=exp_pkt,
Rich Lanee5779d32012-10-05 17:56:04 -0700554 action_list=action_list)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700555 test_count += 1
556 if (max_test > 0) and (test_count > max_test):
Rich Lane9a003812012-10-04 17:17:59 -0700557 logging.info("Ran " + str(test_count) + " tests; exiting")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700558 return
Dan Talayco551befa2010-07-15 17:05:32 -0700559
Rich Lane2014f9b2012-10-05 15:29:40 -0700560def test_param_get(key, default=None):
Dan Talayco4b2bee62010-07-20 14:10:05 -0700561 """
562 Return value passed via test-params if present
563
Dan Talayco4b2bee62010-07-20 14:10:05 -0700564 @param key The lookup key
565 @param default Default value to use if not found
566
567 If the pair 'key=val' appeared in the string passed to --test-params
568 on the command line, return val (as interpreted by exec). Otherwise
569 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700570
571 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
572 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700573 """
574 try:
575 exec config["test_params"]
576 except:
577 return default
578
579 s = "val = " + str(key)
580 try:
581 exec s
582 return val
583 except:
584 return default
585
586def action_generate(parent, field_to_mod, mod_field_vals):
587 """
588 Create an action to modify the field indicated in field_to_mod
589
590 @param parent Must implement, assertTrue
591 @param field_to_mod The field to modify as a string name
592 @param mod_field_vals Hash of values to use for modified values
593 """
594
595 act = None
596
597 if field_to_mod in ['pktlen']:
598 return None
599
600 if field_to_mod == 'dl_dst':
601 act = action.action_set_dl_dst()
602 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
603 elif field_to_mod == 'dl_src':
604 act = action.action_set_dl_src()
605 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
606 elif field_to_mod == 'dl_vlan_enable':
607 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
608 act = action.action_strip_vlan()
609 # Add VLAN tag is handled by dl_vlan field
610 # Will return None in this case
611 elif field_to_mod == 'dl_vlan':
612 act = action.action_set_vlan_vid()
613 act.vlan_vid = mod_field_vals['dl_vlan']
614 elif field_to_mod == 'dl_vlan_pcp':
615 act = action.action_set_vlan_pcp()
616 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
617 elif field_to_mod == 'ip_src':
618 act = action.action_set_nw_src()
619 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
620 elif field_to_mod == 'ip_dst':
621 act = action.action_set_nw_dst()
622 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
623 elif field_to_mod == 'ip_tos':
624 act = action.action_set_nw_tos()
625 act.nw_tos = mod_field_vals['ip_tos']
626 elif field_to_mod == 'tcp_sport':
627 act = action.action_set_tp_src()
628 act.tp_port = mod_field_vals['tcp_sport']
629 elif field_to_mod == 'tcp_dport':
630 act = action.action_set_tp_dst()
631 act.tp_port = mod_field_vals['tcp_dport']
632 else:
633 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
634
635 return act
636
637def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
Rich Lanee5779d32012-10-05 17:56:04 -0700638 mod_fields=[], check_test_params=False):
Dan Talayco4b2bee62010-07-20 14:10:05 -0700639 """
640 Set up the ingress and expected packet and action list for a test
641
Rich Lane2014f9b2012-10-05 15:29:40 -0700642 @param parent Must implement assertTrue
Dan Talayco4b2bee62010-07-20 14:10:05 -0700643 @param start_field_values Field values to use for ingress packet (optional)
644 @param mod_field_values Field values to use for modified packet (optional)
645 @param mod_fields The list of fields to be modified by the switch in the test.
646 @params check_test_params If True, will check the parameters vid, add_vlan
647 and strip_vlan from the command line.
648
649 Returns a triple: pkt-to-send, expected-pkt, action-list
650 """
651
652 new_actions = []
653
Dan Talayco4b2bee62010-07-20 14:10:05 -0700654 base_pkt_params = {}
655 base_pkt_params['pktlen'] = 100
656 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
657 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
658 base_pkt_params['dl_vlan_enable'] = False
659 base_pkt_params['dl_vlan'] = 2
660 base_pkt_params['dl_vlan_pcp'] = 0
661 base_pkt_params['ip_src'] = '192.168.0.1'
662 base_pkt_params['ip_dst'] = '192.168.0.2'
663 base_pkt_params['ip_tos'] = 0
664 base_pkt_params['tcp_sport'] = 1234
665 base_pkt_params['tcp_dport'] = 80
666 for keyname in start_field_vals.keys():
667 base_pkt_params[keyname] = start_field_vals[keyname]
668
669 mod_pkt_params = {}
670 mod_pkt_params['pktlen'] = 100
671 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
672 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
673 mod_pkt_params['dl_vlan_enable'] = False
674 mod_pkt_params['dl_vlan'] = 3
675 mod_pkt_params['dl_vlan_pcp'] = 7
676 mod_pkt_params['ip_src'] = '10.20.30.40'
677 mod_pkt_params['ip_dst'] = '50.60.70.80'
678 mod_pkt_params['ip_tos'] = 0xf0
679 mod_pkt_params['tcp_sport'] = 4321
680 mod_pkt_params['tcp_dport'] = 8765
681 for keyname in mod_field_vals.keys():
682 mod_pkt_params[keyname] = mod_field_vals[keyname]
683
684 # Check for test param modifications
685 strip = False
686 if check_test_params:
Rich Lane2014f9b2012-10-05 15:29:40 -0700687 add_vlan = test_param_get('add_vlan')
688 strip_vlan = test_param_get('strip_vlan')
689 vid = test_param_get('vid')
Dan Talayco4b2bee62010-07-20 14:10:05 -0700690
691 if add_vlan and strip_vlan:
692 parent.assertTrue(0, "Add and strip VLAN both specified")
693
694 if vid:
695 base_pkt_params['dl_vlan_enable'] = True
696 base_pkt_params['dl_vlan'] = vid
697 if 'dl_vlan' in mod_fields:
698 mod_pkt_params['dl_vlan'] = vid + 1
699
700 if add_vlan:
701 base_pkt_params['dl_vlan_enable'] = False
702 mod_pkt_params['dl_vlan_enable'] = True
703 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
704 mod_fields.append('pktlen')
705 mod_fields.append('dl_vlan_enable')
706 if 'dl_vlan' not in mod_fields:
707 mod_fields.append('dl_vlan')
708 elif strip_vlan:
709 base_pkt_params['dl_vlan_enable'] = True
710 mod_pkt_params['dl_vlan_enable'] = False
711 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
712 mod_fields.append('dl_vlan_enable')
713 mod_fields.append('pktlen')
714
715 # Build the ingress packet
716 ingress_pkt = simple_tcp_packet(**base_pkt_params)
717
718 # Build the expected packet, modifying the indicated fields
719 for item in mod_fields:
720 base_pkt_params[item] = mod_pkt_params[item]
721 act = action_generate(parent, item, mod_pkt_params)
722 if act:
723 new_actions.append(act)
724
725 expected_pkt = simple_tcp_packet(**base_pkt_params)
726
727 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700728
729# Generate a simple "drop" flow mod
730# If in_band is true, then only drop from first test port
731def flow_mod_gen(port_map, in_band):
732 request = message.flow_mod()
733 request.match.wildcards = ofp.OFPFW_ALL
734 if in_band:
735 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
736 for of_port, ifname in port_map.items(): # Grab first port
737 break
738 request.match.in_port = of_port
739 request.buffer_id = 0xffffffff
740 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700741
742def skip_message_emit(parent, s):
743 """
744 Print out a 'skipped' message to stderr
745
746 @param s The string to print out to the log file
Rich Lane9a003812012-10-04 17:17:59 -0700747 @param parent Must implement config object
Dan Talaycoba3745c2010-07-21 21:51:08 -0700748 """
749 global skipped_test_count
750
751 skipped_test_count += 1
Rich Lane9a003812012-10-04 17:17:59 -0700752 logging.info("Skipping: " + s)
Rich Lane477f4812012-10-04 22:49:00 -0700753 if config["dbg_level"] < logging.WARNING:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700754 sys.stderr.write("(skipped) ")
755 else:
756 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700757
Dan Talayco8a64e332012-03-28 14:53:20 -0700758
759def all_stats_get(parent):
760 """
761 Get the aggregate stats for all flows in the table
762 @param parent Test instance with controller connection and assert
763 @returns dict with keys flows, packets, bytes, active (flows),
764 lookups, matched
765 """
766 stat_req = message.aggregate_stats_request()
767 stat_req.match = ofp.ofp_match()
768 stat_req.match.wildcards = ofp.OFPFW_ALL
769 stat_req.table_id = 0xff
770 stat_req.out_port = ofp.OFPP_NONE
771
772 rv = {}
773
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700774 (reply, pkt) = parent.controller.transact(stat_req)
Dan Talayco8a64e332012-03-28 14:53:20 -0700775 parent.assertTrue(len(reply.stats) == 1, "Did not receive flow stats reply")
776
777 for obj in reply.stats:
778 (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
779 obj.packet_count, obj.byte_count)
780 break
781
782 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700783 (reply , pkt) = parent.controller.transact(request)
Dan Talayco8a64e332012-03-28 14:53:20 -0700784
785
786 (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
787 for obj in reply.stats:
788 rv["active"] += obj.active_count
789 rv["lookups"] += obj.lookup_count
790 rv["matched"] += obj.matched_count
791
792 return rv
Dan Talayco2baf8b52012-03-30 09:55:42 -0700793
794FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
795 for x in range(256)])
796
797def hex_dump_buffer(src, length=16):
798 """
799 Convert src to a hex dump string and return the string
800 @param src The source buffer
801 @param length The number of bytes shown in each line
802 @returns A string showing the hex dump
803 """
Dan Talaycoc516fa02012-04-12 22:28:43 -0700804 result = ["\n"]
Dan Talayco2baf8b52012-03-30 09:55:42 -0700805 for i in xrange(0, len(src), length):
806 chars = src[i:i+length]
807 hex = ' '.join(["%02x" % ord(x) for x in chars])
808 printable = ''.join(["%s" % ((ord(x) <= 127 and
809 FILTER[ord(x)]) or '.') for x in chars])
810 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
811 return ''.join(result)
812
813def format_packet(pkt):
814 return "Packet length %d \n%s" % (len(str(pkt)),
815 hex_dump_buffer(str(pkt)))