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