blob: 6738d4212e1d8c22a8a673d4e15ac0a8fd81f524 [file] [log] [blame]
Dan Talaycod2ca1032010-03-10 14:40:26 -08001import sys
Dan Talayco92c99122010-06-03 13:53:18 -07002import copy
Rich Lane6242d9f2013-01-06 17:35:39 -08003import logging
4import types
5import time
Rich Lane5a9a1922013-01-11 14:29:30 -08006import re
Dan Talaycod2ca1032010-03-10 14:40:26 -08007
8try:
9 import scapy.all as scapy
10except:
11 try:
12 import scapy as scapy
13 except:
14 sys.exit("Need to install scapy for packet parsing")
Dan Talayco41eae8b2010-03-10 13:57:06 -080015
Rich Lanecd97d3d2013-01-07 18:50:06 -080016import oftest
17import oftest.controller
18import oftest.dataplane
19import of10.cstruct
20import of10.message
21import of10.action
22import of10.parse
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")
Rich Lanecd97d3d2013-01-07 18:50:06 -080041 msg = of10.message.flow_mod()
42 msg.match.wildcards = of10.cstruct.OFPFW_ALL
43 msg.out_port = of10.cstruct.OFPP_NONE
44 msg.command = of10.cstruct.OFPFC_DELETE
Dan Talaycoc901f4d2010-03-07 21:55:45 -080045 msg.buffer_id = 0xffffffff
Rich Lane5c3151c2013-01-03 17:15:41 -080046 ctrl.message_send(msg)
Rich Lane32bf9482013-01-03 17:26:30 -080047 return 0 # for backwards compatibility
Dan Talayco41eae8b2010-03-10 13:57:06 -080048
Ed Swierk99a74de2012-08-22 06:40:54 -070049def required_wildcards(parent):
Rich Lane2014f9b2012-10-05 15:29:40 -070050 w = test_param_get('required_wildcards', default='default')
Ed Swierk99a74de2012-08-22 06:40:54 -070051 if w == 'l3-l4':
Rich Lanecd97d3d2013-01-07 18:50:06 -080052 return (of10.cstruct.OFPFW_NW_SRC_ALL | of10.cstruct.OFPFW_NW_DST_ALL | of10.cstruct.OFPFW_NW_TOS
53 | of10.cstruct.OFPFW_NW_PROTO | of10.cstruct.OFPFW_TP_SRC | of10.cstruct.OFPFW_TP_DST)
Ed Swierk99a74de2012-08-22 06:40:54 -070054 else:
55 return 0
56
Dan Talayco41eae8b2010-03-10 13:57:06 -080057def simple_tcp_packet(pktlen=100,
58 dl_dst='00:01:02:03:04:05',
59 dl_src='00:06:07:08:09:0a',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070060 dl_vlan_enable=False,
61 dl_vlan=0,
62 dl_vlan_pcp=0,
Dan Talayco551befa2010-07-15 17:05:32 -070063 dl_vlan_cfi=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080064 ip_src='192.168.0.1',
65 ip_dst='192.168.0.2',
Tatsuya Yabe460321e2010-05-25 17:50:49 -070066 ip_tos=0,
Dan Talayco41eae8b2010-03-10 13:57:06 -080067 tcp_sport=1234,
Christian Dickmann8b59b4b2012-09-23 16:48:30 -070068 tcp_dport=80,
69 ip_ihl=None,
70 ip_options=False
Dan Talayco41eae8b2010-03-10 13:57:06 -080071 ):
72 """
73 Return a simple dataplane TCP packet
74
75 Supports a few parameters:
76 @param len Length of packet in bytes w/o CRC
77 @param dl_dst Destinatino MAC
78 @param dl_src Source MAC
Tatsuya Yabe460321e2010-05-25 17:50:49 -070079 @param dl_vlan_enable True if the packet is with vlan, False otherwise
80 @param dl_vlan VLAN ID
81 @param dl_vlan_pcp VLAN priority
Dan Talayco41eae8b2010-03-10 13:57:06 -080082 @param ip_src IP source
83 @param ip_dst IP destination
Tatsuya Yabe460321e2010-05-25 17:50:49 -070084 @param ip_tos IP ToS
Dan Talayco41eae8b2010-03-10 13:57:06 -080085 @param tcp_dport TCP destination port
86 @param ip_sport TCP source port
87
88 Generates a simple TCP request. Users
89 shouldn't assume anything about this packet other than that
90 it is a valid ethernet/IP/TCP frame.
91 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +000092
93 if MINSIZE > pktlen:
94 pktlen = MINSIZE
95
Dan Talayco551befa2010-07-15 17:05:32 -070096 # Note Dot1Q.id is really CFI
Shudong Zhou43ee54c2012-12-13 15:52:37 -080097 if (dl_vlan_enable):
Tatsuya Yabe460321e2010-05-25 17:50:49 -070098 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
Dan Talayco551befa2010-07-15 17:05:32 -070099 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700100 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700101 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
102 else:
Christian Dickmann8b59b4b2012-09-23 16:48:30 -0700103 if not ip_options:
104 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
105 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
106 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
107 else:
108 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
109 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl, options=ip_options)/ \
110 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
Tatsuya Yabe460321e2010-05-25 17:50:49 -0700111
Dan Talayco41eae8b2010-03-10 13:57:06 -0800112 pkt = pkt/("D" * (pktlen - len(pkt)))
113
114 return pkt
115
Rich Lane6ee7bea2012-10-26 16:19:29 -0700116def simple_udp_packet(pktlen=100,
117 dl_dst='00:01:02:03:04:05',
118 dl_src='00:06:07:08:09:0a',
119 dl_vlan_enable=False,
120 dl_vlan=0,
121 dl_vlan_pcp=0,
122 dl_vlan_cfi=0,
123 ip_src='192.168.0.1',
124 ip_dst='192.168.0.2',
125 ip_tos=0,
126 udp_sport=1234,
127 udp_dport=80,
128 ip_ihl=None,
129 ip_options=False
130 ):
131 """
132 Return a simple dataplane UDP packet
133
134 Supports a few parameters:
135 @param len Length of packet in bytes w/o CRC
136 @param dl_dst Destination MAC
137 @param dl_src Source MAC
138 @param dl_vlan_enable True if the packet is with vlan, False otherwise
139 @param dl_vlan VLAN ID
140 @param dl_vlan_pcp VLAN priority
141 @param ip_src IP source
142 @param ip_dst IP destination
143 @param ip_tos IP ToS
144 @param udp_dport UDP destination port
145 @param udp_sport UDP source port
146
147 Generates a simple UDP packet. Users shouldn't assume anything about
148 this packet other than that it is a valid ethernet/IP/UDP frame.
149 """
150
151 if MINSIZE > pktlen:
152 pktlen = MINSIZE
153
154 # Note Dot1Q.id is really CFI
155 if (dl_vlan_enable):
156 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
157 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
158 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
159 scapy.UDP(sport=udp_sport, dport=udp_dport)
160 else:
161 if not ip_options:
162 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
163 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
164 scapy.UDP(sport=udp_sport, dport=udp_dport)
165 else:
166 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
167 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl, options=ip_options)/ \
168 scapy.UDP(sport=udp_sport, dport=udp_dport)
169
170 pkt = pkt/("D" * (pktlen - len(pkt)))
171
172 return pkt
173
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700174def simple_icmp_packet(pktlen=60,
175 dl_dst='00:01:02:03:04:05',
176 dl_src='00:06:07:08:09:0a',
177 dl_vlan_enable=False,
178 dl_vlan=0,
179 dl_vlan_pcp=0,
180 ip_src='192.168.0.1',
181 ip_dst='192.168.0.2',
182 ip_tos=0,
183 icmp_type=8,
184 icmp_code=0
185 ):
186 """
187 Return a simple ICMP packet
188
189 Supports a few parameters:
190 @param len Length of packet in bytes w/o CRC
191 @param dl_dst Destinatino MAC
192 @param dl_src Source MAC
193 @param dl_vlan_enable True if the packet is with vlan, False otherwise
194 @param dl_vlan VLAN ID
195 @param dl_vlan_pcp VLAN priority
196 @param ip_src IP source
197 @param ip_dst IP destination
198 @param ip_tos IP ToS
199 @param icmp_type ICMP type
200 @param icmp_code ICMP code
201
202 Generates a simple ICMP ECHO REQUEST. Users
203 shouldn't assume anything about this packet other than that
204 it is a valid ethernet/ICMP frame.
205 """
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000206
207 if MINSIZE > pktlen:
208 pktlen = MINSIZE
209
Tatsuya Yabeb8fb3c32010-06-14 15:48:36 -0700210 if (dl_vlan_enable):
211 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
212 scapy.Dot1Q(prio=dl_vlan_pcp, id=0, vlan=dl_vlan)/ \
213 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
214 scapy.ICMP(type=icmp_type, code=icmp_code)
215 else:
216 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
217 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos)/ \
218 scapy.ICMP(type=icmp_type, code=icmp_code)
219
220 pkt = pkt/("0" * (pktlen - len(pkt)))
221
222 return pkt
223
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700224def simple_eth_packet(pktlen=60,
225 dl_dst='00:01:02:03:04:05',
226 dl_src='01:80:c2:00:00:00',
227 dl_type=0x88cc):
Jeffrey Townsend5cb7ed32012-08-17 18:11:01 +0000228
229 if MINSIZE > pktlen:
230 pktlen = MINSIZE
231
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700232 pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
233
234 pkt = pkt/("0" * (pktlen - len(pkt)))
235
236 return pkt
237
Shudong Zhou43ee54c2012-12-13 15:52:37 -0800238def qinq_tcp_packet(pktlen=100,
239 dl_dst='00:01:02:03:04:05',
240 dl_src='00:06:07:08:09:0a',
241 dl_vlan_outer=20,
242 dl_vlan_pcp_outer=0,
243 dl_vlan_cfi_outer=0,
244 dl_vlan=10,
245 dl_vlan_pcp=0,
246 dl_vlan_cfi=0,
247 ip_src='192.168.0.1',
248 ip_dst='192.168.0.2',
249 ip_tos=0,
250 tcp_sport=1234,
251 tcp_dport=80,
252 ip_ihl=None,
253 ip_options=False
254 ):
255 """
256 Return a doubly tagged dataplane TCP packet
257
258 Supports a few parameters:
259 @param len Length of packet in bytes w/o CRC
260 @param dl_dst Destinatino MAC
261 @param dl_src Source MAC
262 @param dl_vlan_outer Outer VLAN ID
263 @param dl_vlan_pcp_outer Outer VLAN priority
264 @param dl_vlan_cfi_outer Outer VLAN cfi bit
265 @param dl_vlan Inner VLAN ID
266 @param dl_vlan_pcp VLAN priority
267 @param dl_vlan_cfi VLAN cfi bit
268 @param ip_src IP source
269 @param ip_dst IP destination
270 @param ip_tos IP ToS
271 @param tcp_dport TCP destination port
272 @param ip_sport TCP source port
273
274 Generates a TCP request. Users
275 shouldn't assume anything about this packet other than that
276 it is a valid ethernet/IP/TCP frame.
277 """
278
279 if MINSIZE > pktlen:
280 pktlen = MINSIZE
281
282 # Note Dot1Q.id is really CFI
283 pkt = scapy.Ether(dst=dl_dst, src=dl_src)/ \
284 scapy.Dot1Q(prio=dl_vlan_pcp_outer, id=dl_vlan_cfi_outer, vlan=dl_vlan_outer)/ \
285 scapy.Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
286 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ihl=ip_ihl)/ \
287 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
288
289 pkt = pkt/("D" * (pktlen - len(pkt)))
290
291 return pkt
292
Shudong Zhoub7f12462012-11-20 13:01:12 -0800293def do_barrier(ctrl, timeout=-1):
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700294 """
295 Do a barrier command
296 Return 0 on success, -1 on error
297 """
Rich Lanecd97d3d2013-01-07 18:50:06 -0800298 b = of10.message.barrier_request()
Shudong Zhoub7f12462012-11-20 13:01:12 -0800299 (resp, pkt) = ctrl.transact(b, timeout=timeout)
Rich Lane3a261d52013-01-03 17:45:08 -0800300 if resp is None:
301 raise AssertionError("barrier failed")
Dan Talaycof6b94832012-04-12 21:50:57 -0700302 # We'll trust the transaction processing in the controller that xid matched
Rich Lane3a261d52013-01-03 17:45:08 -0800303 return 0 # for backwards compatibility
Dan Talayco92c99122010-06-03 13:53:18 -0700304
Rich Lane9a003812012-10-04 17:17:59 -0700305def port_config_get(controller, port_no):
Dan Talayco92c99122010-06-03 13:53:18 -0700306 """
307 Get a port's configuration
308
309 Gets the switch feature configuration and grabs one port's
310 configuration
311
312 @returns (hwaddr, config, advert) The hwaddress, configuration and
313 advertised values
314 """
Rich Lanecd97d3d2013-01-07 18:50:06 -0800315 request = of10.message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700316 reply, pkt = controller.transact(request)
Rich Lane9a003812012-10-04 17:17:59 -0700317 logging.debug(reply.show())
Dan Talayco92c99122010-06-03 13:53:18 -0700318 if reply is None:
Rich Lane9a003812012-10-04 17:17:59 -0700319 logging.warn("Get feature request failed")
Dan Talayco92c99122010-06-03 13:53:18 -0700320 return None, None, None
321 for idx in range(len(reply.ports)):
322 if reply.ports[idx].port_no == port_no:
323 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
324 reply.ports[idx].advertised)
325
Rich Lane9a003812012-10-04 17:17:59 -0700326 logging.warn("Did not find port number for port config")
Dan Talayco92c99122010-06-03 13:53:18 -0700327 return None, None, None
328
Rich Lane9a003812012-10-04 17:17:59 -0700329def port_config_set(controller, port_no, config, mask):
Dan Talayco92c99122010-06-03 13:53:18 -0700330 """
331 Set the port configuration according the given parameters
332
333 Gets the switch feature configuration and updates one port's
334 configuration value according to config and mask
335 """
Rich Lane9a003812012-10-04 17:17:59 -0700336 logging.info("Setting port " + str(port_no) + " to config " + str(config))
Rich Lanecd97d3d2013-01-07 18:50:06 -0800337 request = of10.message.features_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700338 reply, pkt = controller.transact(request)
Dan Talayco92c99122010-06-03 13:53:18 -0700339 if reply is None:
340 return -1
Rich Lane9a003812012-10-04 17:17:59 -0700341 logging.debug(reply.show())
Ed Swierk8d84ce02012-12-27 15:36:48 -0800342 p = None
Dan Talayco92c99122010-06-03 13:53:18 -0700343 for idx in range(len(reply.ports)):
344 if reply.ports[idx].port_no == port_no:
Ed Swierk8d84ce02012-12-27 15:36:48 -0800345 p = reply.ports[idx]
Dan Talayco92c99122010-06-03 13:53:18 -0700346 break
Rich Lanecd97d3d2013-01-07 18:50:06 -0800347 mod = of10.message.port_mod()
Dan Talayco92c99122010-06-03 13:53:18 -0700348 mod.port_no = port_no
Ed Swierk8d84ce02012-12-27 15:36:48 -0800349 if p:
350 mod.hw_addr = p.hw_addr
Dan Talayco92c99122010-06-03 13:53:18 -0700351 mod.config = config
352 mod.mask = mask
Ed Swierk8d84ce02012-12-27 15:36:48 -0800353 if p:
354 mod.advertise = p.advertised
Rich Lane5c3151c2013-01-03 17:15:41 -0800355 controller.message_send(mod)
356 return 0
Dan Talayco92c99122010-06-03 13:53:18 -0700357
Rich Lane2014f9b2012-10-05 15:29:40 -0700358def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if):
Dan Talayco92c99122010-06-03 13:53:18 -0700359 """
360 Check for proper receive packets across all ports
Ken Chiang1bf01602012-04-04 10:48:23 -0700361 @param dp The dataplane object
Dan Talayco92c99122010-06-03 13:53:18 -0700362 @param pkt Expected packet; may be None if yes_ports is empty
363 @param yes_ports Set or list of ports that should recieve packet
364 @param no_ports Set or list of ports that should not receive packet
365 @param assert_if Object that implements assertXXX
366 """
Rich Lane91765672012-12-06 16:33:04 -0800367
368 # Wait this long for packets that we don't expect to receive.
369 # 100ms is (rarely) too short for positive tests on slow
370 # switches but is definitely not too short for a negative test.
371 negative_timeout = 0.1
372
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700373 exp_pkt_arg = None
Rich Lanecd97d3d2013-01-07 18:50:06 -0800374 if oftest.config["relax"]:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700375 exp_pkt_arg = pkt
376
Dan Talayco92c99122010-06-03 13:53:18 -0700377 for ofport in yes_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700378 logging.debug("Checking for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700379 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700380 port_number=ofport, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700381 assert_if.assertTrue(rcv_pkt is not None,
382 "Did not receive pkt on " + str(ofport))
Rich Lanecd97d3d2013-01-07 18:50:06 -0800383 if not oftest.dataplane.match_exp_pkt(pkt, rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700384 logging.debug("Sent %s" % format_packet(pkt))
385 logging.debug("Resp %s" % format_packet(rcv_pkt))
Rich Lanecd97d3d2013-01-07 18:50:06 -0800386 assert_if.assertTrue(oftest.dataplane.match_exp_pkt(pkt, rcv_pkt),
Ken Chiang1bf01602012-04-04 10:48:23 -0700387 "Response packet does not match send packet " +
388 "on port " + str(ofport))
Dan Talayco73f84012012-10-02 09:23:18 -0700389 if len(no_ports) > 0:
Rich Lane91765672012-12-06 16:33:04 -0800390 time.sleep(negative_timeout)
Dan Talayco92c99122010-06-03 13:53:18 -0700391 for ofport in no_ports:
Rich Lane9a003812012-10-04 17:17:59 -0700392 logging.debug("Negative check for pkt on port " + str(ofport))
Ken Chiang1bf01602012-04-04 10:48:23 -0700393 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
Rich Lane91765672012-12-06 16:33:04 -0800394 port_number=ofport, timeout=0, exp_pkt=exp_pkt_arg)
Dan Talayco92c99122010-06-03 13:53:18 -0700395 assert_if.assertTrue(rcv_pkt is None,
396 "Unexpected pkt on port " + str(ofport))
Dan Talayco551befa2010-07-15 17:05:32 -0700397
398
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700399def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
Dan Talayco551befa2010-07-15 17:05:32 -0700400 """
401 Receive a packet and verify it matches an expected value
Dan Talaycof6e76c02012-03-23 10:56:12 -0700402 @param egr_port A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700403
404 parent must implement dataplane, assertTrue and assertEqual
405 """
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700406 exp_pkt_arg = None
Rich Lanecd97d3d2013-01-07 18:50:06 -0800407 if oftest.config["relax"]:
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700408 exp_pkt_arg = exp_pkt
409
Dan Talaycof6e76c02012-03-23 10:56:12 -0700410 if type(egr_ports) == type([]):
411 egr_port_list = egr_ports
412 else:
413 egr_port_list = [egr_ports]
Dan Talaycocf26b7a2011-08-05 10:15:35 -0700414
Dan Talaycof6e76c02012-03-23 10:56:12 -0700415 # Expect a packet from each port on egr port list
416 for egr_port in egr_port_list:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700417 check_port = egr_port
Rich Lanecd97d3d2013-01-07 18:50:06 -0800418 if egr_port == of10.cstruct.OFPP_IN_PORT:
Dan Talaycod8ae7582012-03-23 12:24:56 -0700419 check_port = ing_port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700420 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700421 port_number=check_port, exp_pkt=exp_pkt_arg)
Dan Talayco551befa2010-07-15 17:05:32 -0700422
Dan Talaycof6e76c02012-03-23 10:56:12 -0700423 if rcv_pkt is None:
Rich Lane9a003812012-10-04 17:17:59 -0700424 logging.error("ERROR: No packet received from " +
Dan Talaycod8ae7582012-03-23 12:24:56 -0700425 str(check_port))
Dan Talayco551befa2010-07-15 17:05:32 -0700426
Dan Talaycof6e76c02012-03-23 10:56:12 -0700427 parent.assertTrue(rcv_pkt is not None,
Dan Talaycod8ae7582012-03-23 12:24:56 -0700428 "Did not receive packet port " + str(check_port))
Rich Lane9a003812012-10-04 17:17:59 -0700429 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700430 str(rcv_port))
431
432 if str(exp_pkt) != str(rcv_pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700433 logging.error("ERROR: Packet match failed.")
434 logging.debug("Expected len " + str(len(exp_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700435 + str(exp_pkt).encode('hex'))
Rich Lane9a003812012-10-04 17:17:59 -0700436 logging.debug("Received len " + str(len(rcv_pkt)) + ": "
Dan Talaycof6e76c02012-03-23 10:56:12 -0700437 + str(rcv_pkt).encode('hex'))
Rich Lane5d7e89a2012-10-26 16:43:13 -0700438 logging.debug("Expected packet: " + inspect_packet(scapy.Ether(str(exp_pkt))))
439 logging.debug("Received packet: " + inspect_packet(scapy.Ether(str(rcv_pkt))))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700440 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
Dan Talaycod8ae7582012-03-23 12:24:56 -0700441 "Packet match error on port " + str(check_port))
Dan Talaycof6e76c02012-03-23 10:56:12 -0700442
Dan Talayco551befa2010-07-15 17:05:32 -0700443def match_verify(parent, req_match, res_match):
444 """
445 Verify flow matches agree; if they disagree, report where
446
447 parent must implement assertEqual
448 Use str() to ensure content is compared and not pointers
449 """
450
451 parent.assertEqual(req_match.wildcards, res_match.wildcards,
452 'Match failed: wildcards: ' + hex(req_match.wildcards) +
453 " != " + hex(res_match.wildcards))
454 parent.assertEqual(req_match.in_port, res_match.in_port,
455 'Match failed: in_port: ' + str(req_match.in_port) +
456 " != " + str(res_match.in_port))
457 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
458 'Match failed: dl_src: ' + str(req_match.dl_src) +
459 " != " + str(res_match.dl_src))
460 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
461 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
462 " != " + str(res_match.dl_dst))
463 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
464 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
465 " != " + str(res_match.dl_vlan))
466 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
467 'Match failed: dl_vlan_pcp: ' +
468 str(req_match.dl_vlan_pcp) + " != " +
469 str(res_match.dl_vlan_pcp))
470 parent.assertEqual(req_match.dl_type, res_match.dl_type,
471 'Match failed: dl_type: ' + str(req_match.dl_type) +
472 " != " + str(res_match.dl_type))
473
Rich Lanecd97d3d2013-01-07 18:50:06 -0800474 if (not(req_match.wildcards & of10.cstruct.OFPFW_DL_TYPE)
Dan Talayco551befa2010-07-15 17:05:32 -0700475 and (req_match.dl_type == IP_ETHERTYPE)):
476 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
477 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
478 " != " + str(res_match.nw_tos))
479 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
480 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
481 " != " + str(res_match.nw_proto))
482 parent.assertEqual(req_match.nw_src, res_match.nw_src,
483 'Match failed: nw_src: ' + str(req_match.nw_src) +
484 " != " + str(res_match.nw_src))
485 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
486 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
487 " != " + str(res_match.nw_dst))
488
Rich Lanecd97d3d2013-01-07 18:50:06 -0800489 if (not(req_match.wildcards & of10.cstruct.OFPFW_NW_PROTO)
Dan Talayco551befa2010-07-15 17:05:32 -0700490 and ((req_match.nw_proto == TCP_PROTOCOL)
491 or (req_match.nw_proto == UDP_PROTOCOL))):
492 parent.assertEqual(req_match.tp_src, res_match.tp_src,
493 'Match failed: tp_src: ' +
494 str(req_match.tp_src) +
495 " != " + str(res_match.tp_src))
496 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
497 'Match failed: tp_dst: ' +
498 str(req_match.tp_dst) +
499 " != " + str(res_match.tp_dst))
500
Ed Swierk99a74de2012-08-22 06:40:54 -0700501def packet_to_flow_match(parent, packet):
Rich Lanecd97d3d2013-01-07 18:50:06 -0800502 match = of10.parse.packet_to_flow_match(packet)
Ed Swierk99a74de2012-08-22 06:40:54 -0700503 match.wildcards |= required_wildcards(parent)
504 return match
505
506def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700507 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700508 """
509 Create a flow message
510
511 Match on packet with given wildcards.
512 See flow_match_test for other parameter descriptoins
513 @param egr_queue if not None, make the output an enqueue action
Dan Talayco677c0b72011-08-23 22:53:38 -0700514 @param in_band if True, do not wildcard ingress port
Dan Talaycof6e76c02012-03-23 10:56:12 -0700515 @param egr_ports None (drop), single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700516 """
Rich Lanecd97d3d2013-01-07 18:50:06 -0800517 match = of10.parse.packet_to_flow_match(pkt)
Dan Talayco551befa2010-07-15 17:05:32 -0700518 parent.assertTrue(match is not None, "Flow match from pkt failed")
Ed Swierk99a74de2012-08-22 06:40:54 -0700519 if wildcards is None:
520 wildcards = required_wildcards(parent)
Dan Talayco677c0b72011-08-23 22:53:38 -0700521 if in_band:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800522 wildcards &= ~of10.cstruct.OFPFW_IN_PORT
Dan Talayco551befa2010-07-15 17:05:32 -0700523 match.wildcards = wildcards
524 match.in_port = ing_port
525
Dan Talaycof6e76c02012-03-23 10:56:12 -0700526 if type(egr_ports) == type([]):
527 egr_port_list = egr_ports
528 else:
529 egr_port_list = [egr_ports]
530
Rich Lanecd97d3d2013-01-07 18:50:06 -0800531 request = of10.message.flow_mod()
Dan Talayco551befa2010-07-15 17:05:32 -0700532 request.match = match
533 request.buffer_id = 0xffffffff
534 if check_expire:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800535 request.flags |= of10.cstruct.OFPFF_SEND_FLOW_REM
Dan Talayco551befa2010-07-15 17:05:32 -0700536 request.hard_timeout = 1
537
538 if action_list is not None:
539 for act in action_list:
Rich Lane9a003812012-10-04 17:17:59 -0700540 logging.debug("Adding action " + act.show())
Rich Lanee30455b2013-01-03 16:24:44 -0800541 request.actions.add(act)
Dan Talayco551befa2010-07-15 17:05:32 -0700542
543 # Set up output/enqueue action if directed
544 if egr_queue is not None:
Dan Talaycof6e76c02012-03-23 10:56:12 -0700545 parent.assertTrue(egr_ports is not None, "Egress port not set")
Rich Lanecd97d3d2013-01-07 18:50:06 -0800546 act = of10.action.action_enqueue()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700547 for egr_port in egr_port_list:
548 act.port = egr_port
549 act.queue_id = egr_queue
Rich Lanee30455b2013-01-03 16:24:44 -0800550 request.actions.add(act)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700551 elif egr_ports is not None:
552 for egr_port in egr_port_list:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800553 act = of10.action.action_output()
Dan Talaycof6e76c02012-03-23 10:56:12 -0700554 act.port = egr_port
Rich Lanee30455b2013-01-03 16:24:44 -0800555 request.actions.add(act)
Dan Talayco551befa2010-07-15 17:05:32 -0700556
Rich Lane9a003812012-10-04 17:17:59 -0700557 logging.debug(request.show())
Dan Talayco551befa2010-07-15 17:05:32 -0700558
559 return request
560
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700561def flow_msg_install(parent, request, clear_table_override=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700562 """
563 Install a flow mod message in the switch
564
565 @param parent Must implement controller, assertEqual, assertTrue
566 @param request The request, all set to go
567 @param clear_table If true, clear the flow table before installing
568 """
Dan Talayco8a64e332012-03-28 14:53:20 -0700569
Rich Lane2014f9b2012-10-05 15:29:40 -0700570 clear_table = test_param_get('clear_table', default=True)
Jeffrey Townsendf3eae9c2012-03-28 18:23:21 -0700571 if(clear_table_override != None):
572 clear_table = clear_table_override
573
574 if clear_table:
Rich Lane9a003812012-10-04 17:17:59 -0700575 logging.debug("Clear flow table")
Rich Lane32bf9482013-01-03 17:26:30 -0800576 delete_all_flows(parent.controller)
Dan Talayco551befa2010-07-15 17:05:32 -0700577
Rich Lane9a003812012-10-04 17:17:59 -0700578 logging.debug("Insert flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800579 parent.controller.message_send(request)
Rich Lanee912d032012-12-22 14:28:33 -0800580
Rich Lane3a261d52013-01-03 17:45:08 -0800581 do_barrier(parent.controller)
Dan Talayco551befa2010-07-15 17:05:32 -0700582
Ed Swierk99a74de2012-08-22 06:40:54 -0700583def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,
Dan Talayco551befa2010-07-15 17:05:32 -0700584 dl_vlan=-1, pkt=None, exp_pkt=None,
Rich Lanee5779d32012-10-05 17:56:04 -0700585 action_list=None):
Dan Talayco551befa2010-07-15 17:05:32 -0700586 """
587 Flow match test on single TCP packet
Dan Talaycof6e76c02012-03-23 10:56:12 -0700588 @param egr_ports A single port or list of ports
Dan Talayco551befa2010-07-15 17:05:32 -0700589
590 Run test with packet through switch from ing_port to egr_port
591 See flow_match_test for parameter descriptions
592 """
593
Ed Swierk99a74de2012-08-22 06:40:54 -0700594 if wildcards is None:
595 wildcards = required_wildcards(parent)
Rich Lane9a003812012-10-04 17:17:59 -0700596 logging.info("Pkt match test: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700597 str(egr_ports))
Rich Lanee5779d32012-10-05 17:56:04 -0700598 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan))
Dan Talayco551befa2010-07-15 17:05:32 -0700599 if pkt is None:
600 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
601
602 request = flow_msg_create(parent, pkt, ing_port=ing_port,
Dan Talaycof6e76c02012-03-23 10:56:12 -0700603 wildcards=wildcards, egr_ports=egr_ports,
Dan Talayco551befa2010-07-15 17:05:32 -0700604 action_list=action_list)
605
606 flow_msg_install(parent, request)
607
Rich Lane9a003812012-10-04 17:17:59 -0700608 logging.debug("Send packet: " + str(ing_port) + " to " +
Dan Talaycof6e76c02012-03-23 10:56:12 -0700609 str(egr_ports))
Dan Talayco551befa2010-07-15 17:05:32 -0700610 parent.dataplane.send(ing_port, str(pkt))
611
612 if exp_pkt is None:
613 exp_pkt = pkt
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700614 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
Dan Talayco551befa2010-07-15 17:05:32 -0700615
Rich Lane89725bb2012-12-03 16:23:27 -0800616def flow_match_test_pktout(parent, ing_port, egr_ports,
617 dl_vlan=-1, pkt=None, exp_pkt=None,
618 action_list=None):
619 """
620 Packet-out test on single TCP packet
621 @param egr_ports A single port or list of ports
622
623 Run test sending packet-out to egr_ports. The goal is to test the actions
624 taken on the packet, not the matching which is of course irrelevant.
625 See flow_match_test for parameter descriptions
626 """
627
628 if pkt is None:
629 pkt = simple_tcp_packet(dl_vlan_enable=(dl_vlan >= 0), dl_vlan=dl_vlan)
630
Rich Lanecd97d3d2013-01-07 18:50:06 -0800631 msg = of10.message.packet_out()
Rich Lane89725bb2012-12-03 16:23:27 -0800632 msg.in_port = ing_port
633 msg.data = str(pkt)
634 if action_list is not None:
635 for act in action_list:
Rich Lanee30455b2013-01-03 16:24:44 -0800636 msg.actions.add(act)
Rich Lane89725bb2012-12-03 16:23:27 -0800637
638 # Set up output action
639 if egr_ports is not None:
640 for egr_port in egr_ports:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800641 act = of10.action.action_output()
Rich Lane89725bb2012-12-03 16:23:27 -0800642 act.port = egr_port
Rich Lanee30455b2013-01-03 16:24:44 -0800643 msg.actions.add(act)
Rich Lane89725bb2012-12-03 16:23:27 -0800644
645 logging.debug(msg.show())
Rich Lane5c3151c2013-01-03 17:15:41 -0800646 parent.controller.message_send(msg)
Rich Lane89725bb2012-12-03 16:23:27 -0800647
648 if exp_pkt is None:
649 exp_pkt = pkt
650 receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
651
Dan Talaycof6e76c02012-03-23 10:56:12 -0700652def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
653 """
654 Generate a list of ports avoiding those in the exclude list
Rich Lane9a003812012-10-04 17:17:59 -0700655 @param parent Supplies logging
Dan Talaycof6e76c02012-03-23 10:56:12 -0700656 @param of_ports List of OF port numbers
657 @param how_many Number of ports to be added to the list
658 @param exclude_list List of ports not to be used
659 @returns An empty list if unable to find enough ports
660 """
661
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700662 if how_many == 0:
663 return []
664
Dan Talaycof6e76c02012-03-23 10:56:12 -0700665 count = 0
666 egr_ports = []
667 for egr_idx in range(len(of_ports)):
668 if of_ports[egr_idx] not in exclude_list:
669 egr_ports.append(of_ports[egr_idx])
670 count += 1
671 if count >= how_many:
672 return egr_ports
Rich Lane9a003812012-10-04 17:17:59 -0700673 logging.debug("Could not generate enough egress ports for test")
Dan Talaycof6e76c02012-03-23 10:56:12 -0700674 return []
675
Ed Swierk99a74de2012-08-22 06:40:54 -0700676def flow_match_test(parent, port_map, wildcards=None, dl_vlan=-1, pkt=None,
Rich Lanee5779d32012-10-05 17:56:04 -0700677 exp_pkt=None, action_list=None,
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700678 max_test=0, egr_count=1, ing_port=False):
Dan Talayco551befa2010-07-15 17:05:32 -0700679 """
Rich Lane89725bb2012-12-03 16:23:27 -0800680 Run flow_match_test_port_pair on all port pairs and packet-out
Dan Talayco551befa2010-07-15 17:05:32 -0700681
682 @param max_test If > 0 no more than this number of tests are executed.
683 @param parent Must implement controller, dataplane, assertTrue, assertEqual
Rich Lane9a003812012-10-04 17:17:59 -0700684 and logging
Dan Talayco551befa2010-07-15 17:05:32 -0700685 @param pkt If not None, use this packet for ingress
686 @param wildcards For flow match entry
Dan Talayco79184222010-11-01 12:24:29 -0700687 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
Dan Talayco551befa2010-07-15 17:05:32 -0700688 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
689 @param action_list Additional actions to add to flow mod
Dan Talaycocfa172f2012-03-23 12:03:00 -0700690 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
Dan Talayco551befa2010-07-15 17:05:32 -0700691 """
Ed Swierk99a74de2012-08-22 06:40:54 -0700692 if wildcards is None:
693 wildcards = required_wildcards(parent)
Dan Talayco551befa2010-07-15 17:05:32 -0700694 of_ports = port_map.keys()
695 of_ports.sort()
696 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
697 test_count = 0
698
Dan Talaycocfa172f2012-03-23 12:03:00 -0700699 if egr_count == -1:
Rich Lane2014f9b2012-10-05 15:29:40 -0700700 egr_count = test_param_get('egr_count', default=2)
Dan Talaycocfa172f2012-03-23 12:03:00 -0700701
Dan Talayco551befa2010-07-15 17:05:32 -0700702 for ing_idx in range(len(of_ports)):
703 ingress_port = of_ports[ing_idx]
Dan Talaycof6e76c02012-03-23 10:56:12 -0700704 egr_ports = get_egr_list(parent, of_ports, egr_count,
705 exclude_list=[ingress_port])
Dan Talaycoc948d0b2012-03-23 12:17:54 -0700706 if ing_port:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800707 egr_ports.append(of10.cstruct.OFPP_IN_PORT)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700708 if len(egr_ports) == 0:
709 parent.assertTrue(0, "Failed to generate egress port list")
710
711 flow_match_test_port_pair(parent, ingress_port, egr_ports,
712 wildcards=wildcards, dl_vlan=dl_vlan,
713 pkt=pkt, exp_pkt=exp_pkt,
Rich Lanee5779d32012-10-05 17:56:04 -0700714 action_list=action_list)
Dan Talaycof6e76c02012-03-23 10:56:12 -0700715 test_count += 1
716 if (max_test > 0) and (test_count > max_test):
Rich Lane9a003812012-10-04 17:17:59 -0700717 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane89725bb2012-12-03 16:23:27 -0800718 break
719
Ed Swierk38eea082013-01-02 19:46:20 -0800720 if not test_param_get('pktout_actions', default=True):
721 return
Rich Lane89725bb2012-12-03 16:23:27 -0800722
723 ingress_port = of_ports[0]
724 egr_ports = get_egr_list(parent, of_ports, egr_count,
725 exclude_list=[ingress_port])
726 if ing_port:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800727 egr_ports.append(of10.cstruct.OFPP_IN_PORT)
Rich Lane89725bb2012-12-03 16:23:27 -0800728 flow_match_test_pktout(parent, ingress_port, egr_ports,
729 dl_vlan=dl_vlan,
730 pkt=pkt, exp_pkt=exp_pkt,
731 action_list=action_list)
Dan Talayco551befa2010-07-15 17:05:32 -0700732
Rich Lane2014f9b2012-10-05 15:29:40 -0700733def test_param_get(key, default=None):
Dan Talayco4b2bee62010-07-20 14:10:05 -0700734 """
735 Return value passed via test-params if present
736
Dan Talayco4b2bee62010-07-20 14:10:05 -0700737 @param key The lookup key
738 @param default Default value to use if not found
739
740 If the pair 'key=val' appeared in the string passed to --test-params
741 on the command line, return val (as interpreted by exec). Otherwise
742 return default value.
Dan Talaycof6e76c02012-03-23 10:56:12 -0700743
744 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
745 eg egr_count, not egr-count.
Dan Talayco4b2bee62010-07-20 14:10:05 -0700746 """
747 try:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800748 exec oftest.config["test_params"]
Dan Talayco4b2bee62010-07-20 14:10:05 -0700749 except:
750 return default
751
752 s = "val = " + str(key)
753 try:
754 exec s
755 return val
756 except:
757 return default
758
759def action_generate(parent, field_to_mod, mod_field_vals):
760 """
761 Create an action to modify the field indicated in field_to_mod
762
763 @param parent Must implement, assertTrue
764 @param field_to_mod The field to modify as a string name
765 @param mod_field_vals Hash of values to use for modified values
766 """
767
768 act = None
769
770 if field_to_mod in ['pktlen']:
771 return None
772
773 if field_to_mod == 'dl_dst':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800774 act = of10.action.action_set_dl_dst()
775 act.dl_addr = of10.parse.parse_mac(mod_field_vals['dl_dst'])
Dan Talayco4b2bee62010-07-20 14:10:05 -0700776 elif field_to_mod == 'dl_src':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800777 act = of10.action.action_set_dl_src()
778 act.dl_addr = of10.parse.parse_mac(mod_field_vals['dl_src'])
Dan Talayco4b2bee62010-07-20 14:10:05 -0700779 elif field_to_mod == 'dl_vlan_enable':
780 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
Rich Lanecd97d3d2013-01-07 18:50:06 -0800781 act = of10.action.action_strip_vlan()
Dan Talayco4b2bee62010-07-20 14:10:05 -0700782 # Add VLAN tag is handled by dl_vlan field
783 # Will return None in this case
784 elif field_to_mod == 'dl_vlan':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800785 act = of10.action.action_set_vlan_vid()
Dan Talayco4b2bee62010-07-20 14:10:05 -0700786 act.vlan_vid = mod_field_vals['dl_vlan']
787 elif field_to_mod == 'dl_vlan_pcp':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800788 act = of10.action.action_set_vlan_pcp()
Dan Talayco4b2bee62010-07-20 14:10:05 -0700789 act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
790 elif field_to_mod == 'ip_src':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800791 act = of10.action.action_set_nw_src()
792 act.nw_addr = of10.parse.parse_ip(mod_field_vals['ip_src'])
Dan Talayco4b2bee62010-07-20 14:10:05 -0700793 elif field_to_mod == 'ip_dst':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800794 act = of10.action.action_set_nw_dst()
795 act.nw_addr = of10.parse.parse_ip(mod_field_vals['ip_dst'])
Dan Talayco4b2bee62010-07-20 14:10:05 -0700796 elif field_to_mod == 'ip_tos':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800797 act = of10.action.action_set_nw_tos()
Dan Talayco4b2bee62010-07-20 14:10:05 -0700798 act.nw_tos = mod_field_vals['ip_tos']
799 elif field_to_mod == 'tcp_sport':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800800 act = of10.action.action_set_tp_src()
Dan Talayco4b2bee62010-07-20 14:10:05 -0700801 act.tp_port = mod_field_vals['tcp_sport']
802 elif field_to_mod == 'tcp_dport':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800803 act = of10.action.action_set_tp_dst()
Dan Talayco4b2bee62010-07-20 14:10:05 -0700804 act.tp_port = mod_field_vals['tcp_dport']
Rich Lane110e0e32012-10-26 16:21:46 -0700805 elif field_to_mod == 'udp_sport':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800806 act = of10.action.action_set_tp_src()
Rich Lane110e0e32012-10-26 16:21:46 -0700807 act.tp_port = mod_field_vals['udp_sport']
808 elif field_to_mod == 'udp_dport':
Rich Lanecd97d3d2013-01-07 18:50:06 -0800809 act = of10.action.action_set_tp_dst()
Rich Lane110e0e32012-10-26 16:21:46 -0700810 act.tp_port = mod_field_vals['udp_dport']
Dan Talayco4b2bee62010-07-20 14:10:05 -0700811 else:
812 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
813
814 return act
815
816def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
Rich Lane110e0e32012-10-26 16:21:46 -0700817 mod_fields=[], tp="tcp", check_test_params=False):
Dan Talayco4b2bee62010-07-20 14:10:05 -0700818 """
819 Set up the ingress and expected packet and action list for a test
820
Rich Lane2014f9b2012-10-05 15:29:40 -0700821 @param parent Must implement assertTrue
Dan Talayco4b2bee62010-07-20 14:10:05 -0700822 @param start_field_values Field values to use for ingress packet (optional)
823 @param mod_field_values Field values to use for modified packet (optional)
824 @param mod_fields The list of fields to be modified by the switch in the test.
825 @params check_test_params If True, will check the parameters vid, add_vlan
826 and strip_vlan from the command line.
827
828 Returns a triple: pkt-to-send, expected-pkt, action-list
829 """
830
831 new_actions = []
832
Dan Talayco4b2bee62010-07-20 14:10:05 -0700833 base_pkt_params = {}
834 base_pkt_params['pktlen'] = 100
835 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
836 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
837 base_pkt_params['dl_vlan_enable'] = False
838 base_pkt_params['dl_vlan'] = 2
839 base_pkt_params['dl_vlan_pcp'] = 0
840 base_pkt_params['ip_src'] = '192.168.0.1'
841 base_pkt_params['ip_dst'] = '192.168.0.2'
842 base_pkt_params['ip_tos'] = 0
Rich Lane110e0e32012-10-26 16:21:46 -0700843 if tp == "tcp":
844 base_pkt_params['tcp_sport'] = 1234
845 base_pkt_params['tcp_dport'] = 80
846 elif tp == "udp":
847 base_pkt_params['udp_sport'] = 1234
848 base_pkt_params['udp_dport'] = 80
Dan Talayco4b2bee62010-07-20 14:10:05 -0700849 for keyname in start_field_vals.keys():
850 base_pkt_params[keyname] = start_field_vals[keyname]
851
852 mod_pkt_params = {}
853 mod_pkt_params['pktlen'] = 100
854 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
855 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
856 mod_pkt_params['dl_vlan_enable'] = False
857 mod_pkt_params['dl_vlan'] = 3
858 mod_pkt_params['dl_vlan_pcp'] = 7
859 mod_pkt_params['ip_src'] = '10.20.30.40'
860 mod_pkt_params['ip_dst'] = '50.60.70.80'
861 mod_pkt_params['ip_tos'] = 0xf0
Rich Lane110e0e32012-10-26 16:21:46 -0700862 if tp == "tcp":
863 mod_pkt_params['tcp_sport'] = 4321
864 mod_pkt_params['tcp_dport'] = 8765
865 elif tp == "udp":
866 mod_pkt_params['udp_sport'] = 4321
867 mod_pkt_params['udp_dport'] = 8765
Dan Talayco4b2bee62010-07-20 14:10:05 -0700868 for keyname in mod_field_vals.keys():
869 mod_pkt_params[keyname] = mod_field_vals[keyname]
870
871 # Check for test param modifications
872 strip = False
873 if check_test_params:
Rich Lane2014f9b2012-10-05 15:29:40 -0700874 add_vlan = test_param_get('add_vlan')
875 strip_vlan = test_param_get('strip_vlan')
876 vid = test_param_get('vid')
Dan Talayco4b2bee62010-07-20 14:10:05 -0700877
878 if add_vlan and strip_vlan:
879 parent.assertTrue(0, "Add and strip VLAN both specified")
880
881 if vid:
882 base_pkt_params['dl_vlan_enable'] = True
883 base_pkt_params['dl_vlan'] = vid
884 if 'dl_vlan' in mod_fields:
885 mod_pkt_params['dl_vlan'] = vid + 1
886
887 if add_vlan:
888 base_pkt_params['dl_vlan_enable'] = False
889 mod_pkt_params['dl_vlan_enable'] = True
890 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
891 mod_fields.append('pktlen')
892 mod_fields.append('dl_vlan_enable')
893 if 'dl_vlan' not in mod_fields:
894 mod_fields.append('dl_vlan')
895 elif strip_vlan:
896 base_pkt_params['dl_vlan_enable'] = True
897 mod_pkt_params['dl_vlan_enable'] = False
898 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
899 mod_fields.append('dl_vlan_enable')
900 mod_fields.append('pktlen')
901
Rich Lane110e0e32012-10-26 16:21:46 -0700902 if tp == "tcp":
903 packet_builder = simple_tcp_packet
904 elif tp == "udp":
905 packet_builder = simple_udp_packet
906 else:
907 raise NotImplementedError("unknown transport protocol %s" % tp)
908
Dan Talayco4b2bee62010-07-20 14:10:05 -0700909 # Build the ingress packet
Rich Lane110e0e32012-10-26 16:21:46 -0700910 ingress_pkt = packet_builder(**base_pkt_params)
Dan Talayco4b2bee62010-07-20 14:10:05 -0700911
912 # Build the expected packet, modifying the indicated fields
913 for item in mod_fields:
914 base_pkt_params[item] = mod_pkt_params[item]
915 act = action_generate(parent, item, mod_pkt_params)
916 if act:
917 new_actions.append(act)
918
Rich Lane110e0e32012-10-26 16:21:46 -0700919 expected_pkt = packet_builder(**base_pkt_params)
Dan Talayco4b2bee62010-07-20 14:10:05 -0700920
921 return (ingress_pkt, expected_pkt, new_actions)
Dan Talayco677c0b72011-08-23 22:53:38 -0700922
923# Generate a simple "drop" flow mod
924# If in_band is true, then only drop from first test port
925def flow_mod_gen(port_map, in_band):
Rich Lanecd97d3d2013-01-07 18:50:06 -0800926 request = of10.message.flow_mod()
927 request.match.wildcards = of10.cstruct.OFPFW_ALL
Dan Talayco677c0b72011-08-23 22:53:38 -0700928 if in_band:
Rich Lanecd97d3d2013-01-07 18:50:06 -0800929 request.match.wildcards = of10.cstruct.OFPFW_ALL - of10.cstruct.OFPFW_IN_PORT
Dan Talayco677c0b72011-08-23 22:53:38 -0700930 for of_port, ifname in port_map.items(): # Grab first port
931 break
932 request.match.in_port = of_port
933 request.buffer_id = 0xffffffff
934 return request
Dan Talaycoba3745c2010-07-21 21:51:08 -0700935
936def skip_message_emit(parent, s):
937 """
938 Print out a 'skipped' message to stderr
939
940 @param s The string to print out to the log file
Dan Talaycoba3745c2010-07-21 21:51:08 -0700941 """
942 global skipped_test_count
943
944 skipped_test_count += 1
Rich Lane9a003812012-10-04 17:17:59 -0700945 logging.info("Skipping: " + s)
Rich Lanecd97d3d2013-01-07 18:50:06 -0800946 if oftest.config["dbg_level"] < logging.WARNING:
Dan Talaycoba3745c2010-07-21 21:51:08 -0700947 sys.stderr.write("(skipped) ")
948 else:
949 sys.stderr.write("(S)")
Dan Talayco677c0b72011-08-23 22:53:38 -0700950
Dan Talayco8a64e332012-03-28 14:53:20 -0700951
952def all_stats_get(parent):
953 """
954 Get the aggregate stats for all flows in the table
955 @param parent Test instance with controller connection and assert
956 @returns dict with keys flows, packets, bytes, active (flows),
957 lookups, matched
958 """
Rich Lanecd97d3d2013-01-07 18:50:06 -0800959 stat_req = of10.message.aggregate_stats_request()
960 stat_req.match = of10.cstruct.ofp_match()
961 stat_req.match.wildcards = of10.cstruct.OFPFW_ALL
Dan Talayco8a64e332012-03-28 14:53:20 -0700962 stat_req.table_id = 0xff
Rich Lanecd97d3d2013-01-07 18:50:06 -0800963 stat_req.out_port = of10.cstruct.OFPP_NONE
Dan Talayco8a64e332012-03-28 14:53:20 -0700964
965 rv = {}
966
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700967 (reply, pkt) = parent.controller.transact(stat_req)
Dan Talayco8a64e332012-03-28 14:53:20 -0700968 parent.assertTrue(len(reply.stats) == 1, "Did not receive flow stats reply")
969
970 for obj in reply.stats:
971 (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
972 obj.packet_count, obj.byte_count)
973 break
974
Rich Lanecd97d3d2013-01-07 18:50:06 -0800975 request = of10.message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700976 (reply , pkt) = parent.controller.transact(request)
Dan Talayco8a64e332012-03-28 14:53:20 -0700977
978
979 (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
980 for obj in reply.stats:
981 rv["active"] += obj.active_count
982 rv["lookups"] += obj.lookup_count
983 rv["matched"] += obj.matched_count
984
985 return rv
Dan Talayco2baf8b52012-03-30 09:55:42 -0700986
987FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
988 for x in range(256)])
989
990def hex_dump_buffer(src, length=16):
991 """
992 Convert src to a hex dump string and return the string
993 @param src The source buffer
994 @param length The number of bytes shown in each line
995 @returns A string showing the hex dump
996 """
Dan Talaycoc516fa02012-04-12 22:28:43 -0700997 result = ["\n"]
Dan Talayco2baf8b52012-03-30 09:55:42 -0700998 for i in xrange(0, len(src), length):
999 chars = src[i:i+length]
1000 hex = ' '.join(["%02x" % ord(x) for x in chars])
1001 printable = ''.join(["%s" % ((ord(x) <= 127 and
1002 FILTER[ord(x)]) or '.') for x in chars])
1003 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
1004 return ''.join(result)
1005
1006def format_packet(pkt):
1007 return "Packet length %d \n%s" % (len(str(pkt)),
1008 hex_dump_buffer(str(pkt)))
Rich Lane5d7e89a2012-10-26 16:43:13 -07001009
1010def inspect_packet(pkt):
1011 """
1012 Wrapper around scapy's show() method.
1013 @returns A string showing the dissected packet.
1014 """
1015 from cStringIO import StringIO
1016 out = None
1017 backup = sys.stdout
1018 try:
1019 sys.stdout = StringIO()
1020 pkt.show2()
1021 out = sys.stdout.getvalue()
1022 sys.stdout.close()
1023 finally:
1024 sys.stdout = backup
1025 return out
Rich Lane0a4f6372013-01-02 14:40:22 -08001026
1027def nonstandard(cls):
1028 """
Rich Lanecc45b8e2013-01-02 15:55:02 -08001029 Testcase decorator that marks the test as being non-standard.
1030 These tests are not automatically added to the "standard" group.
Rich Lane0a4f6372013-01-02 14:40:22 -08001031 """
Rich Lanecc45b8e2013-01-02 15:55:02 -08001032 cls._nonstandard = True
Rich Lane0a4f6372013-01-02 14:40:22 -08001033 return cls
1034
1035def disabled(cls):
1036 """
Rich Lanecc45b8e2013-01-02 15:55:02 -08001037 Testcase decorator that marks the test as being disabled.
1038 These tests are not automatically added to the "standard" group or
1039 their module's group.
Rich Lane0a4f6372013-01-02 14:40:22 -08001040 """
Rich Lanecc45b8e2013-01-02 15:55:02 -08001041 cls._disabled = True
Rich Lane0a4f6372013-01-02 14:40:22 -08001042 return cls
Rich Lane97e99652013-01-02 17:23:20 -08001043
1044def group(name):
1045 """
1046 Testcase decorator that adds the test to a group.
1047 """
1048 def fn(cls):
1049 if not hasattr(cls, "_groups"):
1050 cls._groups = []
1051 cls._groups.append(name)
1052 return cls
1053 return fn
Rich Lane5a9a1922013-01-11 14:29:30 -08001054
1055def version(ver):
1056 """
1057 Testcase decorator that specifies which versions of OpenFlow the test
1058 supports. The default is 1.0+. This decorator may only be used once.
1059
1060 Supported syntax:
1061 1.0 -> 1.0
1062 1.0,1.2,1.3 -> 1.0, 1.2, 1.3
1063 1.0+ -> 1.0, 1.1, 1.2, 1.3
1064 """
1065 versions = parse_version(ver)
1066 def fn(cls):
1067 cls._versions = versions
1068 return cls
1069 return fn
1070
1071def parse_version(ver):
1072 allowed_versions = ["1.0", "1.1", "1.2", "1.3"]
1073 if re.match("^1\.\d+$", ver):
1074 versions = set([ver])
1075 elif re.match("^(1\.\d+)\+$", ver):
1076 if not ver[:-1] in allowed_versions:
1077 raise ValueError("invalid OpenFlow version %s" % ver[:-1])
1078 versions = set()
1079 if ver != "1.1+": versions.add("1.0")
1080 if ver != "1.2+": versions.add("1.1")
1081 if ver != "1.3+": versions.add("1.2")
1082 versions.add("1.3")
1083 else:
1084 versions = set(ver.split(','))
1085
1086 for version in versions:
1087 if not version in allowed_versions:
1088 raise ValueError("invalid OpenFlow version %s" % version)
1089
1090 return versions
1091
1092assert(parse_version("1.0") == set(["1.0"]))
1093assert(parse_version("1.0,1.2,1.3") == set(["1.0", "1.2", "1.3"]))
1094assert(parse_version("1.0+") == set(["1.0", "1.1", "1.2", "1.3"]))