blob: f73873b3e194ac7a8714c474c5e9a6aec163590f [file] [log] [blame]
Rich Lane629393f2013-01-10 15:37:33 -08001
2import sys
3import logging
4from cStringIO import StringIO
5#import types
6
Rich Lane18c9fed2013-05-07 15:22:53 -07007import ofp
8import oftest.parse as parse
Rich Lane629393f2013-01-10 15:37:33 -08009from packet import Packet
10
Rich Lane629393f2013-01-10 15:37:33 -080011try:
12 logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
13 from scapy.all import *
Rich Lane8f2861e2013-01-11 09:13:20 -080014 from oftest.mpls import *
Rich Lane629393f2013-01-10 15:37:33 -080015except ImportError:
16 sys.exit("Need to install scapy for packet parsing")
17
18global skipped_test_count
19skipped_test_count = 0
20
21# Some useful defines
22IP_ETHERTYPE = 0x800
23IPV6_ETHERTYPE = 0x86dd
24ETHERTYPE_VLAN = 0x8100
25ETHERTYPE_MPLS = 0x8847
26TCP_PROTOCOL = 0x6
27UDP_PROTOCOL = 0x11
28ICMPV6_PROTOCOL = 0x3a
29
30
31
32def clear_switch(parent, port_list, logger):
33 """
34 Clear the switch configuration
35
36 @param parent Object implementing controller and assert equal
37 @param logger Logging object
38 """
39 parent.assertTrue(len(port_list) > 2, "Not enough ports for test")
40 for port in port_list:
41 clear_port_config(parent, port, logger)
42 initialize_table_config(parent.controller, logger)
43 delete_all_flows(parent.controller, logger)
44 delete_all_groups(parent.controller, logger)
45
46 return port_list
47
48def initialize_table_config(ctrl, logger):
49 """
50 Initialize all table configs to default setting ("CONTROLLER")
51 @param ctrl The controller object for the test
52 """
53 logger.info("Initializing all table configs")
Rich Lane18c9fed2013-05-07 15:22:53 -070054 request = ofp.message.table_mod()
Rich Lane629393f2013-01-10 15:37:33 -080055 request.config = ofp.OFPTC_TABLE_MISS_CONTROLLER
56 rv = 0
57 for table_id in [0, 1, 2, 3, 4, 5, 6, 7]:
58 request.table_id = table_id
59 rv |= ctrl.message_send(request)
60 return rv
61
62def delete_all_flows(ctrl, logger):
63 """
64 Delete all flows on the switch
65 @param ctrl The controller object for the test
66 @param logger Logging object
67 """
68
69 logger.info("Deleting all flows")
70 #DEFAULT_TABLE_COUNT = 4
71 return delete_all_flows_one_table(ctrl, logger, table_id=0xff)
72
73def delete_all_flows_one_table(ctrl, logger, table_id=0):
74 """
75 Delete all flows on a table
76 @param ctrl The controller object for the test
77 @param logger Logging object
78 @param table_id Table ID
79 """
80 logger.info("Deleting all flows on table ID: " + str(table_id))
Rich Lane18c9fed2013-05-07 15:22:53 -070081 msg = ofp.message.flow_delete()
Rich Lane629393f2013-01-10 15:37:33 -080082 msg.out_port = ofp.OFPP_ANY
83 msg.out_group = ofp.OFPG_ANY
Rich Lane629393f2013-01-10 15:37:33 -080084 msg.buffer_id = 0xffffffff
85 msg.table_id = table_id
86 logger.debug(msg.show())
87
88 return ctrl.message_send(msg)
89
90def delete_all_groups(ctrl, logger):
91 """
92 Delete all groups on the switch
93 @param ctrl The controller object for the test
94 @param logger Logging object
95 """
96
97 logger.info("Deleting all groups")
Rich Lane18c9fed2013-05-07 15:22:53 -070098 msg = ofp.message.group_mod()
Rich Lane629393f2013-01-10 15:37:33 -080099 msg.group_id = ofp.OFPG_ALL
100 msg.command = ofp.OFPGC_DELETE
101 logger.debug(msg.show())
102 return ctrl.message_send(msg)
103
104def clear_port_config(parent, port, logger):
105 """
106 Clear the port configuration
107
108 @param parent Object implementing controller and assert equal
109 @param logger Logging object
110 """
111 rv = port_config_set(parent.controller, port,
112 0, 0, logger)
113 parent.assertEqual(rv, 0, "Failed to reset port config")
114
115def simple_tcp_packet(dl_dst='00:01:02:03:04:05',
116 dl_src='00:06:07:08:09:0a',
117 vlan_tags=[], # {type,vid,pcp,cfi} TODO type
118 mpls_tags=[], # {type,label,tc,ttl} TODO type
119 ip_src='192.168.0.1',
120 ip_dst='192.168.0.2',
121 ip_tos=0,
122 ip_ttl=64,
123 tcp_sport=1234,
124 tcp_dport=80,
125 payload_len = 46):
126 pkt = Ether(dst=dl_dst, src=dl_src)
127
128 vlans_num = 0
129 while len(vlan_tags):
130 tag = vlan_tags.pop(0)
131 dot1q = Dot1Q()
132 if 'vid' in tag:
133 dot1q.vlan = tag['vid']
134 if 'pcp' in tag:
135 dot1q.prio = tag['pcp']
136 if 'cfi' in tag:
137 dot1q.id = tag['cfi']
138 pkt = pkt / dot1q
139 if 'type' in tag:
140 if vlans_num == 0:
141 pkt[Ether].setfieldval('type', tag['type'])
142 else:
143 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
144 vlans_num+=1
145
146 mplss_num = 0
147 while len(mpls_tags):
148 tag = mpls_tags.pop(0)
149 mpls = MPLS()
150 if 'label' in tag:
151 mpls.label = tag['label']
152 if 'tc' in tag:
153 mpls.cos = tag['tc']
154 if 'ttl' in tag:
155 mpls.ttl = tag['ttl']
156 pkt = pkt / mpls
157 if 'type' in tag:
158 if mplss_num == 0:
159 if vlans_num == 0:
160 pkt[Ether].setfieldval('type', tag['type'])
161 else:
162 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
163 mplss_num+=1
164
165 pkt = pkt / IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl) \
166 / TCP(sport=tcp_sport, dport=tcp_dport)
167
168 pkt = pkt / ("D" * payload_len)
169
170 return pkt
171
172def simple_icmp_packet(dl_dst='00:01:02:03:04:05',
173 dl_src='00:06:07:08:09:0a',
174 vlan_tags=[], # {type,vid,pcp,cfi} TODO type
175 mpls_tags=[], # {type,label,tc,ttl} TODO type
176 ip_src='192.168.0.1',
177 ip_dst='192.168.0.2',
178 ip_tos=0,
179 ip_ttl=64,
180 icmp_type=8, # ICMP_ECHO_REQUEST
181 icmp_code=0,
182 payload_len=0):
183
184 #TODO simple_ip_packet
185 pkt = Ether(dst=dl_dst, src=dl_src)
186
187 vlans_num = 0
188 while len(vlan_tags):
189 tag = vlan_tags.pop(0)
190 dot1q = Dot1Q()
191 if 'vid' in tag:
192 dot1q.vlan = tag['vid']
193 if 'pcp' in tag:
194 dot1q.prio = tag['pcp']
195 if 'cfi' in tag:
196 dot1q.id = tag['cfi']
197 pkt = pkt / dot1q
198 if 'type' in tag:
199 if vlans_num == 0:
200 pkt[Ether].setfieldval('type', tag['type'])
201 else:
202 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
203 vlans_num+=1
204
205 mplss_num = 0
206 while len(mpls_tags):
207 tag = mpls_tags.pop(0)
208 mpls = MPLS()
209 if 'label' in tag:
210 mpls.label = tag['label']
211 if 'tc' in tag:
212 mpls.cos = tag['tc']
213 if 'ttl' in tag:
214 mpls.ttl = tag['ttl']
215 pkt = pkt / mpls
216 if 'type' in tag:
217 if mplss_num == 0:
218 if vlans_num == 0:
219 pkt[Ether].setfieldval('type', tag['type'])
220 else:
221 pkt[Dot1Q:vlans_num].setfieldval('type', tag['type'])
222 mplss_num+=1
223
224 pkt = pkt / IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl) \
225 / ICMP(type=icmp_type, code=icmp_code)
226
227 pkt = pkt / ("D" * payload_len)
228
229 return pkt
230
231def simple_ipv6_packet(pktlen=100,
232 dl_dst='00:01:02:03:04:05',
233 dl_src='00:06:07:08:09:0a',
234 dl_vlan_enable=False,
235 dl_vlan=0,
236 dl_vlan_pcp=0,
237 dl_vlan_cfi=0,
238 ip_src='fe80::2420:52ff:fe8f:5189',
239 ip_dst='fe80::2420:52ff:fe8f:5190',
240 ip_tos=0,
241 tcp_sport=0,
242 tcp_dport=0,
243 EH = False,
244 EHpkt = IPv6ExtHdrDestOpt()
245 ):
246
247 """
248 Return a simple IPv6 packet
249
250 Supports a few parameters:
251 @param len Length of packet in bytes w/o CRC
252 @param dl_dst Destinatino MAC
253 @param dl_src Source MAC
254 @param dl_vlan_enable True if the packet is with vlan, False otherwise
255 @param dl_vlan VLAN ID
256 @param dl_vlan_pcp VLAN priority
257 @param ip_src IPv6 source
258 @param ip_dst IPv6 destination
259 @param ip_tos IP ToS
260 @param tcp_dport TCP destination port
261 @param ip_sport TCP source port
262
263 """
264 # Note Dot1Q.id is really CFI
265 if (dl_vlan_enable):
266 pkt = Ether(dst=dl_dst, src=dl_src)/ \
267 Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
268 IPv6(src=ip_src, dst=ip_dst)
269
270 else:
271 pkt = Ether(dst=dl_dst, src=dl_src)/ \
272 IPv6(src=ip_src, dst=ip_dst)
273
274 # Add IPv6 Extension Headers
275 if EH:
276 pkt = pkt / EHpkt
277
278 if (tcp_sport >0 and tcp_dport >0):
279 pkt = pkt / TCP(sport=tcp_sport, dport=tcp_dport)
280
281 if pktlen > len(pkt) :
282 pkt = pkt/("D" * (pktlen - len(pkt)))
283
284 return pkt
285
286def simple_icmpv6_packet(pktlen=100,
287 dl_dst='00:01:02:03:04:05',
288 dl_src='00:06:07:08:09:0a',
289 dl_vlan_enable=False,
290 dl_vlan=0,
291 dl_vlan_pcp=0,
292 dl_vlan_cfi=0,
293 ip_src='fe80::2420:52ff:fe8f:5189',
294 ip_dst='fe80::2420:52ff:fe8f:5190',
295 ip_tos=0,
296 tcp_sport=0,
297 tcp_dport=0,
298 EH = False,
299 EHpkt = IPv6ExtHdrDestOpt(),
300 route_adv = False,
301 sll_enabled = False
302 ):
303
304 """
305 Return a simple dataplane ICMPv6 packet
306
307 Supports a few parameters:
308 @param len Length of packet in bytes w/o CRC
309 @param dl_dst Destinatino MAC
310 @param dl_src Source MAC
311 @param dl_vlan_enable True if the packet is with vlan, False otherwise
312 @param dl_vlan VLAN ID
313 @param dl_vlan_pcp VLAN priority
314 @param ip_src IPv6 source
315 @param ip_dst IPv6 destination
316 @param ip_tos IP ToS
317 @param tcp_dport TCP destination port
318 @param ip_sport TCP source port
319
320 """
321 if (dl_vlan_enable):
322 pkt = Ether(dst=dl_dst, src=dl_src)/ \
323 Dot1Q(prio=dl_vlan_pcp, id=dl_vlan_cfi, vlan=dl_vlan)/ \
324 IPv6(src=ip_src, dst=ip_dst)
325
326 else:
327 pkt = Ether(dst=dl_dst, src=dl_src)/ \
328 IPv6(src=ip_src, dst=ip_dst)
329
330
331 # Add IPv6 Extension Headers
332 if EH:
333 pkt = pkt / EHpkt
334
335 if route_adv:
336 pkt = pkt/ \
337 ICMPv6ND_RA(chlim=255, H=0L, M=0L, O=1L, routerlifetime=1800, P=0L, retranstimer=0, prf=0L, res=0L)/ \
338 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)
339 if sll_enabled :
340 pkt = pkt/ \
341 ICMPv6NDOptSrcLLAddr(type=1, len=1, lladdr='66:6f:df:2d:7c:9c')
342 else :
343 pkt = pkt/ \
344 ICMPv6EchoRequest()
345 if (tcp_sport >0 and tcp_dport >0):
346 pkt = pkt / TCP(sport=tcp_sport, dport=tcp_dport)
347
348 if pktlen > len(pkt) :
349 pkt = pkt/("D" * (pktlen - len(pkt)))
350
351 return pkt
352
353
354def do_barrier(ctrl):
Rich Lane18c9fed2013-05-07 15:22:53 -0700355 b = ofp.message.barrier_request()
Rich Lane629393f2013-01-10 15:37:33 -0800356 ctrl.transact(b)
357
358
359def port_config_get(controller, port_no, logger):
360 """
361 Get a port's configuration
362
363 Gets the switch feature configuration and grabs one port's
364 configuration
365
366 @returns (hwaddr, config, advert) The hwaddress, configuration and
367 advertised values
368 """
Rich Lane18c9fed2013-05-07 15:22:53 -0700369 request = ofp.message.features_request()
Rich Lane629393f2013-01-10 15:37:33 -0800370 reply, _ = controller.transact(request, timeout=2)
371 if reply is None:
372 logger.warn("Get feature request failed")
373 return None, None, None
374 logger.debug(reply.show())
375 for idx in range(len(reply.ports)):
376 if reply.ports[idx].port_no == port_no:
377 return (reply.ports[idx].hw_addr, reply.ports[idx].config,
378 reply.ports[idx].advertised)
379
380 logger.warn("Did not find port number for port config")
381 return None, None, None
382
383def port_config_set(controller, port_no, config, mask, logger):
384 """
385 Set the port configuration according the given parameters
386
387 Gets the switch feature configuration and updates one port's
388 configuration value according to config and mask
389 """
390 logger.info("Setting port " + str(port_no) + " to config " + str(config))
Rich Lane18c9fed2013-05-07 15:22:53 -0700391 request = ofp.message.features_request()
Rich Lane629393f2013-01-10 15:37:33 -0800392 reply, _ = controller.transact(request, timeout=2)
393 if reply is None:
394 return -1
395 logger.debug(reply.show())
396 for idx in range(len(reply.ports)):
397 if reply.ports[idx].port_no == port_no:
398 break
399 if idx >= len(reply.ports):
400 return -1
Rich Lane18c9fed2013-05-07 15:22:53 -0700401 mod = ofp.message.port_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800402 mod.port_no = port_no
403 mod.hw_addr = reply.ports[idx].hw_addr
404 mod.config = config
405 mod.mask = mask
406 mod.advertise = reply.ports[idx].advertised
407 rv = controller.message_send(mod)
408 return rv
409
410def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger):
411 """
412 Check for proper receive packets across all ports
413 @param dataplane The dataplane object
414 @param pkt Expected packet; may be None if yes_ports is empty
415 @param yes_ports Set or list of ports that should recieve packet
416 @param no_ports Set or list of ports that should not receive packet
417 @param assert_if Object that implements assertXXX
418 """
419 for ofport in yes_ports:
420 logger.debug("Checking for pkt on port " + str(ofport))
421 (_, rcv_pkt, _) = dataplane.poll(
422 port_number=ofport, timeout=1)
423 assert_if.assertTrue(rcv_pkt is not None,
424 "Did not receive pkt on " + str(ofport))
425 assert_if.assertEqual(str(pkt), str(rcv_pkt),
426 "Response packet does not match send packet " +
427 "on port " + str(ofport))
428
429 for ofport in no_ports:
430 logger.debug("Negative check for pkt on port " + str(ofport))
431 (_, rcv_pkt, _) = dataplane.poll(
432 port_number=ofport, timeout=1)
433 assert_if.assertTrue(rcv_pkt is None,
434 "Unexpected pkt on port " + str(ofport))
435
436
437def pkt_verify(parent, rcv_pkt, exp_pkt):
438 if str(exp_pkt) != str(rcv_pkt):
Rich Lanef9aa5022013-01-10 16:36:15 -0800439 logging.error("ERROR: Packet match failed.")
440 logging.debug("Expected (" + str(len(exp_pkt)) + ")")
441 logging.debug(str(exp_pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800442 sys.stdout = tmpout = StringIO()
443 exp_pkt.show()
444 sys.stdout = sys.__stdout__
Rich Lanef9aa5022013-01-10 16:36:15 -0800445 logging.debug(tmpout.getvalue())
446 logging.debug("Received (" + str(len(rcv_pkt)) + ")")
447 logging.debug(str(rcv_pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800448 sys.stdout = tmpout = StringIO()
449 Ether(rcv_pkt).show()
450 sys.stdout = sys.__stdout__
Rich Lanef9aa5022013-01-10 16:36:15 -0800451 logging.debug(tmpout.getvalue())
Rich Lane629393f2013-01-10 15:37:33 -0800452 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
453 "Packet match error")
454
455 return rcv_pkt
456
457def receive_pkt_verify(parent, egr_port, exp_pkt):
458 """
459 Receive a packet and verify it matches an expected value
460
461 parent must implement dataplane, assertTrue and assertEqual
462 """
463 (rcv_port, rcv_pkt, _) = parent.dataplane.poll(port_number=egr_port,
464 timeout=1)
465
466 if exp_pkt is None:
467 if rcv_pkt is None:
468 return None
469 else:
Rich Lanef9aa5022013-01-10 16:36:15 -0800470 logging.error("ERROR: Received unexpected packet from " + str(egr_port));
Rich Lane629393f2013-01-10 15:37:33 -0800471 return rcv_pkt
472
473 if rcv_pkt is None:
Rich Lanef9aa5022013-01-10 16:36:15 -0800474 logging.error("ERROR: No packet received from " + str(egr_port))
Rich Lane629393f2013-01-10 15:37:33 -0800475
476 parent.assertTrue(rcv_pkt is not None,
477 "Did not receive packet port " + str(egr_port))
Rich Lanef9aa5022013-01-10 16:36:15 -0800478 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Rich Lane629393f2013-01-10 15:37:33 -0800479 str(rcv_port))
480
481 return pkt_verify(parent, rcv_pkt, exp_pkt)
482
483def packetin_verify(parent, exp_pkt):
484 """
485 Receive packet_in and verify it matches an expected value
486 """
487 (response, _) = parent.controller.poll(ofp.OFPT_PACKET_IN, 2)
488
Rich Lane18c9fed2013-05-07 15:22:53 -0700489 parent.assertTrue(response is not None, 'Packet in ofp.message not received')
Rich Lane629393f2013-01-10 15:37:33 -0800490 if str(exp_pkt) != response.data:
Rich Lanef9aa5022013-01-10 16:36:15 -0800491 logging.debug("pkt len " + str(len(str(exp_pkt))) + ": "
Rich Lane629393f2013-01-10 15:37:33 -0800492 + str(exp_pkt).encode('hex'))
Rich Lanef9aa5022013-01-10 16:36:15 -0800493 logging.debug("resp len " + str(len(str(response.data))) + ": "
Rich Lane629393f2013-01-10 15:37:33 -0800494 + str(response.data).encode('hex'))
495 parent.assertEqual(str(exp_pkt), response.data,
496 'PACKET_IN packet does not match send packet')
497
498def match_verify(parent, req_match, res_match):
499 """
500 Verify flow matches agree; if they disagree, report where
501
502 parent must implement assertEqual
503 Use str() to ensure content is compared and not pointers
504 """
505
506 parent.assertEqual(req_match.wildcards, res_match.wildcards,
507 'Match failed: wildcards: ' + hex(req_match.wildcards) +
508 " != " + hex(res_match.wildcards))
509 parent.assertEqual(req_match.in_port, res_match.in_port,
510 'Match failed: in_port: ' + str(req_match.in_port) +
511 " != " + str(res_match.in_port))
512 parent.assertEqual(str(req_match.dl_src), str(res_match.dl_src),
513 'Match failed: dl_src: ' + str(req_match.dl_src) +
514 " != " + str(res_match.dl_src))
515 parent.assertEqual(str(req_match.dl_dst), str(res_match.dl_dst),
516 'Match failed: dl_dst: ' + str(req_match.dl_dst) +
517 " != " + str(res_match.dl_dst))
518 parent.assertEqual(req_match.dl_vlan, res_match.dl_vlan,
519 'Match failed: dl_vlan: ' + str(req_match.dl_vlan) +
520 " != " + str(res_match.dl_vlan))
521 parent.assertEqual(req_match.dl_vlan_pcp, res_match.dl_vlan_pcp,
522 'Match failed: dl_vlan_pcp: ' +
523 str(req_match.dl_vlan_pcp) + " != " +
524 str(res_match.dl_vlan_pcp))
525 parent.assertEqual(req_match.dl_type, res_match.dl_type,
526 'Match failed: dl_type: ' + str(req_match.dl_type) +
527 " != " + str(res_match.dl_type))
528
529 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
530 and (req_match.dl_type == IP_ETHERTYPE)):
531 parent.assertEqual(req_match.nw_tos, res_match.nw_tos,
532 'Match failed: nw_tos: ' + str(req_match.nw_tos) +
533 " != " + str(res_match.nw_tos))
534 parent.assertEqual(req_match.nw_proto, res_match.nw_proto,
535 'Match failed: nw_proto: ' + str(req_match.nw_proto) +
536 " != " + str(res_match.nw_proto))
537 parent.assertEqual(req_match.nw_src, res_match.nw_src,
538 'Match failed: nw_src: ' + str(req_match.nw_src) +
539 " != " + str(res_match.nw_src))
540 parent.assertEqual(req_match.nw_dst, res_match.nw_dst,
541 'Match failed: nw_dst: ' + str(req_match.nw_dst) +
542 " != " + str(res_match.nw_dst))
543
544 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
545 and ((req_match.nw_proto == TCP_PROTOCOL)
546 or (req_match.nw_proto == UDP_PROTOCOL))):
547 parent.assertEqual(req_match.tp_src, res_match.tp_src,
548 'Match failed: tp_src: ' +
549 str(req_match.tp_src) +
550 " != " + str(res_match.tp_src))
551 parent.assertEqual(req_match.tp_dst, res_match.tp_dst,
552 'Match failed: tp_dst: ' +
553 str(req_match.tp_dst) +
554 " != " + str(res_match.tp_dst))
555
556def flow_removed_verify(parent, request=None, pkt_count=-1, byte_count=-1):
557 """
558 Receive a flow removed msg and verify it matches expected
559
560 @params parent Must implement controller, assertEqual
561 @param pkt_count If >= 0, verify packet count
562 @param byte_count If >= 0, verify byte count
563 """
564 (response, _) = parent.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
Rich Lane18c9fed2013-05-07 15:22:53 -0700565 parent.assertTrue(response is not None, 'No flow removed ofp.message received')
Rich Lane629393f2013-01-10 15:37:33 -0800566
567 if request is None:
568 return
569
570 parent.assertEqual(request.cookie, response.cookie,
571 "Flow removed cookie error: " +
572 hex(request.cookie) + " != " + hex(response.cookie))
573
574 req_match = request.match
575 res_match = response.match
576 verifyMatchField(req_match, res_match)
577
578 if (req_match.wildcards != 0):
579 parent.assertEqual(request.priority, response.priority,
580 'Flow remove prio mismatch: ' +
581 str(request.priority) + " != " +
582 str(response.priority))
583 parent.assertEqual(response.reason, ofp.OFPRR_HARD_TIMEOUT,
584 'Flow remove reason is not HARD TIMEOUT:' +
585 str(response.reason))
586 if pkt_count >= 0:
587 parent.assertEqual(response.packet_count, pkt_count,
588 'Flow removed failed, packet count: ' +
589 str(response.packet_count) + " != " +
590 str(pkt_count))
591 if byte_count >= 0:
592 parent.assertEqual(response.byte_count, byte_count,
593 'Flow removed failed, byte count: ' +
594 str(response.byte_count) + " != " +
595 str(byte_count))
596def flow_msg_create(parent, pkt, ing_port=0, match_fields=None, instruction_list=None,
597 action_list=None,wildcards=0, egr_port=None,
598 egr_queue=None, table_id=0, check_expire=False):
599 """
600 Multi-purpose flow_mod creation utility
601
602 Match on packet with given wildcards.
603 See flow_match_test for other parameter descriptoins
604
605 if egr_queue is set
Rich Lane18c9fed2013-05-07 15:22:53 -0700606 append an out_queue ofp.action to egr_queue to the actions_list
Rich Lane629393f2013-01-10 15:37:33 -0800607 else if egr_port is set:
Rich Lane18c9fed2013-05-07 15:22:53 -0700608 append an output ofp.action to egr_port to the actions_list
Rich Lane629393f2013-01-10 15:37:33 -0800609 if the instruction_list is empty,
Rich Lane18c9fed2013-05-07 15:22:53 -0700610 append an APPLY ofp.instruction to it
611 Add the action_list to the first write or apply ofp.instruction
Rich Lane629393f2013-01-10 15:37:33 -0800612
Rich Lane18c9fed2013-05-07 15:22:53 -0700613 @param egr_queue if not None, make the output an enqueue ofp.action
Rich Lane629393f2013-01-10 15:37:33 -0800614 @param table_id Table ID for writing a flow_mod
615 """
616
617 if match_fields is None:
618 match_fields = parse.packet_to_flow_match(pkt)
619 parent.assertTrue(match_fields is not None, "Flow match from pkt failed")
Rich Lane18c9fed2013-05-07 15:22:53 -0700620 in_port = ofp.oxm.in_port(ing_port)
Rich Lane629393f2013-01-10 15:37:33 -0800621 match_fields.add(in_port)
Rich Lane18c9fed2013-05-07 15:22:53 -0700622 request = ofp.message.flow_add()
Rich Lane629393f2013-01-10 15:37:33 -0800623 request.match_fields = match_fields
624 request.buffer_id = 0xffffffff
625 request.table_id = table_id
626
627 if check_expire:
628 request.flags |= ofp.OFPFF_SEND_FLOW_REM
629 request.hard_timeout = 1
630
631 if action_list is None:
632 action_list = []
633 if instruction_list is None:
634 instruction_list = []
635
Rich Lane18c9fed2013-05-07 15:22:53 -0700636 # Set up output/enqueue ofp.action if directed
Rich Lane629393f2013-01-10 15:37:33 -0800637 if egr_queue is not None:
638 parent.assertTrue(egr_port is not None, "Egress port not set")
Rich Lane18c9fed2013-05-07 15:22:53 -0700639 act = ofp.action.set_queue()
Rich Lane629393f2013-01-10 15:37:33 -0800640 act.port = egr_port
641 act.queue_id = egr_queue
642 action_list.append(act)
643 elif egr_port is not None:
Rich Lane18c9fed2013-05-07 15:22:53 -0700644 act = ofp.action.output()
Rich Lane629393f2013-01-10 15:37:33 -0800645 act.port = egr_port
646 action_list.append(act)
647
648 inst = None
649 if len(instruction_list) == 0:
Rich Lane18c9fed2013-05-07 15:22:53 -0700650 inst = ofp.instruction.instruction_apply_actions()
Rich Lane629393f2013-01-10 15:37:33 -0800651 instruction_list.append(inst)
652 else:
653 for inst in instruction_list:
654 if (inst.type == ofp.OFPIT_WRITE_ACTIONS or
655 inst.type == ofp.OFPIT_APPLY_ACTIONS):
656 break
657
658 # add all the actions to the last inst
659 for act in action_list:
Rich Lane18c9fed2013-05-07 15:22:53 -0700660 logging.debug("Adding ofp.action " + act.show())
Rich Lane629393f2013-01-10 15:37:33 -0800661 rv = inst.actions.add(act)
Rich Lane18c9fed2013-05-07 15:22:53 -0700662 parent.assertTrue(rv, "Could not add ofp.action" + act.show())
Rich Lane629393f2013-01-10 15:37:33 -0800663 # NOTE that the inst has already been added to the flow_mod
664
665 # add all the instrutions to the flow_mod
666 for i in instruction_list:
Rich Lane18c9fed2013-05-07 15:22:53 -0700667 logging.debug("Adding ofp.instruction " + inst.show())
Rich Lane629393f2013-01-10 15:37:33 -0800668 rv = request.instructions.add(i)
Rich Lane18c9fed2013-05-07 15:22:53 -0700669 parent.assertTrue(rv, "Could not add ofp.instruction " + i.show())
Rich Lane629393f2013-01-10 15:37:33 -0800670
671
Rich Lanef9aa5022013-01-10 16:36:15 -0800672 logging.debug(request.show())
Rich Lane629393f2013-01-10 15:37:33 -0800673 return request
674
675def flow_msg_install(parent, request, clear_table=True):
676 """
Rich Lane18c9fed2013-05-07 15:22:53 -0700677 Install a flow mod ofp.message in the switch
Rich Lane629393f2013-01-10 15:37:33 -0800678
679 @param parent Must implement controller, assertEqual, assertTrue
680 @param request The request, all set to go
681 @param clear_table If true, clear the flow table before installing
682 """
683 if clear_table:
Rich Lanef9aa5022013-01-10 16:36:15 -0800684 logging.debug("Clear flow table")
Rich Lane629393f2013-01-10 15:37:33 -0800685 if request.table_id:
686 table_id = request.table_id
687 else:
688 table_id = 0
689 rc = delete_all_flows_one_table(parent.controller,
Rich Lanef9aa5022013-01-10 16:36:15 -0800690 logging,
Rich Lane629393f2013-01-10 15:37:33 -0800691 table_id)
692 parent.assertEqual(rc, 0, "Failed to delete all flows on table: "
693 + str(table_id))
694 do_barrier(parent.controller)
695
Rich Lanef9aa5022013-01-10 16:36:15 -0800696 logging.debug("Insert flow::\n%s" % request.show())
Rich Lane629393f2013-01-10 15:37:33 -0800697 rv = parent.controller.message_send(request)
698 parent.assertTrue(rv != -1, "Error installing flow mod")
699 do_barrier(parent.controller)
700
701def error_verify(parent, exp_type, exp_code):
702 """
703 Receive an error msg and verify if it is as expected
704
705 @param parent Must implement controller, assertEqual
706 @param exp_type Expected error type
707 @param exp_code Expected error code
708 """
709 (response, raw) = parent.controller.poll(ofp.OFPT_ERROR, 2)
Rich Lane18c9fed2013-05-07 15:22:53 -0700710 parent.assertTrue(response is not None, 'No error ofp.message received')
Rich Lane629393f2013-01-10 15:37:33 -0800711
712 if (exp_type is None) or (exp_code is None):
Rich Lanef9aa5022013-01-10 16:36:15 -0800713 logging.debug("Parametrs are not sufficient")
Rich Lane629393f2013-01-10 15:37:33 -0800714 return
715
716 parent.assertEqual(exp_type, response.type,
Rich Lane18c9fed2013-05-07 15:22:53 -0700717 'Error ofp.message type mismatch: ' +
Rich Lane629393f2013-01-10 15:37:33 -0800718 str(exp_type) + " != " +
719 str(response.type))
720 parent.assertEqual(exp_code, response.code,
Rich Lane18c9fed2013-05-07 15:22:53 -0700721 'Error ofp.message code mismatch: ' +
Rich Lane629393f2013-01-10 15:37:33 -0800722 str(exp_code) + " != " +
723 str(response.code))
724
725def flow_match_test_port_pair(parent, ing_port, egr_port, match=None,
726 wildcards=0, mask=None,
727 dl_vlan=-1, pkt=None, exp_pkt=None,
728 apply_action_list=None, check_expire=False):
729 """
730 Flow match test on single TCP packet
731
732 Run test with packet through switch from ing_port to egr_port
733 See flow_match_test for parameter descriptions
734 """
735
Rich Lanef9aa5022013-01-10 16:36:15 -0800736 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
737 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Rich Lane629393f2013-01-10 15:37:33 -0800738 " expire: " + str(check_expire))
739 if pkt is None:
740 if dl_vlan >= 0:
741 pkt = simple_tcp_packet(vlan_tags=[{'vid': dl_vlan}])
742 else:
743 pkt = simple_tcp_packet()
744
745 match = parse.packet_to_flow_match(pkt)
746 parent.assertTrue(match is not None, "Flow match from pkt failed")
747
748 if mask is not None:
749 match.dl_src_mask = mask['dl_src']
750 match.dl_dst_mask = mask['dl_dst']
751 match.nw_src_mask = mask['nw_src']
752 match.nw_dst_mask = mask['nw_dst']
753 #Set unmatching values on corresponding match fields
754 for i in range(ofp.OFP_ETH_ALEN):
755 match.dl_src[i] = match.dl_src[i] ^ match.dl_src_mask[i]
756 match.dl_dst[i] = match.dl_dst[i] ^ match.dl_dst_mask[i]
757 match.nw_src = match.nw_src ^ match.nw_src_mask
758 match.nw_dst = match.nw_dst ^ match.nw_dst_mask
759
760 request = flow_msg_create(parent, pkt, ing_port=ing_port,
761 match=match,
762 wildcards=wildcards, egr_port=egr_port,
763 action_list=apply_action_list)
764
765 flow_msg_install(parent, request)
766
Rich Lanef9aa5022013-01-10 16:36:15 -0800767 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
Rich Lane629393f2013-01-10 15:37:33 -0800768 parent.dataplane.send(ing_port, str(pkt))
769
770 if exp_pkt is None:
771 exp_pkt = pkt
772 receive_pkt_verify(parent, egr_port, exp_pkt)
773
774 if check_expire:
775 #@todo Not all HW supports both pkt and byte counters
776 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
777def flow_match_test(parent, port_map, match=None, wildcards=0,
778 mask=None, dl_vlan=-1, pkt=None,
779 exp_pkt=None, apply_action_list=None,
780 check_expire=False, max_test=0):
781 """
782 Run flow_match_test_port_pair on all port pairs
783
784 @param max_test If > 0 no more than this number of tests are executed.
785 @param parent Must implement controller, dataplane, assertTrue, assertEqual
786 and logger
787 @param pkt If not None, use this packet for ingress
788 @param match If not None, use this value in flow_mod
789 @param wildcards For flow match entry
790 @param mask DL/NW address bit masks as a dictionary. If set, it is tested
791 against the corresponding match fields with the opposite values
792 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
793 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
794 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -0700795 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -0800796 """
797 of_ports = port_map.keys()
798 of_ports.sort()
799 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
800 test_count = 0
801
802 for ing_idx in range(len(of_ports)):
803 ingress_port = of_ports[ing_idx]
804 for egr_idx in range(len(of_ports)):
805 if egr_idx == ing_idx:
806 continue
807 egress_port = of_ports[egr_idx]
808 flow_match_test_port_pair(parent, ingress_port, egress_port,
809 match=match, wildcards=wildcards,
810 dl_vlan=dl_vlan, mask=mask,
811 pkt=pkt, exp_pkt=exp_pkt,
812 apply_action_list=apply_action_list,
813 check_expire=check_expire)
814 test_count += 1
815 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -0800816 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -0800817 return
818
819def flow_match_test_port_pair_vlan(parent, ing_port, egr_port, wildcards=0,
820 dl_vlan=ofp.OFPVID_NONE, dl_vlan_pcp=0,
821 dl_vlan_type=ETHERTYPE_VLAN,
822 dl_vlan_int=-1, dl_vlan_pcp_int=0,
823 vid_match=ofp.OFPVID_NONE, pcp_match=0,
824 exp_vid=-1, exp_pcp=0,
825 exp_vlan_type=ETHERTYPE_VLAN,
826 match_exp=True,
827 add_tag_exp=False,
828 exp_msg=ofp.OFPT_FLOW_REMOVED,
829 exp_msg_type=0, exp_msg_code=0,
830 pkt=None, exp_pkt=None,
831 action_list=None, check_expire=False):
832 """
833 Flow match test for various vlan matching patterns on single TCP packet
834
835 Run test with packet through switch from ing_port to egr_port
836 See flow_match_test_vlan for parameter descriptions
837 """
Rich Lanef9aa5022013-01-10 16:36:15 -0800838 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
839 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Rich Lane629393f2013-01-10 15:37:33 -0800840 " expire: " + str(check_expire))
841 if pkt is None:
842 if dl_vlan >= 0 and dl_vlan != ofp.OFPVID_NONE:
843 if dl_vlan_int >= 0 and dl_vlan_int != ofp.OFPVID_NONE:
844 pkt = simple_tcp_packet(
845 vlan_tags=[{'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp},
846 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
847 else:
848 pkt = simple_tcp_packet(
849 vlan_tags=[{'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp}])
850 else:
851 pkt = simple_tcp_packet()
852
853 if exp_pkt is None:
854 if exp_vid >= 0 and exp_vid != ofp.OFPVID_NONE:
855 if add_tag_exp:
856 if dl_vlan >= 0 and dl_vlan != ofp.OFPVID_NONE:
857 if dl_vlan_int >= 0 and dl_vlan_int != ofp.OFPVID_NONE:
858 exp_pkt = simple_tcp_packet(
859 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
860 {'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp},
861 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
862 else:
863 exp_pkt = simple_tcp_packet(
864 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
865 {'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp}])
866 else:
867 exp_pkt = simple_tcp_packet(
868 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
869 else:
870 if dl_vlan_int >= 0:
871 exp_pkt = simple_tcp_packet(
872 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
873 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
874
875 else:
876 exp_pkt = simple_tcp_packet(
877 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
878 else:
Rich Lane18c9fed2013-05-07 15:22:53 -0700879 #subtract ofp.action
Rich Lane629393f2013-01-10 15:37:33 -0800880 if dl_vlan_int >= 0:
881 exp_pkt = simple_tcp_packet(
882 vlan_tags=[{'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
883 else:
884 exp_pkt = simple_tcp_packet()
885
886 match = parse.packet_to_flow_match(pkt)
887 parent.assertTrue(match is not None, "Flow match from pkt failed")
888
889 match.dl_vlan = vid_match
890 match.dl_vlan_pcp = pcp_match
891 match.wildcards = wildcards
892
893 request = flow_msg_create(parent, pkt, ing_port=ing_port,
894 wildcards=wildcards,
895 match=match,
896 egr_port=egr_port,
897 action_list=action_list)
898
899 flow_msg_install(parent, request)
900
Rich Lanef9aa5022013-01-10 16:36:15 -0800901 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
902 logging.debug("Sent:" + str(pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800903 parent.dataplane.send(ing_port, str(pkt))
904
905 if match_exp:
906 receive_pkt_verify(parent, egr_port, exp_pkt)
907 if check_expire:
908 #@todo Not all HW supports both pkt and byte counters
909 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
910 else:
911 if exp_msg is ofp.OFPT_FLOW_REMOVED:
912 if check_expire:
913 flow_removed_verify(parent, request, pkt_count=0, byte_count=0)
914 elif exp_msg is ofp.OFPT_ERROR:
915 error_verify(parent, exp_msg_type, exp_msg_code)
916 else:
Rich Lane18c9fed2013-05-07 15:22:53 -0700917 parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
Rich Lane629393f2013-01-10 15:37:33 -0800918
919 (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
920 parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
921
922def flow_match_test_vlan(parent, port_map, wildcards=0,
923 dl_vlan=ofp.OFPVID_NONE, dl_vlan_pcp=0, dl_vlan_type=ETHERTYPE_VLAN,
924 dl_vlan_int=-1, dl_vlan_pcp_int=0,
925 vid_match=ofp.OFPVID_NONE, pcp_match=0,
926 exp_vid=-1, exp_pcp=0,
927 exp_vlan_type=ETHERTYPE_VLAN,
928 match_exp=True,
929 add_tag_exp=False,
930 exp_msg=ofp.OFPT_FLOW_REMOVED,
931 exp_msg_type=0, exp_msg_code=0,
932 pkt=None, exp_pkt=None,
933 action_list=None,
934 check_expire=False,
935 max_test=0):
936 """
937 Run flow_match_test_port_pair on all port pairs
938
939 @param max_test If > 0 no more than this number of tests are executed.
940 @param parent Must implement controller, dataplane, assertTrue, assertEqual
941 and logger
942 @param wildcards For flow match entry
943 @param dl_vlan If not -1, and pkt is not None, create a pkt w/ VLAN tag
944 @param dl_vlan_pcp VLAN PCP associated with dl_vlan
945 @param dl_vlan_type VLAN ether type associated with dl_vlan
946 @param dl_vlan_int If not -1, create pkt w/ Inner Vlan tag
947 @param dl_vlan_pcp_int VLAN PCP associated with dl_vlan_2nd
948 @param vid_match Matching value for VLAN VID field
949 @param pcp_match Matching value for VLAN PCP field
950 @param exp_vid Expected VLAN VID value. If -1, no VLAN expected
951 @param exp_vlan_type Expected VLAN ether type
952 @param exp_pcp Expected VLAN PCP value
953 @param match_exp Set whether packet is expected to receive
954 @param add_tag_exp If True, expected_packet has an additional vlan tag,
955 If not, expected_packet's vlan tag is replaced as specified
Rich Lane18c9fed2013-05-07 15:22:53 -0700956 @param exp_msg Expected ofp.message
957 @param exp_msg_type Expected ofp.message type associated with the ofp.message
958 @param exp_msg_code Expected ofp.message code associated with the msg_type
Rich Lane629393f2013-01-10 15:37:33 -0800959 @param pkt If not None, use this packet for ingress
960 @param exp_pkt If not None, use this as the expected output pkt
961 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -0700962 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -0800963 """
964 of_ports = port_map.keys()
965 of_ports.sort()
966 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
967 test_count = 0
968
969 for ing_idx in range(len(of_ports)):
970 ingress_port = of_ports[ing_idx]
971 for egr_idx in range(len(of_ports)):
972 if egr_idx == ing_idx:
973 continue
974 egress_port = of_ports[egr_idx]
975 flow_match_test_port_pair_vlan(parent, ingress_port, egress_port,
976 wildcards=wildcards,
977 dl_vlan=dl_vlan,
978 dl_vlan_pcp=dl_vlan_pcp,
979 dl_vlan_type=dl_vlan_type,
980 dl_vlan_int=dl_vlan_int,
981 dl_vlan_pcp_int=dl_vlan_pcp_int,
982 vid_match=vid_match,
983 pcp_match=pcp_match,
984 exp_vid=exp_vid,
985 exp_pcp=exp_pcp,
986 exp_vlan_type=exp_vlan_type,
987 exp_msg=exp_msg,
988 exp_msg_type=exp_msg_type,
989 exp_msg_code=exp_msg_code,
990 match_exp=match_exp,
991 add_tag_exp=add_tag_exp,
992 pkt=pkt, exp_pkt=exp_pkt,
993 action_list=action_list,
994 check_expire=check_expire)
995 test_count += 1
996 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -0800997 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -0800998 return
999
1000def test_param_get(config, key, default=None):
1001 """
1002 Return value passed via test-params if present
1003
1004 @param config The configuration structure for OFTest
1005 @param key The lookup key
1006 @param default Default value to use if not found
1007
1008 If the pair 'key=val' appeared in the string passed to --test-params
1009 on the command line, return val (as interpreted by exec). Otherwise
1010 return default value.
1011 """
1012 try:
1013 exec config["test_params"]
1014 except:
1015 return default
1016
1017 s = "val = " + str(key)
1018 try:
1019 val = None
1020 exec s
1021 return val
1022 except:
1023 return default
1024
1025def action_generate(parent, field_to_mod, mod_field_vals):
1026 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001027 Create an ofp.action to modify the field indicated in field_to_mod
Rich Lane629393f2013-01-10 15:37:33 -08001028
1029 @param parent Must implement, assertTrue
1030 @param field_to_mod The field to modify as a string name
1031 @param mod_field_vals Hash of values to use for modified values
1032 """
1033
1034 act = None
1035
1036 if field_to_mod in ['pktlen']:
1037 return None
1038
1039 if field_to_mod == 'dl_dst':
Rich Lane18c9fed2013-05-07 15:22:53 -07001040 act = ofp.action.set_dl_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001041 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
1042 elif field_to_mod == 'dl_src':
Rich Lane18c9fed2013-05-07 15:22:53 -07001043 act = ofp.action.set_dl_src()
Rich Lane629393f2013-01-10 15:37:33 -08001044 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
1045 elif field_to_mod == 'vlan_tags':
1046 if len(mod_field_vals['vlan_tags']):
Rich Lane18c9fed2013-05-07 15:22:53 -07001047 act = ofp.action.pop_vlan()
Rich Lane629393f2013-01-10 15:37:33 -08001048 else:
1049 pass
1050# elif field_to_mod == 'dl_vlan_enable':
1051# if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
Rich Lane18c9fed2013-05-07 15:22:53 -07001052# act = ofp.action.pop_vlan()
Rich Lane629393f2013-01-10 15:37:33 -08001053# # Add VLAN tag is handled by dl_vlan field
1054# # Will return None in this case
1055# elif field_to_mod == 'dl_vlan':
Rich Lane18c9fed2013-05-07 15:22:53 -07001056# act = ofp.action.set_vlan_vid()
Rich Lane629393f2013-01-10 15:37:33 -08001057# act.vlan_vid = mod_field_vals['dl_vlan']
1058# elif field_to_mod == 'dl_vlan_pcp':
Rich Lane18c9fed2013-05-07 15:22:53 -07001059# act = ofp.action.set_vlan_pcp()
Rich Lane629393f2013-01-10 15:37:33 -08001060# act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
1061 elif field_to_mod == 'ip_src':
Rich Lane18c9fed2013-05-07 15:22:53 -07001062 act = ofp.action.set_nw_src()
Rich Lane629393f2013-01-10 15:37:33 -08001063 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
1064 elif field_to_mod == 'ip_dst':
Rich Lane18c9fed2013-05-07 15:22:53 -07001065 act = ofp.action.set_nw_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001066 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
1067 elif field_to_mod == 'ip_tos':
Rich Lane18c9fed2013-05-07 15:22:53 -07001068 act = ofp.action.set_nw_tos()
Rich Lane629393f2013-01-10 15:37:33 -08001069 act.nw_tos = mod_field_vals['ip_tos']
1070 elif field_to_mod == 'tcp_sport':
Rich Lane18c9fed2013-05-07 15:22:53 -07001071 act = ofp.action.set_tp_src()
Rich Lane629393f2013-01-10 15:37:33 -08001072 act.tp_port = mod_field_vals['tcp_sport']
1073 elif field_to_mod == 'tcp_dport':
Rich Lane18c9fed2013-05-07 15:22:53 -07001074 act = ofp.action.set_tp_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001075 act.tp_port = mod_field_vals['tcp_dport']
1076 else:
1077 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
1078
1079 return act
1080
1081def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
1082 mod_fields={}, check_test_params=False):
1083 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001084 Set up the ingress and expected packet and ofp.action list for a test
Rich Lane629393f2013-01-10 15:37:33 -08001085
1086 @param parent Must implement, assertTrue, config hash and logger
1087 @param start_field_values Field values to use for ingress packet (optional)
1088 @param mod_field_values Field values to use for modified packet (optional)
1089 @param mod_fields The list of fields to be modified by the switch in the test.
1090 @params check_test_params If True, will check the parameters vid, add_vlan
1091 and strip_vlan from the command line.
1092
Rich Lane18c9fed2013-05-07 15:22:53 -07001093 Returns a triple: pkt-to-send, expected-pkt, ofp.action-list
Rich Lane629393f2013-01-10 15:37:33 -08001094 """
1095
1096 new_actions = []
1097
1098
1099 base_pkt_params = {}
1100 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
1101 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
1102# base_pkt_params['dl_vlan_enable'] = False
1103# base_pkt_params['dl_vlan'] = 2
1104# base_pkt_params['dl_vlan_pcp'] = 0
1105 base_pkt_params['ip_src'] = '192.168.0.1'
1106 base_pkt_params['ip_dst'] = '192.168.0.2'
1107 base_pkt_params['ip_tos'] = 0
1108 base_pkt_params['tcp_sport'] = 1234
1109 base_pkt_params['tcp_dport'] = 80
1110 for keyname in start_field_vals.keys():
1111 base_pkt_params[keyname] = start_field_vals[keyname]
1112
1113 mod_pkt_params = {}
1114 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
1115 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
1116# mod_pkt_params['dl_vlan_enable'] = False
1117# mod_pkt_params['dl_vlan'] = 3
1118# mod_pkt_params['dl_vlan_pcp'] = 7
1119 mod_pkt_params['ip_src'] = '10.20.30.40'
1120 mod_pkt_params['ip_dst'] = '50.60.70.80'
1121 mod_pkt_params['ip_tos'] = 0xf0
1122 mod_pkt_params['tcp_sport'] = 4321
1123 mod_pkt_params['tcp_dport'] = 8765
1124 for keyname in mod_field_vals.keys():
1125 mod_pkt_params[keyname] = mod_field_vals[keyname]
1126
1127 # Check for test param modifications
1128 strip = False
1129 if check_test_params:
1130 add_vlan = test_param_get(parent.config, 'add_vlan')
1131 strip_vlan = test_param_get(parent.config, 'strip_vlan')
1132 vid = test_param_get(parent.config, 'vid')
1133
1134 if add_vlan and strip_vlan:
1135 parent.assertTrue(0, "Add and strip VLAN both specified")
1136
1137 if vid:
1138 base_pkt_params['dl_vlan_enable'] = True
1139 base_pkt_params['dl_vlan'] = vid
1140 if 'dl_vlan' in mod_fields:
1141 mod_pkt_params['dl_vlan'] = vid + 1
1142
1143 if add_vlan:
1144 base_pkt_params['dl_vlan_enable'] = False
1145 mod_pkt_params['dl_vlan_enable'] = True
1146 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
1147 mod_fields.append('pktlen')
1148 mod_fields.append('dl_vlan_enable')
1149 if 'dl_vlan' not in mod_fields:
1150 mod_fields.append('dl_vlan')
1151 elif strip_vlan:
1152 base_pkt_params['dl_vlan_enable'] = True
1153 mod_pkt_params['dl_vlan_enable'] = False
1154 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
1155 mod_fields.append('dl_vlan_enable')
1156 mod_fields.append('pktlen')
1157
1158 # Build the ingress packet
1159 ingress_pkt = simple_tcp_packet(**base_pkt_params)
1160
1161 # Build the expected packet, modifying the indicated fields
1162 for item in mod_fields:
1163 base_pkt_params[item] = mod_pkt_params[item]
1164 act = action_generate(parent, item, mod_pkt_params)
1165 if act:
1166 new_actions.append(act)
1167
1168 expected_pkt = simple_tcp_packet(**base_pkt_params)
1169
1170 return (ingress_pkt, expected_pkt, new_actions)
1171
1172def wildcard_all_set(match):
1173 match.wildcards = ofp.OFPFW_ALL
1174 match.nw_dst_mask = 0xffffffff
1175 match.nw_src_mask = 0xffffffff
1176 match.dl_dst_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
1177 match.dl_src_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
1178 match.metadata_mask = 0xffffffffffffffff
1179
1180def skip_message_emit(parent, s):
1181 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001182 Print out a 'skipped' ofp.message to stderr
Rich Lane629393f2013-01-10 15:37:33 -08001183
1184 @param s The string to print out to the log file
1185 @param parent Must implement config and logger objects
1186 """
1187 global skipped_test_count
1188
1189 skipped_test_count += 1
Rich Lanef9aa5022013-01-10 16:36:15 -08001190 logging.info("Skipping: " + s)
Dan Talayco2940fdd2013-02-06 15:01:35 -08001191 if parent.config["debug"] < logging.WARNING:
Rich Lane629393f2013-01-10 15:37:33 -08001192 sys.stderr.write("(skipped) ")
1193 else:
1194 sys.stderr.write("(S)")
1195
1196def do_echo_request_reply_test(test,controller):
Rich Lane18c9fed2013-05-07 15:22:53 -07001197 request = ofp.message.echo_request()
Rich Lane629393f2013-01-10 15:37:33 -08001198 response, _ = controller.transact(request)
Rich Lane18c9fed2013-05-07 15:22:53 -07001199 test.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
Rich Lane629393f2013-01-10 15:37:33 -08001200 'response is not echo_reply')
Rich Lane18c9fed2013-05-07 15:22:53 -07001201 test.assertEqual(request.xid, response.xid,
Rich Lane629393f2013-01-10 15:37:33 -08001202 'response xid != request xid')
1203 test.assertEqual(len(response.data), 0, 'response data non-empty')
1204
1205def match_all_generate():
1206 match = ofp.ofp_match()
1207 return match
1208
1209def simple_tcp_packet_w_mpls(
1210 dl_dst='00:01:02:03:04:05',
1211 dl_src='00:06:07:08:09:0a',
1212 mpls_type=0x8847,
1213 mpls_label=-1,
1214 mpls_tc=0,
1215 mpls_ttl=64,
1216 mpls_label_int=-1,
1217 mpls_tc_int=0,
1218 mpls_ttl_int=32,
1219 mpls_label_ext=-1,
1220 mpls_tc_ext=0,
1221 mpls_ttl_ext=128,
1222 ip_src='192.168.0.1',
1223 ip_dst='192.168.0.2',
1224 ip_tos=0,
1225 ip_ttl=192,
1226 tcp_sport=1234,
1227 tcp_dport=80
1228 ):
1229 """
1230 Return a simple dataplane TCP packet w/wo MPLS tags
1231
1232 Supports a few parameters:
1233 @param len Length of packet in bytes w/o CRC
1234 @param dl_dst Destinatino MAC
1235 @param dl_src Source MAC
1236 @param mpls_type MPLS type as ether type
1237 @param mpls_label MPLS LABEL if not -1
1238 @param mpls_tc MPLS TC
1239 @param mpls_ttl MPLS TTL
1240 @param mpls_label_int Inner MPLS LABEL if not -1. The shim will be added
1241 inside of mpls_label shim.
1242 @param mpls_tc_int Inner MPLS TC
1243 @param mpls_ttl_int Inner MPLS TTL
1244 @param mpls_label_ext External MPLS LABEL if not -1. The shim will be
1245 added outside of mpls_label shim
1246 @param mpls_tc_ext External MPLS TC
1247 @param mpls_ttl_ext External MPLS TTL
1248 @param ip_src IP source
1249 @param ip_dst IP destination
1250 @param ip_tos IP ToS
1251 @param tcp_dport TCP destination port
1252 @param ip_sport TCP source port
1253
1254 Generates a simple MPLS/IP/TCP request. Users
1255 shouldn't assume anything about this packet other than that
1256 it is a valid ethernet/IP/TCP frame.
1257 """
1258
1259 mpls_tags = []
1260
1261 if mpls_label_ext >= 0:
1262 mpls_tags.append({'type': mpls_type, 'label': mpls_label_ext, 'tc': mpls_tc_ext, 'ttl': mpls_ttl_ext})
1263
1264 if mpls_label >= 0:
1265 mpls_tags.append({'type': mpls_type, 'label': mpls_label, 'tc': mpls_tc, 'ttl': mpls_ttl})
1266
1267 if mpls_label_int >= 0:
1268 mpls_tags.append({'type': mpls_type, 'label': mpls_label_int, 'tc': mpls_tc_int, 'ttl': mpls_ttl_int})
1269
1270 pkt = simple_tcp_packet(dl_dst=dl_dst,
1271 dl_src=dl_src,
1272 mpls_tags=mpls_tags,
1273 ip_src=ip_src,
1274 ip_dst=ip_dst,
1275 ip_tos=ip_tos,
1276 ip_ttl=ip_ttl,
1277 tcp_sport=tcp_sport,
1278 tcp_dport=tcp_dport)
1279 return pkt
1280
1281def flow_match_test_port_pair_mpls(parent, ing_port, egr_port, wildcards=0,
1282 mpls_type=0x8847,
1283 mpls_label=-1, mpls_tc=0,mpls_ttl=64,
1284 mpls_label_int=-1, mpls_tc_int=0,
1285 mpls_ttl_int=32,
1286 ip_ttl=192,
1287 exp_mpls_type=0x8847,
1288 exp_mpls_label=-1, exp_mpls_tc=0,
1289 exp_mpls_ttl=64,
1290 exp_mpls_ttl_int=32,
1291 exp_ip_ttl=192,
1292 label_match=0, tc_match=0,
1293 dl_type_match=ETHERTYPE_MPLS,
1294 match_exp=True,
1295 add_tag_exp=False,
1296 exp_msg=ofp.OFPT_FLOW_REMOVED,
1297 exp_msg_type=0, exp_msg_code=0,
1298 pkt=None,
1299 exp_pkt=None, action_list=None,
1300 check_expire=False):
1301 """
1302 Flow match test on single packet w/ MPLS tags
1303
1304 Run test with packet through switch from ing_port to egr_port
1305 See flow_match_test for parameter descriptions
1306 """
Rich Lanef9aa5022013-01-10 16:36:15 -08001307 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
1308 logging.debug(" WC: " + hex(wildcards) + " MPLS: " +
Rich Lane629393f2013-01-10 15:37:33 -08001309 str(mpls_label) + " expire: " + str(check_expire))
1310
1311 if pkt is None:
1312
1313 pkt = simple_tcp_packet_w_mpls(mpls_type=mpls_type,
1314 mpls_label=mpls_label,
1315 mpls_tc=mpls_tc,
1316 mpls_ttl=mpls_ttl,
1317 mpls_label_int=mpls_label_int,
1318 mpls_tc_int=mpls_tc_int,
1319 mpls_ttl_int=mpls_ttl_int,
1320 ip_ttl=ip_ttl)
1321
1322 if exp_pkt is None:
1323 if add_tag_exp:
1324 exp_pkt = simple_tcp_packet_w_mpls(
1325 mpls_type=exp_mpls_type,
1326 mpls_label_ext=exp_mpls_label,
1327 mpls_tc_ext=exp_mpls_tc,
1328 mpls_ttl_ext=exp_mpls_ttl,
1329 mpls_label=mpls_label,
1330 mpls_tc=mpls_tc,
1331 mpls_ttl=mpls_ttl,
1332 mpls_label_int=mpls_label_int,
1333 mpls_tc_int=mpls_tc_int,
1334 mpls_ttl_int=exp_mpls_ttl_int,
1335 ip_ttl=exp_ip_ttl)
1336 else:
1337 if (exp_mpls_label < 0) and (mpls_label_int >= 0):
1338 exp_pkt = simple_tcp_packet_w_mpls(
1339 mpls_type=mpls_type,
1340 mpls_label=mpls_label_int,
1341 mpls_tc=mpls_tc_int,
1342 mpls_ttl=exp_mpls_ttl_int,
1343 ip_ttl=exp_ip_ttl)
1344 else:
1345 exp_pkt = simple_tcp_packet_w_mpls(
1346 mpls_type=exp_mpls_type,
1347 mpls_label=exp_mpls_label,
1348 mpls_tc=exp_mpls_tc,
1349 mpls_ttl=exp_mpls_ttl,
1350 mpls_label_int=mpls_label_int,
1351 mpls_tc_int=mpls_tc_int,
1352 mpls_ttl_int=exp_mpls_ttl_int,
1353 ip_ttl=exp_ip_ttl)
1354 wildcards = (ofp.OFPFW_ALL & ~(ofp.OFPFW_DL_TYPE | ofp.OFPFW_MPLS_LABEL | ofp.OFPFW_MPLS_TC)) | wildcards
1355
1356 match = parse.packet_to_flow_match(pkt)
1357 parent.assertTrue(match is not None, "Flow match from pkt failed")
1358
1359 match.mpls_label = label_match
1360 match.mpls_tc = tc_match
1361 match.wildcards = wildcards
1362
1363 match.dl_type = dl_type_match
1364 match.nw_tos = 0
1365 match.nw_proto = 0
1366 match.nw_src = 0
1367 match.nw_src_mask = 0xFFFFFFFF
1368 match.nw_dst = 0
1369 match.nw_dst_mask = 0xFFFFFFFF
1370 match.tp_src = 0
1371 match.tp_dst = 0
1372
1373 request = flow_msg_create(parent, pkt, ing_port=ing_port,
1374 wildcards=wildcards,
1375 match=match,
1376 egr_port=egr_port,
1377 action_list=action_list)
1378
1379 flow_msg_install(parent, request)
1380
Rich Lanef9aa5022013-01-10 16:36:15 -08001381 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
1382 #logging.debug(str(pkt).encode("hex"))
Rich Lane629393f2013-01-10 15:37:33 -08001383 parent.dataplane.send(ing_port, str(pkt))
1384
1385 if match_exp:
1386 receive_pkt_verify(parent, egr_port, exp_pkt)
1387 if check_expire:
1388 #@todo Not all HW supports both pkt and byte counters
1389 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
1390 else:
1391 if exp_msg == ofp.OFPT_FLOW_REMOVED:
1392 if check_expire:
1393 flow_removed_verify(parent, request, pkt_count=0, byte_count=0)
1394 elif exp_msg == ofp.OFPT_ERROR:
1395 error_verify(parent, exp_msg_type, exp_msg_code)
1396 else:
Rich Lane18c9fed2013-05-07 15:22:53 -07001397 parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
Rich Lane629393f2013-01-10 15:37:33 -08001398 (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
1399 parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
1400
1401def flow_match_test_mpls(parent, port_map, wildcards=0,
1402 mpls_type=0x8847,
1403 mpls_label=-1, mpls_tc=0, mpls_ttl=64,
1404 mpls_label_int=-1, mpls_tc_int=0, mpls_ttl_int=32,
1405 ip_ttl = 192,
1406 label_match=0, tc_match=0,
1407 dl_type_match=ETHERTYPE_MPLS,
1408 exp_mpls_type=0x8847,
1409 exp_mpls_label=-1, exp_mpls_tc=0, exp_mpls_ttl=64,
1410 exp_mpls_ttl_int=32,
1411 exp_ip_ttl=192,
1412 match_exp=True,
1413 add_tag_exp=False,
1414 exp_msg=ofp.OFPT_FLOW_REMOVED,
1415 exp_msg_type=0, exp_msg_code=0,
1416 pkt=None,
1417 exp_pkt=None, action_list=None, check_expire=False,
1418 max_test=0):
1419 """
1420 Run flow_match_test_port_pair on all port pairs
1421
1422 @param max_test If > 0 no more than this number of tests are executed.
1423 @param parent Must implement controller, dataplane, assertTrue, assertEqual
1424 and logger
1425 @param wildcards For flow match entry
1426 @param mpls_type MPLS type
1427 @param mpls_label If not -1 create a pkt w/ MPLS tag
1428 @param mpls_tc MPLS TC associated with MPLS label
1429 @param mpls_ttl MPLS TTL associated with MPLS label
1430 @param mpls_label_int If not -1 create a pkt w/ Inner MPLS tag
1431 @param mpls_tc_int MPLS TC associated with Inner MPLS label
1432 @param mpls_ttl_int MPLS TTL associated with Inner MPLS label
1433 @param ip_ttl IP TTL
1434 @param label_match Matching value for MPLS LABEL field
1435 @param tc_match Matching value for MPLS TC field
1436 @param exp_mpls_label Expected MPLS LABEL value. If -1, no MPLS expected
1437 @param exp_mpls_tc Expected MPLS TC value
1438 @param exp_mpls_ttl Expected MPLS TTL value
1439 @param exp_mpls_ttl_int Expected Inner MPLS TTL value
1440 @param ip_ttl Expected IP TTL
1441 @param match_exp Set whether packet is expected to receive
1442 @param add_tag_exp If True, expected_packet has an additional MPLS shim,
1443 If not expected_packet's MPLS shim is replaced as specified
Rich Lane18c9fed2013-05-07 15:22:53 -07001444 @param exp_msg Expected ofp.message
1445 @param exp_msg_type Expected ofp.message type associated with the ofp.message
1446 @param exp_msg_code Expected ofp.message code associated with the msg_type
Rich Lane629393f2013-01-10 15:37:33 -08001447 @param pkt If not None, use this packet for ingress
1448 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
1449 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -07001450 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -08001451 """
1452 of_ports = port_map.keys()
1453 of_ports.sort()
1454 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
1455 test_count = 0
1456
1457 for ing_idx in range(len(of_ports)):
1458 ingress_port = of_ports[ing_idx]
1459 for egr_idx in range(len(of_ports)):
1460 if egr_idx == ing_idx:
1461 continue
1462 egress_port = of_ports[egr_idx]
1463 flow_match_test_port_pair_mpls(parent, ingress_port, egress_port,
1464 wildcards=wildcards,
1465 mpls_type=mpls_type,
1466 mpls_label=mpls_label,
1467 mpls_tc=mpls_tc,
1468 mpls_ttl=mpls_ttl,
1469 mpls_label_int=mpls_label_int,
1470 mpls_tc_int=mpls_tc_int,
1471 mpls_ttl_int=mpls_ttl_int,
1472 ip_ttl=ip_ttl,
1473 label_match=label_match,
1474 tc_match=tc_match,
1475 dl_type_match=dl_type_match,
1476 exp_mpls_type=exp_mpls_type,
1477 exp_mpls_label=exp_mpls_label,
1478 exp_mpls_tc=exp_mpls_tc,
1479 exp_mpls_ttl=exp_mpls_ttl,
1480 exp_mpls_ttl_int=exp_mpls_ttl_int,
1481 exp_ip_ttl=exp_ip_ttl,
1482 match_exp=match_exp,
1483 exp_msg=exp_msg,
1484 exp_msg_type=exp_msg_type,
1485 exp_msg_code=exp_msg_code,
1486 add_tag_exp=add_tag_exp,
1487 pkt=pkt, exp_pkt=exp_pkt,
1488 action_list=action_list,
1489 check_expire=check_expire)
1490 test_count += 1
1491 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -08001492 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -08001493 return
1494
1495def flow_stats_get(parent, match_fields = None):
1496 """ Get the flow_stats from the switch
1497 Test the response to make sure it's really a flow_stats object
1498 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001499 request = ofp.message.flow_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -08001500 request.out_port = ofp.OFPP_ANY
1501 request.out_group = ofp.OFPG_ANY
1502 request.table_id = 0xff
1503 if match_fields != None:
1504 request.match_fields = match_fields
1505 response, _ = parent.controller.transact(request, timeout=2)
1506 parent.assertTrue(response is not None, "Did not get response")
Rich Lane18c9fed2013-05-07 15:22:53 -07001507 parent.assertTrue(isinstance(response,ofp.message.flow_stats_reply),
Rich Lane629393f2013-01-10 15:37:33 -08001508 "Expected a flow_stats_reply, but didn't get it")
1509 return response