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