blob: 53c18fe8b662ef94feb9a1e2a21014ce73f4d7a1 [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,
Christian Dickmann8b59b4b2012-09-23 16:48:30 -070067 tcp_dport=80,
68 ip_ihl=None,
69 ip_options=False
Dan Talayco41eae8b2010-03-10 13:57:06 -080070 ):
71 """
72 Return a simple dataplane TCP packet
73
74 Supports a few parameters:
75 @param len Length of packet in bytes w/o CRC
76 @param dl_dst Destinatino MAC
77 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070078 @param dl_vlan_enable True if the packet is with vlan, False otherwise
79 @param dl_vlan VLAN ID
80 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -080081 @param ip_src IP source
82 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -070083 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -080084 @param tcp_dport TCP destination port
85 @param ip_sport TCP source port
86
87 Generates a simple TCP request. Users
88 shouldn't assume anything about this packet other than that
89 it is a valid ethernet/IP/TCP frame.
90 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000091
92 if MINSIZE > pktlen:
93 pktlen = MINSIZE
94
Dan Talayco551befa2010-07-15 17:05:32 -070095 # Note Dot1Q.id is really CFI
Tatsuya Yabe460321e2010-05-25 17:50:49 -070096 if (dl_vlan_enable):
97 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -070098 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Christian Dickmann8b59b4b2012-09-23 16:48:30 -070099 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700100 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
101 else:
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700102 if not ip_options:
103 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
104 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
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, ihl=ip_ihl, options=ip_options)/ \
109 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700110
Dan Talayco41eae8b2010-03-10 13:57:06 -0800111 pkt = pkt/("D" * (pktlen - len(pkt)))
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700112
113 #print pkt.show()
114 #print scapy.Ether(str(pkt)).show()
Dan Talayco41eae8b2010-03-10 13:57:06 -0800115
116 return pkt
117
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700118def simple_icmp_packet(pktlen=60,
119 dl_dst='00:01:02:03:04:05',
120 dl_src='00:06:07:08:09:0a',
121 dl_vlan_enable=False,
122 dl_vlan=0,
123 dl_vlan_pcp=0,
124 ip_src='192.168.0.1',
125 ip_dst='192.168.0.2',
126 ip_tos=0,
127 icmp_type=8,
128 icmp_code=0
129 ):
130 """
131 Return a simple ICMP packet
132
133 Supports a few parameters:
134 @param len Length of packet in bytes w/o CRC
135 @param dl_dst Destinatino MAC
136 @param dl_src Source MAC
137 @param dl_vlan_enable True if the packet is with vlan, False otherwise
138 @param dl_vlan VLAN ID
139 @param dl_vlan_pcp VLAN priority
140 @param ip_src IP source
141 @param ip_dst IP destination
142 @param ip_tos IP ToS
143 @param icmp_type ICMP type
144 @param icmp_code ICMP code
145
146 Generates a simple ICMP ECHO REQUEST. Users
147 shouldn't assume anything about this packet other than that
148 it is a valid ethernet/ICMP frame.
149 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000150
151 if MINSIZE > pktlen:
152 pktlen = MINSIZE
153
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700154 if (dl_vlan_enable):
155 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
156 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
157 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
158 scapy.ICMP(type=icmp_type, code=icmp_code)
159 else:
160 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
161 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
162 scapy.ICMP(type=icmp_type, code=icmp_code)
163
164 pkt = pkt/("0" * (pktlen - len(pkt)))
165
166 return pkt
167
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700168def simple_eth_packet(pktlen=60,
169 dl_dst='00:01:02:03:04:05',
170 dl_src='01:80:c2:00:00:00',
171 dl_type=0x88cc):
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000172
173 if MINSIZE > pktlen:
174 pktlen = MINSIZE
175
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700176 pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
177
178 pkt = pkt/("0" * (pktlen - len(pkt)))
179
180 return pkt
181
Dan Talayco41eae8b2010-03-10 13:57:06 -0800182def do_barrier(ctrl):
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700183 """
184 Do a barrier command
185 Return 0 on success, -1 on error
186 """
Dan Talayco41eae8b2010-03-10 13:57:06 -0800187 b = message.barrier_request()
Dan Talaycof6b94832012-04-12 21:50:57 -0700188 (resp, pkt) = ctrl.transact(b)
189 # We'll trust the transaction processing in the controller that xid matched
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700190 if not resp:
191 return -1
192 return 0
Dan Talayco92c99122010-06-03 13:53:18 -0700193
Rich Lane9a003812012-10-04 17:17:59 -0700194def port_config_get(controller, port_no):
Dan Talayco92c99122010-06-03 13:53:18 -0700195 """
196 Get a port's configuration
197
198 Gets the switch feature configuration and grabs one port's
199 configuration
200
201 @returns (hwaddr, config, advert) The hwaddress, configuration and
202 advertised values
203 """
204 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700205 reply, pkt = controller.transact(request)
Rich Lane9a003812012-10-04 17:17:59 -0700206 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700207 if reply is None:
Rich Lane9a003812012-10-04 17:17:59 -0700208 logging.warn("Get feature request failed")
Dan Talayco92c99122010-06-03 13:53:18 -0700209 return None, None, None
210 for idx in range(len(reply.ports)):
211 if reply.ports[idx].port_no == port_no:
212 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
213 reply.ports[idx].advertised)
214
Rich Lane9a003812012-10-04 17:17:59 -0700215 logging.warn("Did not find port number for port config")
Dan Talayco92c99122010-06-03 13:53:18 -0700216 return None, None, None
217
Rich Lane9a003812012-10-04 17:17:59 -0700218def port_config_set(controller, port_no, config, mask):
Dan Talayco92c99122010-06-03 13:53:18 -0700219 """
220 Set the port configuration according the given parameters
221
222 Gets the switch feature configuration and updates one port's
223 configuration value according to config and mask
224 """
Rich Lane9a003812012-10-04 17:17:59 -0700225 logging.info("Setting port " + str(port_no) + " to config " + str(config))
Dan Talayco92c99122010-06-03 13:53:18 -0700226 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700227 reply, pkt = controller.transact(request)
Dan Talayco92c99122010-06-03 13:53:18 -0700228 if reply is None:
229 return -1
Rich Lane9a003812012-10-04 17:17:59 -0700230 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700231 for idx in range(len(reply.ports)):
232 if reply.ports[idx].port_no == port_no:
233 break
234 if idx >= len(reply.ports):
235 return -1
236 mod = message.port_mod()
237 mod.port_no = port_no
238 mod.hw_addr = reply.ports[idx].hw_addr
239 mod.config = config
240 mod.mask = mask
241 mod.advertise = reply.ports[idx].advertised
242 rv = controller.message_send(mod)
243 return rv
244
Rich Lane2014f9b2012-10-05 15:29:40 -0700245def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if):
Dan Talayco92c99122010-06-03 13:53:18 -0700246 """
247 Check for proper receive packets across all ports
Ken Chiang1bf01602012-04-04 10:48:23 -0700248 @param dp The dataplane object
Dan Talayco92c99122010-06-03 13:53:18 -0700249 @param pkt Expected packet; may be None if yes_ports is empty
250 @param yes_ports Set or list of ports that should recieve packet
251 @param no_ports Set or list of ports that should not receive packet
252 @param assert_if Object that implements assertXXX
253 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700254 exp_pkt_arg = None
Rich Lane2014f9b2012-10-05 15:29:40 -0700255 if config["relax"]:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700256 exp_pkt_arg = pkt
257
Dan Talayco92c99122010-06-03 13:53:18 -0700258 for ofport in yes_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700259 logging.debug("Checking for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700260 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700261 port_number=ofport, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700262 assert_if.assertTrue(rcv_pkt is not None,
263 "Did not receive pkt on " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700264 if not dataplane.match_exp_pkt(pkt, rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700265 logging.debug("Sent %s" % format_packet(pkt))
266 logging.debug("Resp %s" % format_packet(rcv_pkt))
Ken Chiang1bf01602012-04-04 10:48:23 -0700267 assert_if.assertTrue(dataplane.match_exp_pkt(pkt, rcv_pkt),
268 "Response packet does not match send packet " +
269 "on port " + str(ofport))
Dan Talayco73f84012012-10-02 09:23:18 -0700270 if len(no_ports) > 0:
271 time.sleep(1)
Dan Talayco92c99122010-06-03 13:53:18 -0700272 for ofport in no_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700273 logging.debug("Negative check for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700274 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700275 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700276 assert_if.assertTrue(rcv_pkt is None,
277 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700278
279
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700280def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700281 """
282 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700283 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700284
285 parent must implement dataplane, assertTrue and assertEqual
286 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700287 exp_pkt_arg = None
Rich Lane477f4812012-10-04 22:49:00 -0700288 if config["relax"]:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700289 exp_pkt_arg = exp_pkt
290
Dan Talaycof6e76c02012-03-23 10:56:12 -0700291 if type(egr_ports) == type([]):
292 egr_port_list = egr_ports
293 else:
294 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700295
Dan Talaycof6e76c02012-03-23 10:56:12 -0700296 # Expect a packet from each port on egr port list
297 for egr_port in egr_port_list:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700298 check_port = egr_port
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700299 if egr_port == ofp.OFPP_IN_PORT:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700300 check_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700301 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700302 port_number=check_port, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700303
Dan Talaycof6e76c02012-03-23 10:56:12 -0700304 if rcv_pkt is None:
Rich Lane9a003812012-10-04 17:17:59 -0700305 logging.error("ERROR: No packet received from " +
Dan Talaycod8ae7582012-03-23 12:24:56 -0700306 str(check_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700307
Dan Talaycof6e76c02012-03-23 10:56:12 -0700308 parent.assertTrue(rcv_pkt is not None,
Dan Talaycod8ae7582012-03-23 12:24:56 -0700309 "Did not receive packet port " + str(check_port))
Rich Lane9a003812012-10-04 17:17:59 -0700310 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700311 str(rcv_port))
312
313 if str(exp_pkt) != str(rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700314 logging.error("ERROR: Packet match failed.")
315 logging.debug("Expected len " + str(len(exp_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700316 + str(exp_pkt).encode('hex'))
Rich Lane9a003812012-10-04 17:17:59 -0700317 logging.debug("Received len " + str(len(rcv_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700318 + str(rcv_pkt).encode('hex'))
319 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
Dan Talaycod8ae7582012-03-23 12:24:56 -0700320 "Packet match error on port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700321
Dan Talayco551befa2010-07-15 17:05:32 -0700322def match_verify(parent, req_match, res_match):
323 """
324 Verify flow matches agree; if they disagree, report where
325
326 parent must implement assertEqual
327 Use str() to ensure content is compared and not pointers
328 """
329
330 parent.assertEqual(req_match.wildcards, res_match.wildcards,
331 'Match failed: wildcards: ' + hex(req_match.wildcards) +
332 " != " + hex(res_match.wildcards))
333 parent.assertEqual(req_match.in_port, res_match.in_port,
334 'Match failed: in_port: ' + str(req_match.in_port) +
335 " != " + str(res_match.in_port))
336 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
337 'Match failed: dl_src: ' + str(req_match.dl_src) +
338 " != " + str(res_match.dl_src))
339 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
340 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
341 " != " + str(res_match.dl_dst))
342 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
343 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
344 " != " + str(res_match.dl_vlan))
345 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
346 'Match failed: dl_vlan_pcp: ' +
347 str(req_match.dl_vlan_pcp) + " != " +
348 str(res_match.dl_vlan_pcp))
349 parent.assertEqual(req_match.dl_type, res_match.dl_type,
350 'Match failed: dl_type: ' + str(req_match.dl_type) +
351 " != " + str(res_match.dl_type))
352
353 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
354 and (req_match.dl_type == IP_ETHERTYPE)):
355 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
356 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
357 " != " + str(res_match.nw_tos))
358 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
359 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
360 " != " + str(res_match.nw_proto))
361 parent.assertEqual(req_match.nw_src, res_match.nw_src,
362 'Match failed: nw_src: ' + str(req_match.nw_src) +
363 " != " + str(res_match.nw_src))
364 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
365 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
366 " != " + str(res_match.nw_dst))
367
368 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
369 and ((req_match.nw_proto == TCP_PROTOCOL)
370 or (req_match.nw_proto == UDP_PROTOCOL))):
371 parent.assertEqual(req_match.tp_src, res_match.tp_src,
372 'Match failed: tp_src: ' +
373 str(req_match.tp_src) +
374 " != " + str(res_match.tp_src))
375 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
376 'Match failed: tp_dst: ' +
377 str(req_match.tp_dst) +
378 " != " + str(res_match.tp_dst))
379
Ed Swierk99a74de2012-08-22 06:40:54 -0700380def packet_to_flow_match(parent, packet):
381 match = parse.packet_to_flow_match(packet)
382 match.wildcards |= required_wildcards(parent)
383 return match
384
385def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700386 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700387 """
388 Create a flow message
389
390 Match on packet with given wildcards.
391 See flow_match_test for other parameter descriptoins
392 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700393 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700394 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700395 """
396 match = parse.packet_to_flow_match(pkt)
397 parent.assertTrue(match is not None, "Flow match from pkt failed")
Ed Swierk99a74de2012-08-22 06:40:54 -0700398 if wildcards is None:
399 wildcards = required_wildcards(parent)
Dan Talayco677c0b72011-08-23 22:53:38 -0700400 if in_band:
401 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700402 match.wildcards = wildcards
403 match.in_port = ing_port
404
Dan Talaycof6e76c02012-03-23 10:56:12 -0700405 if type(egr_ports) == type([]):
406 egr_port_list = egr_ports
407 else:
408 egr_port_list = [egr_ports]
409
Dan Talayco551befa2010-07-15 17:05:32 -0700410 request = message.flow_mod()
411 request.match = match
412 request.buffer_id = 0xffffffff
413 if check_expire:
414 request.flags |= ofp.OFPFF_SEND_FLOW_REM
415 request.hard_timeout = 1
416
417 if action_list is not None:
418 for act in action_list:
Rich Lane9a003812012-10-04 17:17:59 -0700419 logging.debug("Adding action " + act.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700420 rv = request.actions.add(act)
421 parent.assertTrue(rv, "Could not add action" + act.show())
422
423 # Set up output/enqueue action if directed
424 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700425 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700426 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700427 for egr_port in egr_port_list:
428 act.port = egr_port
429 act.queue_id = egr_queue
430 rv = request.actions.add(act)
431 parent.assertTrue(rv, "Could not add enqueue action " +
432 str(egr_port) + " Q: " + str(egr_queue))
433 elif egr_ports is not None:
434 for egr_port in egr_port_list:
435 act = action.action_output()
436 act.port = egr_port
437 rv = request.actions.add(act)
438 parent.assertTrue(rv, "Could not add output action " +
439 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700440
Rich Lane9a003812012-10-04 17:17:59 -0700441 logging.debug(request.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700442
443 return request
444
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700445def flow_msg_install(parent, request, clear_table_override=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700446 """
447 Install a flow mod message in the switch
448
449 @param parent Must implement controller, assertEqual, assertTrue
450 @param request The request, all set to go
451 @param clear_table If true, clear the flow table before installing
452 """
Dan Talayco8a64e332012-03-28 14:53:20 -0700453
Rich Lane2014f9b2012-10-05 15:29:40 -0700454 clear_table = test_param_get('clear_table', default=True)
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700455 if(clear_table_override != None):
456 clear_table = clear_table_override
457
458 if clear_table:
Rich Lane9a003812012-10-04 17:17:59 -0700459 logging.debug("Clear flow table")
460 rc = delete_all_flows(parent.controller)
Dan Talayco551befa2010-07-15 17:05:32 -0700461 parent.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700462 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700463
Rich Lane9a003812012-10-04 17:17:59 -0700464 logging.debug("Insert flow")
Dan Talayco551befa2010-07-15 17:05:32 -0700465 rv = parent.controller.message_send(request)
466 parent.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700467 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700468
Ed Swierk99a74de2012-08-22 06:40:54 -0700469def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700470 dl_vlan=-1, pkt=None, exp_pkt=None,
Rich Lanee5779d32012-10-05 17:56:04 -0700471 action_list=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700472 """
473 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700474 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700475
476 Run test with packet through switch from ing_port to egr_port
477 See flow_match_test for parameter descriptions
478 """
479
Ed Swierk99a74de2012-08-22 06:40:54 -0700480 if wildcards is None:
481 wildcards = required_wildcards(parent)
Rich Lane9a003812012-10-04 17:17:59 -0700482 logging.info("Pkt match test: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700483 str(egr_ports))
Rich Lanee5779d32012-10-05 17:56:04 -0700484 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan))
Dan Talayco551befa2010-07-15 17:05:32 -0700485 if pkt is None:
486 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
487
488 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700489 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700490 action_list=action_list)
491
492 flow_msg_install(parent, request)
493
Rich Lane9a003812012-10-04 17:17:59 -0700494 logging.debug("Send packet: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700495 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700496 parent.dataplane.send(ing_port, str(pkt))
497
498 if exp_pkt is None:
499 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700500 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700501
Dan Talaycof6e76c02012-03-23 10:56:12 -0700502def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
503 """
504 Generate a list of ports avoiding those in the exclude list
Rich Lane9a003812012-10-04 17:17:59 -0700505 @param parent Supplies logging
Dan Talaycof6e76c02012-03-23 10:56:12 -0700506 @param of_ports List of OF port numbers
507 @param how_many Number of ports to be added to the list
508 @param exclude_list List of ports not to be used
509 @returns An empty list if unable to find enough ports
510 """
511
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700512 if how_many == 0:
513 return []
514
Dan Talaycof6e76c02012-03-23 10:56:12 -0700515 count = 0
516 egr_ports = []
517 for egr_idx in range(len(of_ports)):
518 if of_ports[egr_idx] not in exclude_list:
519 egr_ports.append(of_ports[egr_idx])
520 count += 1
521 if count >= how_many:
522 return egr_ports
Rich Lane9a003812012-10-04 17:17:59 -0700523 logging.debug("Could not generate enough egress ports for test")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700524 return []
525
Ed Swierk99a74de2012-08-22 06:40:54 -0700526def flow_match_test(parent, port_map, wildcards=None, dl_vlan=-1, pkt=None,
Rich Lanee5779d32012-10-05 17:56:04 -0700527 exp_pkt=None, action_list=None,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700528 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700529 """
530 Run flow_match_test_port_pair on all port pairs
531
532 @param max_test If > 0 no more than this number of tests are executed.
533 @param parent Must implement controller, dataplane, assertTrue, assertEqual
Rich Lane9a003812012-10-04 17:17:59 -0700534 and logging
Dan Talayco551befa2010-07-15 17:05:32 -0700535 @param pkt If not None, use this packet for ingress
536 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700537 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700538 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
539 @param action_list Additional actions to add to flow mod
Dan Talaycocfa172f2012-03-23 12:03:00 -0700540 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700541 """
Ed Swierk99a74de2012-08-22 06:40:54 -0700542 if wildcards is None:
543 wildcards = required_wildcards(parent)
Dan Talayco551befa2010-07-15 17:05:32 -0700544 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:
Rich Lane2014f9b2012-10-05 15:29:40 -0700550 egr_count = test_param_get('egr_count', default=2)
Dan Talaycocfa172f2012-03-23 12:03:00 -0700551
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,
Rich Lanee5779d32012-10-05 17:56:04 -0700564 action_list=action_list)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700565 test_count += 1
566 if (max_test > 0) and (test_count > max_test):
Rich Lane9a003812012-10-04 17:17:59 -0700567 logging.info("Ran " + str(test_count) + " tests; exiting")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700568 return
Dan Talayco551befa2010-07-15 17:05:32 -0700569
Rich Lane2014f9b2012-10-05 15:29:40 -0700570def test_param_get(key, default=None):
Dan Talayco4b2bee62010-07-20 14:10:05 -0700571 """
572 Return value passed via test-params if present
573
Dan Talayco4b2bee62010-07-20 14:10:05 -0700574 @param key The lookup key
575 @param default Default value to use if not found
576
577 If the pair 'key=val' appeared in the string passed to --test-params
578 on the command line, return val (as interpreted by exec). Otherwise
579 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700580
581 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
582 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700583 """
584 try:
585 exec config["test_params"]
586 except:
587 return default
588
589 s = "val = " + str(key)
590 try:
591 exec s
592 return val
593 except:
594 return default
595
596def action_generate(parent, field_to_mod, mod_field_vals):
597 """
598 Create an action to modify the field indicated in field_to_mod
599
600 @param parent Must implement, assertTrue
601 @param field_to_mod The field to modify as a string name
602 @param mod_field_vals Hash of values to use for modified values
603 """
604
605 act = None
606
607 if field_to_mod in ['pktlen']:
608 return None
609
610 if field_to_mod == 'dl_dst':
611 act = action.action_set_dl_dst()
612 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
613 elif field_to_mod == 'dl_src':
614 act = action.action_set_dl_src()
615 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
616 elif field_to_mod == 'dl_vlan_enable':
617 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
618 act = action.action_strip_vlan()
619 # Add VLAN tag is handled by dl_vlan field
620 # Will return None in this case
621 elif field_to_mod == 'dl_vlan':
622 act = action.action_set_vlan_vid()
623 act.vlan_vid = mod_field_vals['dl_vlan']
624 elif field_to_mod == 'dl_vlan_pcp':
625 act = action.action_set_vlan_pcp()
626 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
627 elif field_to_mod == 'ip_src':
628 act = action.action_set_nw_src()
629 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
630 elif field_to_mod == 'ip_dst':
631 act = action.action_set_nw_dst()
632 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
633 elif field_to_mod == 'ip_tos':
634 act = action.action_set_nw_tos()
635 act.nw_tos = mod_field_vals['ip_tos']
636 elif field_to_mod == 'tcp_sport':
637 act = action.action_set_tp_src()
638 act.tp_port = mod_field_vals['tcp_sport']
639 elif field_to_mod == 'tcp_dport':
640 act = action.action_set_tp_dst()
641 act.tp_port = mod_field_vals['tcp_dport']
642 else:
643 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
644
645 return act
646
647def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
Rich Lanee5779d32012-10-05 17:56:04 -0700648 mod_fields=[], check_test_params=False):
Dan Talayco4b2bee62010-07-20 14:10:05 -0700649 """
650 Set up the ingress and expected packet and action list for a test
651
Rich Lane2014f9b2012-10-05 15:29:40 -0700652 @param parent Must implement assertTrue
Dan Talayco4b2bee62010-07-20 14:10:05 -0700653 @param start_field_values Field values to use for ingress packet (optional)
654 @param mod_field_values Field values to use for modified packet (optional)
655 @param mod_fields The list of fields to be modified by the switch in the test.
656 @params check_test_params If True, will check the parameters vid, add_vlan
657 and strip_vlan from the command line.
658
659 Returns a triple: pkt-to-send, expected-pkt, action-list
660 """
661
662 new_actions = []
663
Dan Talayco4b2bee62010-07-20 14:10:05 -0700664 base_pkt_params = {}
665 base_pkt_params['pktlen'] = 100
666 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
667 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
668 base_pkt_params['dl_vlan_enable'] = False
669 base_pkt_params['dl_vlan'] = 2
670 base_pkt_params['dl_vlan_pcp'] = 0
671 base_pkt_params['ip_src'] = '192.168.0.1'
672 base_pkt_params['ip_dst'] = '192.168.0.2'
673 base_pkt_params['ip_tos'] = 0
674 base_pkt_params['tcp_sport'] = 1234
675 base_pkt_params['tcp_dport'] = 80
676 for keyname in start_field_vals.keys():
677 base_pkt_params[keyname] = start_field_vals[keyname]
678
679 mod_pkt_params = {}
680 mod_pkt_params['pktlen'] = 100
681 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
682 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
683 mod_pkt_params['dl_vlan_enable'] = False
684 mod_pkt_params['dl_vlan'] = 3
685 mod_pkt_params['dl_vlan_pcp'] = 7
686 mod_pkt_params['ip_src'] = '10.20.30.40'
687 mod_pkt_params['ip_dst'] = '50.60.70.80'
688 mod_pkt_params['ip_tos'] = 0xf0
689 mod_pkt_params['tcp_sport'] = 4321
690 mod_pkt_params['tcp_dport'] = 8765
691 for keyname in mod_field_vals.keys():
692 mod_pkt_params[keyname] = mod_field_vals[keyname]
693
694 # Check for test param modifications
695 strip = False
696 if check_test_params:
Rich Lane2014f9b2012-10-05 15:29:40 -0700697 add_vlan = test_param_get('add_vlan')
698 strip_vlan = test_param_get('strip_vlan')
699 vid = test_param_get('vid')
Dan Talayco4b2bee62010-07-20 14:10:05 -0700700
701 if add_vlan and strip_vlan:
702 parent.assertTrue(0, "Add and strip VLAN both specified")
703
704 if vid:
705 base_pkt_params['dl_vlan_enable'] = True
706 base_pkt_params['dl_vlan'] = vid
707 if 'dl_vlan' in mod_fields:
708 mod_pkt_params['dl_vlan'] = vid + 1
709
710 if add_vlan:
711 base_pkt_params['dl_vlan_enable'] = False
712 mod_pkt_params['dl_vlan_enable'] = True
713 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
714 mod_fields.append('pktlen')
715 mod_fields.append('dl_vlan_enable')
716 if 'dl_vlan' not in mod_fields:
717 mod_fields.append('dl_vlan')
718 elif strip_vlan:
719 base_pkt_params['dl_vlan_enable'] = True
720 mod_pkt_params['dl_vlan_enable'] = False
721 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
722 mod_fields.append('dl_vlan_enable')
723 mod_fields.append('pktlen')
724
725 # Build the ingress packet
726 ingress_pkt = simple_tcp_packet(**base_pkt_params)
727
728 # Build the expected packet, modifying the indicated fields
729 for item in mod_fields:
730 base_pkt_params[item] = mod_pkt_params[item]
731 act = action_generate(parent, item, mod_pkt_params)
732 if act:
733 new_actions.append(act)
734
735 expected_pkt = simple_tcp_packet(**base_pkt_params)
736
737 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700738
739# Generate a simple "drop" flow mod
740# If in_band is true, then only drop from first test port
741def flow_mod_gen(port_map, in_band):
742 request = message.flow_mod()
743 request.match.wildcards = ofp.OFPFW_ALL
744 if in_band:
745 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
746 for of_port, ifname in port_map.items(): # Grab first port
747 break
748 request.match.in_port = of_port
749 request.buffer_id = 0xffffffff
750 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700751
752def skip_message_emit(parent, s):
753 """
754 Print out a 'skipped' message to stderr
755
756 @param s The string to print out to the log file
Rich Lane9a003812012-10-04 17:17:59 -0700757 @param parent Must implement config object
Dan Talaycoba3745c2010-07-21 21:51:08 -0700758 """
759 global skipped_test_count
760
761 skipped_test_count += 1
Rich Lane9a003812012-10-04 17:17:59 -0700762 logging.info("Skipping: " + s)
Rich Lane477f4812012-10-04 22:49:00 -0700763 if config["dbg_level"] < logging.WARNING:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700764 sys.stderr.write("(skipped) ")
765 else:
766 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700767
Dan Talayco8a64e332012-03-28 14:53:20 -0700768
769def all_stats_get(parent):
770 """
771 Get the aggregate stats for all flows in the table
772 @param parent Test instance with controller connection and assert
773 @returns dict with keys flows, packets, bytes, active (flows),
774 lookups, matched
775 """
776 stat_req = message.aggregate_stats_request()
777 stat_req.match = ofp.ofp_match()
778 stat_req.match.wildcards = ofp.OFPFW_ALL
779 stat_req.table_id = 0xff
780 stat_req.out_port = ofp.OFPP_NONE
781
782 rv = {}
783
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700784 (reply, pkt) = parent.controller.transact(stat_req)
Dan Talayco8a64e332012-03-28 14:53:20 -0700785 parent.assertTrue(len(reply.stats) == 1, "Did not receive flow stats reply")
786
787 for obj in reply.stats:
788 (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
789 obj.packet_count, obj.byte_count)
790 break
791
792 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700793 (reply , pkt) = parent.controller.transact(request)
Dan Talayco8a64e332012-03-28 14:53:20 -0700794
795
796 (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
797 for obj in reply.stats:
798 rv["active"] += obj.active_count
799 rv["lookups"] += obj.lookup_count
800 rv["matched"] += obj.matched_count
801
802 return rv
Dan Talayco2baf8b52012-03-30 09:55:42 -0700803
804FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
805 for x in range(256)])
806
807def hex_dump_buffer(src, length=16):
808 """
809 Convert src to a hex dump string and return the string
810 @param src The source buffer
811 @param length The number of bytes shown in each line
812 @returns A string showing the hex dump
813 """
Dan Talaycoc516fa02012-04-12 22:28:43 -0700814 result = ["\n"]
Dan Talayco2baf8b52012-03-30 09:55:42 -0700815 for i in xrange(0, len(src), length):
816 chars = src[i:i+length]
817 hex = ' '.join(["%02x" % ord(x) for x in chars])
818 printable = ''.join(["%s" % ((ord(x) <= 127 and
819 FILTER[ord(x)]) or '.') for x in chars])
820 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
821 return ''.join(result)
822
823def format_packet(pkt):
824 return "Packet length %d \n%s" % (len(str(pkt)),
825 hex_dump_buffer(str(pkt)))