blob: 3a147c7dce9fefd883cbd218913091c82f53c756 [file] [log] [blame]
Sreeju Sreedhare3fefd92019-04-02 15:57:15 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
17import sys
18import copy
19import logging
20import types
21import time
22import re
23from Queue import Queue
24
25import packet as scapy
26
27import oftest
28import oftest.controller
29import oftest.dataplane
30import oftest.parse
31import oftest.ofutils
32import ofp
33
34global skipped_test_count
35skipped_test_count = 0
36
37_import_blacklist = set(locals().keys())
38
39# Some useful defines
40IP_ETHERTYPE = 0x800
41TCP_PROTOCOL = 0x6
42UDP_PROTOCOL = 0x11
43
44MINSIZE = 0
45
46def delete_all_flows(ctrl, send_barrier=True):
47 """
48 Delete all flows on the switch
49 @param ctrl The controller object for the test
50 @param send_barrier Whether or not to send a barrier message
51 """
52
53 logging.info("Deleting all flows")
54 msg = ofp.message.flow_delete()
55 if ofp.OFP_VERSION in [1, 2]:
56 msg.match.wildcards = ofp.OFPFW_ALL
57 msg.out_port = ofp.OFPP_NONE
58 msg.buffer_id = 0xffffffff
59 elif ofp.OFP_VERSION >= 3:
60 msg.table_id = ofp.OFPTT_ALL
61 msg.buffer_id = ofp.OFP_NO_BUFFER
62 msg.out_port = ofp.OFPP_ANY
63 msg.out_group = ofp.OFPG_ANY
64 ctrl.message_send(msg)
65 if send_barrier:
66 do_barrier(ctrl)
67 return 0 # for backwards compatibility
68
69def delete_all_groups(ctrl):
70 """
71 Delete all groups on the switch
72 @param ctrl The controller object for the test
73 """
74
75 logging.info("Deleting all groups")
76 msg = ofp.message.group_delete(group_id=ofp.OFPG_ALL)
77 ctrl.message_send(msg)
78 do_barrier(ctrl)
79
80def delete_groups(ctrl, group_queue=Queue()):
81 """
82 Delete all groups on list
83 @param ctrl The controller object for the test
84 :param group_queue:
85 """
86 logging.info("Deleting groups")
87 while (not group_queue.empty()):
88 msg = ofp.message.group_delete(group_id=group_queue.get())
89 ctrl.message_send(msg)
90 do_barrier(ctrl)
91
92def delete_group(ctrl, group_id):
93 """
94 Delete a single group
95 @param ctrl The controller object for the test
96 :param group_id
97 """
98 logging.info("Deleting a single group with groupId:" + str(group_id))
99 msg = ofp.message.group_delete(group_id=group_id)
100 ctrl.message_send(msg)
101 do_barrier(ctrl)
102
103def required_wildcards(parent):
104 w = test_param_get('required_wildcards', default='default')
105 if w == 'l3-l4':
106 return (ofp.OFPFW_NW_SRC_ALL | ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS
107 | ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST)
108 else:
109 return 0
110
111def simple_packet(content='00 00 00 11 33 55 00 00 00 11 22 33 81 00 00 03 '
112 '08 00 45 00 00 2e 04 d2 00 00 7f 00 b2 47 c0 a8 '
113 '01 64 c0 a8 02 02 00 00 00 00 00 00 00 00 00 00 '
114 '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00'):
115
116 pkt = ''.join(content.split(" ")).decode('hex')
117 pkt = scapy.Ether(pkt)
118 if len(pkt) < 64:
119 pkt = pkt/("D" * (64 - len(pkt)))
120 #scapy.hexdump(pkt)
121 return pkt
122
123def simple_tcp_packet(pktlen=100,
124 eth_dst='00:01:02:03:04:05',
125 eth_src='00:06:07:08:09:0a',
126 dl_vlan_enable=False,
127 vlan_vid=0,
128 outer_vlan=None,
129 vlan_pcp=0,
130 dl_vlan_cfi=0,
131 ip_src='192.168.0.1',
132 ip_dst='192.168.0.2',
133 ip_tos=0,
134 ip_ttl=64,
135 tcp_sport=1234,
136 tcp_dport=80,
137 tcp_flags="S",
138 ip_ihl=None,
139 ip_options=False
140 ):
141 """
142 Return a simple dataplane TCP packet
143
144 Supports a few parameters:
145 @param len Length of packet in bytes w/o CRC
146 @param eth_dst Destinatino MAC
147 @param eth_src Source MAC
148 @param dl_vlan_enable True if the packet is with vlan, False otherwise
149 @param vlan_vid VLAN ID
150 @param vlan_pcp VLAN priority
151 @param ip_src IP source
152 @param ip_dst IP destination
153 @param ip_tos IP ToS
154 @param ip_ttl IP TTL
155 @param tcp_dport TCP destination port
156 @param tcp_sport TCP source port
157 @param tcp_flags TCP Control flags
158
159 Generates a simple TCP request. Users
160 shouldn't assume anything about this packet other than that
161 it is a valid ethernet/IP/TCP frame.
162 """
163
164 if MINSIZE > pktlen:
165 pktlen = MINSIZE
166
167 # Note Dot1Q.id is really CFI
168 if (dl_vlan_enable):
169 pkt = scapy.Ether( dst=eth_dst, src=eth_src )
170 if outer_vlan:
171 pkt = pkt/scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=outer_vlan)
172
173 pkt = pkt/scapy.Dot1Q( prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid )/ \
174 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
175 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
176 else:
177 if not ip_options:
178 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
179 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
180 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
181 else:
182 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
183 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl, options=ip_options)/ \
184 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
185
186 pkt = pkt/("D" * (pktlen - len(pkt)))
187
188 return pkt
189
190def simple_tcpv6_packet(pktlen=100,
191 eth_dst='00:01:02:03:04:05',
192 eth_src='00:06:07:08:09:0a',
193 dl_vlan_enable=False,
194 vlan_vid=0,
195 vlan_pcp=0,
196 ipv6_src='2001:db8:85a3::8a2e:370:7334',
197 ipv6_dst='2001:db8:85a3::8a2e:370:7335',
198 ipv6_tc=0,
199 ipv6_hlim=64,
200 ipv6_fl=0,
201 tcp_sport=1234,
202 tcp_dport=80,
203 tcp_flags="S"):
204 """
205 Return a simple IPv6/TCP packet
206
207 Supports a few parameters:
208 @param len Length of packet in bytes w/o CRC
209 @param eth_dst Destination MAC
210 @param eth_src Source MAC
211 @param dl_vlan_enable True if the packet is with vlan, False otherwise
212 @param vlan_vid VLAN ID
213 @param vlan_pcp VLAN priority
214 @param ipv6_src IPv6 source
215 @param ipv6_dst IPv6 destination
216 @param ipv6_tc IPv6 traffic class
217 @param ipv6_ttl IPv6 hop limit
218 @param ipv6_fl IPv6 flow label
219 @param tcp_dport TCP destination port
220 @param tcp_sport TCP source port
221 @param tcp_flags TCP Control flags
222
223 Generates a simple TCP request. Users shouldn't assume anything about this
224 packet other than that it is a valid ethernet/IPv6/TCP frame.
225 """
226
227 if MINSIZE > pktlen:
228 pktlen = MINSIZE
229
230 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
231 if dl_vlan_enable or vlan_vid or vlan_pcp:
232 pkt /= scapy.Dot1Q(vlan=vlan_vid, prio=vlan_pcp)
233 pkt /= scapy.IPv6(src=ipv6_src, dst=ipv6_dst, fl=ipv6_fl, tc=ipv6_tc, hlim=ipv6_hlim)
234 pkt /= scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
235 pkt /= ("D" * (pktlen - len(pkt)))
236
237 return pkt
238
239def simple_udp_packet(pktlen=100,
240 eth_dst='00:01:02:03:04:05',
241 eth_src='00:06:07:08:09:0a',
242 dl_vlan_enable=False,
243 vlan_vid=0,
244 vlan_pcp=0,
245 dl_vlan_cfi=0,
246 ip_src='192.168.0.1',
247 ip_dst='192.168.0.2',
248 ip_tos=0,
249 ip_ttl=64,
250 udp_sport=1234,
251 udp_dport=80,
252 ip_ihl=None,
253 ip_options=False
254 ):
255 """
256 Return a simple dataplane UDP packet
257
258 Supports a few parameters:
259 @param len Length of packet in bytes w/o CRC
260 @param eth_dst Destination MAC
261 @param eth_src Source MAC
262 @param dl_vlan_enable True if the packet is with vlan, False otherwise
263 @param vlan_vid VLAN ID
264 @param vlan_pcp VLAN priority
265 @param ip_src IP source
266 @param ip_dst IP destination
267 @param ip_tos IP ToS
268 @param ip_ttl IP TTL
269 @param udp_dport UDP destination port
270 @param udp_sport UDP source port
271
272 Generates a simple UDP packet. Users shouldn't assume anything about
273 this packet other than that it is a valid ethernet/IP/UDP frame.
274 """
275
276 if MINSIZE > pktlen:
277 pktlen = MINSIZE
278
279 # Note Dot1Q.id is really CFI
280 if (dl_vlan_enable):
281 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
282 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)/ \
283 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
284 scapy.UDP(sport=udp_sport, dport=udp_dport)
285 else:
286 if not ip_options:
287 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
288 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
289 scapy.UDP(sport=udp_sport, dport=udp_dport)
290 else:
291 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
292 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl, options=ip_options)/ \
293 scapy.UDP(sport=udp_sport, dport=udp_dport)
294
295 pkt = pkt/("D" * (pktlen - len(pkt)))
296
297 return pkt
298
299
300def simple_tcp_packet_two_vlan(pktlen=100,
301 eth_dst='00:01:02:03:04:05',
302 eth_src='00:06:07:08:09:0a',
303 out_dl_vlan_enable=False,
304 in_dl_vlan_enable=False,
305 out_vlan_vid=0,
306 out_vlan_pcp=0,
307 out_vlan_tpid=0x8100,
308 out_dl_vlan_cfi=0,
309 in_vlan_vid=0,
310 in_vlan_pcp=0,
311 in_dl_vlan_cfi=0,
312 ip_src='192.168.0.1',
313 ip_dst='192.168.0.2',
314 ip_tos=0,
315 ip_ttl=64,
316 tcp_sport=1234,
317 tcp_dport=80,
318 tcp_flags="S",
319 ip_ihl=None,
320 ip_options=False
321 ):
322 """
323 Return a simple dataplane TCP packet
324
325 Supports a few parameters:
326 @param len Length of packet in bytes w/o CRC
327 @param eth_dst Destinatino MAC
328 @param eth_src Source MAC
329 @param dl_vlan_enable True if the packet is with vlan, False otherwise
330 @param vlan_vid VLAN ID
331 @param vlan_pcp VLAN priority
332 @param ip_src IP source
333 @param ip_dst IP destination
334 @param ip_tos IP ToS
335 @param ip_ttl IP TTL
336 @param tcp_dport TCP destination port
337 @param tcp_sport TCP source port
338 @param tcp_flags TCP Control flags
339
340 Generates a simple TCP request. Users
341 shouldn't assume anything about this packet other than that
342 it is a valid ethernet/IP/TCP frame.
343 """
344
345 if MINSIZE > pktlen:
346 pktlen = MINSIZE
347
348 # Note Dot1Q.id is really CFI
349 if (out_dl_vlan_enable and in_dl_vlan_enable):
350 pkt = scapy.Ether(dst=eth_dst, src=eth_src, type=out_vlan_tpid)/ \
351 scapy.Dot1Q(prio=out_vlan_pcp, id=out_dl_vlan_cfi, vlan=out_vlan_vid)
352
353 if in_dl_vlan_enable:
354 pkt = pkt/scapy.Dot1Q(prio=in_vlan_pcp, id=in_dl_vlan_cfi, vlan=in_vlan_vid)
355
356 pkt = pkt/scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
357 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
358 elif (out_dl_vlan_enable and (not in_dl_vlan_enable)):
359 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
360 scapy.Dot1Q(prio=out_vlan_pcp, id=out_dl_vlan_cfi, vlan=out_vlan_vid)/ \
361 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
362 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
363 elif ((not out_dl_vlan_enable) and in_dl_vlan_enable):
364 assert(0) #shall not have this caes
365 else:
366 if not ip_options:
367 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
368 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
369 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
370 else:
371 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
372 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl, options=ip_options)/ \
373 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
374
375 pkt = pkt/("D" * (pktlen - len(pkt)))
376
377 return pkt
378
379def simple_vxlan_packet(eth_dst='00:01:02:03:04:05',
380 eth_src='00:06:07:08:09:0a',
381 dl_vlan_enable=False,
382 vlan_vid=0,
383 vlan_pcp=0,
384 dl_vlan_cfi=0,
385 ip_src='192.168.0.1',
386 ip_dst='192.168.0.2',
387 ip_tos=0,
388 ip_ttl=64,
389 udp_sport=1234,
390 udp_dport=4789,
391 vnid=1,
392 inner_payload=None,
393 ip_ihl=None,
394 ip_options=False
395 ):
396 """
397 Return a simple dataplane UDP packet
398
399 Supports a few parameters:
400 @param len Length of packet in bytes w/o CRC
401 @param eth_dst Destination MAC
402 @param eth_src Source MAC
403 @param dl_vlan_enable True if the packet is with vlan, False otherwise
404 @param vlan_vid VLAN ID
405 @param vlan_pcp VLAN priority
406 @param ip_src IP source
407 @param ip_dst IP destination
408 @param ip_tos IP ToS
409 @param ip_ttl IP TTL
410 @param udp_dport UDP destination port
411 @param udp_sport UDP source port
412 @param inner_pyload inner pacekt content
413 Generates a simple UDP packet. Users shouldn't assume anything about
414 this packet other than that it is a valid ethernet/IP/UDP frame.
415 """
416
417 # Note Dot1Q.id is really CFI
418 if (dl_vlan_enable):
419 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
420 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)/ \
421 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
422 scapy.UDP(sport=udp_sport, dport=udp_dport)
423 else:
424 if not ip_options:
425 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
426 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
427 scapy.UDP(sport=udp_sport, dport=udp_dport)
428 else:
429 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
430 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl, options=ip_options)/ \
431 scapy.UDP(sport=udp_sport, dport=udp_dport)
432
433 #add vxlan header
434 pkt = pkt/scapy.VXLAN(vni=vnid)
435 #add innder payload
436 if inner_payload!=None:
437 pkt=pkt/inner_payload
438
439 return pkt
440
441def mpls_packet(pktlen=100,
442 eth_dst='00:01:02:03:04:05',
443 eth_src='00:06:07:08:09:0a',
444 dl_vlan_enable=False,
445 vlan_vid=0,
446 vlan_pcp=0,
447 dl_vlan_cfi=0,
448 ip_src='192.168.0.1',
449 ip_dst='192.168.0.2',
450 ip_tos=0,
451 ip_ttl=64,
452 tcp_sport=1234,
453 tcp_dport=80,
454 tcp_flags="S",
455 ip_ihl=None,
456 ip_options=False,
457 label=None,
458 inner_payload=True,
459 encapsulated_ethernet=False,
460 encapsulated_eth_src='01:02:03:04:05:11',
461 encapsulated_eth_dst='01:02:03:04:05:22'
462 ):
463 if MINSIZE > pktlen:
464 pktlen = MINSIZE
465
466 # Note Dot1Q.id is really CFI
467 if (dl_vlan_enable):
468 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
469 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)
470 else:
471 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
472
473 #add MPLS header
474 for i in range(len(label)):
475 l,c,s,t=label[i]
476 pkt = pkt/scapy.MPLS(label=l, cos=c, s=s, ttl=t)
477
478 #add innder payload
479 if inner_payload!=None:
480 if not encapsulated_ethernet:
481 pkt=pkt / \
482 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
483 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
484 else:
485 pkt=pkt / \
486 scapy.Ether(dst=encapsulated_eth_dst, src=encapsulated_eth_src)/ \
487 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
488 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
489
490 pkt = pkt/("D" * (pktlen - len(pkt)))
491
492 return pkt
493
494def pw_packet(pktlen=100,
495 out_eth_dst='00:01:02:03:04:05',
496 out_eth_src='00:06:07:08:09:0a',
497 label=None,
498 cw=None,
499 in_eth_dst='00:01:02:03:04:05',
500 in_eth_src='00:06:07:08:09:0a',
501 out_dl_vlan_enable=False,
502 in_dl_vlan_enable=False,
503 out_vlan_vid=0,
504 out_vlan_pcp=0,
505 out_dl_vlan_cfi=0,
506 in_vlan_vid=0,
507 in_vlan_pcp=0,
508 in_dl_vlan_cfi=0,
509 ip_src='192.168.0.1',
510 ip_dst='192.168.0.2',
511 ip_tos=0,
512 ip_ttl=64,
513 tcp_sport=1234,
514 tcp_dport=80,
515 tcp_flags="S",
516 ip_ihl=None,
517 ip_options=False
518 ):
519 """
520 Return a simple dataplane TCP packet encapsulated
521 in a pw packet
522 """
523
524 # Add the outer ethernet header
525 if MINSIZE > pktlen:
526 pktlen = MINSIZE
527
528 pkt = scapy.Ether(dst=out_eth_dst, src=out_eth_src)
529
530 #add MPLS header
531 for i in range(len(label)):
532 l,c,s,t=label[i]
533 pkt = pkt/scapy.MPLS(label=l, cos=c, s=s, ttl=t)
534
535 #add the PW CW
536 l,c,s,t=cw
537 pkt = pkt/scapy.MPLS(label=l, cos=c, s=s, ttl=t)
538
539 # Note Dot1Q.id is really CFI
540 if (out_dl_vlan_enable and in_dl_vlan_enable):
541
542 pkt = pkt/scapy.Ether(dst=in_eth_dst, src=in_eth_src)/ \
543 scapy.Dot1Q(prio=out_vlan_pcp, id=out_dl_vlan_cfi, vlan=out_vlan_vid)
544
545 pkt = pkt/scapy.Dot1Q(prio=in_vlan_pcp, id=in_dl_vlan_cfi, vlan=in_vlan_vid)
546
547 pkt = pkt/scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
548 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
549
550 elif (out_dl_vlan_enable and (not in_dl_vlan_enable)):
551
552 pkt = pkt/scapy.Ether(dst=in_eth_dst, src=in_eth_src)/ \
553 scapy.Dot1Q(prio=out_vlan_pcp, id=out_dl_vlan_cfi, vlan=out_vlan_vid)/ \
554 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
555 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
556
557 elif ((not out_dl_vlan_enable) and in_dl_vlan_enable):
558
559 assert(0) #shall not have this caes
560
561 else:
562 if not ip_options:
563 pkt = pkt/scapy.Ether(dst=in_eth_dst, src=in_eth_src)/ \
564 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
565 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
566 else:
567 pkt = pkt/scapy.Ether(dst=in_eth_dst, src=in_eth_src)/ \
568 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl, options=ip_options)/ \
569 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
570
571 pkt = pkt/("D" * (pktlen - len(pkt)))
572
573 return pkt
574
575def mplsv6_packet(pktlen=100,
576 eth_dst='00:01:02:03:04:05',
577 eth_src='00:06:07:08:09:0a',
578 dl_vlan_enable=False,
579 vlan_vid=0,
580 vlan_pcp=0,
581 dl_vlan_cfi=0,
582 ipv6_src='2001:db8:85a3::8a2e:370:7334',
583 ipv6_dst='2001:db8:85a3::8a2e:370:7335',
584 ipv6_tc=0,
585 ipv6_hlim=64,
586 ipv6_fl=0,
587 tcp_sport=1234,
588 tcp_dport=80,
589 tcp_flags="S",
590 label=None,
591 inner_payload=True
592 ):
593 if MINSIZE > pktlen:
594 pktlen = MINSIZE
595
596 # Note Dot1Q.id is really CFI
597 if (dl_vlan_enable):
598 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
599 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)
600 else:
601 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
602
603 #add MPLS header
604 for i in range(len(label)):
605 l,c,s,t=label[i]
606 pkt = pkt/scapy.MPLS(label=l, cos=c, s=s, ttl=t)
607
608 #add innder payload
609 if inner_payload!=None:
610 pkt=pkt / \
611 scapy.IPv6(src=ipv6_src, dst=ipv6_dst, fl=ipv6_fl, tc=ipv6_tc, hlim=ipv6_hlim)/ \
612 scapy.TCP(sport=tcp_sport, dport=tcp_dport, flags=tcp_flags)
613
614 pkt = pkt/("D" * (pktlen - len(pkt)))
615
616 return pkt
617
618def simple_mpls_packet(eth_dst='00:01:02:03:04:05',
619 eth_src='00:06:07:08:09:0a',
620 dl_vlan_enable=False,
621 vlan_vid=0,
622 vlan_pcp=0,
623 dl_vlan_cfi=0,
624 label=None,
625 inner_payload=None
626 ):
627 """
628 Return a simple dataplane MPLS packet
629
630 Supports a few parameters:
631 @param len Length of packet in bytes w/o CRC
632 @param eth_dst Destination MAC
633 @param eth_src Source MAC
634 @param dl_vlan_enable True if the packet is with vlan, False otherwise
635 @param vlan_vid VLAN ID
636 @param vlan_pcp VLAN priority
637 @param inner_pyload inner pacekt content
638 Generates a simple MPLS packet. Users shouldn't assume anything about
639 this packet other than that it is a valid ethernet/IP/UDP frame.
640 """
641
642 # Note Dot1Q.id is really CFI
643 if (dl_vlan_enable):
644 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
645 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)
646 else:
647 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
648
649 #add MPLS header
650 for i in range(len(label)):
651 l,c,s,t=label[i]
652 pkt = pkt/scapy.MPLS(label=l, cos=c, s=s, ttl=t)
653
654 #add innder payload
655 if inner_payload!=None:
656 pkt=pkt/inner_payload
657
658 return pkt
659
660def simple_udpv6_packet(pktlen=100,
661 eth_dst='00:01:02:03:04:05',
662 eth_src='00:06:07:08:09:0a',
663 dl_vlan_enable=False,
664 vlan_vid=0,
665 vlan_pcp=0,
666 ipv6_src='2001:db8:85a3::8a2e:370:7334',
667 ipv6_dst='2001:db8:85a3::8a2e:370:7335',
668 ipv6_tc=0,
669 ipv6_hlim=64,
670 ipv6_fl=0,
671 udp_sport=1234,
672 udp_dport=80):
673 """
674 Return a simple IPv6/UDP packet
675
676 Supports a few parameters:
677 @param len Length of packet in bytes w/o CRC
678 @param eth_dst Destination MAC
679 @param eth_src Source MAC
680 @param dl_vlan_enable True if the packet is with vlan, False otherwise
681 @param vlan_vid VLAN ID
682 @param vlan_pcp VLAN priority
683 @param ipv6_src IPv6 source
684 @param ipv6_dst IPv6 destination
685 @param ipv6_tc IPv6 traffic class
686 @param ipv6_ttl IPv6 hop limit
687 @param ipv6_fl IPv6 flow label
688 @param udp_dport UDP destination port
689 @param udp_sport UDP source port
690
691 Generates a simple UDP request. Users shouldn't assume anything about this
692 packet other than that it is a valid ethernet/IPv6/UDP frame.
693 """
694
695 if MINSIZE > pktlen:
696 pktlen = MINSIZE
697
698 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
699 if dl_vlan_enable or vlan_vid or vlan_pcp:
700 pkt /= scapy.Dot1Q(vlan=vlan_vid, prio=vlan_pcp)
701 pkt /= scapy.IPv6(src=ipv6_src, dst=ipv6_dst, fl=ipv6_fl, tc=ipv6_tc, hlim=ipv6_hlim)
702 pkt /= scapy.UDP(sport=udp_sport, dport=udp_dport)
703 pkt /= ("D" * (pktlen - len(pkt)))
704
705 return pkt
706
707def simple_icmp_packet(pktlen=60,
708 eth_dst='00:01:02:03:04:05',
709 eth_src='00:06:07:08:09:0a',
710 dl_vlan_enable=False,
711 vlan_vid=0,
712 vlan_pcp=0,
713 ip_src='192.168.0.1',
714 ip_dst='192.168.0.2',
715 ip_tos=0,
716 ip_ttl=64,
717 ip_id=1,
718 icmp_type=8,
719 icmp_code=0,
720 icmp_data=''):
721 """
722 Return a simple ICMP packet
723
724 Supports a few parameters:
725 @param len Length of packet in bytes w/o CRC
726 @param eth_dst Destinatino MAC
727 @param eth_src Source MAC
728 @param dl_vlan_enable True if the packet is with vlan, False otherwise
729 @param vlan_vid VLAN ID
730 @param vlan_pcp VLAN priority
731 @param ip_src IP source
732 @param ip_dst IP destination
733 @param ip_tos IP ToS
734 @param ip_ttl IP TTL
735 @param ip_id IP Identification
736 @param icmp_type ICMP type
737 @param icmp_code ICMP code
738 @param icmp_data ICMP data
739
740 Generates a simple ICMP ECHO REQUEST. Users
741 shouldn't assume anything about this packet other than that
742 it is a valid ethernet/ICMP frame.
743 """
744
745 if MINSIZE > pktlen:
746 pktlen = MINSIZE
747
748 if (dl_vlan_enable):
749 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
750 scapy.Dot1Q(prio=vlan_pcp, id=0, vlan=vlan_vid)/ \
751 scapy.IP(src=ip_src, dst=ip_dst, ttl=ip_ttl, tos=ip_tos, id=ip_id)/ \
752 scapy.ICMP(type=icmp_type, code=icmp_code)/ icmp_data
753 else:
754 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
755 scapy.IP(src=ip_src, dst=ip_dst, ttl=ip_ttl, tos=ip_tos, id=ip_id)/ \
756 scapy.ICMP(type=icmp_type, code=icmp_code)/ icmp_data
757
758 pkt = pkt/("0" * (pktlen - len(pkt)))
759
760 return pkt
761
762def simple_icmpv6_packet(pktlen=100,
763 eth_dst='00:01:02:03:04:05',
764 eth_src='00:06:07:08:09:0a',
765 dl_vlan_enable=False,
766 vlan_vid=0,
767 vlan_pcp=0,
768 ipv6_src='2001:db8:85a3::8a2e:370:7334',
769 ipv6_dst='2001:db8:85a3::8a2e:370:7335',
770 ipv6_tc=0,
771 ipv6_hlim=64,
772 ipv6_fl=0,
773 icmp_type=8,
774 icmp_code=0):
775 """
776 Return a simple ICMPv6 packet
777
778 Supports a few parameters:
779 @param len Length of packet in bytes w/o CRC
780 @param eth_dst Destination MAC
781 @param eth_src Source MAC
782 @param dl_vlan_enable True if the packet is with vlan, False otherwise
783 @param vlan_vid VLAN ID
784 @param vlan_pcp VLAN priority
785 @param ipv6_src IPv6 source
786 @param ipv6_dst IPv6 destination
787 @param ipv6_tc IPv6 traffic class
788 @param ipv6_ttl IPv6 hop limit
789 @param ipv6_fl IPv6 flow label
790 @param icmp_type ICMP type
791 @param icmp_code ICMP code
792
793 Generates a simple ICMP ECHO REQUEST. Users shouldn't assume anything
794 about this packet other than that it is a valid ethernet/IPv6/ICMP frame.
795 """
796
797 if MINSIZE > pktlen:
798 pktlen = MINSIZE
799
800 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
801 if dl_vlan_enable or vlan_vid or vlan_pcp:
802 pkt /= scapy.Dot1Q(vlan=vlan_vid, prio=vlan_pcp)
803 pkt /= scapy.IPv6(src=ipv6_src, dst=ipv6_dst, fl=ipv6_fl, tc=ipv6_tc, hlim=ipv6_hlim)
804 pkt /= scapy.ICMPv6Unknown(type=icmp_type, code=icmp_code)
805 pkt /= ("D" * (pktlen - len(pkt)))
806
807 return pkt
808
809def simple_arp_packet(pktlen=60,
810 eth_dst='ff:ff:ff:ff:ff:ff',
811 eth_src='00:06:07:08:09:0a',
812 vlan_vid=0,
813 vlan_pcp=0,
814 arp_op=1,
815 ip_snd='192.168.0.1',
816 ip_tgt='192.168.0.2',
817 hw_snd='00:06:07:08:09:0a',
818 hw_tgt='00:00:00:00:00:00',
819 ):
820 """
821 Return a simple ARP packet
822
823 Supports a few parameters:
824 @param len Length of packet in bytes w/o CRC
825 @param eth_dst Destinatino MAC
826 @param eth_src Source MAC
827 @param arp_op Operation (1=request, 2=reply)
828 @param ip_snd Sender IP
829 @param ip_tgt Target IP
830 @param hw_snd Sender hardware address
831 @param hw_tgt Target hardware address
832
833 Generates a simple ARP REQUEST. Users
834 shouldn't assume anything about this packet other than that
835 it is a valid ethernet/ARP frame.
836 """
837
838 if MINSIZE > pktlen:
839 pktlen = MINSIZE
840
841 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
842 if vlan_vid or vlan_pcp:
843 pkt /= scapy.Dot1Q(vlan=vlan_vid, prio=vlan_pcp)
844 pkt /= scapy.ARP(hwsrc=hw_snd, hwdst=hw_tgt, pdst=ip_tgt, psrc=ip_snd, op=arp_op)
845
846 pkt = pkt/("\0" * (pktlen - len(pkt)))
847
848 return pkt
849
850def simple_eth_packet(pktlen=60,
851 eth_dst='00:01:02:03:04:05',
852 eth_src='00:06:07:08:09:0a',
853 eth_type=0x88cc):
854
855 if MINSIZE > pktlen:
856 pktlen = MINSIZE
857
858 pkt = scapy.Ether(dst=eth_dst, src=eth_src, type=eth_type)
859
860 pkt = pkt/("0" * (pktlen - len(pkt)))
861
862 return pkt
863
864def qinq_tcp_packet(pktlen=100,
865 eth_dst='00:01:02:03:04:05',
866 eth_src='00:06:07:08:09:0a',
867 dl_vlan_outer=20,
868 dl_vlan_pcp_outer=0,
869 dl_vlan_cfi_outer=0,
870 vlan_vid=10,
871 vlan_pcp=0,
872 dl_vlan_cfi=0,
873 ip_src='192.168.0.1',
874 ip_dst='192.168.0.2',
875 ip_tos=0,
876 ip_ttl=64,
877 tcp_sport=1234,
878 tcp_dport=80,
879 ip_ihl=None,
880 ip_options=False
881 ):
882 """
883 Return a doubly tagged dataplane TCP packet
884
885 Supports a few parameters:
886 @param len Length of packet in bytes w/o CRC
887 @param eth_dst Destinatino MAC
888 @param eth_src Source MAC
889 @param dl_vlan_outer Outer VLAN ID
890 @param dl_vlan_pcp_outer Outer VLAN priority
891 @param dl_vlan_cfi_outer Outer VLAN cfi bit
892 @param vlan_vid Inner VLAN ID
893 @param vlan_pcp VLAN priority
894 @param dl_vlan_cfi VLAN cfi bit
895 @param ip_src IP source
896 @param ip_dst IP destination
897 @param ip_tos IP ToS
898 @param tcp_dport TCP destination port
899 @param ip_sport TCP source port
900
901 Generates a TCP request. Users
902 shouldn't assume anything about this packet other than that
903 it is a valid ethernet/IP/TCP frame.
904 """
905
906 if MINSIZE > pktlen:
907 pktlen = MINSIZE
908
909 # Note Dot1Q.id is really CFI
910 pkt = scapy.Ether(dst=eth_dst, src=eth_src)/ \
911 scapy.Dot1Q(prio=dl_vlan_pcp_outer, id=dl_vlan_cfi_outer, vlan=dl_vlan_outer)/ \
912 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid)/ \
913 scapy.IP(src=ip_src, dst=ip_dst, tos=ip_tos, ttl=ip_ttl, ihl=ip_ihl)/ \
914 scapy.TCP(sport=tcp_sport, dport=tcp_dport)
915
916 pkt = pkt/("D" * (pktlen - len(pkt)))
917
918 return pkt
919
920def qinq_packet(pktlen=100,
921 type=0x0800,
922 eth_dst='00:01:02:03:04:05',
923 eth_src='00:06:07:08:09:0a',
924 dl_vlan_outer=20,
925 dl_vlan_pcp_outer=0,
926 dl_vlan_cfi_outer=0,
927 vlan_vid=10,
928 vlan_pcp=0,
929 dl_vlan_cfi=0,
930 ):
931 """
932 Return a doubly tagged dataplane qinq packet
933
934 Supports a few parameters:
935 @param len Length of packet in bytes w/o CRC
936 @param type ethernet type
937 @param eth_dst Destinatino MAC
938 @param eth_src Source MAC
939 @param dl_vlan_outer Outer VLAN ID
940 @param dl_vlan_pcp_outer Outer VLAN priority
941 @param dl_vlan_cfi_outer Outer VLAN cfi bit
942 @param vlan_vid Inner VLAN ID
943 @param vlan_pcp VLAN priority
944 @param dl_vlan_cfi VLAN cfi bit
945
946 Generates a qinq request.
947 """
948
949 if MINSIZE > pktlen:
950 pktlen = MINSIZE
951
952 # Note Dot1Q.id is really CFI
953 pkt = scapy.Ether(dst=eth_dst, src=eth_src, type=type) / \
954 scapy.Dot1Q(prio=dl_vlan_pcp_outer, id=dl_vlan_cfi_outer, vlan=dl_vlan_outer) / \
955 scapy.Dot1Q(prio=vlan_pcp, id=dl_vlan_cfi, vlan=vlan_vid) / \
956 scapy.IP()
957
958 pkt = pkt / ("D" * (pktlen - len(pkt)))
959 return pkt
960
961def simple_ether_packet_two_vlan(pktlen=100,
962 eth_dst='00:01:02:03:04:05',
963 eth_src='00:06:07:08:09:0a',
964 out_dl_vlan_enable=True,
965 in_dl_vlan_enable=True,
966 out_vlan_vid=100,
967 out_vlan_pcp=0,
968 out_vlan_tpid=0x8100,
969 out_dl_vlan_cfi=0,
970 in_vlan_vid=100,
971 in_vlan_pcp=0,
972 in_vlan_tpid=0x0800,
973 in_dl_vlan_cfi=0):
974 """
975 Return a simple dataplane ether packet
976
977 Supports a few parameters:
978 @param len Length of packet in bytes w/o CRC
979 @param eth_dst Destinatino MAC
980 @param eth_src Source MAC
981 @param dl_vlan_enable True if the packet is with vlan, False otherwise
982 @param vlan_vid VLAN ID
983 @param vlan_pcp VLAN priority
984 """
985
986 if MINSIZE > pktlen:
987 pktlen = MINSIZE
988
989 if (out_dl_vlan_enable and in_dl_vlan_enable):
990 pkt = scapy.Ether(dst=eth_dst, src=eth_src, type=out_vlan_tpid) / \
991 scapy.Dot1Q(prio=out_vlan_pcp, id=out_dl_vlan_cfi, vlan=out_vlan_vid)
992 pkt = pkt / scapy.Dot1Q(prio=in_vlan_pcp, id=in_dl_vlan_cfi, vlan=in_vlan_vid, type=in_vlan_tpid)
993
994 elif (out_dl_vlan_enable and (not in_dl_vlan_enable)):
995 pkt = scapy.Ether(dst=eth_dst, src=eth_src) / \
996 scapy.Dot1Q(prio=out_vlan_pcp, id=out_dl_vlan_cfi, vlan=out_vlan_vid)
997
998 elif ((not out_dl_vlan_enable) and in_dl_vlan_enable):
999 assert (0) # shall not have this case
1000 else:
1001 pkt = scapy.Ether(dst=eth_dst, src=eth_src)
1002
1003 pkt = pkt / ("D" * (pktlen - len(pkt)))
1004 return pkt
1005
1006
1007def do_barrier(ctrl, timeout=-1):
1008 """
1009 Do a barrier command
1010 Return 0 on success, -1 on error
1011 """
1012 b = ofp.message.barrier_request()
1013 (resp, pkt) = ctrl.transact(b, timeout=timeout)
1014 if resp is None:
1015 raise AssertionError("barrier failed")
1016 # We'll trust the transaction processing in the controller that xid matched
1017 return 0 # for backwards compatibility
1018
1019def port_config_get(controller, port_no):
1020 """
1021 Get a port's configuration
1022
1023 Gets the switch feature configuration and grabs one port's
1024 configuration
1025
1026 @returns (hwaddr, config, advert) The hwaddress, configuration and
1027 advertised values
1028 """
1029
1030 if ofp.OFP_VERSION <= 3:
1031 request = ofp.message.features_request()
1032 reply, _ = controller.transact(request)
1033 if reply is None:
1034 logging.warn("Get feature request failed")
1035 return None, None, None
1036 logging.debug(reply.show())
1037 ports = reply.ports
1038 else:
1039 request = ofp.message.port_desc_stats_request()
1040 # TODO do multipart correctly
1041 reply, _ = controller.transact(request)
1042 if reply is None:
1043 logging.warn("Port desc stats request failed")
1044 return None, None, None
1045 logging.debug(reply.show())
1046 ports = reply.entries
1047
1048 for port in ports:
1049 if port.port_no == port_no:
1050 return (port.hw_addr, port.config, port.advertised)
1051
1052 logging.warn("Did not find port number for port config")
1053 return None, None, None
1054
1055def port_config_set(controller, port_no, config, mask):
1056 """
1057 Set the port configuration according the given parameters
1058
1059 Gets the switch feature configuration and updates one port's
1060 configuration value according to config and mask
1061 """
1062 logging.info("Setting port " + str(port_no) + " to config " + str(config))
1063
1064 hw_addr, _, _ = port_config_get(controller, port_no)
1065
1066 mod = ofp.message.port_mod()
1067 mod.port_no = port_no
1068 if hw_addr != None:
1069 mod.hw_addr = hw_addr
1070 mod.config = config
1071 mod.mask = mask
1072 mod.advertise = 0 # No change
1073 controller.message_send(mod)
1074 return 0
1075
1076def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if):
1077 """
1078 Check for proper receive packets across all ports
1079 @param dp The dataplane object
1080 @param pkt Expected packet; may be None if yes_ports is empty
1081 @param yes_ports Set or list of ports that should recieve packet
1082 @param no_ports Set or list of ports that should not receive packet
1083 @param assert_if Object that implements assertXXX
1084
1085 DEPRECATED in favor in verify_packets
1086 """
1087
1088 exp_pkt_arg = None
1089 if oftest.config["relax"]:
1090 exp_pkt_arg = pkt
1091
1092 for ofport in yes_ports:
1093 logging.debug("Checking for pkt on port " + str(ofport))
1094 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
1095 port_number=ofport, exp_pkt=exp_pkt_arg)
1096 assert_if.assertTrue(rcv_pkt is not None,
1097 "Did not receive pkt on " + str(ofport))
1098 if not oftest.dataplane.match_exp_pkt(pkt, rcv_pkt):
1099 logging.debug("Expected %s" % format_packet(pkt))
1100 logging.debug("Received %s" % format_packet(rcv_pkt))
1101 assert_if.assertTrue(oftest.dataplane.match_exp_pkt(pkt, rcv_pkt),
1102 "Received packet does not match expected packet " +
1103 "on port " + str(ofport))
1104 if len(no_ports) > 0:
1105 time.sleep(oftest.ofutils.default_negative_timeout)
1106 for ofport in no_ports:
1107 logging.debug("Negative check for pkt on port " + str(ofport))
1108 (rcv_port, rcv_pkt, pkt_time) = dp.poll(
1109 port_number=ofport, timeout=0, exp_pkt=exp_pkt_arg)
1110 assert_if.assertTrue(rcv_pkt is None,
1111 "Unexpected pkt on port " + str(ofport))
1112
1113
1114def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
1115 """
1116 Receive a packet and verify it matches an expected value
1117 @param egr_port A single port or list of ports
1118
1119 parent must implement dataplane, assertTrue and assertEqual
1120
1121 DEPRECATED in favor in verify_packets
1122 """
1123 exp_pkt_arg = None
1124 if oftest.config["relax"]:
1125 exp_pkt_arg = exp_pkt
1126
1127 if type(egr_ports) == type([]):
1128 egr_port_list = egr_ports
1129 else:
1130 egr_port_list = [egr_ports]
1131
1132 # Expect a packet from each port on egr port list
1133 for egr_port in egr_port_list:
1134 check_port = egr_port
1135 if egr_port == ofp.OFPP_IN_PORT:
1136 check_port = ing_port
1137 (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
1138 port_number=check_port, exp_pkt=exp_pkt_arg)
1139
1140 if rcv_pkt is None:
1141 logging.error("ERROR: No packet received from " +
1142 str(check_port))
1143
1144 parent.assertTrue(rcv_pkt is not None,
1145 "Did not receive packet port " + str(check_port))
1146 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
1147 str(rcv_port))
1148
1149 if str(exp_pkt) != str(rcv_pkt):
1150 logging.error("ERROR: Packet match failed.")
1151 logging.debug("Expected len " + str(len(exp_pkt)) + ": "
1152 + str(exp_pkt).encode('hex'))
1153 logging.debug("Received len " + str(len(rcv_pkt)) + ": "
1154 + str(rcv_pkt).encode('hex'))
1155 logging.debug("Expected packet: " + inspect_packet(scapy.Ether(str(exp_pkt))))
1156 logging.debug("Received packet: " + inspect_packet(scapy.Ether(str(rcv_pkt))))
1157 parent.assertEqual(str(exp_pkt), str(rcv_pkt),
1158 "Packet match error on port " + str(check_port))
1159
1160def match_verify(parent, req_match, res_match):
1161 """
1162 Verify flow matches agree; if they disagree, report where
1163
1164 parent must implement assertEqual
1165 Use str() to ensure content is compared and not pointers
1166 """
1167
1168 parent.assertEqual(req_match.wildcards, res_match.wildcards,
1169 'Match failed: wildcards: ' + hex(req_match.wildcards) +
1170 " != " + hex(res_match.wildcards))
1171 parent.assertEqual(req_match.in_port, res_match.in_port,
1172 'Match failed: in_port: ' + str(req_match.in_port) +
1173 " != " + str(res_match.in_port))
1174 parent.assertEqual(str(req_match.eth_src), str(res_match.eth_src),
1175 'Match failed: eth_src: ' + str(req_match.eth_src) +
1176 " != " + str(res_match.eth_src))
1177 parent.assertEqual(str(req_match.eth_dst), str(res_match.eth_dst),
1178 'Match failed: eth_dst: ' + str(req_match.eth_dst) +
1179 " != " + str(res_match.eth_dst))
1180 parent.assertEqual(req_match.vlan_vid, res_match.vlan_vid,
1181 'Match failed: vlan_vid: ' + str(req_match.vlan_vid) +
1182 " != " + str(res_match.vlan_vid))
1183 parent.assertEqual(req_match.vlan_pcp, res_match.vlan_pcp,
1184 'Match failed: vlan_pcp: ' +
1185 str(req_match.vlan_pcp) + " != " +
1186 str(res_match.vlan_pcp))
1187 parent.assertEqual(req_match.eth_type, res_match.eth_type,
1188 'Match failed: eth_type: ' + str(req_match.eth_type) +
1189 " != " + str(res_match.eth_type))
1190
1191 if (not(req_match.wildcards & ofp.OFPFW_DL_TYPE)
1192 and (req_match.eth_type == IP_ETHERTYPE)):
1193 parent.assertEqual(req_match.ip_dscp, res_match.ip_dscp,
1194 'Match failed: ip_dscp: ' + str(req_match.ip_dscp) +
1195 " != " + str(res_match.ip_dscp))
1196 parent.assertEqual(req_match.ip_proto, res_match.ip_proto,
1197 'Match failed: ip_proto: ' + str(req_match.ip_proto) +
1198 " != " + str(res_match.ip_proto))
1199 parent.assertEqual(req_match.ipv4_src, res_match.ipv4_src,
1200 'Match failed: ipv4_src: ' + str(req_match.ipv4_src) +
1201 " != " + str(res_match.ipv4_src))
1202 parent.assertEqual(req_match.ipv4_dst, res_match.ipv4_dst,
1203 'Match failed: ipv4_dst: ' + str(req_match.ipv4_dst) +
1204 " != " + str(res_match.ipv4_dst))
1205
1206 if (not(req_match.wildcards & ofp.OFPFW_NW_PROTO)
1207 and ((req_match.ip_proto == TCP_PROTOCOL)
1208 or (req_match.ip_proto == UDP_PROTOCOL))):
1209 parent.assertEqual(req_match.tcp_src, res_match.tcp_src,
1210 'Match failed: tcp_src: ' +
1211 str(req_match.tcp_src) +
1212 " != " + str(res_match.tcp_src))
1213 parent.assertEqual(req_match.tcp_dst, res_match.tcp_dst,
1214 'Match failed: tcp_dst: ' +
1215 str(req_match.tcp_dst) +
1216 " != " + str(res_match.tcp_dst))
1217
1218def packet_to_flow_match(parent, packet):
1219 match = oftest.parse.packet_to_flow_match(packet)
1220 if ofp.OFP_VERSION in [1, 2]:
1221 match.wildcards |= required_wildcards(parent)
1222 else:
1223 # TODO remove incompatible OXM entries
1224 pass
1225 return match
1226
1227def flow_msg_create(parent, pkt, ing_port=None, action_list=None, wildcards=None,
1228 egr_ports=None, egr_queue=None, check_expire=False, in_band=False):
1229 """
1230 Create a flow message
1231
1232 Match on packet with given wildcards.
1233 See flow_match_test for other parameter descriptoins
1234 @param egr_queue if not None, make the output an enqueue action
1235 @param in_band if True, do not wildcard ingress port
1236 @param egr_ports None (drop), single port or list of ports
1237 """
1238 match = oftest.parse.packet_to_flow_match(pkt)
1239 parent.assertTrue(match is not None, "Flow match from pkt failed")
1240 if wildcards is None:
1241 wildcards = required_wildcards(parent)
1242 if in_band:
1243 wildcards &= ~ofp.OFPFW_IN_PORT
1244 match.wildcards = wildcards
1245 match.in_port = ing_port
1246
1247 if type(egr_ports) == type([]):
1248 egr_port_list = egr_ports
1249 else:
1250 egr_port_list = [egr_ports]
1251
1252 request = ofp.message.flow_add()
1253 request.match = match
1254 request.buffer_id = 0xffffffff
1255 if check_expire:
1256 request.flags |= ofp.OFPFF_SEND_FLOW_REM
1257 request.hard_timeout = 1
1258
1259 if ofp.OFP_VERSION == 1:
1260 actions = request.actions
1261 else:
1262 actions = []
1263 request.instructions.append(ofp.instruction.apply_actions(actions))
1264
1265 if action_list is not None:
1266 actions.extend(action_list)
1267
1268 # Set up output/enqueue action if directed
1269 if egr_queue is not None:
1270 parent.assertTrue(egr_ports is not None, "Egress port not set")
1271 act = ofp.action.enqueue()
1272 for egr_port in egr_port_list:
1273 act.port = egr_port
1274 act.queue_id = egr_queue
1275 actions.append(act)
1276 elif egr_ports is not None:
1277 for egr_port in egr_port_list:
1278 act = ofp.action.output()
1279 act.port = egr_port
1280 actions.append(act)
1281
1282 logging.debug(request.show())
1283
1284 return request
1285
1286def flow_msg_install(parent, request, clear_table_override=None):
1287 """
1288 Install a flow mod message in the switch
1289
1290 @param parent Must implement controller, assertEqual, assertTrue
1291 @param request The request, all set to go
1292 @param clear_table If true, clear the flow table before installing
1293 """
1294
1295 clear_table = test_param_get('clear_table', default=True)
1296 if(clear_table_override != None):
1297 clear_table = clear_table_override
1298
1299 if clear_table:
1300 logging.debug("Clear flow table")
1301 delete_all_flows(parent.controller)
1302
1303 logging.debug("Insert flow")
1304 parent.controller.message_send(request)
1305
1306 do_barrier(parent.controller)
1307
1308def flow_match_test_port_pair(parent, ing_port, egr_ports, wildcards=None,
1309 vlan_vid=-1, pkt=None, exp_pkt=None,
1310 action_list=None):
1311 """
1312 Flow match test on single TCP packet
1313 @param egr_ports A single port or list of ports
1314
1315 Run test with packet through switch from ing_port to egr_port
1316 See flow_match_test for parameter descriptions
1317 """
1318
1319 if wildcards is None:
1320 wildcards = required_wildcards(parent)
1321 logging.info("Pkt match test: " + str(ing_port) + " to " +
1322 str(egr_ports))
1323 logging.debug(" WC: " + hex(wildcards) + " vlan: " + str(vlan_vid))
1324 if pkt is None:
1325 pkt = simple_tcp_packet(dl_vlan_enable=(vlan_vid >= 0), vlan_vid=vlan_vid)
1326 if exp_pkt is None:
1327 exp_pkt = pkt
1328
1329 request = flow_msg_create(parent, pkt, ing_port=ing_port,
1330 wildcards=wildcards, egr_ports=egr_ports,
1331 action_list=action_list)
1332
1333 flow_msg_install(parent, request)
1334
1335 logging.debug("Send packet: " + str(ing_port) + " to " +
1336 str(egr_ports))
1337 parent.dataplane.send(ing_port, str(pkt))
1338
1339 exp_ports = [ing_port if port == ofp.OFPP_IN_PORT else port for port in egr_ports]
1340 verify_packets(parent, exp_pkt, exp_ports)
1341
1342def flow_match_test_pktout(parent, ing_port, egr_ports,
1343 vlan_vid=-1, pkt=None, exp_pkt=None,
1344 action_list=None):
1345 """
1346 Packet-out test on single TCP packet
1347 @param egr_ports A single port or list of ports
1348
1349 Run test sending packet-out to egr_ports. The goal is to test the actions
1350 taken on the packet, not the matching which is of course irrelevant.
1351 See flow_match_test for parameter descriptions
1352 """
1353
1354 if pkt is None:
1355 pkt = simple_tcp_packet(dl_vlan_enable=(vlan_vid >= 0), vlan_vid=vlan_vid)
1356 if exp_pkt is None:
1357 exp_pkt = pkt
1358
1359 msg = ofp.message.packet_out()
1360 msg.in_port = ing_port
1361 msg.buffer_id = 0xffffffff
1362 msg.data = str(pkt)
1363 if action_list is not None:
1364 for act in action_list:
1365 msg.actions.append(act)
1366
1367 # Set up output action
1368 if egr_ports is not None:
1369 for egr_port in egr_ports:
1370 act = ofp.action.output()
1371 act.port = egr_port
1372 msg.actions.append(act)
1373
1374 logging.debug(msg.show())
1375 parent.controller.message_send(msg)
1376
1377 exp_ports = [ing_port if port == ofp.OFPP_IN_PORT else port for port in egr_ports]
1378 verify_packets(parent, exp_pkt, exp_ports)
1379
1380def get_egr_list(parent, of_ports, how_many, exclude_list=[]):
1381 """
1382 Generate a list of ports avoiding those in the exclude list
1383 @param parent Supplies logging
1384 @param of_ports List of OF port numbers
1385 @param how_many Number of ports to be added to the list
1386 @param exclude_list List of ports not to be used
1387 @returns An empty list if unable to find enough ports
1388 """
1389
1390 if how_many == 0:
1391 return []
1392
1393 count = 0
1394 egr_ports = []
1395 for egr_idx in range(len(of_ports)):
1396 if of_ports[egr_idx] not in exclude_list:
1397 egr_ports.append(of_ports[egr_idx])
1398 count += 1
1399 if count >= how_many:
1400 return egr_ports
1401 logging.debug("Could not generate enough egress ports for test")
1402 return []
1403
1404def flow_match_test(parent, port_map, wildcards=None, vlan_vid=-1, pkt=None,
1405 exp_pkt=None, action_list=None,
1406 max_test=0, egr_count=1, ing_port=False):
1407 """
1408 Run flow_match_test_port_pair on all port pairs and packet-out
1409
1410 @param max_test If > 0 no more than this number of tests are executed.
1411 @param parent Must implement controller, dataplane, assertTrue, assertEqual
1412 and logging
1413 @param pkt If not None, use this packet for ingress
1414 @param wildcards For flow match entry
1415 @param vlan_vid If not -1, and pkt is None, create a pkt w/ VLAN tag
1416 @param exp_pkt If not None, use this as the expected output pkt; els use pkt
1417 @param action_list Additional actions to add to flow mod
1418 @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
1419 """
1420 if wildcards is None:
1421 wildcards = required_wildcards(parent)
1422 of_ports = port_map.keys()
1423 of_ports.sort()
1424 parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
1425 test_count = 0
1426
1427 if egr_count == -1:
1428 egr_count = test_param_get('egr_count', default=2)
1429
1430 for ing_idx in range(len(of_ports)):
1431 ingress_port = of_ports[ing_idx]
1432 egr_ports = get_egr_list(parent, of_ports, egr_count,
1433 exclude_list=[ingress_port])
1434 if ing_port:
1435 egr_ports.append(ofp.OFPP_IN_PORT)
1436 if len(egr_ports) == 0:
1437 parent.assertTrue(0, "Failed to generate egress port list")
1438
1439 flow_match_test_port_pair(parent, ingress_port, egr_ports,
1440 wildcards=wildcards, vlan_vid=vlan_vid,
1441 pkt=pkt, exp_pkt=exp_pkt,
1442 action_list=action_list)
1443 test_count += 1
1444 if (max_test > 0) and (test_count > max_test):
1445 logging.info("Ran " + str(test_count) + " tests; exiting")
1446 break
1447
1448 if not test_param_get('pktout_actions', default=True):
1449 return
1450
1451 ingress_port = of_ports[0]
1452 egr_ports = get_egr_list(parent, of_ports, egr_count,
1453 exclude_list=[ingress_port])
1454 if ing_port:
1455 egr_ports.append(ofp.OFPP_IN_PORT)
1456 flow_match_test_pktout(parent, ingress_port, egr_ports,
1457 vlan_vid=vlan_vid,
1458 pkt=pkt, exp_pkt=exp_pkt,
1459 action_list=action_list)
1460
1461def test_param_get(key, default=None):
1462 """
1463 Return value passed via test-params if present
1464
1465 @param key The lookup key
1466 @param default Default value to use if not found
1467
1468 If the pair 'key=val' appeared in the string passed to --test-params
1469 on the command line, return val (as interpreted by exec). Otherwise
1470 return default value.
1471
1472 WARNING: TEST PARAMETERS MUST BE PYTHON IDENTIFIERS;
1473 eg egr_count, not egr-count.
1474 """
1475 try:
1476 exec oftest.config["test_params"]
1477 except:
1478 return default
1479
1480 try:
1481 return eval(str(key))
1482 except:
1483 return default
1484
1485def action_generate(parent, field_to_mod, mod_field_vals):
1486 """
1487 Create an action to modify the field indicated in field_to_mod
1488
1489 @param parent Must implement, assertTrue
1490 @param field_to_mod The field to modify as a string name
1491 @param mod_field_vals Hash of values to use for modified values
1492 """
1493
1494 act = None
1495
1496 if field_to_mod in ['pktlen']:
1497 return None
1498
1499 if field_to_mod == 'eth_dst':
1500 act = ofp.action.set_dl_dst()
1501 act.dl_addr = oftest.parse.parse_mac(mod_field_vals['eth_dst'])
1502 elif field_to_mod == 'eth_src':
1503 act = ofp.action.set_dl_src()
1504 act.dl_addr = oftest.parse.parse_mac(mod_field_vals['eth_src'])
1505 elif field_to_mod == 'dl_vlan_enable':
1506 if not mod_field_vals['dl_vlan_enable']: # Strip VLAN tag
1507 act = ofp.action.strip_vlan()
1508 # Add VLAN tag is handled by vlan_vid field
1509 # Will return None in this case
1510 elif field_to_mod == 'vlan_vid':
1511 act = ofp.action.set_vlan_vid()
1512 act.vlan_vid = mod_field_vals['vlan_vid']
1513 elif field_to_mod == 'vlan_pcp':
1514 act = ofp.action.set_vlan_pcp()
1515 act.vlan_pcp = mod_field_vals['vlan_pcp']
1516 elif field_to_mod == 'ip_src':
1517 act = ofp.action.set_nw_src()
1518 act.nw_addr = oftest.parse.parse_ip(mod_field_vals['ip_src'])
1519 elif field_to_mod == 'ip_dst':
1520 act = ofp.action.set_nw_dst()
1521 act.nw_addr = oftest.parse.parse_ip(mod_field_vals['ip_dst'])
1522 elif field_to_mod == 'ip_tos':
1523 act = ofp.action.set_nw_tos()
1524 act.nw_tos = mod_field_vals['ip_tos']
1525 elif field_to_mod == 'tcp_sport':
1526 act = ofp.action.set_tp_src()
1527 act.tp_port = mod_field_vals['tcp_sport']
1528 elif field_to_mod == 'tcp_dport':
1529 act = ofp.action.set_tp_dst()
1530 act.tp_port = mod_field_vals['tcp_dport']
1531 elif field_to_mod == 'udp_sport':
1532 act = ofp.action.set_tp_src()
1533 act.tp_port = mod_field_vals['udp_sport']
1534 elif field_to_mod == 'udp_dport':
1535 act = ofp.action.set_tp_dst()
1536 act.tp_port = mod_field_vals['udp_dport']
1537 else:
1538 parent.assertTrue(0, "Unknown field to modify: " + str(field_to_mod))
1539
1540 return act
1541
1542def pkt_action_setup(parent, start_field_vals={}, mod_field_vals={},
1543 mod_fields=[], tp="tcp", check_test_params=False):
1544 """
1545 Set up the ingress and expected packet and action list for a test
1546
1547 @param parent Must implement assertTrue
1548 @param start_field_values Field values to use for ingress packet (optional)
1549 @param mod_field_values Field values to use for modified packet (optional)
1550 @param mod_fields The list of fields to be modified by the switch in the test.
1551 @params check_test_params If True, will check the parameters vid, add_vlan
1552 and strip_vlan from the command line.
1553
1554 Returns a triple: pkt-to-send, expected-pkt, action-list
1555 """
1556
1557 new_actions = []
1558
1559 base_pkt_params = {}
1560 base_pkt_params['pktlen'] = 100
1561 base_pkt_params['eth_dst'] = '00:DE:F0:12:34:56'
1562 base_pkt_params['eth_src'] = '00:23:45:67:89:AB'
1563 base_pkt_params['dl_vlan_enable'] = False
1564 base_pkt_params['vlan_vid'] = 2
1565 base_pkt_params['vlan_pcp'] = 0
1566 base_pkt_params['ip_src'] = '192.168.0.1'
1567 base_pkt_params['ip_dst'] = '192.168.0.2'
1568 base_pkt_params['ip_tos'] = 0
1569 if tp == "tcp":
1570 base_pkt_params['tcp_sport'] = 1234
1571 base_pkt_params['tcp_dport'] = 80
1572 elif tp == "udp":
1573 base_pkt_params['udp_sport'] = 1234
1574 base_pkt_params['udp_dport'] = 80
1575 for keyname in start_field_vals.keys():
1576 base_pkt_params[keyname] = start_field_vals[keyname]
1577
1578 mod_pkt_params = {}
1579 mod_pkt_params['pktlen'] = 100
1580 mod_pkt_params['eth_dst'] = '00:21:0F:ED:CB:A9'
1581 mod_pkt_params['eth_src'] = '00:ED:CB:A9:87:65'
1582 mod_pkt_params['dl_vlan_enable'] = False
1583 mod_pkt_params['vlan_vid'] = 3
1584 mod_pkt_params['vlan_pcp'] = 7
1585 mod_pkt_params['ip_src'] = '10.20.30.40'
1586 mod_pkt_params['ip_dst'] = '50.60.70.80'
1587 mod_pkt_params['ip_tos'] = 0xf0
1588 if tp == "tcp":
1589 mod_pkt_params['tcp_sport'] = 4321
1590 mod_pkt_params['tcp_dport'] = 8765
1591 elif tp == "udp":
1592 mod_pkt_params['udp_sport'] = 4321
1593 mod_pkt_params['udp_dport'] = 8765
1594 for keyname in mod_field_vals.keys():
1595 mod_pkt_params[keyname] = mod_field_vals[keyname]
1596
1597 # Check for test param modifications
1598 strip = False
1599 if check_test_params:
1600 add_vlan = test_param_get('add_vlan')
1601 strip_vlan = test_param_get('strip_vlan')
1602 vid = test_param_get('vid')
1603
1604 if add_vlan and strip_vlan:
1605 parent.assertTrue(0, "Add and strip VLAN both specified")
1606
1607 if vid:
1608 base_pkt_params['dl_vlan_enable'] = True
1609 base_pkt_params['vlan_vid'] = vid
1610 if 'vlan_vid' in mod_fields:
1611 mod_pkt_params['vlan_vid'] = vid + 1
1612
1613 if add_vlan:
1614 base_pkt_params['dl_vlan_enable'] = False
1615 mod_pkt_params['dl_vlan_enable'] = True
1616 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] + 4
1617 mod_fields.append('pktlen')
1618 mod_fields.append('dl_vlan_enable')
1619 if 'vlan_vid' not in mod_fields:
1620 mod_fields.append('vlan_vid')
1621 elif strip_vlan:
1622 base_pkt_params['dl_vlan_enable'] = True
1623 mod_pkt_params['dl_vlan_enable'] = False
1624 mod_pkt_params['pktlen'] = base_pkt_params['pktlen'] - 4
1625 mod_fields.append('dl_vlan_enable')
1626 mod_fields.append('pktlen')
1627
1628 if tp == "tcp":
1629 packet_builder = simple_tcp_packet
1630 elif tp == "udp":
1631 packet_builder = simple_udp_packet
1632 else:
1633 raise NotImplementedError("unknown transport protocol %s" % tp)
1634
1635 # Build the ingress packet
1636 ingress_pkt = packet_builder(**base_pkt_params)
1637
1638 # Build the expected packet, modifying the indicated fields
1639 for item in mod_fields:
1640 base_pkt_params[item] = mod_pkt_params[item]
1641 act = action_generate(parent, item, mod_pkt_params)
1642 if act:
1643 new_actions.append(act)
1644
1645 expected_pkt = packet_builder(**base_pkt_params)
1646
1647 return (ingress_pkt, expected_pkt, new_actions)
1648
1649# Generate a simple "drop" flow mod
1650# If in_band is true, then only drop from first test port
1651def flow_mod_gen(port_map, in_band):
1652 request = ofp.message.flow_add()
1653 request.match.wildcards = ofp.OFPFW_ALL
1654 if in_band:
1655 request.match.wildcards = ofp.OFPFW_ALL - ofp.OFPFW_IN_PORT
1656 for of_port, ifname in port_map.items(): # Grab first port
1657 break
1658 request.match.in_port = of_port
1659 request.buffer_id = 0xffffffff
1660 return request
1661
1662def skip_message_emit(parent, s):
1663 """
1664 Print out a 'skipped' message to stderr
1665
1666 @param s The string to print out to the log file
1667 """
1668 global skipped_test_count
1669
1670 skipped_test_count += 1
1671 logging.info("Skipping: " + s)
1672 if oftest.config["debug"] < logging.WARNING:
1673 sys.stderr.write("(skipped) ")
1674 else:
1675 sys.stderr.write("(S)")
1676
1677
1678def all_stats_get(parent):
1679 """
1680 Get the aggregate stats for all flows in the table
1681 @param parent Test instance with controller connection and assert
1682 @returns dict with keys flows, packets, bytes, active (flows),
1683 lookups, matched
1684 """
1685 stat_req = ofp.message.aggregate_stats_request()
1686 stat_req.match = ofp.match()
1687 stat_req.match.wildcards = ofp.OFPFW_ALL
1688 stat_req.table_id = 0xff
1689 stat_req.out_port = ofp.OFPP_NONE
1690
1691 rv = {}
1692
1693 (reply, pkt) = parent.controller.transact(stat_req)
1694 parent.assertTrue(len(reply.entries) == 1, "Did not receive flow stats reply")
1695
1696 for obj in reply.entries:
1697 (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count,
1698 obj.packet_count, obj.byte_count)
1699 break
1700
1701 request = ofp.message.table_stats_request()
1702 (reply , pkt) = parent.controller.transact(request)
1703
1704
1705 (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
1706 for obj in reply.entries:
1707 rv["active"] += obj.active_count
1708 rv["lookups"] += obj.lookup_count
1709 rv["matched"] += obj.matched_count
1710
1711 return rv
1712
1713_import_blacklist.add('FILTER')
1714FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
1715 for x in range(256)])
1716
1717def hex_dump_buffer(src, length=16):
1718 """
1719 Convert src to a hex dump string and return the string
1720 @param src The source buffer
1721 @param length The number of bytes shown in each line
1722 @returns A string showing the hex dump
1723 """
1724 result = ["\n"]
1725 for i in xrange(0, len(src), length):
1726 chars = src[i:i+length]
1727 hex = ' '.join(["%02x" % ord(x) for x in chars])
1728 printable = ''.join(["%s" % ((ord(x) <= 127 and
1729 FILTER[ord(x)]) or '.') for x in chars])
1730 result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
1731 return ''.join(result)
1732
1733def format_packet(pkt):
1734 return "Packet length %d \n%s" % (len(str(pkt)),
1735 hex_dump_buffer(str(pkt)))
1736
1737def inspect_packet(pkt):
1738 """
1739 Wrapper around scapy's show() method.
1740 @returns A string showing the dissected packet.
1741 """
1742 from cStringIO import StringIO
1743 out = None
1744 backup = sys.stdout
1745 try:
1746 tmp = StringIO()
1747 sys.stdout = tmp
1748 pkt.show2()
1749 out = tmp.getvalue()
1750 tmp.close()
1751 finally:
1752 sys.stdout = backup
1753 return out
1754
1755def nonstandard(cls):
1756 """
1757 Testcase decorator that marks the test as being non-standard.
1758 These tests are not automatically added to the "standard" group.
1759 """
1760 cls._nonstandard = True
1761 return cls
1762
1763def disabled(cls):
1764 """
1765 Testcase decorator that marks the test as being disabled.
1766 These tests are not automatically added to the "standard" group or
1767 their module's group.
1768 """
1769 cls._disabled = True
1770 return cls
1771
1772def group(name):
1773 """
1774 Testcase decorator that adds the test to a group.
1775 """
1776 def fn(cls):
1777 if not hasattr(cls, "_groups"):
1778 cls._groups = []
1779 cls._groups.append(name)
1780 return cls
1781 return fn
1782
1783def version(ver):
1784 """
1785 Testcase decorator that specifies which versions of OpenFlow the test
1786 supports. The default is 1.0+. This decorator may only be used once.
1787
1788 Supported syntax:
1789 1.0 -> 1.0
1790 1.0,1.2,1.3 -> 1.0, 1.2, 1.3
1791 1.0+ -> 1.0, 1.1, 1.2, 1.3
1792 """
1793 versions = parse_version(ver)
1794 def fn(cls):
1795 cls._versions = versions
1796 return cls
1797 return fn
1798
1799def parse_version(ver):
1800 allowed_versions = ["1.0", "1.1", "1.2", "1.3"]
1801 if re.match("^1\.\d+$", ver):
1802 versions = set([ver])
1803 elif re.match("^(1\.\d+)\+$", ver):
1804 if not ver[:-1] in allowed_versions:
1805 raise ValueError("invalid OpenFlow version %s" % ver[:-1])
1806 versions = set()
1807 if ver != "1.1+": versions.add("1.0")
1808 if ver != "1.2+": versions.add("1.1")
1809 if ver != "1.3+": versions.add("1.2")
1810 versions.add("1.3")
1811 else:
1812 versions = set(ver.split(','))
1813
1814 for version in versions:
1815 if not version in allowed_versions:
1816 raise ValueError("invalid OpenFlow version %s" % version)
1817
1818 return versions
1819
1820assert(parse_version("1.0") == set(["1.0"]))
1821assert(parse_version("1.0,1.2,1.3") == set(["1.0", "1.2", "1.3"]))
1822assert(parse_version("1.0+") == set(["1.0", "1.1", "1.2", "1.3"]))
1823
1824def get_stats(test, req):
1825 """
1826 Retrieve a list of stats entries. Handles OFPSF_REPLY_MORE.
1827 """
1828 msgtype = ofp.OFPT_STATS_REPLY
1829 more_flag = ofp.OFPSF_REPLY_MORE
1830 stats = []
1831 reply, _ = test.controller.transact(req)
1832 test.assertTrue(reply is not None, "No response to stats request")
1833 test.assertEquals(reply.type, msgtype, "Response had unexpected message type")
1834 stats.extend(reply.entries)
1835 while reply.flags & more_flag != 0:
1836 reply, pkt = test.controller.poll(exp_msg=msgtype)
1837 test.assertTrue(reply is not None, "No response to stats request")
1838 stats.extend(reply.entries)
1839 return stats
1840
1841def get_flow_stats(test, match, table_id=None,
1842 out_port=None, out_group=None,
1843 cookie=0, cookie_mask=0):
1844 """
1845 Retrieve a list of flow stats entries.
1846 """
1847
1848 if table_id == None:
1849 if ofp.OFP_VERSION <= 2:
1850 table_id = 0xff
1851 else:
1852 table_id = ofp.OFPTT_ALL
1853
1854 if out_port == None:
1855 if ofp.OFP_VERSION == 1:
1856 out_port = ofp.OFPP_NONE
1857 else:
1858 out_port = ofp.OFPP_ANY
1859
1860 if out_group == None:
1861 if ofp.OFP_VERSION > 1:
1862 out_group = ofp.OFPP_ANY
1863
1864 req = ofp.message.flow_stats_request(match=match,
1865 table_id=table_id,
1866 out_port=out_port)
1867 if ofp.OFP_VERSION > 1:
1868 req.out_group = out_group
1869 req.cookie = cookie
1870 req.cookie_mask = cookie_mask
1871
1872 return get_stats(test, req)
1873
1874def get_port_stats(test, port_no):
1875 """
1876 Retrieve a list of port stats entries.
1877 """
1878 req = ofp.message.port_stats_request(port_no=port_no)
1879 return get_stats(test, req)
1880
1881def get_queue_stats(test, port_no, queue_id):
1882 """
1883 Retrieve a list of queue stats entries.
1884 """
1885 req = ofp.message.queue_stats_request(port_no=port_no, queue_id=queue_id)
1886 return get_stats(test, req)
1887
1888def verify_flow_stats(test, match, table_id=0xff,
1889 initial=[],
1890 pkts=None, bytes=None):
1891 """
1892 Verify that flow stats changed as expected.
1893
1894 Optionally takes an 'initial' list of stats entries, as returned by
1895 get_flow_stats(). If 'initial' is not given the counters are assumed to
1896 begin at 0.
1897 """
1898
1899 def accumulate(stats):
1900 pkts_acc = bytes_acc = 0
1901 for stat in stats:
1902 pkts_acc += stat.packet_count
1903 bytes_acc += stat.byte_count
1904 return (pkts_acc, bytes_acc)
1905
1906 pkts_before, bytes_before = accumulate(initial)
1907
1908 # Wait 10s for counters to update
1909 pkt_diff = byte_diff = None
1910 for i in range(0, 100):
1911 stats = get_flow_stats(test, match, table_id=table_id)
1912 pkts_after, bytes_after = accumulate(stats)
1913 pkt_diff = pkts_after - pkts_before
1914 byte_diff = bytes_after - bytes_before
1915 if (pkts == None or pkt_diff >= pkts) and \
1916 (bytes == None or byte_diff >= bytes):
1917 break
1918 time.sleep(0.1)
1919
1920 if pkts != None:
1921 test.assertEquals(pkt_diff, pkts, "Flow packet counter not updated properly (expected increase of %d, got increase of %d)" % (pkts, pkt_diff))
1922
1923 if bytes != None:
1924 test.assertTrue(byte_diff >= bytes and byte_diff <= bytes*1.1,
1925 "Flow byte counter not updated properly (expected increase of %d, got increase of %d)" % (bytes, byte_diff))
1926
1927def verify_port_stats(test, port,
1928 initial=[],
1929 tx_pkts=None, rx_pkts=None,
1930 tx_bytes=None, rx_bytes=None):
1931 """
1932 Verify that port stats changed as expected.
1933
1934 Optionally takes an 'initial' list of stats entries, as returned by
1935 get_port_stats(). If 'initial' is not given the counters are assumed to
1936 begin at 0.
1937 """
1938 def accumulate(stats):
1939 tx_pkts_acc = rx_pkts_acc = tx_bytes_acc = rx_bytes_acc = 0
1940 for stat in stats:
1941 tx_pkts_acc += stat.tx_packets
1942 rx_pkts_acc += stat.rx_packets
1943 tx_bytes_acc += stat.tx_bytes
1944 rx_bytes_acc += stat.rx_bytes
1945 return (tx_pkts_acc, rx_pkts_acc, tx_bytes_acc, rx_bytes_acc)
1946
1947 tx_pkts_before, rx_pkts_before, \
1948 tx_bytes_before, rx_bytes_before = accumulate(initial)
1949
1950 # Wait 10s for counters to update
1951 for i in range(0, 100):
1952 stats = get_port_stats(test, port)
1953 tx_pkts_after, rx_pkts_after, \
1954 tx_bytes_after, rx_bytes_after = accumulate(stats)
1955 tx_pkts_diff = tx_pkts_after - tx_pkts_before
1956 rx_pkts_diff = rx_pkts_after - rx_pkts_before
1957 tx_bytes_diff = tx_bytes_after - tx_bytes_before
1958 rx_bytes_diff = rx_bytes_after - rx_bytes_before
1959 if (tx_pkts == None or tx_pkts <= tx_pkts_diff) and \
1960 (rx_pkts == None or rx_pkts <= rx_pkts_diff) and \
1961 (tx_bytes == None or tx_bytes <= tx_bytes_diff) and \
1962 (rx_bytes == None or rx_bytes <= rx_bytes_diff):
1963 break
1964 time.sleep(0.1)
1965
1966 if (tx_pkts != None):
1967 test.assertGreaterEqual(tx_pkts_diff, tx_pkts,
1968 "Port TX packet counter is not updated correctly (expected increase of %d, got increase of %d)" % (tx_pkts, tx_pkts_diff))
1969 if (rx_pkts != None):
1970 test.assertGreaterEqual(rx_pkts_diff, rx_pkts,
1971 "Port RX packet counter is not updated correctly (expected increase of %d, got increase of %d)" % (rx_pkts, rx_pkts_diff))
1972 if (tx_bytes != None):
1973 test.assertGreaterEqual(tx_bytes_diff, tx_bytes,
1974 "Port TX byte counter is not updated correctly (expected increase of %d, got increase of %d)" % (tx_bytes, tx_bytes_diff))
1975 if (rx_bytes != None):
1976 test.assertGreaterEqual(rx_bytes_diff, rx_bytes,
1977 "Port RX byte counter is not updated correctly (expected increase of %d, got increase of %d)" % (rx_bytes, rx_bytes_diff))
1978
1979def verify_queue_stats(test, port_no, queue_id,
1980 initial=[],
1981 pkts=None, bytes=None):
1982 """
1983 Verify that queue stats changed as expected.
1984
1985 Optionally takes an 'initial' list of stats entries, as returned by
1986 get_queue_stats(). If 'initial' is not given the counters are assumed to
1987 begin at 0.
1988 """
1989 def accumulate(stats):
1990 pkts_acc = bytes_acc = 0
1991 for stat in stats:
1992 pkts_acc += stat.tx_packets
1993 bytes_acc += stat.tx_bytes
1994 return (pkts_acc, bytes_acc)
1995
1996 pkts_before, bytes_before = accumulate(initial)
1997
1998 # Wait 10s for counters to update
1999 pkt_diff = byte_diff = None
2000 for i in range(0, 100):
2001 stats = get_queue_stats(test, port_no, queue_id)
2002 pkts_after, bytes_after = accumulate(stats)
2003 pkt_diff = pkts_after - pkts_before
2004 byte_diff = bytes_after - bytes_before
2005 if (pkts == None or pkt_diff >= pkts) and \
2006 (bytes == None or byte_diff >= bytes):
2007 break
2008 time.sleep(0.1)
2009
2010 if pkts != None:
2011 test.assertEquals(pkt_diff, pkts, "Queue packet counter not updated properly (expected increase of %d, got increase of %d)" % (pkts, pkt_diff))
2012
2013 if bytes != None:
2014 test.assertTrue(byte_diff >= bytes and byte_diff <= bytes*1.1,
2015 "Queue byte counter not updated properly (expected increase of %d, got increase of %d)" % (bytes, byte_diff))
2016
2017def packet_in_match(msg, data, in_port=None, reason=None):
2018 """
2019 Check whether the packet_in message 'msg' has fields matching 'data',
2020 'in_port', and 'reason'.
2021
2022 This function handles truncated packet_in data. The 'in_port' and 'reason'
2023 parameters are optional.
2024
2025 @param msg packet_in message
2026 @param data Expected packet_in data
2027 @param in_port Expected packet_in in_port, or None
2028 @param reason Expected packet_in reason, or None
2029 """
2030
2031 if ofp.OFP_VERSION <= 2:
2032 pkt_in_port = msg.in_port
2033 else:
2034 oxms = { type(oxm): oxm for oxm in msg.match.oxm_list }
2035 if ofp.oxm.in_port in oxms:
2036 pkt_in_port = oxms[ofp.oxm.in_port].value
2037 else:
2038 logging.warn("Missing in_port in packet-in message")
2039 pkt_in_port = None
2040
2041 if in_port != None and in_port != pkt_in_port:
2042 logging.debug("Incorrect packet_in in_port (expected %d, received %d)", in_port, pkt_in_port)
2043 return False
2044
2045 if reason != None and reason != msg.reason:
2046 logging.debug("Incorrect packet_in reason (expected %d, received %d)", reason, msg.reason)
2047 return False
2048
2049 # Check that one of the packets is a prefix of the other.
2050 # The received packet may be either truncated or padded, but not both.
2051 # (Some of the padding may be truncated but that's irrelevant). We
2052 # need to check that the smaller packet is a prefix of the larger one.
2053 # Note that this check succeeds if the switch sends a zero-length
2054 # packet-in.
2055 compare_len = min(len(msg.data), len(data))
2056 if data[:compare_len] != msg.data[:compare_len]:
2057 logging.debug("Incorrect packet_in data")
2058 logging.debug("Expected %s" % format_packet(data[:compare_len]))
2059 logging.debug("Received %s" % format_packet(msg.data[:compare_len]))
2060 return False
2061
2062 return True
2063
2064def verify_packet_in(test, data, in_port, reason, controller=None):
2065 """
2066 Assert that the controller receives a packet_in message matching data 'data'
2067 from port 'in_port' with reason 'reason'. Does not trigger the packet_in
2068 itself, that's up to the test case.
2069
2070 @param test Instance of base_tests.SimpleProtocol
2071 @param pkt String to expect as the packet_in data
2072 @param in_port OpenFlow port number to expect as the packet_in in_port
2073 @param reason One of OFPR_* to expect as the packet_in reason
2074 @param controller Controller instance, defaults to test.controller
2075 @returns The received packet-in message
2076 """
2077
2078 if controller == None:
2079 controller = test.controller
2080
2081 end_time = time.time() + oftest.ofutils.default_timeout
2082
2083 while True:
2084 msg, _ = controller.poll(ofp.OFPT_PACKET_IN, end_time - time.time())
2085 if not msg:
2086 # Timeout
2087 break
2088 elif packet_in_match(msg, data, in_port, reason):
2089 # Found a matching message
2090 break
2091
2092 test.assertTrue(msg is not None, 'Packet in message not received on port %r' % in_port)
2093 return msg
2094
2095def verify_no_packet_in(test, data, in_port, controller=None):
2096 """
2097 Assert that the controller does not receive a packet_in message matching
2098 data 'data' from port 'in_port'.
2099
2100 @param test Instance of base_tests.SimpleProtocol
2101 @param pkt String to expect as the packet_in data
2102 @param in_port OpenFlow port number to expect as the packet_in in_port
2103 @param controller Controller instance, defaults to test.controller
2104 """
2105
2106 if controller == None:
2107 controller = test.controller
2108
2109 # Negative test, need to wait a short amount of time before checking we
2110 # didn't receive the message.
2111 time.sleep(oftest.ofutils.default_negative_timeout)
2112
2113 # Check every packet_in queued in the controller
2114 while True:
2115 msg, _ = controller.poll(ofp.OFPT_PACKET_IN, timeout=0)
2116 if msg == None:
2117 # No more queued packet_in messages
2118 break
2119 elif packet_in_match(msg, data, in_port, None):
2120 # Found a matching message
2121 break
2122
2123 if in_port == None:
2124 test.assertTrue(msg == None, "Did not expect a packet-in message on any port")
2125 else:
2126 test.assertTrue(msg == None, "Did not expect a packet-in message on port %d" % in_port)
2127
2128def openflow_ports(num=None):
2129 """
2130 Return a list of 'num' OpenFlow port numbers
2131
2132 If 'num' is None, return all available ports. Otherwise, limit the length
2133 of the result to 'num' and raise an exception if not enough ports are
2134 available.
2135 """
2136 ports = sorted(oftest.config["port_map"].keys())
2137 if num != None and len(ports) < num:
2138 raise Exception("test requires %d ports but only %d are available" % (num, len(ports)))
2139 return ports[:num]
2140
2141def verify_packet(test, pkt, ofport):
2142 """
2143 Check that an expected packet is received
2144 """
2145 logging.debug("Checking for pkt on port %r", ofport)
2146 (rcv_port, rcv_pkt, pkt_time) = test.dataplane.poll(port_number=ofport, exp_pkt=str(pkt))
2147 test.assertTrue(rcv_pkt != None, "Did not receive pkt on %r" % ofport)
2148 return (rcv_port, rcv_pkt, pkt_time)
2149
2150def verify_no_packet(test, pkt, ofport):
2151 """
2152 Check that a particular packet is not received
2153 """
2154 logging.debug("Negative check for pkt on port %r", ofport)
2155 (rcv_port, rcv_pkt, pkt_time) = \
2156 test.dataplane.poll(
2157 port_number=ofport, exp_pkt=str(pkt),
2158 timeout=oftest.ofutils.default_negative_timeout)
2159 test.assertTrue(rcv_pkt == None, "Received packet on %r" % ofport)
2160
2161def verify_no_other_packets(test):
2162 """
2163 Check that no unexpected packets are received
2164
2165 This is a no-op if the --relax option is in effect.
2166 """
2167 if oftest.config["relax"]:
2168 return
2169 logging.debug("Checking for unexpected packets on all ports")
2170 (rcv_port, rcv_pkt, pkt_time) = test.dataplane.poll(timeout=oftest.ofutils.default_negative_timeout)
2171 if rcv_pkt != None:
2172 logging.debug("Received unexpected packet on port %r: %s", rcv_port, format_packet(rcv_pkt))
2173 test.assertTrue(rcv_pkt == None, "Unexpected packet on port %r" % rcv_port)
2174
2175def verify_packets(test, pkt, ofports):
2176 """
2177 Check that a packet is received on certain ports
2178
2179 Also verifies that the packet is not received on any other ports, and that no
2180 other packets are received (unless --relax is in effect).
2181
2182 This covers the common and simplest cases for checking dataplane outputs.
2183 For more complex usage, like multiple different packets being output, or
2184 multiple packets on the same port, use the primitive verify_packet,
2185 verify_no_packet, and verify_no_other_packets functions directly.
2186 """
2187 pkt = str(pkt)
2188 for ofport in openflow_ports():
2189 if ofport in ofports:
2190 verify_packet(test, pkt, ofport)
2191 else:
2192 verify_no_packet(test, pkt, ofport)
2193 verify_no_other_packets(test)
2194
2195def verify_no_errors(ctrl):
2196 error, _ = ctrl.poll(ofp.OFPT_ERROR, 0)
2197 if error:
2198 raise AssertionError("unexpected error type=%d code=%d" % (error.err_type, error.code))
2199
2200def verify_capability(test, capability):
2201 """
2202 Return True if DUT supports the specified capability.
2203
2204 @param test Instance of base_tests.SimpleProtocol
2205 @param capability One of ofp_capabilities.
2206 """
2207 logging.info("Verifing that capability code is valid.")
2208 test.assertIn(capability, ofp.const.ofp_capabilities_map,
2209 "Capability code %d does not exist." % capability)
2210 capability_str = ofp.const.ofp_capabilities_map[capability]
2211
2212 logging.info(("Sending features_request to test if capability "
2213 "%s is supported."), capability_str)
2214 req = ofp.message.features_request()
2215 res, raw = test.controller.transact(req)
2216 test.assertIsNotNone(res, "Did not receive a response from the DUT.")
2217 test.assertEqual(res.type, ofp.OFPT_FEATURES_REPLY,
2218 ("Unexpected packet type %d received in response to "
2219 "OFPT_FEATURES_REQUEST") % res.type)
2220 logging.info("Received features_reply.")
2221
2222 if (res.capabilities & capability) > 0:
2223 logging.info("Switch capabilities bitmask claims to support %s",
2224 capability_str)
2225 return True, res.capabilities
2226 else:
2227 logging.info("Capabilities bitmask does not support %s.",
2228 capability_str)
2229 return False, res.capabilities
2230
2231def verify_configuration_flag(test, flag):
2232 """
2233 Return True if DUT supports specified configuration flag.
2234
2235 @param test Instance of base_tests.SimpleProtocol
2236 @param flag One of ofp_config_flags.
2237 @returns (supported, flags) Bool if flag is set and flag values.
2238 """
2239 logging.info("Verifing that flag is valid.")
2240 test.assertIn(flag, ofp.const.ofp_config_flags_map,
2241 "flag %s does not exist." % flag)
2242 flag_str = ofp.const.ofp_config_flags_map[flag]
2243
2244 logging.info("Sending OFPT_GET_CONFIG_REQUEST.")
2245 req = ofp.message.get_config_request()
2246 rv = test.controller.message_send(req)
2247 test.assertNotEqual(rv, -1, "Not able to send get_config_request.")
2248
2249 logging.info("Expecting OFPT_GET_CONFIG_REPLY ")
2250 (res, pkt) = test.controller.poll(exp_msg=ofp.OFPT_GET_CONFIG_REPLY,
2251 timeout=2)
2252 test.assertIsNotNone(res, "Did not receive OFPT_GET_CONFIG_REPLY")
2253 logging.info("Received OFPT_GET_CONFIG_REPLY ")
2254
2255 if res.flags == flag:
2256 logging.info("%s flag is set.", flag_str)
2257 return True, res.flags
2258 else:
2259 logging.info("%s flag is not set.", flag_str)
2260 return False, res.flags
2261
2262__all__ = list(set(locals()) - _import_blacklist)