blob: 3ce2e75de74f05781816bd9ae57c00c4366b4588 [file] [log] [blame]
Rich Lane629393f2013-01-10 15:37:33 -08001
Matteo Scandoloa229eca2017-08-08 13:05:28 -07002# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
17
Rich Lane629393f2013-01-10 15:37:33 -080018import sys
19import logging
20from cStringIO import StringIO
21#import types
22
Rich Lane18c9fed2013-05-07 15:22:53 -070023import ofp
24import oftest.parse as parse
Rich Lane629393f2013-01-10 15:37:33 -080025from packet import Packet
26
Rich Lane629393f2013-01-10 15:37:33 -080027try:
28 logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
29 from scapy.all import *
Rich Lane8f2861e2013-01-11 09:13:20 -080030 from oftest.mpls import *
Rich Lane629393f2013-01-10 15:37:33 -080031except ImportError:
32 sys.exit("Need to install scapy for packet parsing")
33
34global skipped_test_count
35skipped_test_count = 0
36
37# Some useful defines
38IP_ETHERTYPE = 0x800
39IPV6_ETHERTYPE = 0x86dd
40ETHERTYPE_VLAN = 0x8100
41ETHERTYPE_MPLS = 0x8847
42TCP_PROTOCOL = 0x6
43UDP_PROTOCOL = 0x11
44ICMPV6_PROTOCOL = 0x3a
45
46
47
48def clear_switch(parent, port_list, logger):
49 """
50 Clear the switch configuration
51
52 @param parent Object implementing controller and assert equal
53 @param logger Logging object
54 """
55 parent.assertTrue(len(port_list) > 2, "Not enough ports for test")
56 for port in port_list:
57 clear_port_config(parent, port, logger)
58 initialize_table_config(parent.controller, logger)
59 delete_all_flows(parent.controller, logger)
60 delete_all_groups(parent.controller, logger)
61
62 return port_list
63
64def initialize_table_config(ctrl, logger):
65 """
66 Initialize all table configs to default setting ("CONTROLLER")
67 @param ctrl The controller object for the test
68 """
69 logger.info("Initializing all table configs")
Rich Lane18c9fed2013-05-07 15:22:53 -070070 request = ofp.message.table_mod()
Rich Lane629393f2013-01-10 15:37:33 -080071 request.config = ofp.OFPTC_TABLE_MISS_CONTROLLER
72 rv = 0
73 for table_id in [0, 1, 2, 3, 4, 5, 6, 7]:
74 request.table_id = table_id
75 rv |= ctrl.message_send(request)
76 return rv
77
78def delete_all_flows(ctrl, logger):
79 """
80 Delete all flows on the switch
81 @param ctrl The controller object for the test
82 @param logger Logging object
83 """
84
85 logger.info("Deleting all flows")
86 #DEFAULT_TABLE_COUNT = 4
87 return delete_all_flows_one_table(ctrl, logger, table_id=0xff)
88
89def delete_all_flows_one_table(ctrl, logger, table_id=0):
90 """
91 Delete all flows on a table
92 @param ctrl The controller object for the test
93 @param logger Logging object
94 @param table_id Table ID
95 """
96 logger.info("Deleting all flows on table ID: " + str(table_id))
Rich Lane18c9fed2013-05-07 15:22:53 -070097 msg = ofp.message.flow_delete()
Rich Lane629393f2013-01-10 15:37:33 -080098 msg.out_port = ofp.OFPP_ANY
99 msg.out_group = ofp.OFPG_ANY
Rich Lane629393f2013-01-10 15:37:33 -0800100 msg.buffer_id = 0xffffffff
101 msg.table_id = table_id
102 logger.debug(msg.show())
103
104 return ctrl.message_send(msg)
105
106def delete_all_groups(ctrl, logger):
107 """
108 Delete all groups on the switch
109 @param ctrl The controller object for the test
110 @param logger Logging object
111 """
112
113 logger.info("Deleting all groups")
Rich Lane18c9fed2013-05-07 15:22:53 -0700114 msg = ofp.message.group_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800115 msg.group_id = ofp.OFPG_ALL
116 msg.command = ofp.OFPGC_DELETE
117 logger.debug(msg.show())
118 return ctrl.message_send(msg)
119
120def clear_port_config(parent, port, logger):
121 """
122 Clear the port configuration
123
124 @param parent Object implementing controller and assert equal
125 @param logger Logging object
126 """
127 rv = port_config_set(parent.controller, port,
128 0, 0, logger)
129 parent.assertEqual(rv, 0, "Failed to reset port config")
130
131def simple_tcp_packet(dl_dst='00:01:02:03:04:05',
132 dl_src='00:06:07:08:09:0a',
133 vlan_tags=[], # {type,vid,pcp,cfi} TODO type
134 mpls_tags=[], # {type,label,tc,ttl} TODO type
135 ip_src='192.168.0.1',
136 ip_dst='192.168.0.2',
137 ip_tos=0,
138 ip_ttl=64,
139 tcp_sport=1234,
140 tcp_dport=80,
141 payload_len = 46):
142 pkt = Ether(dst=dl_dst, src=dl_src)
143
144 vlans_num = 0
145 while len(vlan_tags):
146 tag = vlan_tags.pop(0)
147 dot1q = Dot1Q()
148 if 'vid' in tag:
149 dot1q.vlan = tag['vid']
150 if 'pcp' in tag:
151 dot1q.prio = tag['pcp']
152 if 'cfi' in tag:
153 dot1q.id = tag['cfi']
154 pkt = pkt / dot1q
155 if 'type' in tag:
156 if vlans_num == 0:
157 pkt[Ether].setfieldval('type', tag['type'])
158 else:
159 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
160 vlans_num+=1
161
162 mplss_num = 0
163 while len(mpls_tags):
164 tag = mpls_tags.pop(0)
165 mpls = MPLS()
166 if 'label' in tag:
167 mpls.label = tag['label']
168 if 'tc' in tag:
169 mpls.cos = tag['tc']
170 if 'ttl' in tag:
171 mpls.ttl = tag['ttl']
172 pkt = pkt / mpls
173 if 'type' in tag:
174 if mplss_num == 0:
175 if vlans_num == 0:
176 pkt[Ether].setfieldval('type', tag['type'])
177 else:
178 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
179 mplss_num+=1
180
181 pkt = pkt / IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl) \
182 / TCP(sport=tcp_sport, dport=tcp_dport)
183
184 pkt = pkt / ("D" * payload_len)
185
186 return pkt
187
188def simple_icmp_packet(dl_dst='00:01:02:03:04:05',
189 dl_src='00:06:07:08:09:0a',
190 vlan_tags=[], # {type,vid,pcp,cfi} TODO type
191 mpls_tags=[], # {type,label,tc,ttl} TODO type
192 ip_src='192.168.0.1',
193 ip_dst='192.168.0.2',
194 ip_tos=0,
195 ip_ttl=64,
196 icmp_type=8, # ICMP_ECHO_REQUEST
197 icmp_code=0,
198 payload_len=0):
199
200 #TODO simple_ip_packet
201 pkt = Ether(dst=dl_dst, src=dl_src)
202
203 vlans_num = 0
204 while len(vlan_tags):
205 tag = vlan_tags.pop(0)
206 dot1q = Dot1Q()
207 if 'vid' in tag:
208 dot1q.vlan = tag['vid']
209 if 'pcp' in tag:
210 dot1q.prio = tag['pcp']
211 if 'cfi' in tag:
212 dot1q.id = tag['cfi']
213 pkt = pkt / dot1q
214 if 'type' in tag:
215 if vlans_num == 0:
216 pkt[Ether].setfieldval('type', tag['type'])
217 else:
218 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
219 vlans_num+=1
220
221 mplss_num = 0
222 while len(mpls_tags):
223 tag = mpls_tags.pop(0)
224 mpls = MPLS()
225 if 'label' in tag:
226 mpls.label = tag['label']
227 if 'tc' in tag:
228 mpls.cos = tag['tc']
229 if 'ttl' in tag:
230 mpls.ttl = tag['ttl']
231 pkt = pkt / mpls
232 if 'type' in tag:
233 if mplss_num == 0:
234 if vlans_num == 0:
235 pkt[Ether].setfieldval('type', tag['type'])
236 else:
237 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
238 mplss_num+=1
239
240 pkt = pkt / IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl) \
241 / ICMP(type=icmp_type, code=icmp_code)
242
243 pkt = pkt / ("D" * payload_len)
244
245 return pkt
246
247def simple_ipv6_packet(pktlen=100,
248 dl_dst='00:01:02:03:04:05',
249 dl_src='00:06:07:08:09:0a',
250 dl_vlan_enable=False,
251 dl_vlan=0,
252 dl_vlan_pcp=0,
253 dl_vlan_cfi=0,
254 ip_src='fe80::2420:52ff:fe8f:5189',
255 ip_dst='fe80::2420:52ff:fe8f:5190',
256 ip_tos=0,
257 tcp_sport=0,
258 tcp_dport=0,
259 EH = False,
260 EHpkt = IPv6ExtHdrDestOpt()
261 ):
262
263 """
264 Return a simple IPv6 packet
265
266 Supports a few parameters:
267 @param len Length of packet in bytes w/o CRC
268 @param dl_dst Destinatino MAC
269 @param dl_src Source MAC
270 @param dl_vlan_enable True if the packet is with vlan, False otherwise
271 @param dl_vlan VLAN ID
272 @param dl_vlan_pcp VLAN priority
273 @param ip_src IPv6 source
274 @param ip_dst IPv6 destination
275 @param ip_tos IP ToS
276 @param tcp_dport TCP destination port
277 @param ip_sport TCP source port
278
279 """
280 # Note Dot1Q.id is really CFI
281 if (dl_vlan_enable):
282 pkt = Ether(dst=dl_dst, src=dl_src)/ \
283 Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
284 IPv6(src=ip_src, dst=ip_dst)
285
286 else:
287 pkt = Ether(dst=dl_dst, src=dl_src)/ \
288 IPv6(src=ip_src, dst=ip_dst)
289
290 # Add IPv6 Extension Headers
291 if EH:
292 pkt = pkt / EHpkt
293
294 if (tcp_sport >0 and tcp_dport >0):
295 pkt = pkt / TCP(sport=tcp_sport, dport=tcp_dport)
296
297 if pktlen > len(pkt) :
298 pkt = pkt/("D" * (pktlen - len(pkt)))
299
300 return pkt
301
302def simple_icmpv6_packet(pktlen=100,
303 dl_dst='00:01:02:03:04:05',
304 dl_src='00:06:07:08:09:0a',
305 dl_vlan_enable=False,
306 dl_vlan=0,
307 dl_vlan_pcp=0,
308 dl_vlan_cfi=0,
309 ip_src='fe80::2420:52ff:fe8f:5189',
310 ip_dst='fe80::2420:52ff:fe8f:5190',
311 ip_tos=0,
312 tcp_sport=0,
313 tcp_dport=0,
314 EH = False,
315 EHpkt = IPv6ExtHdrDestOpt(),
316 route_adv = False,
317 sll_enabled = False
318 ):
319
320 """
321 Return a simple dataplane ICMPv6 packet
322
323 Supports a few parameters:
324 @param len Length of packet in bytes w/o CRC
325 @param dl_dst Destinatino MAC
326 @param dl_src Source MAC
327 @param dl_vlan_enable True if the packet is with vlan, False otherwise
328 @param dl_vlan VLAN ID
329 @param dl_vlan_pcp VLAN priority
330 @param ip_src IPv6 source
331 @param ip_dst IPv6 destination
332 @param ip_tos IP ToS
333 @param tcp_dport TCP destination port
334 @param ip_sport TCP source port
335
336 """
337 if (dl_vlan_enable):
338 pkt = Ether(dst=dl_dst, src=dl_src)/ \
339 Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
340 IPv6(src=ip_src, dst=ip_dst)
341
342 else:
343 pkt = Ether(dst=dl_dst, src=dl_src)/ \
344 IPv6(src=ip_src, dst=ip_dst)
345
346
347 # Add IPv6 Extension Headers
348 if EH:
349 pkt = pkt / EHpkt
350
351 if route_adv:
352 pkt = pkt/ \
353 ICMPv6ND_RA(chlim=255, H=0L, M=0L, O=1L, routerlifetime=1800, P=0L, retranstimer=0, prf=0L, res=0L)/ \
354 ICMPv6NDOptPrefixInfo(A=1L, res2=0, res1=0L, L=1L, len=4, prefix='fd00:141:64:1::', R=0L, validlifetime=1814400, prefixlen=64, preferredlifetime=604800, type=3)
355 if sll_enabled :
356 pkt = pkt/ \
357 ICMPv6NDOptSrcLLAddr(type=1, len=1, lladdr='66:6f:df:2d:7c:9c')
358 else :
359 pkt = pkt/ \
360 ICMPv6EchoRequest()
361 if (tcp_sport >0 and tcp_dport >0):
362 pkt = pkt / TCP(sport=tcp_sport, dport=tcp_dport)
363
364 if pktlen > len(pkt) :
365 pkt = pkt/("D" * (pktlen - len(pkt)))
366
367 return pkt
368
369
370def do_barrier(ctrl):
Rich Lane18c9fed2013-05-07 15:22:53 -0700371 b = ofp.message.barrier_request()
Rich Lane629393f2013-01-10 15:37:33 -0800372 ctrl.transact(b)
373
374
375def port_config_get(controller, port_no, logger):
376 """
377 Get a port's configuration
378
379 Gets the switch feature configuration and grabs one port's
380 configuration
381
382 @returns (hwaddr, config, advert) The hwaddress, configuration and
383 advertised values
384 """
Rich Lane18c9fed2013-05-07 15:22:53 -0700385 request = ofp.message.features_request()
Rich Lane629393f2013-01-10 15:37:33 -0800386 reply, _ = controller.transact(request, timeout=2)
387 if reply is None:
388 logger.warn("Get feature request failed")
389 return None, None, None
390 logger.debug(reply.show())
391 for idx in range(len(reply.ports)):
392 if reply.ports[idx].port_no == port_no:
393 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
394 reply.ports[idx].advertised)
395
396 logger.warn("Did not find port number for port config")
397 return None, None, None
398
399def port_config_set(controller, port_no, config, mask, logger):
400 """
401 Set the port configuration according the given parameters
402
403 Gets the switch feature configuration and updates one port's
404 configuration value according to config and mask
405 """
406 logger.info("Setting port " + str(port_no) + " to config " + str(config))
Rich Lane18c9fed2013-05-07 15:22:53 -0700407 request = ofp.message.features_request()
Rich Lane629393f2013-01-10 15:37:33 -0800408 reply, _ = controller.transact(request, timeout=2)
409 if reply is None:
410 return -1
411 logger.debug(reply.show())
412 for idx in range(len(reply.ports)):
413 if reply.ports[idx].port_no == port_no:
414 break
415 if idx >= len(reply.ports):
416 return -1
Rich Lane18c9fed2013-05-07 15:22:53 -0700417 mod = ofp.message.port_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800418 mod.port_no = port_no
419 mod.hw_addr = reply.ports[idx].hw_addr
420 mod.config = config
421 mod.mask = mask
422 mod.advertise = reply.ports[idx].advertised
423 rv = controller.message_send(mod)
424 return rv
425
426def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger):
427 """
428 Check for proper receive packets across all ports
429 @param dataplane The dataplane object
430 @param pkt Expected packet; may be None if yes_ports is empty
431 @param yes_ports Set or list of ports that should recieve packet
432 @param no_ports Set or list of ports that should not receive packet
433 @param assert_if Object that implements assertXXX
434 """
435 for ofport in yes_ports:
436 logger.debug("Checking for pkt on port " + str(ofport))
437 (_, rcv_pkt, _) = dataplane.poll(
438 port_number=ofport, timeout=1)
439 assert_if.assertTrue(rcv_pkt is not None,
440 "Did not receive pkt on " + str(ofport))
441 assert_if.assertEqual(str(pkt), str(rcv_pkt),
442 "Response packet does not match send packet " +
443 "on port " + str(ofport))
444
445 for ofport in no_ports:
446 logger.debug("Negative check for pkt on port " + str(ofport))
447 (_, rcv_pkt, _) = dataplane.poll(
448 port_number=ofport, timeout=1)
449 assert_if.assertTrue(rcv_pkt is None,
450 "Unexpected pkt on port " + str(ofport))
451
452
453def pkt_verify(parent, rcv_pkt, exp_pkt):
454 if str(exp_pkt) != str(rcv_pkt):
Rich Lanef9aa5022013-01-10 16:36:15 -0800455 logging.error("ERROR: Packet match failed.")
456 logging.debug("Expected (" + str(len(exp_pkt)) + ")")
457 logging.debug(str(exp_pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800458 sys.stdout = tmpout = StringIO()
459 exp_pkt.show()
460 sys.stdout = sys.__stdout__
Rich Lanef9aa5022013-01-10 16:36:15 -0800461 logging.debug(tmpout.getvalue())
462 logging.debug("Received (" + str(len(rcv_pkt)) + ")")
463 logging.debug(str(rcv_pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800464 sys.stdout = tmpout = StringIO()
465 Ether(rcv_pkt).show()
466 sys.stdout = sys.__stdout__
Rich Lanef9aa5022013-01-10 16:36:15 -0800467 logging.debug(tmpout.getvalue())
Rich Lane629393f2013-01-10 15:37:33 -0800468 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
469 "Packet match error")
470
471 return rcv_pkt
472
473def receive_pkt_verify(parent, egr_port, exp_pkt):
474 """
475 Receive a packet and verify it matches an expected value
476
477 parent must implement dataplane, assertTrue and assertEqual
478 """
479 (rcv_port, rcv_pkt, _) = parent.dataplane.poll(port_number=egr_port,
480 timeout=1)
481
482 if exp_pkt is None:
483 if rcv_pkt is None:
484 return None
485 else:
Rich Lanef9aa5022013-01-10 16:36:15 -0800486 logging.error("ERROR: Received unexpected packet from " + str(egr_port));
Rich Lane629393f2013-01-10 15:37:33 -0800487 return rcv_pkt
488
489 if rcv_pkt is None:
Rich Lanef9aa5022013-01-10 16:36:15 -0800490 logging.error("ERROR: No packet received from " + str(egr_port))
Rich Lane629393f2013-01-10 15:37:33 -0800491
492 parent.assertTrue(rcv_pkt is not None,
493 "Did not receive packet port " + str(egr_port))
Rich Lanef9aa5022013-01-10 16:36:15 -0800494 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Rich Lane629393f2013-01-10 15:37:33 -0800495 str(rcv_port))
496
497 return pkt_verify(parent, rcv_pkt, exp_pkt)
498
499def packetin_verify(parent, exp_pkt):
500 """
501 Receive packet_in and verify it matches an expected value
502 """
503 (response, _) = parent.controller.poll(ofp.OFPT_PACKET_IN, 2)
504
Rich Lane18c9fed2013-05-07 15:22:53 -0700505 parent.assertTrue(response is not None, 'Packet in ofp.message not received')
Rich Lane629393f2013-01-10 15:37:33 -0800506 if str(exp_pkt) != response.data:
Rich Lanef9aa5022013-01-10 16:36:15 -0800507 logging.debug("pkt len " + str(len(str(exp_pkt))) + ": "
Rich Lane629393f2013-01-10 15:37:33 -0800508 + str(exp_pkt).encode('hex'))
Rich Lanef9aa5022013-01-10 16:36:15 -0800509 logging.debug("resp len " + str(len(str(response.data))) + ": "
Rich Lane629393f2013-01-10 15:37:33 -0800510 + str(response.data).encode('hex'))
511 parent.assertEqual(str(exp_pkt), response.data,
512 'PACKET_IN packet does not match send packet')
513
514def match_verify(parent, req_match, res_match):
515 """
516 Verify flow matches agree; if they disagree, report where
517
518 parent must implement assertEqual
519 Use str() to ensure content is compared and not pointers
520 """
521
522 parent.assertEqual(req_match.wildcards, res_match.wildcards,
523 'Match failed: wildcards: ' + hex(req_match.wildcards) +
524 " != " + hex(res_match.wildcards))
525 parent.assertEqual(req_match.in_port, res_match.in_port,
526 'Match failed: in_port: ' + str(req_match.in_port) +
527 " != " + str(res_match.in_port))
528 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
529 'Match failed: dl_src: ' + str(req_match.dl_src) +
530 " != " + str(res_match.dl_src))
531 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
532 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
533 " != " + str(res_match.dl_dst))
534 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
535 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
536 " != " + str(res_match.dl_vlan))
537 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
538 'Match failed: dl_vlan_pcp: ' +
539 str(req_match.dl_vlan_pcp) + " != " +
540 str(res_match.dl_vlan_pcp))
541 parent.assertEqual(req_match.dl_type, res_match.dl_type,
542 'Match failed: dl_type: ' + str(req_match.dl_type) +
543 " != " + str(res_match.dl_type))
544
545 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
546 and (req_match.dl_type == IP_ETHERTYPE)):
547 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
548 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
549 " != " + str(res_match.nw_tos))
550 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
551 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
552 " != " + str(res_match.nw_proto))
553 parent.assertEqual(req_match.nw_src, res_match.nw_src,
554 'Match failed: nw_src: ' + str(req_match.nw_src) +
555 " != " + str(res_match.nw_src))
556 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
557 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
558 " != " + str(res_match.nw_dst))
559
560 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
561 and ((req_match.nw_proto == TCP_PROTOCOL)
562 or (req_match.nw_proto == UDP_PROTOCOL))):
563 parent.assertEqual(req_match.tp_src, res_match.tp_src,
564 'Match failed: tp_src: ' +
565 str(req_match.tp_src) +
566 " != " + str(res_match.tp_src))
567 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
568 'Match failed: tp_dst: ' +
569 str(req_match.tp_dst) +
570 " != " + str(res_match.tp_dst))
571
572def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
573 """
574 Receive a flow removed msg and verify it matches expected
575
576 @params parent Must implement controller, assertEqual
577 @param pkt_count If >= 0, verify packet count
578 @param byte_count If >= 0, verify byte count
579 """
580 (response, _) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
Rich Lane18c9fed2013-05-07 15:22:53 -0700581 parent.assertTrue(response is not None, 'No flow removed ofp.message received')
Rich Lane629393f2013-01-10 15:37:33 -0800582
583 if request is None:
584 return
585
586 parent.assertEqual(request.cookie, response.cookie,
587 "Flow removed cookie error: " +
588 hex(request.cookie) + " != " + hex(response.cookie))
589
590 req_match = request.match
591 res_match = response.match
592 verifyMatchField(req_match, res_match)
593
594 if (req_match.wildcards != 0):
595 parent.assertEqual(request.priority, response.priority,
596 'Flow remove prio mismatch: ' +
597 str(request.priority) + " != " +
598 str(response.priority))
599 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
600 'Flow remove reason is not HARD TIMEOUT:' +
601 str(response.reason))
602 if pkt_count >= 0:
603 parent.assertEqual(response.packet_count, pkt_count,
604 'Flow removed failed, packet count: ' +
605 str(response.packet_count) + " != " +
606 str(pkt_count))
607 if byte_count >= 0:
608 parent.assertEqual(response.byte_count, byte_count,
609 'Flow removed failed, byte count: ' +
610 str(response.byte_count) + " != " +
611 str(byte_count))
612def flow_msg_create(parent, pkt, ing_port=0, match_fields=None, instruction_list=None,
613 action_list=None,wildcards=0, egr_port=None,
614 egr_queue=None, table_id=0, check_expire=False):
615 """
616 Multi-purpose flow_mod creation utility
617
618 Match on packet with given wildcards.
619 See flow_match_test for other parameter descriptoins
620
621 if egr_queue is set
Rich Lane18c9fed2013-05-07 15:22:53 -0700622 append an out_queue ofp.action to egr_queue to the actions_list
Rich Lane629393f2013-01-10 15:37:33 -0800623 else if egr_port is set:
Rich Lane18c9fed2013-05-07 15:22:53 -0700624 append an output ofp.action to egr_port to the actions_list
Rich Lane629393f2013-01-10 15:37:33 -0800625 if the instruction_list is empty,
Rich Lane18c9fed2013-05-07 15:22:53 -0700626 append an APPLY ofp.instruction to it
627 Add the action_list to the first write or apply ofp.instruction
Rich Lane629393f2013-01-10 15:37:33 -0800628
Rich Lane18c9fed2013-05-07 15:22:53 -0700629 @param egr_queue if not None, make the output an enqueue ofp.action
Rich Lane629393f2013-01-10 15:37:33 -0800630 @param table_id Table ID for writing a flow_mod
631 """
632
633 if match_fields is None:
634 match_fields = parse.packet_to_flow_match(pkt)
635 parent.assertTrue(match_fields is not None, "Flow match from pkt failed")
Rich Lane18c9fed2013-05-07 15:22:53 -0700636 in_port = ofp.oxm.in_port(ing_port)
Rich Lane48e447e2013-05-07 15:23:35 -0700637 match_fields.oxm_list.append(in_port)
Rich Lane18c9fed2013-05-07 15:22:53 -0700638 request = ofp.message.flow_add()
Rich Lane48e447e2013-05-07 15:23:35 -0700639 request.match= match_fields
Rich Lane629393f2013-01-10 15:37:33 -0800640 request.buffer_id = 0xffffffff
641 request.table_id = table_id
642
643 if check_expire:
644 request.flags |= ofp.OFPFF_SEND_FLOW_REM
645 request.hard_timeout = 1
646
647 if action_list is None:
648 action_list = []
649 if instruction_list is None:
650 instruction_list = []
651
Rich Lane18c9fed2013-05-07 15:22:53 -0700652 # Set up output/enqueue ofp.action if directed
Rich Lane629393f2013-01-10 15:37:33 -0800653 if egr_queue is not None:
654 parent.assertTrue(egr_port is not None, "Egress port not set")
Rich Lane18c9fed2013-05-07 15:22:53 -0700655 act = ofp.action.set_queue()
Rich Lane629393f2013-01-10 15:37:33 -0800656 act.port = egr_port
657 act.queue_id = egr_queue
658 action_list.append(act)
659 elif egr_port is not None:
Rich Lane18c9fed2013-05-07 15:22:53 -0700660 act = ofp.action.output()
Rich Lane629393f2013-01-10 15:37:33 -0800661 act.port = egr_port
662 action_list.append(act)
663
664 inst = None
665 if len(instruction_list) == 0:
Rich Lane48e447e2013-05-07 15:23:35 -0700666 inst = ofp.instruction.apply_actions()
Rich Lane629393f2013-01-10 15:37:33 -0800667 instruction_list.append(inst)
668 else:
669 for inst in instruction_list:
670 if (inst.type == ofp.OFPIT_WRITE_ACTIONS or
671 inst.type == ofp.OFPIT_APPLY_ACTIONS):
672 break
673
Rich Lane48e447e2013-05-07 15:23:35 -0700674
Rich Lane629393f2013-01-10 15:37:33 -0800675 # add all the actions to the last inst
Rich Lane48e447e2013-05-07 15:23:35 -0700676 inst.actions += action_list
Rich Lane629393f2013-01-10 15:37:33 -0800677
678 # add all the instrutions to the flow_mod
Rich Lane48e447e2013-05-07 15:23:35 -0700679 request.instructions += instruction_list
Rich Lane629393f2013-01-10 15:37:33 -0800680
Rich Lanef9aa5022013-01-10 16:36:15 -0800681 logging.debug(request.show())
Rich Lane629393f2013-01-10 15:37:33 -0800682 return request
683
684def flow_msg_install(parent, request, clear_table=True):
685 """
Rich Lane18c9fed2013-05-07 15:22:53 -0700686 Install a flow mod ofp.message in the switch
Rich Lane629393f2013-01-10 15:37:33 -0800687
688 @param parent Must implement controller, assertEqual, assertTrue
689 @param request The request, all set to go
690 @param clear_table If true, clear the flow table before installing
691 """
692 if clear_table:
Rich Lanef9aa5022013-01-10 16:36:15 -0800693 logging.debug("Clear flow table")
Rich Lane629393f2013-01-10 15:37:33 -0800694 if request.table_id:
695 table_id = request.table_id
696 else:
697 table_id = 0
698 rc = delete_all_flows_one_table(parent.controller,
Rich Lanef9aa5022013-01-10 16:36:15 -0800699 logging,
Rich Lane629393f2013-01-10 15:37:33 -0800700 table_id)
701 parent.assertEqual(rc, 0, "Failed to delete all flows on table: "
702 + str(table_id))
703 do_barrier(parent.controller)
704
Rich Lanef9aa5022013-01-10 16:36:15 -0800705 logging.debug("Insert flow::\n%s" % request.show())
Rich Lane629393f2013-01-10 15:37:33 -0800706 rv = parent.controller.message_send(request)
707 parent.assertTrue(rv != -1, "Error installing flow mod")
708 do_barrier(parent.controller)
709
710def error_verify(parent, exp_type, exp_code):
711 """
712 Receive an error msg and verify if it is as expected
713
714 @param parent Must implement controller, assertEqual
715 @param exp_type Expected error type
716 @param exp_code Expected error code
717 """
718 (response, raw) = parent.controller.poll(ofp.OFPT_ERROR, 2)
Rich Lane18c9fed2013-05-07 15:22:53 -0700719 parent.assertTrue(response is not None, 'No error ofp.message received')
Rich Lane629393f2013-01-10 15:37:33 -0800720
721 if (exp_type is None) or (exp_code is None):
Rich Lanef9aa5022013-01-10 16:36:15 -0800722 logging.debug("Parametrs are not sufficient")
Rich Lane629393f2013-01-10 15:37:33 -0800723 return
724
725 parent.assertEqual(exp_type, response.type,
Rich Lane18c9fed2013-05-07 15:22:53 -0700726 'Error ofp.message type mismatch: ' +
Rich Lane629393f2013-01-10 15:37:33 -0800727 str(exp_type) + " != " +
728 str(response.type))
729 parent.assertEqual(exp_code, response.code,
Rich Lane18c9fed2013-05-07 15:22:53 -0700730 'Error ofp.message code mismatch: ' +
Rich Lane629393f2013-01-10 15:37:33 -0800731 str(exp_code) + " != " +
732 str(response.code))
733
734def flow_match_test_port_pair(parent, ing_port, egr_port, match=None,
735 wildcards=0, mask=None,
736 dl_vlan=-1, pkt=None, exp_pkt=None,
737 apply_action_list=None, check_expire=False):
738 """
739 Flow match test on single TCP packet
740
741 Run test with packet through switch from ing_port to egr_port
742 See flow_match_test for parameter descriptions
743 """
744
Rich Lanef9aa5022013-01-10 16:36:15 -0800745 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
746 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Rich Lane629393f2013-01-10 15:37:33 -0800747 " expire: " + str(check_expire))
748 if pkt is None:
749 if dl_vlan >= 0:
750 pkt = simple_tcp_packet(vlan_tags=[{'vid': dl_vlan}])
751 else:
752 pkt = simple_tcp_packet()
753
754 match = parse.packet_to_flow_match(pkt)
755 parent.assertTrue(match is not None, "Flow match from pkt failed")
756
757 if mask is not None:
758 match.dl_src_mask = mask['dl_src']
759 match.dl_dst_mask = mask['dl_dst']
760 match.nw_src_mask = mask['nw_src']
761 match.nw_dst_mask = mask['nw_dst']
762 #Set unmatching values on corresponding match fields
763 for i in range(ofp.OFP_ETH_ALEN):
764 match.dl_src[i] = match.dl_src[i] ^ match.dl_src_mask[i]
765 match.dl_dst[i] = match.dl_dst[i] ^ match.dl_dst_mask[i]
766 match.nw_src = match.nw_src ^ match.nw_src_mask
767 match.nw_dst = match.nw_dst ^ match.nw_dst_mask
768
769 request = flow_msg_create(parent, pkt, ing_port=ing_port,
770 match=match,
771 wildcards=wildcards, egr_port=egr_port,
772 action_list=apply_action_list)
773
774 flow_msg_install(parent, request)
775
Rich Lanef9aa5022013-01-10 16:36:15 -0800776 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
Rich Lane629393f2013-01-10 15:37:33 -0800777 parent.dataplane.send(ing_port, str(pkt))
778
779 if exp_pkt is None:
780 exp_pkt = pkt
781 receive_pkt_verify(parent, egr_port, exp_pkt)
782
783 if check_expire:
784 #@todo Not all HW supports both pkt and byte counters
785 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
786def flow_match_test(parent, port_map, match=None, wildcards=0,
787 mask=None, dl_vlan=-1, pkt=None,
788 exp_pkt=None, apply_action_list=None,
789 check_expire=False, max_test=0):
790 """
791 Run flow_match_test_port_pair on all port pairs
792
793 @param max_test If > 0 no more than this number of tests are executed.
794 @param parent Must implement controller, dataplane, assertTrue, assertEqual
795 and logger
796 @param pkt If not None, use this packet for ingress
797 @param match If not None, use this value in flow_mod
798 @param wildcards For flow match entry
799 @param mask DL/NW address bit masks as a dictionary. If set, it is tested
800 against the corresponding match fields with the opposite values
801 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
802 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
803 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -0700804 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -0800805 """
806 of_ports = port_map.keys()
807 of_ports.sort()
808 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
809 test_count = 0
810
811 for ing_idx in range(len(of_ports)):
812 ingress_port = of_ports[ing_idx]
813 for egr_idx in range(len(of_ports)):
814 if egr_idx == ing_idx:
815 continue
816 egress_port = of_ports[egr_idx]
817 flow_match_test_port_pair(parent, ingress_port, egress_port,
818 match=match, wildcards=wildcards,
819 dl_vlan=dl_vlan, mask=mask,
820 pkt=pkt, exp_pkt=exp_pkt,
821 apply_action_list=apply_action_list,
822 check_expire=check_expire)
823 test_count += 1
824 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -0800825 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -0800826 return
827
828def flow_match_test_port_pair_vlan(parent, ing_port, egr_port, wildcards=0,
829 dl_vlan=ofp.OFPVID_NONE, dl_vlan_pcp=0,
830 dl_vlan_type=ETHERTYPE_VLAN,
831 dl_vlan_int=-1, dl_vlan_pcp_int=0,
832 vid_match=ofp.OFPVID_NONE, pcp_match=0,
833 exp_vid=-1, exp_pcp=0,
834 exp_vlan_type=ETHERTYPE_VLAN,
835 match_exp=True,
836 add_tag_exp=False,
837 exp_msg=ofp.OFPT_FLOW_REMOVED,
838 exp_msg_type=0, exp_msg_code=0,
839 pkt=None, exp_pkt=None,
840 action_list=None, check_expire=False):
841 """
842 Flow match test for various vlan matching patterns on single TCP packet
843
844 Run test with packet through switch from ing_port to egr_port
845 See flow_match_test_vlan for parameter descriptions
846 """
Rich Lanef9aa5022013-01-10 16:36:15 -0800847 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
848 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Rich Lane629393f2013-01-10 15:37:33 -0800849 " expire: " + str(check_expire))
850 if pkt is None:
851 if dl_vlan >= 0 and dl_vlan != ofp.OFPVID_NONE:
852 if dl_vlan_int >= 0 and dl_vlan_int != ofp.OFPVID_NONE:
853 pkt = simple_tcp_packet(
854 vlan_tags=[{'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp},
855 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
856 else:
857 pkt = simple_tcp_packet(
858 vlan_tags=[{'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp}])
859 else:
860 pkt = simple_tcp_packet()
861
862 if exp_pkt is None:
863 if exp_vid >= 0 and exp_vid != ofp.OFPVID_NONE:
864 if add_tag_exp:
865 if dl_vlan >= 0 and dl_vlan != ofp.OFPVID_NONE:
866 if dl_vlan_int >= 0 and dl_vlan_int != ofp.OFPVID_NONE:
867 exp_pkt = simple_tcp_packet(
868 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
869 {'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp},
870 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
871 else:
872 exp_pkt = simple_tcp_packet(
873 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
874 {'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp}])
875 else:
876 exp_pkt = simple_tcp_packet(
877 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
878 else:
879 if dl_vlan_int >= 0:
880 exp_pkt = simple_tcp_packet(
881 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
882 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
883
884 else:
885 exp_pkt = simple_tcp_packet(
886 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
887 else:
Rich Lane18c9fed2013-05-07 15:22:53 -0700888 #subtract ofp.action
Rich Lane629393f2013-01-10 15:37:33 -0800889 if dl_vlan_int >= 0:
890 exp_pkt = simple_tcp_packet(
891 vlan_tags=[{'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
892 else:
893 exp_pkt = simple_tcp_packet()
894
895 match = parse.packet_to_flow_match(pkt)
896 parent.assertTrue(match is not None, "Flow match from pkt failed")
897
898 match.dl_vlan = vid_match
899 match.dl_vlan_pcp = pcp_match
900 match.wildcards = wildcards
901
902 request = flow_msg_create(parent, pkt, ing_port=ing_port,
903 wildcards=wildcards,
904 match=match,
905 egr_port=egr_port,
906 action_list=action_list)
907
908 flow_msg_install(parent, request)
909
Rich Lanef9aa5022013-01-10 16:36:15 -0800910 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
911 logging.debug("Sent:" + str(pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800912 parent.dataplane.send(ing_port, str(pkt))
913
914 if match_exp:
915 receive_pkt_verify(parent, egr_port, exp_pkt)
916 if check_expire:
917 #@todo Not all HW supports both pkt and byte counters
918 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
919 else:
920 if exp_msg is ofp.OFPT_FLOW_REMOVED:
921 if check_expire:
922 flow_removed_verify(parent, request, pkt_count=0, byte_count=0)
923 elif exp_msg is ofp.OFPT_ERROR:
924 error_verify(parent, exp_msg_type, exp_msg_code)
925 else:
Rich Lane18c9fed2013-05-07 15:22:53 -0700926 parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
Rich Lane629393f2013-01-10 15:37:33 -0800927
928 (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
929 parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
930
931def flow_match_test_vlan(parent, port_map, wildcards=0,
932 dl_vlan=ofp.OFPVID_NONE, dl_vlan_pcp=0, dl_vlan_type=ETHERTYPE_VLAN,
933 dl_vlan_int=-1, dl_vlan_pcp_int=0,
934 vid_match=ofp.OFPVID_NONE, pcp_match=0,
935 exp_vid=-1, exp_pcp=0,
936 exp_vlan_type=ETHERTYPE_VLAN,
937 match_exp=True,
938 add_tag_exp=False,
939 exp_msg=ofp.OFPT_FLOW_REMOVED,
940 exp_msg_type=0, exp_msg_code=0,
941 pkt=None, exp_pkt=None,
942 action_list=None,
943 check_expire=False,
944 max_test=0):
945 """
946 Run flow_match_test_port_pair on all port pairs
947
948 @param max_test If > 0 no more than this number of tests are executed.
949 @param parent Must implement controller, dataplane, assertTrue, assertEqual
950 and logger
951 @param wildcards For flow match entry
952 @param dl_vlan If not -1, and pkt is not None, create a pkt w/ VLAN tag
953 @param dl_vlan_pcp VLAN PCP associated with dl_vlan
954 @param dl_vlan_type VLAN ether type associated with dl_vlan
955 @param dl_vlan_int If not -1, create pkt w/ Inner Vlan tag
956 @param dl_vlan_pcp_int VLAN PCP associated with dl_vlan_2nd
957 @param vid_match Matching value for VLAN VID field
958 @param pcp_match Matching value for VLAN PCP field
959 @param exp_vid Expected VLAN VID value. If -1, no VLAN expected
960 @param exp_vlan_type Expected VLAN ether type
961 @param exp_pcp Expected VLAN PCP value
962 @param match_exp Set whether packet is expected to receive
963 @param add_tag_exp If True, expected_packet has an additional vlan tag,
964 If not, expected_packet's vlan tag is replaced as specified
Rich Lane18c9fed2013-05-07 15:22:53 -0700965 @param exp_msg Expected ofp.message
966 @param exp_msg_type Expected ofp.message type associated with the ofp.message
967 @param exp_msg_code Expected ofp.message code associated with the msg_type
Rich Lane629393f2013-01-10 15:37:33 -0800968 @param pkt If not None, use this packet for ingress
969 @param exp_pkt If not None, use this as the expected output pkt
970 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -0700971 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -0800972 """
973 of_ports = port_map.keys()
974 of_ports.sort()
975 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
976 test_count = 0
977
978 for ing_idx in range(len(of_ports)):
979 ingress_port = of_ports[ing_idx]
980 for egr_idx in range(len(of_ports)):
981 if egr_idx == ing_idx:
982 continue
983 egress_port = of_ports[egr_idx]
984 flow_match_test_port_pair_vlan(parent, ingress_port, egress_port,
985 wildcards=wildcards,
986 dl_vlan=dl_vlan,
987 dl_vlan_pcp=dl_vlan_pcp,
988 dl_vlan_type=dl_vlan_type,
989 dl_vlan_int=dl_vlan_int,
990 dl_vlan_pcp_int=dl_vlan_pcp_int,
991 vid_match=vid_match,
992 pcp_match=pcp_match,
993 exp_vid=exp_vid,
994 exp_pcp=exp_pcp,
995 exp_vlan_type=exp_vlan_type,
996 exp_msg=exp_msg,
997 exp_msg_type=exp_msg_type,
998 exp_msg_code=exp_msg_code,
999 match_exp=match_exp,
1000 add_tag_exp=add_tag_exp,
1001 pkt=pkt, exp_pkt=exp_pkt,
1002 action_list=action_list,
1003 check_expire=check_expire)
1004 test_count += 1
1005 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -08001006 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -08001007 return
1008
1009def test_param_get(config, key, default=None):
1010 """
1011 Return value passed via test-params if present
1012
1013 @param config The configuration structure for OFTest
1014 @param key The lookup key
1015 @param default Default value to use if not found
1016
1017 If the pair 'key=val' appeared in the string passed to --test-params
1018 on the command line, return val (as interpreted by exec). Otherwise
1019 return default value.
1020 """
1021 try:
1022 exec config["test_params"]
1023 except:
1024 return default
1025
1026 s = "val = " + str(key)
1027 try:
1028 val = None
1029 exec s
1030 return val
1031 except:
1032 return default
1033
1034def action_generate(parent, field_to_mod, mod_field_vals):
1035 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001036 Create an ofp.action to modify the field indicated in field_to_mod
Rich Lane629393f2013-01-10 15:37:33 -08001037
1038 @param parent Must implement, assertTrue
1039 @param field_to_mod The field to modify as a string name
1040 @param mod_field_vals Hash of values to use for modified values
1041 """
1042
1043 act = None
1044
1045 if field_to_mod in ['pktlen']:
1046 return None
1047
1048 if field_to_mod == 'dl_dst':
Rich Lane18c9fed2013-05-07 15:22:53 -07001049 act = ofp.action.set_dl_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001050 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
1051 elif field_to_mod == 'dl_src':
Rich Lane18c9fed2013-05-07 15:22:53 -07001052 act = ofp.action.set_dl_src()
Rich Lane629393f2013-01-10 15:37:33 -08001053 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
1054 elif field_to_mod == 'vlan_tags':
1055 if len(mod_field_vals['vlan_tags']):
Rich Lane18c9fed2013-05-07 15:22:53 -07001056 act = ofp.action.pop_vlan()
Rich Lane629393f2013-01-10 15:37:33 -08001057 else:
1058 pass
1059# elif field_to_mod == 'dl_vlan_enable':
1060# if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
Rich Lane18c9fed2013-05-07 15:22:53 -07001061# act = ofp.action.pop_vlan()
Rich Lane629393f2013-01-10 15:37:33 -08001062# # Add VLAN tag is handled by dl_vlan field
1063# # Will return None in this case
1064# elif field_to_mod == 'dl_vlan':
Rich Lane18c9fed2013-05-07 15:22:53 -07001065# act = ofp.action.set_vlan_vid()
Rich Lane629393f2013-01-10 15:37:33 -08001066# act.vlan_vid = mod_field_vals['dl_vlan']
1067# elif field_to_mod == 'dl_vlan_pcp':
Rich Lane18c9fed2013-05-07 15:22:53 -07001068# act = ofp.action.set_vlan_pcp()
Rich Lane629393f2013-01-10 15:37:33 -08001069# act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
1070 elif field_to_mod == 'ip_src':
Rich Lane18c9fed2013-05-07 15:22:53 -07001071 act = ofp.action.set_nw_src()
Rich Lane629393f2013-01-10 15:37:33 -08001072 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
1073 elif field_to_mod == 'ip_dst':
Rich Lane18c9fed2013-05-07 15:22:53 -07001074 act = ofp.action.set_nw_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001075 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
1076 elif field_to_mod == 'ip_tos':
Rich Lane18c9fed2013-05-07 15:22:53 -07001077 act = ofp.action.set_nw_tos()
Rich Lane629393f2013-01-10 15:37:33 -08001078 act.nw_tos = mod_field_vals['ip_tos']
1079 elif field_to_mod == 'tcp_sport':
Rich Lane18c9fed2013-05-07 15:22:53 -07001080 act = ofp.action.set_tp_src()
Rich Lane629393f2013-01-10 15:37:33 -08001081 act.tp_port = mod_field_vals['tcp_sport']
1082 elif field_to_mod == 'tcp_dport':
Rich Lane18c9fed2013-05-07 15:22:53 -07001083 act = ofp.action.set_tp_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001084 act.tp_port = mod_field_vals['tcp_dport']
1085 else:
1086 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
1087
1088 return act
1089
1090def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
1091 mod_fields={}, check_test_params=False):
1092 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001093 Set up the ingress and expected packet and ofp.action list for a test
Rich Lane629393f2013-01-10 15:37:33 -08001094
1095 @param parent Must implement, assertTrue, config hash and logger
1096 @param start_field_values Field values to use for ingress packet (optional)
1097 @param mod_field_values Field values to use for modified packet (optional)
1098 @param mod_fields The list of fields to be modified by the switch in the test.
1099 @params check_test_params If True, will check the parameters vid, add_vlan
1100 and strip_vlan from the command line.
1101
Rich Lane18c9fed2013-05-07 15:22:53 -07001102 Returns a triple: pkt-to-send, expected-pkt, ofp.action-list
Rich Lane629393f2013-01-10 15:37:33 -08001103 """
1104
1105 new_actions = []
1106
1107
1108 base_pkt_params = {}
1109 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
1110 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
1111# base_pkt_params['dl_vlan_enable'] = False
1112# base_pkt_params['dl_vlan'] = 2
1113# base_pkt_params['dl_vlan_pcp'] = 0
1114 base_pkt_params['ip_src'] = '192.168.0.1'
1115 base_pkt_params['ip_dst'] = '192.168.0.2'
1116 base_pkt_params['ip_tos'] = 0
1117 base_pkt_params['tcp_sport'] = 1234
1118 base_pkt_params['tcp_dport'] = 80
1119 for keyname in start_field_vals.keys():
1120 base_pkt_params[keyname] = start_field_vals[keyname]
1121
1122 mod_pkt_params = {}
1123 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
1124 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
1125# mod_pkt_params['dl_vlan_enable'] = False
1126# mod_pkt_params['dl_vlan'] = 3
1127# mod_pkt_params['dl_vlan_pcp'] = 7
1128 mod_pkt_params['ip_src'] = '10.20.30.40'
1129 mod_pkt_params['ip_dst'] = '50.60.70.80'
1130 mod_pkt_params['ip_tos'] = 0xf0
1131 mod_pkt_params['tcp_sport'] = 4321
1132 mod_pkt_params['tcp_dport'] = 8765
1133 for keyname in mod_field_vals.keys():
1134 mod_pkt_params[keyname] = mod_field_vals[keyname]
1135
1136 # Check for test param modifications
1137 strip = False
1138 if check_test_params:
1139 add_vlan = test_param_get(parent.config, 'add_vlan')
1140 strip_vlan = test_param_get(parent.config, 'strip_vlan')
1141 vid = test_param_get(parent.config, 'vid')
1142
1143 if add_vlan and strip_vlan:
1144 parent.assertTrue(0, "Add and strip VLAN both specified")
1145
1146 if vid:
1147 base_pkt_params['dl_vlan_enable'] = True
1148 base_pkt_params['dl_vlan'] = vid
1149 if 'dl_vlan' in mod_fields:
1150 mod_pkt_params['dl_vlan'] = vid + 1
1151
1152 if add_vlan:
1153 base_pkt_params['dl_vlan_enable'] = False
1154 mod_pkt_params['dl_vlan_enable'] = True
1155 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
1156 mod_fields.append('pktlen')
1157 mod_fields.append('dl_vlan_enable')
1158 if 'dl_vlan' not in mod_fields:
1159 mod_fields.append('dl_vlan')
1160 elif strip_vlan:
1161 base_pkt_params['dl_vlan_enable'] = True
1162 mod_pkt_params['dl_vlan_enable'] = False
1163 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
1164 mod_fields.append('dl_vlan_enable')
1165 mod_fields.append('pktlen')
1166
1167 # Build the ingress packet
1168 ingress_pkt = simple_tcp_packet(**base_pkt_params)
1169
1170 # Build the expected packet, modifying the indicated fields
1171 for item in mod_fields:
1172 base_pkt_params[item] = mod_pkt_params[item]
1173 act = action_generate(parent, item, mod_pkt_params)
1174 if act:
1175 new_actions.append(act)
1176
1177 expected_pkt = simple_tcp_packet(**base_pkt_params)
1178
1179 return (ingress_pkt, expected_pkt, new_actions)
1180
1181def wildcard_all_set(match):
1182 match.wildcards = ofp.OFPFW_ALL
1183 match.nw_dst_mask = 0xffffffff
1184 match.nw_src_mask = 0xffffffff
1185 match.dl_dst_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
1186 match.dl_src_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
1187 match.metadata_mask = 0xffffffffffffffff
1188
1189def skip_message_emit(parent, s):
1190 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001191 Print out a 'skipped' ofp.message to stderr
Rich Lane629393f2013-01-10 15:37:33 -08001192
1193 @param s The string to print out to the log file
1194 @param parent Must implement config and logger objects
1195 """
1196 global skipped_test_count
1197
1198 skipped_test_count += 1
Rich Lanef9aa5022013-01-10 16:36:15 -08001199 logging.info("Skipping: " + s)
Dan Talayco2940fdd2013-02-06 15:01:35 -08001200 if parent.config["debug"] < logging.WARNING:
Rich Lane629393f2013-01-10 15:37:33 -08001201 sys.stderr.write("(skipped) ")
1202 else:
1203 sys.stderr.write("(S)")
1204
1205def do_echo_request_reply_test(test,controller):
Rich Lane18c9fed2013-05-07 15:22:53 -07001206 request = ofp.message.echo_request()
Rich Lane629393f2013-01-10 15:37:33 -08001207 response, _ = controller.transact(request)
Rich Lane18c9fed2013-05-07 15:22:53 -07001208 test.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
Rich Lane629393f2013-01-10 15:37:33 -08001209 'response is not echo_reply')
Rich Lane18c9fed2013-05-07 15:22:53 -07001210 test.assertEqual(request.xid, response.xid,
Rich Lane629393f2013-01-10 15:37:33 -08001211 'response xid != request xid')
1212 test.assertEqual(len(response.data), 0, 'response data non-empty')
1213
1214def match_all_generate():
1215 match = ofp.ofp_match()
1216 return match
1217
1218def simple_tcp_packet_w_mpls(
1219 dl_dst='00:01:02:03:04:05',
1220 dl_src='00:06:07:08:09:0a',
1221 mpls_type=0x8847,
1222 mpls_label=-1,
1223 mpls_tc=0,
1224 mpls_ttl=64,
1225 mpls_label_int=-1,
1226 mpls_tc_int=0,
1227 mpls_ttl_int=32,
1228 mpls_label_ext=-1,
1229 mpls_tc_ext=0,
1230 mpls_ttl_ext=128,
1231 ip_src='192.168.0.1',
1232 ip_dst='192.168.0.2',
1233 ip_tos=0,
1234 ip_ttl=192,
1235 tcp_sport=1234,
1236 tcp_dport=80
1237 ):
1238 """
1239 Return a simple dataplane TCP packet w/wo MPLS tags
1240
1241 Supports a few parameters:
1242 @param len Length of packet in bytes w/o CRC
1243 @param dl_dst Destinatino MAC
1244 @param dl_src Source MAC
1245 @param mpls_type MPLS type as ether type
1246 @param mpls_label MPLS LABEL if not -1
1247 @param mpls_tc MPLS TC
1248 @param mpls_ttl MPLS TTL
1249 @param mpls_label_int Inner MPLS LABEL if not -1. The shim will be added
1250 inside of mpls_label shim.
1251 @param mpls_tc_int Inner MPLS TC
1252 @param mpls_ttl_int Inner MPLS TTL
1253 @param mpls_label_ext External MPLS LABEL if not -1. The shim will be
1254 added outside of mpls_label shim
1255 @param mpls_tc_ext External MPLS TC
1256 @param mpls_ttl_ext External MPLS TTL
1257 @param ip_src IP source
1258 @param ip_dst IP destination
1259 @param ip_tos IP ToS
1260 @param tcp_dport TCP destination port
1261 @param ip_sport TCP source port
1262
1263 Generates a simple MPLS/IP/TCP request. Users
1264 shouldn't assume anything about this packet other than that
1265 it is a valid ethernet/IP/TCP frame.
1266 """
1267
1268 mpls_tags = []
1269
1270 if mpls_label_ext >= 0:
1271 mpls_tags.append({'type': mpls_type, 'label': mpls_label_ext, 'tc': mpls_tc_ext, 'ttl': mpls_ttl_ext})
1272
1273 if mpls_label >= 0:
1274 mpls_tags.append({'type': mpls_type, 'label': mpls_label, 'tc': mpls_tc, 'ttl': mpls_ttl})
1275
1276 if mpls_label_int >= 0:
1277 mpls_tags.append({'type': mpls_type, 'label': mpls_label_int, 'tc': mpls_tc_int, 'ttl': mpls_ttl_int})
1278
1279 pkt = simple_tcp_packet(dl_dst=dl_dst,
1280 dl_src=dl_src,
1281 mpls_tags=mpls_tags,
1282 ip_src=ip_src,
1283 ip_dst=ip_dst,
1284 ip_tos=ip_tos,
1285 ip_ttl=ip_ttl,
1286 tcp_sport=tcp_sport,
1287 tcp_dport=tcp_dport)
1288 return pkt
1289
1290def flow_match_test_port_pair_mpls(parent, ing_port, egr_port, wildcards=0,
1291 mpls_type=0x8847,
1292 mpls_label=-1, mpls_tc=0,mpls_ttl=64,
1293 mpls_label_int=-1, mpls_tc_int=0,
1294 mpls_ttl_int=32,
1295 ip_ttl=192,
1296 exp_mpls_type=0x8847,
1297 exp_mpls_label=-1, exp_mpls_tc=0,
1298 exp_mpls_ttl=64,
1299 exp_mpls_ttl_int=32,
1300 exp_ip_ttl=192,
1301 label_match=0, tc_match=0,
1302 dl_type_match=ETHERTYPE_MPLS,
1303 match_exp=True,
1304 add_tag_exp=False,
1305 exp_msg=ofp.OFPT_FLOW_REMOVED,
1306 exp_msg_type=0, exp_msg_code=0,
1307 pkt=None,
1308 exp_pkt=None, action_list=None,
1309 check_expire=False):
1310 """
1311 Flow match test on single packet w/ MPLS tags
1312
1313 Run test with packet through switch from ing_port to egr_port
1314 See flow_match_test for parameter descriptions
1315 """
Rich Lanef9aa5022013-01-10 16:36:15 -08001316 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
1317 logging.debug(" WC: " + hex(wildcards) + " MPLS: " +
Rich Lane629393f2013-01-10 15:37:33 -08001318 str(mpls_label) + " expire: " + str(check_expire))
1319
1320 if pkt is None:
1321
1322 pkt = simple_tcp_packet_w_mpls(mpls_type=mpls_type,
1323 mpls_label=mpls_label,
1324 mpls_tc=mpls_tc,
1325 mpls_ttl=mpls_ttl,
1326 mpls_label_int=mpls_label_int,
1327 mpls_tc_int=mpls_tc_int,
1328 mpls_ttl_int=mpls_ttl_int,
1329 ip_ttl=ip_ttl)
1330
1331 if exp_pkt is None:
1332 if add_tag_exp:
1333 exp_pkt = simple_tcp_packet_w_mpls(
1334 mpls_type=exp_mpls_type,
1335 mpls_label_ext=exp_mpls_label,
1336 mpls_tc_ext=exp_mpls_tc,
1337 mpls_ttl_ext=exp_mpls_ttl,
1338 mpls_label=mpls_label,
1339 mpls_tc=mpls_tc,
1340 mpls_ttl=mpls_ttl,
1341 mpls_label_int=mpls_label_int,
1342 mpls_tc_int=mpls_tc_int,
1343 mpls_ttl_int=exp_mpls_ttl_int,
1344 ip_ttl=exp_ip_ttl)
1345 else:
1346 if (exp_mpls_label < 0) and (mpls_label_int >= 0):
1347 exp_pkt = simple_tcp_packet_w_mpls(
1348 mpls_type=mpls_type,
1349 mpls_label=mpls_label_int,
1350 mpls_tc=mpls_tc_int,
1351 mpls_ttl=exp_mpls_ttl_int,
1352 ip_ttl=exp_ip_ttl)
1353 else:
1354 exp_pkt = simple_tcp_packet_w_mpls(
1355 mpls_type=exp_mpls_type,
1356 mpls_label=exp_mpls_label,
1357 mpls_tc=exp_mpls_tc,
1358 mpls_ttl=exp_mpls_ttl,
1359 mpls_label_int=mpls_label_int,
1360 mpls_tc_int=mpls_tc_int,
1361 mpls_ttl_int=exp_mpls_ttl_int,
1362 ip_ttl=exp_ip_ttl)
1363 wildcards = (ofp.OFPFW_ALL & ~(ofp.OFPFW_DL_TYPE | ofp.OFPFW_MPLS_LABEL | ofp.OFPFW_MPLS_TC)) | wildcards
1364
1365 match = parse.packet_to_flow_match(pkt)
1366 parent.assertTrue(match is not None, "Flow match from pkt failed")
1367
1368 match.mpls_label = label_match
1369 match.mpls_tc = tc_match
1370 match.wildcards = wildcards
1371
1372 match.dl_type = dl_type_match
1373 match.nw_tos = 0
1374 match.nw_proto = 0
1375 match.nw_src = 0
1376 match.nw_src_mask = 0xFFFFFFFF
1377 match.nw_dst = 0
1378 match.nw_dst_mask = 0xFFFFFFFF
1379 match.tp_src = 0
1380 match.tp_dst = 0
1381
1382 request = flow_msg_create(parent, pkt, ing_port=ing_port,
1383 wildcards=wildcards,
1384 match=match,
1385 egr_port=egr_port,
1386 action_list=action_list)
1387
1388 flow_msg_install(parent, request)
1389
Rich Lanef9aa5022013-01-10 16:36:15 -08001390 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
1391 #logging.debug(str(pkt).encode("hex"))
Rich Lane629393f2013-01-10 15:37:33 -08001392 parent.dataplane.send(ing_port, str(pkt))
1393
1394 if match_exp:
1395 receive_pkt_verify(parent, egr_port, exp_pkt)
1396 if check_expire:
1397 #@todo Not all HW supports both pkt and byte counters
1398 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
1399 else:
1400 if exp_msg == ofp.OFPT_FLOW_REMOVED:
1401 if check_expire:
1402 flow_removed_verify(parent, request, pkt_count=0, byte_count=0)
1403 elif exp_msg == ofp.OFPT_ERROR:
1404 error_verify(parent, exp_msg_type, exp_msg_code)
1405 else:
Rich Lane18c9fed2013-05-07 15:22:53 -07001406 parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
Rich Lane629393f2013-01-10 15:37:33 -08001407 (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
1408 parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
1409
1410def flow_match_test_mpls(parent, port_map, wildcards=0,
1411 mpls_type=0x8847,
1412 mpls_label=-1, mpls_tc=0, mpls_ttl=64,
1413 mpls_label_int=-1, mpls_tc_int=0, mpls_ttl_int=32,
1414 ip_ttl = 192,
1415 label_match=0, tc_match=0,
1416 dl_type_match=ETHERTYPE_MPLS,
1417 exp_mpls_type=0x8847,
1418 exp_mpls_label=-1, exp_mpls_tc=0, exp_mpls_ttl=64,
1419 exp_mpls_ttl_int=32,
1420 exp_ip_ttl=192,
1421 match_exp=True,
1422 add_tag_exp=False,
1423 exp_msg=ofp.OFPT_FLOW_REMOVED,
1424 exp_msg_type=0, exp_msg_code=0,
1425 pkt=None,
1426 exp_pkt=None, action_list=None, check_expire=False,
1427 max_test=0):
1428 """
1429 Run flow_match_test_port_pair on all port pairs
1430
1431 @param max_test If > 0 no more than this number of tests are executed.
1432 @param parent Must implement controller, dataplane, assertTrue, assertEqual
1433 and logger
1434 @param wildcards For flow match entry
1435 @param mpls_type MPLS type
1436 @param mpls_label If not -1 create a pkt w/ MPLS tag
1437 @param mpls_tc MPLS TC associated with MPLS label
1438 @param mpls_ttl MPLS TTL associated with MPLS label
1439 @param mpls_label_int If not -1 create a pkt w/ Inner MPLS tag
1440 @param mpls_tc_int MPLS TC associated with Inner MPLS label
1441 @param mpls_ttl_int MPLS TTL associated with Inner MPLS label
1442 @param ip_ttl IP TTL
1443 @param label_match Matching value for MPLS LABEL field
1444 @param tc_match Matching value for MPLS TC field
1445 @param exp_mpls_label Expected MPLS LABEL value. If -1, no MPLS expected
1446 @param exp_mpls_tc Expected MPLS TC value
1447 @param exp_mpls_ttl Expected MPLS TTL value
1448 @param exp_mpls_ttl_int Expected Inner MPLS TTL value
1449 @param ip_ttl Expected IP TTL
1450 @param match_exp Set whether packet is expected to receive
1451 @param add_tag_exp If True, expected_packet has an additional MPLS shim,
1452 If not expected_packet's MPLS shim is replaced as specified
Rich Lane18c9fed2013-05-07 15:22:53 -07001453 @param exp_msg Expected ofp.message
1454 @param exp_msg_type Expected ofp.message type associated with the ofp.message
1455 @param exp_msg_code Expected ofp.message code associated with the msg_type
Rich Lane629393f2013-01-10 15:37:33 -08001456 @param pkt If not None, use this packet for ingress
1457 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
1458 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -07001459 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -08001460 """
1461 of_ports = port_map.keys()
1462 of_ports.sort()
1463 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
1464 test_count = 0
1465
1466 for ing_idx in range(len(of_ports)):
1467 ingress_port = of_ports[ing_idx]
1468 for egr_idx in range(len(of_ports)):
1469 if egr_idx == ing_idx:
1470 continue
1471 egress_port = of_ports[egr_idx]
1472 flow_match_test_port_pair_mpls(parent, ingress_port, egress_port,
1473 wildcards=wildcards,
1474 mpls_type=mpls_type,
1475 mpls_label=mpls_label,
1476 mpls_tc=mpls_tc,
1477 mpls_ttl=mpls_ttl,
1478 mpls_label_int=mpls_label_int,
1479 mpls_tc_int=mpls_tc_int,
1480 mpls_ttl_int=mpls_ttl_int,
1481 ip_ttl=ip_ttl,
1482 label_match=label_match,
1483 tc_match=tc_match,
1484 dl_type_match=dl_type_match,
1485 exp_mpls_type=exp_mpls_type,
1486 exp_mpls_label=exp_mpls_label,
1487 exp_mpls_tc=exp_mpls_tc,
1488 exp_mpls_ttl=exp_mpls_ttl,
1489 exp_mpls_ttl_int=exp_mpls_ttl_int,
1490 exp_ip_ttl=exp_ip_ttl,
1491 match_exp=match_exp,
1492 exp_msg=exp_msg,
1493 exp_msg_type=exp_msg_type,
1494 exp_msg_code=exp_msg_code,
1495 add_tag_exp=add_tag_exp,
1496 pkt=pkt, exp_pkt=exp_pkt,
1497 action_list=action_list,
1498 check_expire=check_expire)
1499 test_count += 1
1500 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -08001501 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -08001502 return
1503
1504def flow_stats_get(parent, match_fields = None):
1505 """ Get the flow_stats from the switch
1506 Test the response to make sure it's really a flow_stats object
1507 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001508 request = ofp.message.flow_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -08001509 request.out_port = ofp.OFPP_ANY
1510 request.out_group = ofp.OFPG_ANY
1511 request.table_id = 0xff
1512 if match_fields != None:
1513 request.match_fields = match_fields
1514 response, _ = parent.controller.transact(request, timeout=2)
1515 parent.assertTrue(response is not None, "Did not get response")
Rich Lane18c9fed2013-05-07 15:22:53 -07001516 parent.assertTrue(isinstance(response,ofp.message.flow_stats_reply),
Rich Lane629393f2013-01-10 15:37:33 -08001517 "Expected a flow_stats_reply, but didn't get it")
1518 return response