blob: 2fdc9ceca0c8eaf29fc305b1872625cd83492474 [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 Talayco73f84012012-10-02 09:23:18 -070021import time
Dan Talaycoc901f4d2010-03-07 21:55:45 -080022
Dan Talaycoba3745c2010-07-21 21:51:08 -070023global skipped_test_count
24skipped_test_count = 0
25
Dan Talayco551befa2010-07-15 17:05:32 -070026# Some useful defines
27IP_ETHERTYPE = 0x800
28TCP_PROTOCOL = 0x6
29UDP_PROTOCOL = 0x11
30
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000031MINSIZE = 0
32
Rich Lane9a003812012-10-04 17:17:59 -070033def clear_switch(parent, port_list):
Dan Talayco98fada92010-07-17 00:36:21 -070034 """
35 Clear the switch configuration
36
37 @param parent Object implementing controller and assert equal
Dan Talayco98fada92010-07-17 00:36:21 -070038 """
39 for port in port_list:
Rich Lane9a003812012-10-04 17:17:59 -070040 clear_port_config(parent, port)
41 delete_all_flows(parent.controller)
Dan Talayco98fada92010-07-17 00:36:21 -070042
Rich Lane9a003812012-10-04 17:17:59 -070043def delete_all_flows(ctrl):
Dan Talayco41eae8b2010-03-10 13:57:06 -080044 """
45 Delete all flows on the switch
46 @param ctrl The controller object for the test
Dan Talayco41eae8b2010-03-10 13:57:06 -080047 """
48
Rich Lane9a003812012-10-04 17:17:59 -070049 logging.info("Deleting all flows")
Dan Talaycoc901f4d2010-03-07 21:55:45 -080050 msg = message.flow_mod()
51 msg.match.wildcards = ofp.OFPFW_ALL
Dan Talayco41eae8b2010-03-10 13:57:06 -080052 msg.out_port = ofp.OFPP_NONE
Dan Talaycoc901f4d2010-03-07 21:55:45 -080053 msg.command = ofp.OFPFC_DELETE
54 msg.buffer_id = 0xffffffff
Dan Talayco41eae8b2010-03-10 13:57:06 -080055 return ctrl.message_send(msg)
56
Rich Lane9a003812012-10-04 17:17:59 -070057def clear_port_config(parent, port):
Dan Talayco98fada92010-07-17 00:36:21 -070058 """
59 Clear the port configuration (currently only no flood setting)
60
61 @param parent Object implementing controller and assert equal
Dan Talayco98fada92010-07-17 00:36:21 -070062 """
63 rv = port_config_set(parent.controller, port,
Rich Lane9a003812012-10-04 17:17:59 -070064 0, ofp.OFPPC_NO_FLOOD)
Dan Talayco98fada92010-07-17 00:36:21 -070065 self.assertEqual(rv, 0, "Failed to reset port config")
66
Ed Swierk99a74de2012-08-22 06:40:54 -070067def required_wildcards(parent):
68 w = test_param_get(parent.config, 'required_wildcards', default='default')
69 if w == 'l3-l4':
70 return (ofp.OFPFW_NW_SRC_ALL | ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS
71 | ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST)
72 else:
73 return 0
74
Dan Talayco41eae8b2010-03-10 13:57:06 -080075def simple_tcp_packet(pktlen=100,
76 dl_dst='00:01:02:03:04:05',
77 dl_src='00:06:07:08:09:0a',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070078 dl_vlan_enable=False,
79 dl_vlan=0,
80 dl_vlan_pcp=0,
Dan Talayco551befa2010-07-15 17:05:32 -070081 dl_vlan_cfi=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080082 ip_src='192.168.0.1',
83 ip_dst='192.168.0.2',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070084 ip_tos=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080085 tcp_sport=1234,
86 tcp_dport=80
87 ):
88 """
89 Return a simple dataplane TCP packet
90
91 Supports a few parameters:
92 @param len Length of packet in bytes w/o CRC
93 @param dl_dst Destinatino MAC
94 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070095 @param dl_vlan_enable True if the packet is with vlan, False otherwise
96 @param dl_vlan VLAN ID
97 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -080098 @param ip_src IP source
99 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700100 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -0800101 @param tcp_dport TCP destination port
102 @param ip_sport TCP source port
103
104 Generates a simple TCP request. Users
105 shouldn't assume anything about this packet other than that
106 it is a valid ethernet/IP/TCP frame.
107 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000108
109 if MINSIZE > pktlen:
110 pktlen = MINSIZE
111
Dan Talayco551befa2010-07-15 17:05:32 -0700112 # Note Dot1Q.id is really CFI
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700113 if (dl_vlan_enable):
114 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -0700115 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700116 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
117 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
118 else:
119 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
120 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
121 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
122
Dan Talayco41eae8b2010-03-10 13:57:06 -0800123 pkt = pkt/("D" * (pktlen - len(pkt)))
124
125 return pkt
126
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700127def simple_icmp_packet(pktlen=60,
128 dl_dst='00:01:02:03:04:05',
129 dl_src='00:06:07:08:09:0a',
130 dl_vlan_enable=False,
131 dl_vlan=0,
132 dl_vlan_pcp=0,
133 ip_src='192.168.0.1',
134 ip_dst='192.168.0.2',
135 ip_tos=0,
136 icmp_type=8,
137 icmp_code=0
138 ):
139 """
140 Return a simple ICMP packet
141
142 Supports a few parameters:
143 @param len Length of packet in bytes w/o CRC
144 @param dl_dst Destinatino MAC
145 @param dl_src Source MAC
146 @param dl_vlan_enable True if the packet is with vlan, False otherwise
147 @param dl_vlan VLAN ID
148 @param dl_vlan_pcp VLAN priority
149 @param ip_src IP source
150 @param ip_dst IP destination
151 @param ip_tos IP ToS
152 @param icmp_type ICMP type
153 @param icmp_code ICMP code
154
155 Generates a simple ICMP ECHO REQUEST. Users
156 shouldn't assume anything about this packet other than that
157 it is a valid ethernet/ICMP frame.
158 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000159
160 if MINSIZE > pktlen:
161 pktlen = MINSIZE
162
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700163 if (dl_vlan_enable):
164 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
165 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
166 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
167 scapy.ICMP(type=icmp_type, code=icmp_code)
168 else:
169 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
170 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
171 scapy.ICMP(type=icmp_type, code=icmp_code)
172
173 pkt = pkt/("0" * (pktlen - len(pkt)))
174
175 return pkt
176
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700177def simple_eth_packet(pktlen=60,
178 dl_dst='00:01:02:03:04:05',
179 dl_src='01:80:c2:00:00:00',
180 dl_type=0x88cc):
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000181
182 if MINSIZE > pktlen:
183 pktlen = MINSIZE
184
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700185 pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
186
187 pkt = pkt/("0" * (pktlen - len(pkt)))
188
189 return pkt
190
Dan Talayco41eae8b2010-03-10 13:57:06 -0800191def do_barrier(ctrl):
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700192 """
193 Do a barrier command
194 Return 0 on success, -1 on error
195 """
Dan Talayco41eae8b2010-03-10 13:57:06 -0800196 b = message.barrier_request()
Dan Talaycof6b94832012-04-12 21:50:57 -0700197 (resp, pkt) = ctrl.transact(b)
198 # We'll trust the transaction processing in the controller that xid matched
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700199 if not resp:
200 return -1
201 return 0
Dan Talayco92c99122010-06-03 13:53:18 -0700202
Rich Lane9a003812012-10-04 17:17:59 -0700203def port_config_get(controller, port_no):
Dan Talayco92c99122010-06-03 13:53:18 -0700204 """
205 Get a port's configuration
206
207 Gets the switch feature configuration and grabs one port's
208 configuration
209
210 @returns (hwaddr, config, advert) The hwaddress, configuration and
211 advertised values
212 """
213 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700214 reply, pkt = controller.transact(request)
Rich Lane9a003812012-10-04 17:17:59 -0700215 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700216 if reply is None:
Rich Lane9a003812012-10-04 17:17:59 -0700217 logging.warn("Get feature request failed")
Dan Talayco92c99122010-06-03 13:53:18 -0700218 return None, None, None
219 for idx in range(len(reply.ports)):
220 if reply.ports[idx].port_no == port_no:
221 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
222 reply.ports[idx].advertised)
223
Rich Lane9a003812012-10-04 17:17:59 -0700224 logging.warn("Did not find port number for port config")
Dan Talayco92c99122010-06-03 13:53:18 -0700225 return None, None, None
226
Rich Lane9a003812012-10-04 17:17:59 -0700227def port_config_set(controller, port_no, config, mask):
Dan Talayco92c99122010-06-03 13:53:18 -0700228 """
229 Set the port configuration according the given parameters
230
231 Gets the switch feature configuration and updates one port's
232 configuration value according to config and mask
233 """
Rich Lane9a003812012-10-04 17:17:59 -0700234 logging.info("Setting port " + str(port_no) + " to config " + str(config))
Dan Talayco92c99122010-06-03 13:53:18 -0700235 request = message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700236 reply, pkt = controller.transact(request)
Dan Talayco92c99122010-06-03 13:53:18 -0700237 if reply is None:
238 return -1
Rich Lane9a003812012-10-04 17:17:59 -0700239 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700240 for idx in range(len(reply.ports)):
241 if reply.ports[idx].port_no == port_no:
242 break
243 if idx >= len(reply.ports):
244 return -1
245 mod = message.port_mod()
246 mod.port_no = port_no
247 mod.hw_addr = reply.ports[idx].hw_addr
248 mod.config = config
249 mod.mask = mask
250 mod.advertise = reply.ports[idx].advertised
251 rv = controller.message_send(mod)
252 return rv
253
Rich Lane9a003812012-10-04 17:17:59 -0700254def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if, config):
Dan Talayco92c99122010-06-03 13:53:18 -0700255 """
256 Check for proper receive packets across all ports
Ken Chiang1bf01602012-04-04 10:48:23 -0700257 @param dp The dataplane object
Dan Talayco92c99122010-06-03 13:53:18 -0700258 @param pkt Expected packet; may be None if yes_ports is empty
259 @param yes_ports Set or list of ports that should recieve packet
260 @param no_ports Set or list of ports that should not receive packet
261 @param assert_if Object that implements assertXXX
262 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700263 exp_pkt_arg = None
264 if config and config["relax"]:
265 exp_pkt_arg = pkt
266
Dan Talayco92c99122010-06-03 13:53:18 -0700267 for ofport in yes_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700268 logging.debug("Checking for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700269 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700270 port_number=ofport, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700271 assert_if.assertTrue(rcv_pkt is not None,
272 "Did not receive pkt on " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700273 if not dataplane.match_exp_pkt(pkt, rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700274 logging.debug("Sent %s" % format_packet(pkt))
275 logging.debug("Resp %s" % format_packet(rcv_pkt))
Ken Chiang1bf01602012-04-04 10:48:23 -0700276 assert_if.assertTrue(dataplane.match_exp_pkt(pkt, rcv_pkt),
277 "Response packet does not match send packet " +
278 "on port " + str(ofport))
Dan Talayco73f84012012-10-02 09:23:18 -0700279 if len(no_ports) > 0:
280 time.sleep(1)
Dan Talayco92c99122010-06-03 13:53:18 -0700281 for ofport in no_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700282 logging.debug("Negative check for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700283 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700284 port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700285 assert_if.assertTrue(rcv_pkt is None,
286 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700287
288
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700289def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700290 """
291 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700292 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700293
294 parent must implement dataplane, assertTrue and assertEqual
295 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700296 exp_pkt_arg = None
297 if parent.config["relax"]:
298 exp_pkt_arg = exp_pkt
299
Dan Talaycof6e76c02012-03-23 10:56:12 -0700300 if type(egr_ports) == type([]):
301 egr_port_list = egr_ports
302 else:
303 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700304
Dan Talaycof6e76c02012-03-23 10:56:12 -0700305 # Expect a packet from each port on egr port list
306 for egr_port in egr_port_list:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700307 check_port = egr_port
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700308 if egr_port == ofp.OFPP_IN_PORT:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700309 check_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700310 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700311 port_number=check_port, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700312
Dan Talaycof6e76c02012-03-23 10:56:12 -0700313 if rcv_pkt is None:
Rich Lane9a003812012-10-04 17:17:59 -0700314 logging.error("ERROR: No packet received from " +
Dan Talaycod8ae7582012-03-23 12:24:56 -0700315 str(check_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700316
Dan Talaycof6e76c02012-03-23 10:56:12 -0700317 parent.assertTrue(rcv_pkt is not None,
Dan Talaycod8ae7582012-03-23 12:24:56 -0700318 "Did not receive packet port " + str(check_port))
Rich Lane9a003812012-10-04 17:17:59 -0700319 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700320 str(rcv_port))
321
322 if str(exp_pkt) != str(rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700323 logging.error("ERROR: Packet match failed.")
324 logging.debug("Expected len " + str(len(exp_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700325 + str(exp_pkt).encode('hex'))
Rich Lane9a003812012-10-04 17:17:59 -0700326 logging.debug("Received len " + str(len(rcv_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700327 + str(rcv_pkt).encode('hex'))
328 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
Dan Talaycod8ae7582012-03-23 12:24:56 -0700329 "Packet match error on port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700330
Dan Talayco551befa2010-07-15 17:05:32 -0700331def match_verify(parent, req_match, res_match):
332 """
333 Verify flow matches agree; if they disagree, report where
334
335 parent must implement assertEqual
336 Use str() to ensure content is compared and not pointers
337 """
338
339 parent.assertEqual(req_match.wildcards, res_match.wildcards,
340 'Match failed: wildcards: ' + hex(req_match.wildcards) +
341 " != " + hex(res_match.wildcards))
342 parent.assertEqual(req_match.in_port, res_match.in_port,
343 'Match failed: in_port: ' + str(req_match.in_port) +
344 " != " + str(res_match.in_port))
345 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
346 'Match failed: dl_src: ' + str(req_match.dl_src) +
347 " != " + str(res_match.dl_src))
348 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
349 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
350 " != " + str(res_match.dl_dst))
351 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
352 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
353 " != " + str(res_match.dl_vlan))
354 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
355 'Match failed: dl_vlan_pcp: ' +
356 str(req_match.dl_vlan_pcp) + " != " +
357 str(res_match.dl_vlan_pcp))
358 parent.assertEqual(req_match.dl_type, res_match.dl_type,
359 'Match failed: dl_type: ' + str(req_match.dl_type) +
360 " != " + str(res_match.dl_type))
361
362 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
363 and (req_match.dl_type == IP_ETHERTYPE)):
364 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
365 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
366 " != " + str(res_match.nw_tos))
367 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
368 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
369 " != " + str(res_match.nw_proto))
370 parent.assertEqual(req_match.nw_src, res_match.nw_src,
371 'Match failed: nw_src: ' + str(req_match.nw_src) +
372 " != " + str(res_match.nw_src))
373 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
374 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
375 " != " + str(res_match.nw_dst))
376
377 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
378 and ((req_match.nw_proto == TCP_PROTOCOL)
379 or (req_match.nw_proto == UDP_PROTOCOL))):
380 parent.assertEqual(req_match.tp_src, res_match.tp_src,
381 'Match failed: tp_src: ' +
382 str(req_match.tp_src) +
383 " != " + str(res_match.tp_src))
384 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
385 'Match failed: tp_dst: ' +
386 str(req_match.tp_dst) +
387 " != " + str(res_match.tp_dst))
388
389def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
390 """
391 Receive a flow removed msg and verify it matches expected
392
393 @params parent Must implement controller, assertEqual
394 @param pkt_count If >= 0, verify packet count
395 @param byte_count If >= 0, verify byte count
396 """
397 (response, raw) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
398 parent.assertTrue(response is not None, 'No flow removed message received')
399
400 if request is None:
401 return
402
403 parent.assertEqual(request.cookie, response.cookie,
404 "Flow removed cookie error: " +
405 hex(request.cookie) + " != " + hex(response.cookie))
406
407 req_match = request.match
408 res_match = response.match
409 verifyMatchField(req_match, res_match)
410
411 if (req_match.wildcards != 0):
412 parent.assertEqual(request.priority, response.priority,
413 'Flow remove prio mismatch: ' +
414 str(request,priority) + " != " +
415 str(response.priority))
416 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
417 'Flow remove reason is not HARD TIMEOUT:' +
418 str(response.reason))
419 if pkt_count >= 0:
420 parent.assertEqual(response.packet_count, pkt_count,
421 'Flow removed failed, packet count: ' +
422 str(response.packet_count) + " != " +
423 str(pkt_count))
424 if byte_count >= 0:
425 parent.assertEqual(response.byte_count, byte_count,
426 'Flow removed failed, byte count: ' +
427 str(response.byte_count) + " != " +
428 str(byte_count))
429
Ed Swierk99a74de2012-08-22 06:40:54 -0700430def packet_to_flow_match(parent, packet):
431 match = parse.packet_to_flow_match(packet)
432 match.wildcards |= required_wildcards(parent)
433 return match
434
435def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700436 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700437 """
438 Create a flow message
439
440 Match on packet with given wildcards.
441 See flow_match_test for other parameter descriptoins
442 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700443 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700444 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700445 """
446 match = parse.packet_to_flow_match(pkt)
447 parent.assertTrue(match is not None, "Flow match from pkt failed")
Ed Swierk99a74de2012-08-22 06:40:54 -0700448 if wildcards is None:
449 wildcards = required_wildcards(parent)
Dan Talayco677c0b72011-08-23 22:53:38 -0700450 if in_band:
451 wildcards &= ~ofp.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700452 match.wildcards = wildcards
453 match.in_port = ing_port
454
Dan Talaycof6e76c02012-03-23 10:56:12 -0700455 if type(egr_ports) == type([]):
456 egr_port_list = egr_ports
457 else:
458 egr_port_list = [egr_ports]
459
Dan Talayco551befa2010-07-15 17:05:32 -0700460 request = message.flow_mod()
461 request.match = match
462 request.buffer_id = 0xffffffff
463 if check_expire:
464 request.flags |= ofp.OFPFF_SEND_FLOW_REM
465 request.hard_timeout = 1
466
467 if action_list is not None:
468 for act in action_list:
Rich Lane9a003812012-10-04 17:17:59 -0700469 logging.debug("Adding action " + act.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700470 rv = request.actions.add(act)
471 parent.assertTrue(rv, "Could not add action" + act.show())
472
473 # Set up output/enqueue action if directed
474 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700475 parent.assertTrue(egr_ports is not None, "Egress port not set")
Dan Talayco551befa2010-07-15 17:05:32 -0700476 act = action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700477 for egr_port in egr_port_list:
478 act.port = egr_port
479 act.queue_id = egr_queue
480 rv = request.actions.add(act)
481 parent.assertTrue(rv, "Could not add enqueue action " +
482 str(egr_port) + " Q: " + str(egr_queue))
483 elif egr_ports is not None:
484 for egr_port in egr_port_list:
485 act = action.action_output()
486 act.port = egr_port
487 rv = request.actions.add(act)
488 parent.assertTrue(rv, "Could not add output action " +
489 str(egr_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700490
Rich Lane9a003812012-10-04 17:17:59 -0700491 logging.debug(request.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700492
493 return request
494
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700495def flow_msg_install(parent, request, clear_table_override=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700496 """
497 Install a flow mod message in the switch
498
499 @param parent Must implement controller, assertEqual, assertTrue
500 @param request The request, all set to go
501 @param clear_table If true, clear the flow table before installing
502 """
Dan Talayco8a64e332012-03-28 14:53:20 -0700503
504 clear_table = test_param_get(parent.config, 'clear_table', default=True)
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700505 if(clear_table_override != None):
506 clear_table = clear_table_override
507
508 if clear_table:
Rich Lane9a003812012-10-04 17:17:59 -0700509 logging.debug("Clear flow table")
510 rc = delete_all_flows(parent.controller)
Dan Talayco551befa2010-07-15 17:05:32 -0700511 parent.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700512 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700513
Rich Lane9a003812012-10-04 17:17:59 -0700514 logging.debug("Insert flow")
Dan Talayco551befa2010-07-15 17:05:32 -0700515 rv = parent.controller.message_send(request)
516 parent.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700517 parent.assertEqual(do_barrier(parent.controller), 0, "Barrier failed")
Dan Talayco551befa2010-07-15 17:05:32 -0700518
Ed Swierk99a74de2012-08-22 06:40:54 -0700519def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700520 dl_vlan=-1, pkt=None, exp_pkt=None,
521 action_list=None, check_expire=False):
522 """
523 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700524 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700525
526 Run test with packet through switch from ing_port to egr_port
527 See flow_match_test for parameter descriptions
528 """
529
Ed Swierk99a74de2012-08-22 06:40:54 -0700530 if wildcards is None:
531 wildcards = required_wildcards(parent)
Rich Lane9a003812012-10-04 17:17:59 -0700532 logging.info("Pkt match test: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700533 str(egr_ports))
Rich Lane9a003812012-10-04 17:17:59 -0700534 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Dan Talayco98fada92010-07-17 00:36:21 -0700535 " expire: " + str(check_expire))
Dan Talayco551befa2010-07-15 17:05:32 -0700536 if pkt is None:
537 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
538
539 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700540 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700541 action_list=action_list)
542
543 flow_msg_install(parent, request)
544
Rich Lane9a003812012-10-04 17:17:59 -0700545 logging.debug("Send packet: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700546 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700547 parent.dataplane.send(ing_port, str(pkt))
548
549 if exp_pkt is None:
550 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700551 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700552
553 if check_expire:
554 #@todo Not all HW supports both pkt and byte counters
555 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
556
Dan Talaycof6e76c02012-03-23 10:56:12 -0700557def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
558 """
559 Generate a list of ports avoiding those in the exclude list
Rich Lane9a003812012-10-04 17:17:59 -0700560 @param parent Supplies logging
Dan Talaycof6e76c02012-03-23 10:56:12 -0700561 @param of_ports List of OF port numbers
562 @param how_many Number of ports to be added to the list
563 @param exclude_list List of ports not to be used
564 @returns An empty list if unable to find enough ports
565 """
566
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700567 if how_many == 0:
568 return []
569
Dan Talaycof6e76c02012-03-23 10:56:12 -0700570 count = 0
571 egr_ports = []
572 for egr_idx in range(len(of_ports)):
573 if of_ports[egr_idx] not in exclude_list:
574 egr_ports.append(of_ports[egr_idx])
575 count += 1
576 if count >= how_many:
577 return egr_ports
Rich Lane9a003812012-10-04 17:17:59 -0700578 logging.debug("Could not generate enough egress ports for test")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700579 return []
580
Ed Swierk99a74de2012-08-22 06:40:54 -0700581def flow_match_test(parent, port_map, wildcards=None, dl_vlan=-1, pkt=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700582 exp_pkt=None, action_list=None, check_expire=False,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700583 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700584 """
585 Run flow_match_test_port_pair on all port pairs
586
587 @param max_test If > 0 no more than this number of tests are executed.
588 @param parent Must implement controller, dataplane, assertTrue, assertEqual
Rich Lane9a003812012-10-04 17:17:59 -0700589 and logging
Dan Talayco551befa2010-07-15 17:05:32 -0700590 @param pkt If not None, use this packet for ingress
591 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700592 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700593 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
594 @param action_list Additional actions to add to flow mod
595 @param check_expire Check for flow expiration message
Dan Talaycocfa172f2012-03-23 12:03:00 -0700596 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700597 """
Ed Swierk99a74de2012-08-22 06:40:54 -0700598 if wildcards is None:
599 wildcards = required_wildcards(parent)
Dan Talayco551befa2010-07-15 17:05:32 -0700600 of_ports = port_map.keys()
601 of_ports.sort()
602 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
603 test_count = 0
604
Dan Talaycocfa172f2012-03-23 12:03:00 -0700605 if egr_count == -1:
606 egr_count = test_param_get(parent.config, 'egr_count', default=2)
607
Dan Talayco551befa2010-07-15 17:05:32 -0700608 for ing_idx in range(len(of_ports)):
609 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700610 egr_ports = get_egr_list(parent, of_ports, egr_count,
611 exclude_list=[ingress_port])
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700612 if ing_port:
613 egr_ports.append(ofp.OFPP_IN_PORT)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700614 if len(egr_ports) == 0:
615 parent.assertTrue(0, "Failed to generate egress port list")
616
617 flow_match_test_port_pair(parent, ingress_port, egr_ports,
618 wildcards=wildcards, dl_vlan=dl_vlan,
619 pkt=pkt, exp_pkt=exp_pkt,
620 action_list=action_list,
621 check_expire=check_expire)
622 test_count += 1
623 if (max_test > 0) and (test_count > max_test):
Rich Lane9a003812012-10-04 17:17:59 -0700624 logging.info("Ran " + str(test_count) + " tests; exiting")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700625 return
Dan Talayco551befa2010-07-15 17:05:32 -0700626
Dan Talayco4b2bee62010-07-20 14:10:05 -0700627def test_param_get(config, key, default=None):
628 """
629 Return value passed via test-params if present
630
631 @param config The configuration structure for OFTest
632 @param key The lookup key
633 @param default Default value to use if not found
634
635 If the pair 'key=val' appeared in the string passed to --test-params
636 on the command line, return val (as interpreted by exec). Otherwise
637 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700638
639 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
640 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700641 """
642 try:
643 exec config["test_params"]
644 except:
645 return default
646
647 s = "val = " + str(key)
648 try:
649 exec s
650 return val
651 except:
652 return default
653
654def action_generate(parent, field_to_mod, mod_field_vals):
655 """
656 Create an action to modify the field indicated in field_to_mod
657
658 @param parent Must implement, assertTrue
659 @param field_to_mod The field to modify as a string name
660 @param mod_field_vals Hash of values to use for modified values
661 """
662
663 act = None
664
665 if field_to_mod in ['pktlen']:
666 return None
667
668 if field_to_mod == 'dl_dst':
669 act = action.action_set_dl_dst()
670 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
671 elif field_to_mod == 'dl_src':
672 act = action.action_set_dl_src()
673 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
674 elif field_to_mod == 'dl_vlan_enable':
675 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
676 act = action.action_strip_vlan()
677 # Add VLAN tag is handled by dl_vlan field
678 # Will return None in this case
679 elif field_to_mod == 'dl_vlan':
680 act = action.action_set_vlan_vid()
681 act.vlan_vid = mod_field_vals['dl_vlan']
682 elif field_to_mod == 'dl_vlan_pcp':
683 act = action.action_set_vlan_pcp()
684 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
685 elif field_to_mod == 'ip_src':
686 act = action.action_set_nw_src()
687 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
688 elif field_to_mod == 'ip_dst':
689 act = action.action_set_nw_dst()
690 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
691 elif field_to_mod == 'ip_tos':
692 act = action.action_set_nw_tos()
693 act.nw_tos = mod_field_vals['ip_tos']
694 elif field_to_mod == 'tcp_sport':
695 act = action.action_set_tp_src()
696 act.tp_port = mod_field_vals['tcp_sport']
697 elif field_to_mod == 'tcp_dport':
698 act = action.action_set_tp_dst()
699 act.tp_port = mod_field_vals['tcp_dport']
700 else:
701 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
702
703 return act
704
705def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
706 mod_fields={}, check_test_params=False):
707 """
708 Set up the ingress and expected packet and action list for a test
709
Rich Lane9a003812012-10-04 17:17:59 -0700710 @param parent Must implement, assertTrue, and config hash
Dan Talayco4b2bee62010-07-20 14:10:05 -0700711 @param start_field_values Field values to use for ingress packet (optional)
712 @param mod_field_values Field values to use for modified packet (optional)
713 @param mod_fields The list of fields to be modified by the switch in the test.
714 @params check_test_params If True, will check the parameters vid, add_vlan
715 and strip_vlan from the command line.
716
717 Returns a triple: pkt-to-send, expected-pkt, action-list
718 """
719
720 new_actions = []
721
Dan Talayco4b2bee62010-07-20 14:10:05 -0700722 base_pkt_params = {}
723 base_pkt_params['pktlen'] = 100
724 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
725 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
726 base_pkt_params['dl_vlan_enable'] = False
727 base_pkt_params['dl_vlan'] = 2
728 base_pkt_params['dl_vlan_pcp'] = 0
729 base_pkt_params['ip_src'] = '192.168.0.1'
730 base_pkt_params['ip_dst'] = '192.168.0.2'
731 base_pkt_params['ip_tos'] = 0
732 base_pkt_params['tcp_sport'] = 1234
733 base_pkt_params['tcp_dport'] = 80
734 for keyname in start_field_vals.keys():
735 base_pkt_params[keyname] = start_field_vals[keyname]
736
737 mod_pkt_params = {}
738 mod_pkt_params['pktlen'] = 100
739 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
740 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
741 mod_pkt_params['dl_vlan_enable'] = False
742 mod_pkt_params['dl_vlan'] = 3
743 mod_pkt_params['dl_vlan_pcp'] = 7
744 mod_pkt_params['ip_src'] = '10.20.30.40'
745 mod_pkt_params['ip_dst'] = '50.60.70.80'
746 mod_pkt_params['ip_tos'] = 0xf0
747 mod_pkt_params['tcp_sport'] = 4321
748 mod_pkt_params['tcp_dport'] = 8765
749 for keyname in mod_field_vals.keys():
750 mod_pkt_params[keyname] = mod_field_vals[keyname]
751
752 # Check for test param modifications
753 strip = False
754 if check_test_params:
755 add_vlan = test_param_get(parent.config, 'add_vlan')
756 strip_vlan = test_param_get(parent.config, 'strip_vlan')
757 vid = test_param_get(parent.config, 'vid')
758
759 if add_vlan and strip_vlan:
760 parent.assertTrue(0, "Add and strip VLAN both specified")
761
762 if vid:
763 base_pkt_params['dl_vlan_enable'] = True
764 base_pkt_params['dl_vlan'] = vid
765 if 'dl_vlan' in mod_fields:
766 mod_pkt_params['dl_vlan'] = vid + 1
767
768 if add_vlan:
769 base_pkt_params['dl_vlan_enable'] = False
770 mod_pkt_params['dl_vlan_enable'] = True
771 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
772 mod_fields.append('pktlen')
773 mod_fields.append('dl_vlan_enable')
774 if 'dl_vlan' not in mod_fields:
775 mod_fields.append('dl_vlan')
776 elif strip_vlan:
777 base_pkt_params['dl_vlan_enable'] = True
778 mod_pkt_params['dl_vlan_enable'] = False
779 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
780 mod_fields.append('dl_vlan_enable')
781 mod_fields.append('pktlen')
782
783 # Build the ingress packet
784 ingress_pkt = simple_tcp_packet(**base_pkt_params)
785
786 # Build the expected packet, modifying the indicated fields
787 for item in mod_fields:
788 base_pkt_params[item] = mod_pkt_params[item]
789 act = action_generate(parent, item, mod_pkt_params)
790 if act:
791 new_actions.append(act)
792
793 expected_pkt = simple_tcp_packet(**base_pkt_params)
794
795 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700796
797# Generate a simple "drop" flow mod
798# If in_band is true, then only drop from first test port
799def flow_mod_gen(port_map, in_band):
800 request = message.flow_mod()
801 request.match.wildcards = ofp.OFPFW_ALL
802 if in_band:
803 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
804 for of_port, ifname in port_map.items(): # Grab first port
805 break
806 request.match.in_port = of_port
807 request.buffer_id = 0xffffffff
808 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700809
810def skip_message_emit(parent, s):
811 """
812 Print out a 'skipped' message to stderr
813
814 @param s The string to print out to the log file
Rich Lane9a003812012-10-04 17:17:59 -0700815 @param parent Must implement config object
Dan Talaycoba3745c2010-07-21 21:51:08 -0700816 """
817 global skipped_test_count
818
819 skipped_test_count += 1
Rich Lane9a003812012-10-04 17:17:59 -0700820 logging.info("Skipping: " + s)
Dan Talaycoba3745c2010-07-21 21:51:08 -0700821 if parent.config["dbg_level"] < logging.WARNING:
822 sys.stderr.write("(skipped) ")
823 else:
824 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700825
Dan Talayco8a64e332012-03-28 14:53:20 -0700826
827def all_stats_get(parent):
828 """
829 Get the aggregate stats for all flows in the table
830 @param parent Test instance with controller connection and assert
831 @returns dict with keys flows, packets, bytes, active (flows),
832 lookups, matched
833 """
834 stat_req = message.aggregate_stats_request()
835 stat_req.match = ofp.ofp_match()
836 stat_req.match.wildcards = ofp.OFPFW_ALL
837 stat_req.table_id = 0xff
838 stat_req.out_port = ofp.OFPP_NONE
839
840 rv = {}
841
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700842 (reply, pkt) = parent.controller.transact(stat_req)
Dan Talayco8a64e332012-03-28 14:53:20 -0700843 parent.assertTrue(len(reply.stats) == 1, "Did not receive flow stats reply")
844
845 for obj in reply.stats:
846 (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
847 obj.packet_count, obj.byte_count)
848 break
849
850 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700851 (reply , pkt) = parent.controller.transact(request)
Dan Talayco8a64e332012-03-28 14:53:20 -0700852
853
854 (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
855 for obj in reply.stats:
856 rv["active"] += obj.active_count
857 rv["lookups"] += obj.lookup_count
858 rv["matched"] += obj.matched_count
859
860 return rv
Dan Talayco2baf8b52012-03-30 09:55:42 -0700861
862FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
863 for x in range(256)])
864
865def hex_dump_buffer(src, length=16):
866 """
867 Convert src to a hex dump string and return the string
868 @param src The source buffer
869 @param length The number of bytes shown in each line
870 @returns A string showing the hex dump
871 """
Dan Talaycoc516fa02012-04-12 22:28:43 -0700872 result = ["\n"]
Dan Talayco2baf8b52012-03-30 09:55:42 -0700873 for i in xrange(0, len(src), length):
874 chars = src[i:i+length]
875 hex = ' '.join(["%02x" % ord(x) for x in chars])
876 printable = ''.join(["%s" % ((ord(x) <= 127 and
877 FILTER[ord(x)]) or '.') for x in chars])
878 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
879 return ''.join(result)
880
881def format_packet(pkt):
882 return "Packet length %d \n%s" % (len(str(pkt)),
883 hex_dump_buffer(str(pkt)))