blob: d6dbdf0a4d023f32afdfa7bbe178fe15881c5f53 [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 Lane48e447e2013-05-07 15:23:35 -0700621 match_fields.oxm_list.append(in_port)
Rich Lane18c9fed2013-05-07 15:22:53 -0700622 request = ofp.message.flow_add()
Rich Lane48e447e2013-05-07 15:23:35 -0700623 request.match= match_fields
Rich Lane629393f2013-01-10 15:37:33 -0800624 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 Lane48e447e2013-05-07 15:23:35 -0700650 inst = ofp.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
Rich Lane48e447e2013-05-07 15:23:35 -0700658
Rich Lane629393f2013-01-10 15:37:33 -0800659 # add all the actions to the last inst
Rich Lane48e447e2013-05-07 15:23:35 -0700660 inst.actions += action_list
Rich Lane629393f2013-01-10 15:37:33 -0800661
662 # add all the instrutions to the flow_mod
Rich Lane48e447e2013-05-07 15:23:35 -0700663 request.instructions += instruction_list
Rich Lane629393f2013-01-10 15:37:33 -0800664
Rich Lanef9aa5022013-01-10 16:36:15 -0800665 logging.debug(request.show())
Rich Lane629393f2013-01-10 15:37:33 -0800666 return request
667
668def flow_msg_install(parent, request, clear_table=True):
669 """
Rich Lane18c9fed2013-05-07 15:22:53 -0700670 Install a flow mod ofp.message in the switch
Rich Lane629393f2013-01-10 15:37:33 -0800671
672 @param parent Must implement controller, assertEqual, assertTrue
673 @param request The request, all set to go
674 @param clear_table If true, clear the flow table before installing
675 """
676 if clear_table:
Rich Lanef9aa5022013-01-10 16:36:15 -0800677 logging.debug("Clear flow table")
Rich Lane629393f2013-01-10 15:37:33 -0800678 if request.table_id:
679 table_id = request.table_id
680 else:
681 table_id = 0
682 rc = delete_all_flows_one_table(parent.controller,
Rich Lanef9aa5022013-01-10 16:36:15 -0800683 logging,
Rich Lane629393f2013-01-10 15:37:33 -0800684 table_id)
685 parent.assertEqual(rc, 0, "Failed to delete all flows on table: "
686 + str(table_id))
687 do_barrier(parent.controller)
688
Rich Lanef9aa5022013-01-10 16:36:15 -0800689 logging.debug("Insert flow::\n%s" % request.show())
Rich Lane629393f2013-01-10 15:37:33 -0800690 rv = parent.controller.message_send(request)
691 parent.assertTrue(rv != -1, "Error installing flow mod")
692 do_barrier(parent.controller)
693
694def error_verify(parent, exp_type, exp_code):
695 """
696 Receive an error msg and verify if it is as expected
697
698 @param parent Must implement controller, assertEqual
699 @param exp_type Expected error type
700 @param exp_code Expected error code
701 """
702 (response, raw) = parent.controller.poll(ofp.OFPT_ERROR, 2)
Rich Lane18c9fed2013-05-07 15:22:53 -0700703 parent.assertTrue(response is not None, 'No error ofp.message received')
Rich Lane629393f2013-01-10 15:37:33 -0800704
705 if (exp_type is None) or (exp_code is None):
Rich Lanef9aa5022013-01-10 16:36:15 -0800706 logging.debug("Parametrs are not sufficient")
Rich Lane629393f2013-01-10 15:37:33 -0800707 return
708
709 parent.assertEqual(exp_type, response.type,
Rich Lane18c9fed2013-05-07 15:22:53 -0700710 'Error ofp.message type mismatch: ' +
Rich Lane629393f2013-01-10 15:37:33 -0800711 str(exp_type) + " != " +
712 str(response.type))
713 parent.assertEqual(exp_code, response.code,
Rich Lane18c9fed2013-05-07 15:22:53 -0700714 'Error ofp.message code mismatch: ' +
Rich Lane629393f2013-01-10 15:37:33 -0800715 str(exp_code) + " != " +
716 str(response.code))
717
718def flow_match_test_port_pair(parent, ing_port, egr_port, match=None,
719 wildcards=0, mask=None,
720 dl_vlan=-1, pkt=None, exp_pkt=None,
721 apply_action_list=None, check_expire=False):
722 """
723 Flow match test on single TCP packet
724
725 Run test with packet through switch from ing_port to egr_port
726 See flow_match_test for parameter descriptions
727 """
728
Rich Lanef9aa5022013-01-10 16:36:15 -0800729 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
730 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Rich Lane629393f2013-01-10 15:37:33 -0800731 " expire: " + str(check_expire))
732 if pkt is None:
733 if dl_vlan >= 0:
734 pkt = simple_tcp_packet(vlan_tags=[{'vid': dl_vlan}])
735 else:
736 pkt = simple_tcp_packet()
737
738 match = parse.packet_to_flow_match(pkt)
739 parent.assertTrue(match is not None, "Flow match from pkt failed")
740
741 if mask is not None:
742 match.dl_src_mask = mask['dl_src']
743 match.dl_dst_mask = mask['dl_dst']
744 match.nw_src_mask = mask['nw_src']
745 match.nw_dst_mask = mask['nw_dst']
746 #Set unmatching values on corresponding match fields
747 for i in range(ofp.OFP_ETH_ALEN):
748 match.dl_src[i] = match.dl_src[i] ^ match.dl_src_mask[i]
749 match.dl_dst[i] = match.dl_dst[i] ^ match.dl_dst_mask[i]
750 match.nw_src = match.nw_src ^ match.nw_src_mask
751 match.nw_dst = match.nw_dst ^ match.nw_dst_mask
752
753 request = flow_msg_create(parent, pkt, ing_port=ing_port,
754 match=match,
755 wildcards=wildcards, egr_port=egr_port,
756 action_list=apply_action_list)
757
758 flow_msg_install(parent, request)
759
Rich Lanef9aa5022013-01-10 16:36:15 -0800760 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
Rich Lane629393f2013-01-10 15:37:33 -0800761 parent.dataplane.send(ing_port, str(pkt))
762
763 if exp_pkt is None:
764 exp_pkt = pkt
765 receive_pkt_verify(parent, egr_port, exp_pkt)
766
767 if check_expire:
768 #@todo Not all HW supports both pkt and byte counters
769 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
770def flow_match_test(parent, port_map, match=None, wildcards=0,
771 mask=None, dl_vlan=-1, pkt=None,
772 exp_pkt=None, apply_action_list=None,
773 check_expire=False, max_test=0):
774 """
775 Run flow_match_test_port_pair on all port pairs
776
777 @param max_test If > 0 no more than this number of tests are executed.
778 @param parent Must implement controller, dataplane, assertTrue, assertEqual
779 and logger
780 @param pkt If not None, use this packet for ingress
781 @param match If not None, use this value in flow_mod
782 @param wildcards For flow match entry
783 @param mask DL/NW address bit masks as a dictionary. If set, it is tested
784 against the corresponding match fields with the opposite values
785 @param dl_vlan If not -1, and pkt is None, create a pkt w/ VLAN tag
786 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
787 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -0700788 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -0800789 """
790 of_ports = port_map.keys()
791 of_ports.sort()
792 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
793 test_count = 0
794
795 for ing_idx in range(len(of_ports)):
796 ingress_port = of_ports[ing_idx]
797 for egr_idx in range(len(of_ports)):
798 if egr_idx == ing_idx:
799 continue
800 egress_port = of_ports[egr_idx]
801 flow_match_test_port_pair(parent, ingress_port, egress_port,
802 match=match, wildcards=wildcards,
803 dl_vlan=dl_vlan, mask=mask,
804 pkt=pkt, exp_pkt=exp_pkt,
805 apply_action_list=apply_action_list,
806 check_expire=check_expire)
807 test_count += 1
808 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -0800809 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -0800810 return
811
812def flow_match_test_port_pair_vlan(parent, ing_port, egr_port, wildcards=0,
813 dl_vlan=ofp.OFPVID_NONE, dl_vlan_pcp=0,
814 dl_vlan_type=ETHERTYPE_VLAN,
815 dl_vlan_int=-1, dl_vlan_pcp_int=0,
816 vid_match=ofp.OFPVID_NONE, pcp_match=0,
817 exp_vid=-1, exp_pcp=0,
818 exp_vlan_type=ETHERTYPE_VLAN,
819 match_exp=True,
820 add_tag_exp=False,
821 exp_msg=ofp.OFPT_FLOW_REMOVED,
822 exp_msg_type=0, exp_msg_code=0,
823 pkt=None, exp_pkt=None,
824 action_list=None, check_expire=False):
825 """
826 Flow match test for various vlan matching patterns on single TCP packet
827
828 Run test with packet through switch from ing_port to egr_port
829 See flow_match_test_vlan for parameter descriptions
830 """
Rich Lanef9aa5022013-01-10 16:36:15 -0800831 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
832 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(dl_vlan) +
Rich Lane629393f2013-01-10 15:37:33 -0800833 " expire: " + str(check_expire))
834 if pkt is None:
835 if dl_vlan >= 0 and dl_vlan != ofp.OFPVID_NONE:
836 if dl_vlan_int >= 0 and dl_vlan_int != ofp.OFPVID_NONE:
837 pkt = simple_tcp_packet(
838 vlan_tags=[{'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp},
839 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
840 else:
841 pkt = simple_tcp_packet(
842 vlan_tags=[{'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp}])
843 else:
844 pkt = simple_tcp_packet()
845
846 if exp_pkt is None:
847 if exp_vid >= 0 and exp_vid != ofp.OFPVID_NONE:
848 if add_tag_exp:
849 if dl_vlan >= 0 and dl_vlan != ofp.OFPVID_NONE:
850 if dl_vlan_int >= 0 and dl_vlan_int != ofp.OFPVID_NONE:
851 exp_pkt = simple_tcp_packet(
852 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
853 {'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp},
854 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
855 else:
856 exp_pkt = simple_tcp_packet(
857 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
858 {'type': dl_vlan_type, 'vid': dl_vlan, 'pcp': dl_vlan_pcp}])
859 else:
860 exp_pkt = simple_tcp_packet(
861 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
862 else:
863 if dl_vlan_int >= 0:
864 exp_pkt = simple_tcp_packet(
865 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp},
866 {'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
867
868 else:
869 exp_pkt = simple_tcp_packet(
870 vlan_tags=[{'type': exp_vlan_type, 'vid': exp_vid, 'pcp': exp_pcp}])
871 else:
Rich Lane18c9fed2013-05-07 15:22:53 -0700872 #subtract ofp.action
Rich Lane629393f2013-01-10 15:37:33 -0800873 if dl_vlan_int >= 0:
874 exp_pkt = simple_tcp_packet(
875 vlan_tags=[{'vid': dl_vlan_int, 'pcp': dl_vlan_pcp_int}])
876 else:
877 exp_pkt = simple_tcp_packet()
878
879 match = parse.packet_to_flow_match(pkt)
880 parent.assertTrue(match is not None, "Flow match from pkt failed")
881
882 match.dl_vlan = vid_match
883 match.dl_vlan_pcp = pcp_match
884 match.wildcards = wildcards
885
886 request = flow_msg_create(parent, pkt, ing_port=ing_port,
887 wildcards=wildcards,
888 match=match,
889 egr_port=egr_port,
890 action_list=action_list)
891
892 flow_msg_install(parent, request)
893
Rich Lanef9aa5022013-01-10 16:36:15 -0800894 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
895 logging.debug("Sent:" + str(pkt).encode('hex'))
Rich Lane629393f2013-01-10 15:37:33 -0800896 parent.dataplane.send(ing_port, str(pkt))
897
898 if match_exp:
899 receive_pkt_verify(parent, egr_port, exp_pkt)
900 if check_expire:
901 #@todo Not all HW supports both pkt and byte counters
902 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
903 else:
904 if exp_msg is ofp.OFPT_FLOW_REMOVED:
905 if check_expire:
906 flow_removed_verify(parent, request, pkt_count=0, byte_count=0)
907 elif exp_msg is ofp.OFPT_ERROR:
908 error_verify(parent, exp_msg_type, exp_msg_code)
909 else:
Rich Lane18c9fed2013-05-07 15:22:53 -0700910 parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
Rich Lane629393f2013-01-10 15:37:33 -0800911
912 (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
913 parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
914
915def flow_match_test_vlan(parent, port_map, wildcards=0,
916 dl_vlan=ofp.OFPVID_NONE, dl_vlan_pcp=0, dl_vlan_type=ETHERTYPE_VLAN,
917 dl_vlan_int=-1, dl_vlan_pcp_int=0,
918 vid_match=ofp.OFPVID_NONE, pcp_match=0,
919 exp_vid=-1, exp_pcp=0,
920 exp_vlan_type=ETHERTYPE_VLAN,
921 match_exp=True,
922 add_tag_exp=False,
923 exp_msg=ofp.OFPT_FLOW_REMOVED,
924 exp_msg_type=0, exp_msg_code=0,
925 pkt=None, exp_pkt=None,
926 action_list=None,
927 check_expire=False,
928 max_test=0):
929 """
930 Run flow_match_test_port_pair on all port pairs
931
932 @param max_test If > 0 no more than this number of tests are executed.
933 @param parent Must implement controller, dataplane, assertTrue, assertEqual
934 and logger
935 @param wildcards For flow match entry
936 @param dl_vlan If not -1, and pkt is not None, create a pkt w/ VLAN tag
937 @param dl_vlan_pcp VLAN PCP associated with dl_vlan
938 @param dl_vlan_type VLAN ether type associated with dl_vlan
939 @param dl_vlan_int If not -1, create pkt w/ Inner Vlan tag
940 @param dl_vlan_pcp_int VLAN PCP associated with dl_vlan_2nd
941 @param vid_match Matching value for VLAN VID field
942 @param pcp_match Matching value for VLAN PCP field
943 @param exp_vid Expected VLAN VID value. If -1, no VLAN expected
944 @param exp_vlan_type Expected VLAN ether type
945 @param exp_pcp Expected VLAN PCP value
946 @param match_exp Set whether packet is expected to receive
947 @param add_tag_exp If True, expected_packet has an additional vlan tag,
948 If not, expected_packet's vlan tag is replaced as specified
Rich Lane18c9fed2013-05-07 15:22:53 -0700949 @param exp_msg Expected ofp.message
950 @param exp_msg_type Expected ofp.message type associated with the ofp.message
951 @param exp_msg_code Expected ofp.message code associated with the msg_type
Rich Lane629393f2013-01-10 15:37:33 -0800952 @param pkt If not None, use this packet for ingress
953 @param exp_pkt If not None, use this as the expected output pkt
954 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -0700955 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -0800956 """
957 of_ports = port_map.keys()
958 of_ports.sort()
959 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
960 test_count = 0
961
962 for ing_idx in range(len(of_ports)):
963 ingress_port = of_ports[ing_idx]
964 for egr_idx in range(len(of_ports)):
965 if egr_idx == ing_idx:
966 continue
967 egress_port = of_ports[egr_idx]
968 flow_match_test_port_pair_vlan(parent, ingress_port, egress_port,
969 wildcards=wildcards,
970 dl_vlan=dl_vlan,
971 dl_vlan_pcp=dl_vlan_pcp,
972 dl_vlan_type=dl_vlan_type,
973 dl_vlan_int=dl_vlan_int,
974 dl_vlan_pcp_int=dl_vlan_pcp_int,
975 vid_match=vid_match,
976 pcp_match=pcp_match,
977 exp_vid=exp_vid,
978 exp_pcp=exp_pcp,
979 exp_vlan_type=exp_vlan_type,
980 exp_msg=exp_msg,
981 exp_msg_type=exp_msg_type,
982 exp_msg_code=exp_msg_code,
983 match_exp=match_exp,
984 add_tag_exp=add_tag_exp,
985 pkt=pkt, exp_pkt=exp_pkt,
986 action_list=action_list,
987 check_expire=check_expire)
988 test_count += 1
989 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -0800990 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -0800991 return
992
993def test_param_get(config, key, default=None):
994 """
995 Return value passed via test-params if present
996
997 @param config The configuration structure for OFTest
998 @param key The lookup key
999 @param default Default value to use if not found
1000
1001 If the pair 'key=val' appeared in the string passed to --test-params
1002 on the command line, return val (as interpreted by exec). Otherwise
1003 return default value.
1004 """
1005 try:
1006 exec config["test_params"]
1007 except:
1008 return default
1009
1010 s = "val = " + str(key)
1011 try:
1012 val = None
1013 exec s
1014 return val
1015 except:
1016 return default
1017
1018def action_generate(parent, field_to_mod, mod_field_vals):
1019 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001020 Create an ofp.action to modify the field indicated in field_to_mod
Rich Lane629393f2013-01-10 15:37:33 -08001021
1022 @param parent Must implement, assertTrue
1023 @param field_to_mod The field to modify as a string name
1024 @param mod_field_vals Hash of values to use for modified values
1025 """
1026
1027 act = None
1028
1029 if field_to_mod in ['pktlen']:
1030 return None
1031
1032 if field_to_mod == 'dl_dst':
Rich Lane18c9fed2013-05-07 15:22:53 -07001033 act = ofp.action.set_dl_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001034 act.dl_addr = parse.parse_mac(mod_field_vals['dl_dst'])
1035 elif field_to_mod == 'dl_src':
Rich Lane18c9fed2013-05-07 15:22:53 -07001036 act = ofp.action.set_dl_src()
Rich Lane629393f2013-01-10 15:37:33 -08001037 act.dl_addr = parse.parse_mac(mod_field_vals['dl_src'])
1038 elif field_to_mod == 'vlan_tags':
1039 if len(mod_field_vals['vlan_tags']):
Rich Lane18c9fed2013-05-07 15:22:53 -07001040 act = ofp.action.pop_vlan()
Rich Lane629393f2013-01-10 15:37:33 -08001041 else:
1042 pass
1043# elif field_to_mod == 'dl_vlan_enable':
1044# if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
Rich Lane18c9fed2013-05-07 15:22:53 -07001045# act = ofp.action.pop_vlan()
Rich Lane629393f2013-01-10 15:37:33 -08001046# # Add VLAN tag is handled by dl_vlan field
1047# # Will return None in this case
1048# elif field_to_mod == 'dl_vlan':
Rich Lane18c9fed2013-05-07 15:22:53 -07001049# act = ofp.action.set_vlan_vid()
Rich Lane629393f2013-01-10 15:37:33 -08001050# act.vlan_vid = mod_field_vals['dl_vlan']
1051# elif field_to_mod == 'dl_vlan_pcp':
Rich Lane18c9fed2013-05-07 15:22:53 -07001052# act = ofp.action.set_vlan_pcp()
Rich Lane629393f2013-01-10 15:37:33 -08001053# act.vlan_pcp = mod_field_vals['dl_vlan_pcp']
1054 elif field_to_mod == 'ip_src':
Rich Lane18c9fed2013-05-07 15:22:53 -07001055 act = ofp.action.set_nw_src()
Rich Lane629393f2013-01-10 15:37:33 -08001056 act.nw_addr = parse.parse_ip(mod_field_vals['ip_src'])
1057 elif field_to_mod == 'ip_dst':
Rich Lane18c9fed2013-05-07 15:22:53 -07001058 act = ofp.action.set_nw_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001059 act.nw_addr = parse.parse_ip(mod_field_vals['ip_dst'])
1060 elif field_to_mod == 'ip_tos':
Rich Lane18c9fed2013-05-07 15:22:53 -07001061 act = ofp.action.set_nw_tos()
Rich Lane629393f2013-01-10 15:37:33 -08001062 act.nw_tos = mod_field_vals['ip_tos']
1063 elif field_to_mod == 'tcp_sport':
Rich Lane18c9fed2013-05-07 15:22:53 -07001064 act = ofp.action.set_tp_src()
Rich Lane629393f2013-01-10 15:37:33 -08001065 act.tp_port = mod_field_vals['tcp_sport']
1066 elif field_to_mod == 'tcp_dport':
Rich Lane18c9fed2013-05-07 15:22:53 -07001067 act = ofp.action.set_tp_dst()
Rich Lane629393f2013-01-10 15:37:33 -08001068 act.tp_port = mod_field_vals['tcp_dport']
1069 else:
1070 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
1071
1072 return act
1073
1074def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
1075 mod_fields={}, check_test_params=False):
1076 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001077 Set up the ingress and expected packet and ofp.action list for a test
Rich Lane629393f2013-01-10 15:37:33 -08001078
1079 @param parent Must implement, assertTrue, config hash and logger
1080 @param start_field_values Field values to use for ingress packet (optional)
1081 @param mod_field_values Field values to use for modified packet (optional)
1082 @param mod_fields The list of fields to be modified by the switch in the test.
1083 @params check_test_params If True, will check the parameters vid, add_vlan
1084 and strip_vlan from the command line.
1085
Rich Lane18c9fed2013-05-07 15:22:53 -07001086 Returns a triple: pkt-to-send, expected-pkt, ofp.action-list
Rich Lane629393f2013-01-10 15:37:33 -08001087 """
1088
1089 new_actions = []
1090
1091
1092 base_pkt_params = {}
1093 base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'
1094 base_pkt_params['dl_src'] = '00:23:45:67:89:AB'
1095# base_pkt_params['dl_vlan_enable'] = False
1096# base_pkt_params['dl_vlan'] = 2
1097# base_pkt_params['dl_vlan_pcp'] = 0
1098 base_pkt_params['ip_src'] = '192.168.0.1'
1099 base_pkt_params['ip_dst'] = '192.168.0.2'
1100 base_pkt_params['ip_tos'] = 0
1101 base_pkt_params['tcp_sport'] = 1234
1102 base_pkt_params['tcp_dport'] = 80
1103 for keyname in start_field_vals.keys():
1104 base_pkt_params[keyname] = start_field_vals[keyname]
1105
1106 mod_pkt_params = {}
1107 mod_pkt_params['dl_dst'] = '00:21:0F:ED:CB:A9'
1108 mod_pkt_params['dl_src'] = '00:ED:CB:A9:87:65'
1109# mod_pkt_params['dl_vlan_enable'] = False
1110# mod_pkt_params['dl_vlan'] = 3
1111# mod_pkt_params['dl_vlan_pcp'] = 7
1112 mod_pkt_params['ip_src'] = '10.20.30.40'
1113 mod_pkt_params['ip_dst'] = '50.60.70.80'
1114 mod_pkt_params['ip_tos'] = 0xf0
1115 mod_pkt_params['tcp_sport'] = 4321
1116 mod_pkt_params['tcp_dport'] = 8765
1117 for keyname in mod_field_vals.keys():
1118 mod_pkt_params[keyname] = mod_field_vals[keyname]
1119
1120 # Check for test param modifications
1121 strip = False
1122 if check_test_params:
1123 add_vlan = test_param_get(parent.config, 'add_vlan')
1124 strip_vlan = test_param_get(parent.config, 'strip_vlan')
1125 vid = test_param_get(parent.config, 'vid')
1126
1127 if add_vlan and strip_vlan:
1128 parent.assertTrue(0, "Add and strip VLAN both specified")
1129
1130 if vid:
1131 base_pkt_params['dl_vlan_enable'] = True
1132 base_pkt_params['dl_vlan'] = vid
1133 if 'dl_vlan' in mod_fields:
1134 mod_pkt_params['dl_vlan'] = vid + 1
1135
1136 if add_vlan:
1137 base_pkt_params['dl_vlan_enable'] = False
1138 mod_pkt_params['dl_vlan_enable'] = True
1139 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
1140 mod_fields.append('pktlen')
1141 mod_fields.append('dl_vlan_enable')
1142 if 'dl_vlan' not in mod_fields:
1143 mod_fields.append('dl_vlan')
1144 elif strip_vlan:
1145 base_pkt_params['dl_vlan_enable'] = True
1146 mod_pkt_params['dl_vlan_enable'] = False
1147 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
1148 mod_fields.append('dl_vlan_enable')
1149 mod_fields.append('pktlen')
1150
1151 # Build the ingress packet
1152 ingress_pkt = simple_tcp_packet(**base_pkt_params)
1153
1154 # Build the expected packet, modifying the indicated fields
1155 for item in mod_fields:
1156 base_pkt_params[item] = mod_pkt_params[item]
1157 act = action_generate(parent, item, mod_pkt_params)
1158 if act:
1159 new_actions.append(act)
1160
1161 expected_pkt = simple_tcp_packet(**base_pkt_params)
1162
1163 return (ingress_pkt, expected_pkt, new_actions)
1164
1165def wildcard_all_set(match):
1166 match.wildcards = ofp.OFPFW_ALL
1167 match.nw_dst_mask = 0xffffffff
1168 match.nw_src_mask = 0xffffffff
1169 match.dl_dst_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
1170 match.dl_src_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
1171 match.metadata_mask = 0xffffffffffffffff
1172
1173def skip_message_emit(parent, s):
1174 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001175 Print out a 'skipped' ofp.message to stderr
Rich Lane629393f2013-01-10 15:37:33 -08001176
1177 @param s The string to print out to the log file
1178 @param parent Must implement config and logger objects
1179 """
1180 global skipped_test_count
1181
1182 skipped_test_count += 1
Rich Lanef9aa5022013-01-10 16:36:15 -08001183 logging.info("Skipping: " + s)
Dan Talayco2940fdd2013-02-06 15:01:35 -08001184 if parent.config["debug"] < logging.WARNING:
Rich Lane629393f2013-01-10 15:37:33 -08001185 sys.stderr.write("(skipped) ")
1186 else:
1187 sys.stderr.write("(S)")
1188
1189def do_echo_request_reply_test(test,controller):
Rich Lane18c9fed2013-05-07 15:22:53 -07001190 request = ofp.message.echo_request()
Rich Lane629393f2013-01-10 15:37:33 -08001191 response, _ = controller.transact(request)
Rich Lane18c9fed2013-05-07 15:22:53 -07001192 test.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
Rich Lane629393f2013-01-10 15:37:33 -08001193 'response is not echo_reply')
Rich Lane18c9fed2013-05-07 15:22:53 -07001194 test.assertEqual(request.xid, response.xid,
Rich Lane629393f2013-01-10 15:37:33 -08001195 'response xid != request xid')
1196 test.assertEqual(len(response.data), 0, 'response data non-empty')
1197
1198def match_all_generate():
1199 match = ofp.ofp_match()
1200 return match
1201
1202def simple_tcp_packet_w_mpls(
1203 dl_dst='00:01:02:03:04:05',
1204 dl_src='00:06:07:08:09:0a',
1205 mpls_type=0x8847,
1206 mpls_label=-1,
1207 mpls_tc=0,
1208 mpls_ttl=64,
1209 mpls_label_int=-1,
1210 mpls_tc_int=0,
1211 mpls_ttl_int=32,
1212 mpls_label_ext=-1,
1213 mpls_tc_ext=0,
1214 mpls_ttl_ext=128,
1215 ip_src='192.168.0.1',
1216 ip_dst='192.168.0.2',
1217 ip_tos=0,
1218 ip_ttl=192,
1219 tcp_sport=1234,
1220 tcp_dport=80
1221 ):
1222 """
1223 Return a simple dataplane TCP packet w/wo MPLS tags
1224
1225 Supports a few parameters:
1226 @param len Length of packet in bytes w/o CRC
1227 @param dl_dst Destinatino MAC
1228 @param dl_src Source MAC
1229 @param mpls_type MPLS type as ether type
1230 @param mpls_label MPLS LABEL if not -1
1231 @param mpls_tc MPLS TC
1232 @param mpls_ttl MPLS TTL
1233 @param mpls_label_int Inner MPLS LABEL if not -1. The shim will be added
1234 inside of mpls_label shim.
1235 @param mpls_tc_int Inner MPLS TC
1236 @param mpls_ttl_int Inner MPLS TTL
1237 @param mpls_label_ext External MPLS LABEL if not -1. The shim will be
1238 added outside of mpls_label shim
1239 @param mpls_tc_ext External MPLS TC
1240 @param mpls_ttl_ext External MPLS TTL
1241 @param ip_src IP source
1242 @param ip_dst IP destination
1243 @param ip_tos IP ToS
1244 @param tcp_dport TCP destination port
1245 @param ip_sport TCP source port
1246
1247 Generates a simple MPLS/IP/TCP request. Users
1248 shouldn't assume anything about this packet other than that
1249 it is a valid ethernet/IP/TCP frame.
1250 """
1251
1252 mpls_tags = []
1253
1254 if mpls_label_ext >= 0:
1255 mpls_tags.append({'type': mpls_type, 'label': mpls_label_ext, 'tc': mpls_tc_ext, 'ttl': mpls_ttl_ext})
1256
1257 if mpls_label >= 0:
1258 mpls_tags.append({'type': mpls_type, 'label': mpls_label, 'tc': mpls_tc, 'ttl': mpls_ttl})
1259
1260 if mpls_label_int >= 0:
1261 mpls_tags.append({'type': mpls_type, 'label': mpls_label_int, 'tc': mpls_tc_int, 'ttl': mpls_ttl_int})
1262
1263 pkt = simple_tcp_packet(dl_dst=dl_dst,
1264 dl_src=dl_src,
1265 mpls_tags=mpls_tags,
1266 ip_src=ip_src,
1267 ip_dst=ip_dst,
1268 ip_tos=ip_tos,
1269 ip_ttl=ip_ttl,
1270 tcp_sport=tcp_sport,
1271 tcp_dport=tcp_dport)
1272 return pkt
1273
1274def flow_match_test_port_pair_mpls(parent, ing_port, egr_port, wildcards=0,
1275 mpls_type=0x8847,
1276 mpls_label=-1, mpls_tc=0,mpls_ttl=64,
1277 mpls_label_int=-1, mpls_tc_int=0,
1278 mpls_ttl_int=32,
1279 ip_ttl=192,
1280 exp_mpls_type=0x8847,
1281 exp_mpls_label=-1, exp_mpls_tc=0,
1282 exp_mpls_ttl=64,
1283 exp_mpls_ttl_int=32,
1284 exp_ip_ttl=192,
1285 label_match=0, tc_match=0,
1286 dl_type_match=ETHERTYPE_MPLS,
1287 match_exp=True,
1288 add_tag_exp=False,
1289 exp_msg=ofp.OFPT_FLOW_REMOVED,
1290 exp_msg_type=0, exp_msg_code=0,
1291 pkt=None,
1292 exp_pkt=None, action_list=None,
1293 check_expire=False):
1294 """
1295 Flow match test on single packet w/ MPLS tags
1296
1297 Run test with packet through switch from ing_port to egr_port
1298 See flow_match_test for parameter descriptions
1299 """
Rich Lanef9aa5022013-01-10 16:36:15 -08001300 logging.info("Pkt match test: " + str(ing_port) + " to " + str(egr_port))
1301 logging.debug(" WC: " + hex(wildcards) + " MPLS: " +
Rich Lane629393f2013-01-10 15:37:33 -08001302 str(mpls_label) + " expire: " + str(check_expire))
1303
1304 if pkt is None:
1305
1306 pkt = simple_tcp_packet_w_mpls(mpls_type=mpls_type,
1307 mpls_label=mpls_label,
1308 mpls_tc=mpls_tc,
1309 mpls_ttl=mpls_ttl,
1310 mpls_label_int=mpls_label_int,
1311 mpls_tc_int=mpls_tc_int,
1312 mpls_ttl_int=mpls_ttl_int,
1313 ip_ttl=ip_ttl)
1314
1315 if exp_pkt is None:
1316 if add_tag_exp:
1317 exp_pkt = simple_tcp_packet_w_mpls(
1318 mpls_type=exp_mpls_type,
1319 mpls_label_ext=exp_mpls_label,
1320 mpls_tc_ext=exp_mpls_tc,
1321 mpls_ttl_ext=exp_mpls_ttl,
1322 mpls_label=mpls_label,
1323 mpls_tc=mpls_tc,
1324 mpls_ttl=mpls_ttl,
1325 mpls_label_int=mpls_label_int,
1326 mpls_tc_int=mpls_tc_int,
1327 mpls_ttl_int=exp_mpls_ttl_int,
1328 ip_ttl=exp_ip_ttl)
1329 else:
1330 if (exp_mpls_label < 0) and (mpls_label_int >= 0):
1331 exp_pkt = simple_tcp_packet_w_mpls(
1332 mpls_type=mpls_type,
1333 mpls_label=mpls_label_int,
1334 mpls_tc=mpls_tc_int,
1335 mpls_ttl=exp_mpls_ttl_int,
1336 ip_ttl=exp_ip_ttl)
1337 else:
1338 exp_pkt = simple_tcp_packet_w_mpls(
1339 mpls_type=exp_mpls_type,
1340 mpls_label=exp_mpls_label,
1341 mpls_tc=exp_mpls_tc,
1342 mpls_ttl=exp_mpls_ttl,
1343 mpls_label_int=mpls_label_int,
1344 mpls_tc_int=mpls_tc_int,
1345 mpls_ttl_int=exp_mpls_ttl_int,
1346 ip_ttl=exp_ip_ttl)
1347 wildcards = (ofp.OFPFW_ALL & ~(ofp.OFPFW_DL_TYPE | ofp.OFPFW_MPLS_LABEL | ofp.OFPFW_MPLS_TC)) | wildcards
1348
1349 match = parse.packet_to_flow_match(pkt)
1350 parent.assertTrue(match is not None, "Flow match from pkt failed")
1351
1352 match.mpls_label = label_match
1353 match.mpls_tc = tc_match
1354 match.wildcards = wildcards
1355
1356 match.dl_type = dl_type_match
1357 match.nw_tos = 0
1358 match.nw_proto = 0
1359 match.nw_src = 0
1360 match.nw_src_mask = 0xFFFFFFFF
1361 match.nw_dst = 0
1362 match.nw_dst_mask = 0xFFFFFFFF
1363 match.tp_src = 0
1364 match.tp_dst = 0
1365
1366 request = flow_msg_create(parent, pkt, ing_port=ing_port,
1367 wildcards=wildcards,
1368 match=match,
1369 egr_port=egr_port,
1370 action_list=action_list)
1371
1372 flow_msg_install(parent, request)
1373
Rich Lanef9aa5022013-01-10 16:36:15 -08001374 logging.debug("Send packet: " + str(ing_port) + " to " + str(egr_port))
1375 #logging.debug(str(pkt).encode("hex"))
Rich Lane629393f2013-01-10 15:37:33 -08001376 parent.dataplane.send(ing_port, str(pkt))
1377
1378 if match_exp:
1379 receive_pkt_verify(parent, egr_port, exp_pkt)
1380 if check_expire:
1381 #@todo Not all HW supports both pkt and byte counters
1382 flow_removed_verify(parent, request, pkt_count=1, byte_count=len(pkt))
1383 else:
1384 if exp_msg == ofp.OFPT_FLOW_REMOVED:
1385 if check_expire:
1386 flow_removed_verify(parent, request, pkt_count=0, byte_count=0)
1387 elif exp_msg == ofp.OFPT_ERROR:
1388 error_verify(parent, exp_msg_type, exp_msg_code)
1389 else:
Rich Lane18c9fed2013-05-07 15:22:53 -07001390 parent.assertTrue(0, "Rcv: Unexpected ofp.message: " + str(exp_msg))
Rich Lane629393f2013-01-10 15:37:33 -08001391 (_, rcv_pkt, _) = parent.dataplane.poll(timeout=1)
1392 parent.assertFalse(rcv_pkt is not None, "Packet on dataplane")
1393
1394def flow_match_test_mpls(parent, port_map, wildcards=0,
1395 mpls_type=0x8847,
1396 mpls_label=-1, mpls_tc=0, mpls_ttl=64,
1397 mpls_label_int=-1, mpls_tc_int=0, mpls_ttl_int=32,
1398 ip_ttl = 192,
1399 label_match=0, tc_match=0,
1400 dl_type_match=ETHERTYPE_MPLS,
1401 exp_mpls_type=0x8847,
1402 exp_mpls_label=-1, exp_mpls_tc=0, exp_mpls_ttl=64,
1403 exp_mpls_ttl_int=32,
1404 exp_ip_ttl=192,
1405 match_exp=True,
1406 add_tag_exp=False,
1407 exp_msg=ofp.OFPT_FLOW_REMOVED,
1408 exp_msg_type=0, exp_msg_code=0,
1409 pkt=None,
1410 exp_pkt=None, action_list=None, check_expire=False,
1411 max_test=0):
1412 """
1413 Run flow_match_test_port_pair on all port pairs
1414
1415 @param max_test If > 0 no more than this number of tests are executed.
1416 @param parent Must implement controller, dataplane, assertTrue, assertEqual
1417 and logger
1418 @param wildcards For flow match entry
1419 @param mpls_type MPLS type
1420 @param mpls_label If not -1 create a pkt w/ MPLS tag
1421 @param mpls_tc MPLS TC associated with MPLS label
1422 @param mpls_ttl MPLS TTL associated with MPLS label
1423 @param mpls_label_int If not -1 create a pkt w/ Inner MPLS tag
1424 @param mpls_tc_int MPLS TC associated with Inner MPLS label
1425 @param mpls_ttl_int MPLS TTL associated with Inner MPLS label
1426 @param ip_ttl IP TTL
1427 @param label_match Matching value for MPLS LABEL field
1428 @param tc_match Matching value for MPLS TC field
1429 @param exp_mpls_label Expected MPLS LABEL value. If -1, no MPLS expected
1430 @param exp_mpls_tc Expected MPLS TC value
1431 @param exp_mpls_ttl Expected MPLS TTL value
1432 @param exp_mpls_ttl_int Expected Inner MPLS TTL value
1433 @param ip_ttl Expected IP TTL
1434 @param match_exp Set whether packet is expected to receive
1435 @param add_tag_exp If True, expected_packet has an additional MPLS shim,
1436 If not expected_packet's MPLS shim is replaced as specified
Rich Lane18c9fed2013-05-07 15:22:53 -07001437 @param exp_msg Expected ofp.message
1438 @param exp_msg_type Expected ofp.message type associated with the ofp.message
1439 @param exp_msg_code Expected ofp.message code associated with the msg_type
Rich Lane629393f2013-01-10 15:37:33 -08001440 @param pkt If not None, use this packet for ingress
1441 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
1442 @param action_list Additional actions to add to flow mod
Rich Lane18c9fed2013-05-07 15:22:53 -07001443 @param check_expire Check for flow expiration ofp.message
Rich Lane629393f2013-01-10 15:37:33 -08001444 """
1445 of_ports = port_map.keys()
1446 of_ports.sort()
1447 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
1448 test_count = 0
1449
1450 for ing_idx in range(len(of_ports)):
1451 ingress_port = of_ports[ing_idx]
1452 for egr_idx in range(len(of_ports)):
1453 if egr_idx == ing_idx:
1454 continue
1455 egress_port = of_ports[egr_idx]
1456 flow_match_test_port_pair_mpls(parent, ingress_port, egress_port,
1457 wildcards=wildcards,
1458 mpls_type=mpls_type,
1459 mpls_label=mpls_label,
1460 mpls_tc=mpls_tc,
1461 mpls_ttl=mpls_ttl,
1462 mpls_label_int=mpls_label_int,
1463 mpls_tc_int=mpls_tc_int,
1464 mpls_ttl_int=mpls_ttl_int,
1465 ip_ttl=ip_ttl,
1466 label_match=label_match,
1467 tc_match=tc_match,
1468 dl_type_match=dl_type_match,
1469 exp_mpls_type=exp_mpls_type,
1470 exp_mpls_label=exp_mpls_label,
1471 exp_mpls_tc=exp_mpls_tc,
1472 exp_mpls_ttl=exp_mpls_ttl,
1473 exp_mpls_ttl_int=exp_mpls_ttl_int,
1474 exp_ip_ttl=exp_ip_ttl,
1475 match_exp=match_exp,
1476 exp_msg=exp_msg,
1477 exp_msg_type=exp_msg_type,
1478 exp_msg_code=exp_msg_code,
1479 add_tag_exp=add_tag_exp,
1480 pkt=pkt, exp_pkt=exp_pkt,
1481 action_list=action_list,
1482 check_expire=check_expire)
1483 test_count += 1
1484 if (max_test > 0) and (test_count >= max_test):
Rich Lanef9aa5022013-01-10 16:36:15 -08001485 logging.info("Ran " + str(test_count) + " tests; exiting")
Rich Lane629393f2013-01-10 15:37:33 -08001486 return
1487
1488def flow_stats_get(parent, match_fields = None):
1489 """ Get the flow_stats from the switch
1490 Test the response to make sure it's really a flow_stats object
1491 """
Rich Lane18c9fed2013-05-07 15:22:53 -07001492 request = ofp.message.flow_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -08001493 request.out_port = ofp.OFPP_ANY
1494 request.out_group = ofp.OFPG_ANY
1495 request.table_id = 0xff
1496 if match_fields != None:
1497 request.match_fields = match_fields
1498 response, _ = parent.controller.transact(request, timeout=2)
1499 parent.assertTrue(response is not None, "Did not get response")
Rich Lane18c9fed2013-05-07 15:22:53 -07001500 parent.assertTrue(isinstance(response,ofp.message.flow_stats_reply),
Rich Lane629393f2013-01-10 15:37:33 -08001501 "Expected a flow_stats_reply, but didn't get it")
1502 return response