blob: 23a3e6e124594e4b4d2e01abd678a96a7cae79ad [file] [log] [blame]
Rich Lane629393f2013-01-10 15:37:33 -08001######################################################################
2#
3# All files associated with the OpenFlow Python Test (oftest) are
4# made available for public use and benefit with the expectation
5# that others will use, modify and enhance the Software and contribute
6# those enhancements back to the community. However, since we would
7# like to make the Software available for broadest use, with as few
8# restrictions as possible permission is hereby granted, free of
9# charge, to any person obtaining a copy of this Software to deal in
10# the Software under the copyrights without restriction, including
11# without limitation the rights to use, copy, modify, merge, publish,
12# distribute, sublicense, and/or sell copies of the Software, and to
13# permit persons to whom the Software is furnished to do so, subject
14# to the following conditions:
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23# SOFTWARE.
24#
25######################################################################
26import os
27
28"""
29OpenFlow packet class
30
31This class implements the basic abstraction of an OpenFlow packet. It
32includes parsing functionality and OpenFlow actions related to
33packet modifications.
34"""
35
36import socket
37import struct
38import logging
39import oftest.cstruct as ofp
40import unittest
41import binascii
42import string
43import collections #@UnresolvedImport
44import oftest.action as action
45
46ETHERTYPE_IP = 0x0800
47ETHERTYPE_VLAN = 0x8100
48ETHERTYPE_VLAN_QinQ = 0x88a8
49ETHERTYPE_ARP = 0x0806
50ETHERTYPE_MPLS = 0x8847
51ETHERTYPE_MPLS_MCAST = 0x8848
52ETHERTYPES_MPLS = [ETHERTYPE_MPLS, ETHERTYPE_MPLS_MCAST]
53
54DL_MASK_ALL = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
55NW_MASK_ALL = 0xffffffff
56
57MPLS_BOTTOM_OF_STACK = 0x00000100
58
59# Sigh.. not python26
60#MplsTag = collections.namedtuple("MplsTag", "label tc ttl")
61
62class MplsTag(object):
63 def __init__(self, label, tc, ttl):
64 self.label = label
65 self.tc = tc
66 self.ttl = ttl
67 def pack(self, bos = 0):
68 packed_tag = ((self.label & 0xfffff) << 12) | \
69 ((self.tc & 0x7) << 9) | \
70 (self.ttl & 0xFF) | \
71 (MPLS_BOTTOM_OF_STACK if bos else 0)
72 return packed_tag
73
74 def unpack(packed_tag):
75 tag = MplsTag(packed_tag >> 12,
76 (packed_tag >> 9) & 0x0007,
77 packed_tag & 0xFF)
78 bos = bool(packed_tag & MPLS_BOTTOM_OF_STACK)
79 return (tag, bos)
80 unpack = staticmethod(unpack)
81
82class Packet(object):
83 """
84 Packet abstraction
85
86 This is meant to support the abstraction of a packet in an
87 OpenFlow 1.1 switch so it includes an action set, ingress port,
88 and metadata. These members may be ignored and the rest of the
89 packet parsing and modification functions used to manipulate
90 a packet.
91 """
92
93 icmp_counter = 1
94
95 def __init__(self, in_port=None, data=""):
96 # Use entries in match when possible.
97 self.in_port = in_port
98 self.data = data
99 self.bytes = len(data)
100 self.match = ofp.ofp_match()
101 self.logger = logging.getLogger("packet")
102 self.instructions = []
103 # parsable tags
104 self.ip_header_offset = None
105 self.tcp_header_offset = None
106 self.mpls_tag_offset = None # pointer to outer mpls tag
107 self.vlan_tag_offset = None # pointer to outer vlan tag
108 self.action_set = {}
109 self.queue_id = 0
110
111 if self.data != "":
112 self.parse()
113
114 def show(self):
115 """ Return a ascii hex representation of the packet's data"""
116 ret = ""
117 c = 0
118 for b in list(self.data):
119 if c != 0:
120 if c % 16 == 0:
121 ret += '\n'
122 elif c % 8 == 0:
123 ret += ' '
124 c += 1
125 ret += "%0.2x " % struct.unpack('B', b)
126 return ret
127
128 def __repr__(self):
129 return self.data
130
131 def __str__(self):
132 return self.__repr__()
133
134 def __len__(self):
135 return len(self.data)
136
137 def simple_tcp_packet(self,
138 pktlen=100,
139 dl_dst='00:01:02:03:04:05',
140 dl_src='00:06:07:08:09:0a',
141 dl_vlan_enable=False,
142 dl_vlan_type=ETHERTYPE_VLAN,
143 dl_vlan=0,
144 dl_vlan_pcp=0,
145 dl_vlan_cfi=0,
146 mpls_type=None,
147 mpls_tags=None,
148 ip_src='192.168.0.1',
149 ip_dst='192.168.0.2',
150 ip_tos=0,
151 ip_ttl=64,
152 tcp_sport=1234,
153 tcp_dport=80):
154 """
155 Return a simple dataplane TCP packet
156
157 Supports a few parameters:
158 @param len Length of packet in bytes w/o CRC
159 @param dl_dst Destinatino MAC
160 @param dl_src Source MAC
161 @param dl_vlan_enable True if the packet is with vlan, False otherwise
162 @param dl_vlan_type Ether type for VLAN
163 @param dl_vlan VLAN ID
164 @param dl_vlan_pcp VLAN priority
165 @param ip_src IP source
166 @param ip_dst IP destination
167 @param ip_tos IP ToS
168 @param tcp_dport TCP destination port
169 @param tcp_sport TCP source port
170
171 Generates a simple TCP request. Users shouldn't assume anything
172 about this packet other than that it is a valid ethernet/IP/TCP frame.
173 """
174 self.data = ""
175 self._make_ip_packet(dl_dst, dl_src, dl_vlan_enable, dl_vlan_type,
176 dl_vlan, dl_vlan_pcp, dl_vlan_cfi,
177 mpls_type, mpls_tags,
178 ip_tos, ip_ttl, ip_src, ip_dst, socket.IPPROTO_TCP)
179
180 # Add TCP header
181 self.data += struct.pack("!HHLLBBHHH",
182 tcp_sport,
183 tcp_dport,
184 1, # tcp.seq
185 0, # tcp.ack
186 0x50, # tcp.doff + tcp.res1
187 0x12, # tcp.syn + tcp.ack
188 0, # tcp.wnd
189 0, # tcp.check
190 0, # tcp.urgent pointer
191 )
192
193 # Fill out packet
194 self.data += "D" * (pktlen - len(self.data))
195 return self
196
197 def simple_icmp_packet(self,
198 pktlen=100,
199 dl_dst='00:01:02:03:04:05',
200 dl_src='00:06:07:08:09:0a',
201 dl_vlan_enable=False,
202 dl_vlan_type=ETHERTYPE_VLAN,
203 dl_vlan=0,
204 dl_vlan_pcp=0,
205 dl_vlan_cfi=0,
206 mpls_type=None,
207 mpls_tags=None,
208 ip_src='192.168.0.1',
209 ip_dst='192.168.0.2',
210 ip_tos=0,
211 ip_ttl=64,
212 icmp_type=8, # ICMP_ECHO_REQUEST
213 icmp_code=0,
214 ):
215 """
216 Return a simple dataplane ICMP packet
217
218 Supports a few parameters:
219 @param len Length of packet in bytes w/o CRC
220 @param dl_dst Destinatino MAC
221 @param dl_src Source MAC
222 @param dl_vlan_enable True if the packet is with vlan, False otherwise
223 @param dl_vlan_type Ether type for VLAN
224 @param dl_vlan VLAN ID
225 @param dl_vlan_pcp VLAN priority
226 @param ip_src IP source
227 @param ip_dst IP destination
228 @param ip_tos IP ToS
229 @param tcp_dport TCP destination port
230 @param ip_sport TCP source port
231
232 Generates a simple TCP request. Users shouldn't assume anything
233 about this packet other than that it is a valid ethernet/IP/TCP frame.
234 """
235 self.data = ""
236 self._make_ip_packet(dl_dst, dl_src, dl_vlan_enable, dl_vlan_type,
237 dl_vlan, dl_vlan_pcp, dl_vlan_cfi,
238 mpls_type, mpls_tags,
239 ip_tos,
240 ip_ttl, ip_src, ip_dst, socket.IPPROTO_ICMP)
241
242 # Add ICMP header
243 self.data += struct.pack("!BBHHH",
244 icmp_type,
245 icmp_code,
246 0, # icmp.checksum
247 os.getpid() & 0xffff, # icmp.echo.id
248 Packet.icmp_counter # icmp.echo.seq
249 )
250 Packet.icmp_counter += 1
251
252 # Fill out packet
253 self.data += "D" * (pktlen - len(self.data))
254
255 return self
256
257 def _make_ip_packet(self, dl_dst, dl_src, dl_vlan_enable, dl_vlan_type,
258 dl_vlan, dl_vlan_pcp, dl_vlan_cfi, mpls_type, mpls_tags,
259 ip_tos, ip_ttl, ip_src, ip_dst, ip_proto):
260 self.data = ""
261 addr = dl_dst.split(":")
262 for byte in map(lambda z: int(z, 16), addr):
263 self.data += struct.pack("!B", byte)
264 addr = dl_src.split(":")
265 for byte in map(lambda z: int(z, 16), addr):
266 self.data += struct.pack("!B", byte)
267
268 if (dl_vlan_enable):
269 # Form and add VLAN tag
270 self.data += struct.pack("!H", dl_vlan_type)
271 vtag = (dl_vlan & 0x0fff) | \
272 (dl_vlan_pcp & 0x7) << 13 | \
273 (dl_vlan_cfi & 0x1) << 12
274 self.data += struct.pack("!H", vtag)
275
276 if mpls_tags:
277 # Add type/len field
278 self.data += struct.pack("!H", mpls_type)
279 mpls_tags = list(mpls_tags)
280 while len(mpls_tags):
281 tag = mpls_tags.pop(0)
282 packed_tag = tag.pack(bos = not len(mpls_tags))
283 self.data += struct.pack("!I", packed_tag)
284
285 else:
286 # Add type/len field
287 self.data += struct.pack("!H", ETHERTYPE_IP)
288
289 # Add IP header
290 v_and_hlen = 0x45 # assumes no ip or tcp options
291 ip_len = 120 + 40 # assumes no ip or tcp options
292 self.data += struct.pack("!BBHHHBBH", v_and_hlen, ip_tos, ip_len,
293 0, # ip.id
294 0, # ip.frag_off
295 ip_ttl, # ip.ttl
296 ip_proto,
297 0) # ip.checksum
298 # convert ipsrc/dst to ints
299 self.data += struct.pack("!LL", ascii_ip_to_bin(ip_src),
300 ascii_ip_to_bin(ip_dst))
301
302 def length(self):
303 return len(self.data)
304
305 def clear_actions(self):
306 self.action_set = {}
307
308 def parse(self):
309 """
310 Update the headers in self.match based on self.data
311
312 Parses the relevant header features out of the packet, using
313 the table outlined in the OF1.1 spec, Figure 4
314 """
315 self.bytes = len(self.data)
316 self.match.in_port = self.in_port
317 self.match.type = ofp.OFPMT_STANDARD
318 self.match.length = ofp.OFPMT_STANDARD_LENGTH
319 self.match.wildcards = 0
320 self.match.nw_dst_mask = 0
321 self.match.nw_dst_mask = 0
322 self.match.dl_dst_mask = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
323 self.match.dl_src_mask = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
324 self.mpls_tag_offset = None
325 self.ip_header_offset = None
326
327 idx = 0
328 try:
329 idx = self._parse_l2(idx)
330
331 if self.match.dl_type == ETHERTYPE_IP:
332 self.ip_header_offset = idx
333 idx = self._parse_ip(idx)
334 if self.match.nw_proto in [ socket.IPPROTO_TCP,
335 socket.IPPROTO_UDP,
336 socket.IPPROTO_ICMP]:
337 self.tcp_header_offset = idx
338 if self.match.nw_proto != socket.IPPROTO_ICMP:
339 idx = self._parse_l4(idx)
340 else:
341 idx = self._parse_icmp(idx)
342 elif self.match.dl_type == ETHERTYPE_ARP:
343 self._parse_arp(idx)
344 except (parse_error), e:
345 self.logger.warn("Giving up on parsing packet, got %s" %
346 (str(e)))
347 return None
348 return self.match
349
350 def _parse_arp(self, idx):
351 # @todo Implement
352 pass
353
354 def _parse_l2(self, idx):
355 """
356 Parse Layer2 Headers of packet
357
358 Parse ether src,dst,type (and vlan and QinQ headers if exists) from
359 self.data starting at idx
360 """
361 if self.bytes < 14 :
362 raise parse_error("_parse_l2:: packet too shorter <14 bytes")
363
364 self.match.dl_dst = list(struct.unpack("!6B", self.data[idx:idx+6]))
365 self.match.dl_dst_mask = DL_MASK_ALL
366 idx += 6
367 self.match.dl_src = list(struct.unpack("!6B", self.data[idx:idx+6]))
368 self.match.dl_src_mask = DL_MASK_ALL
369 idx += 6
370 #pdb.set_trace()
371 l2_type = struct.unpack("!H", self.data[idx:idx+2])[0]
372 idx += 2
373 if l2_type in [ETHERTYPE_VLAN, ETHERTYPE_VLAN_QinQ] :
374 self.vlan_tag_offset = 12
375 blob = struct.unpack("!H", self.data[idx:idx+2])[0]
376 idx += 2
377 self.match.dl_vlan_pcp = (blob & 0xe000) >> 13
378 #cfi = blob & 0x1000 #@todo figure out what to do if cfi!=0
379 self.match.dl_vlan = blob & 0x0fff
380 l2_type = struct.unpack("!H", self.data[idx:idx+2])[0]
381 # now skip past any more nest VLAN tags (per the spec)
382 while l2_type in [ETHERTYPE_VLAN, ETHERTYPE_VLAN_QinQ] :
383 idx += 4
384 if self.bytes < idx :
385 raise parse_error("_parse_l2(): Too many vlan tags")
386 l2_type = struct.unpack("!H", self.data[idx:idx+2])[0]
387 idx += 2
388 else:
389 self.vlan_tag_offset = None
390 self.match.dl_vlan = ofp.OFPVID_NONE
391 self.match.dl_vlan_pcp = 0
392
393 if l2_type in ETHERTYPES_MPLS:
394 if self.bytes < (idx + 4):
395 raise parse_error("_parse_l2: Invalid MPLS header")
396 self.mpls_tag_offset = idx
397 tag = struct.unpack("!I", self.data[idx:idx+4])[0]
398 self.match.mpls_label = tag >> 12
399 self.match.mpls_tc = (tag >> 9) & 0x0007
400 idx += 4
401 else:
402 self.match.mpls_label = 0
403 self.match.mpls_tc = 0
404
405 self.match.dl_type = l2_type
406 return idx
407
408 def _parse_ip(self, idx):
409 """
410 Parse IP Headers of a packet starting at self.data[idx]
411 """
412 if self.bytes < (idx + 20) :
413 raise parse_error("_parse_ip: Invalid IP header")
414 # the three blanks are id (2bytes), frag offset (2bytes),
415 # and ttl (1byte)
416 (hlen_and_v, self.match.nw_tos, len, _,_,_, self.match.nw_proto) = \
417 struct.unpack("!BBHHHBB", self.data[idx:idx+10])
418 #@todo add fragmentation parsing
419 hlen = hlen_and_v & 0x0f
420 (self.match.nw_src, self.match.nw_dst) = \
421 struct.unpack("!II", self.data[idx + 12:idx+20])
422 self.match.nw_dst_mask = NW_MASK_ALL
423 self.match.nw_src_mask = NW_MASK_ALL
424 return idx + (hlen *4) # this should correctly skip IP options
425
426 def _parse_l4(self, idx):
427 """
428 Parse the src/dst ports of UDP and TCP packets
429 """
430 if self.bytes < (idx + 8):
431 raise parse_error("_parse_l4: Invalid L4 header")
432 (self.match.tp_src, self.match.tp_dst) = \
433 struct.unpack("!HH", self.data[idx:idx+4])
434
435 def _parse_icmp(self, idx):
436 """
437 Parse the type/code of ICMP Packets
438 """
439 if self.bytes < (idx + 4):
440 raise parse_error("_parse_icmp: Invalid icmp header")
441 # yes, type and code get stored into tp_dst and tp_src...
442 (self.match.tp_src, self.match.tp_dst) = \
443 struct.unpack("!BB", self.data[idx:idx+2])
444
445
446 #
447 # NOTE: See comment string in write_action regarding exactly
448 # when actions are executed (for apply vs write instructions)
449 #
450
451 def write_action(self, action):
452 """
453 Write the action into the packet's action set
454
455 Note that we do nothing to the packet when the write_action
456 instruction is executed. We only record the actions for
457 later processing. Because of this, the output port is not
458 explicitly recorded in the packet; that state is recorded
459 in the action_set[set_output] item.
460 """
461 self.action_set[action.__class__] = action
462
463 def _set_1bytes(self,offset,byte):
464 """ Writes the byte at data[offset]
465
466 This function only exists to match the other _set_Xbytes() and
467 is trivial
468
469 """
470 tmp= "%s%s" % (self.data[0:offset],
471 struct.pack('B',byte & 0xff))
472 if len(self.data) > offset + 1 :
473 tmp += self.data[offset+1:len(self.data)]
474 self.data=tmp
475
476 def _set_2bytes(self,offset,short):
477 """ Writes the 2 byte short in network byte order at data[offset] """
478 tmp= "%s%s" % (self.data[0:offset],
479 struct.pack('!H',short & 0xffff))
480 if len(self.data) > offset + 2 :
481 tmp += self.data[offset+2:len(self.data)]
482 self.data=tmp
483
484 def _set_4bytes(self,offset,word,forceNBO=True):
485 """ Writes the 4 byte word at data[offset]
486
487 Use network byte order if forceNBO is True,
488 else it's assumed that word is already in NBO
489
490 """
491 # @todo Verify byte order
492 #pdb.set_trace()
493 fmt ="L"
494 if forceNBO:
495 fmt = '!' + fmt
496
497 tmp= "%s%s" % (self.data[0:offset],
498 struct.pack(fmt,word & 0xffffffff))
499 if len(self.data) > offset + 4 :
500 tmp += self.data[offset+4:len(self.data)]
501 self.data=tmp
502
503 def _set_6bytes(self,offset,byte_list):
504 """ Writes the 6 byte sequence in the given order to data[offset] """
505 # @todo Do as a slice
506 tmp= self.data[0:offset]
507 tmp += struct.pack("BBBBBB", *byte_list)
508 if len(self.data) > offset + 6 :
509 tmp += self.data[offset+6:len(self.data)]
510 self.data=tmp
511
512 def _update_l4_checksum(self):
513 """ Recalculate the L4 checksum, if there
514
515 Can be safely called on non-tcp/non-udp packets as a NOOP
516 """
517 if (self.ip_header_offset is None or
518 self.tcp_header_offset is None):
519 return
520 if self.match.nw_proto == socket.IPPROTO_TCP:
521 return self._update_tcp_checksum()
522 elif self.match.nw_proto == socket.IPPROTO_UDP:
523 return self._update_udp_checksum()
524
525 def _update_tcp_checksum(self):
526 """ Recalculate the TCP checksum
527
528 @warning: Must only be called on actual TCP Packets
529 """
530 #@todo implemnt tcp checksum update
531 pass
532
533 def _update_udp_checksum(self):
534 """ Recalculate the TCP checksum
535
536 @warning: Must only be called on actual TCP Packets
537 """
538 #@todo implemnt udp checksum update
539 pass
540
541 def set_metadata(self, value, mask):
542 self.match.metadata = (self.match.metadata & ~mask) | \
543 (value & mask)
544
545 #
546 # These are the main action operations that take the
547 # required parameters
548 #
549 # Note that 'group', 'experimenter' and 'set_output' are only
550 # implemented for the action versions.
551
552 def set_queue(self, queue_id):
553 self.queue_id = queue_id
554
555 def set_vlan_vid(self, vid):
556 # @todo Verify proper location of VLAN id
557 if self.vlan_tag_offset is None:
558 self.logger.debug("set_vlan_vid(): Adding new vlan tag to untagged packet")
559 self.push_vlan(ETHERTYPE_VLAN)
560 offset = self.vlan_tag_offset + 2
561 short = struct.unpack('!H', self.data[offset:offset+2])[0]
562 short = (short & 0xf000) | ((vid & 0x0fff) )
563 self.data = self.data[0:offset] + struct.pack('!H',short) + \
564 self.data[offset+2:len(self.data)]
565 self.match.dl_vlan = vid & 0x0fff
566 self.logger.debug("set_vlan_vid(): setting packet vlan_vid to 0x%x " %
567 self.match.dl_vlan)
568
569 def set_vlan_pcp(self, pcp):
570 # @todo Verify proper location of VLAN pcp
571 if self.vlan_tag_offset is None:
572 return
573 offset = self.vlan_tag_offset + 2
574 short = struct.unpack('!H', self.data[offset:offset+2])[0]
575 short = (pcp<<13 & 0xf000) | ((short & 0x0fff) )
576 self.data = self.data[0:offset] + struct.pack('!H',short) + \
577 self.data[offset+2:len(self.data)]
578 self.match.dl_vlan_pcp = pcp & 0xf
579
580 def set_dl_src(self, dl_src):
581 self._set_6bytes(6, dl_src)
582 self.match.dl_src = dl_src
583
584 def set_dl_dst(self, dl_dst):
585 self._set_6bytes(0, dl_dst)
586 self.match.dl_dst = dl_dst
587
588 def set_nw_src(self, nw_src):
589 if self.ip_header_offset is None:
590 return
591 self._set_4bytes(self.ip_header_offset + 12, nw_src)
592 self._update_l4_checksum()
593 self.match.nw_src = nw_src
594
595 def set_nw_dst(self, nw_dst):
596 # @todo Verify byte order
597 if self.ip_header_offset is None:
598 return
599 self._set_4bytes(self.ip_header_offset + 16, nw_dst)
600 self._update_l4_checksum()
601 self.match.nw_dst = nw_dst
602
603 def set_nw_tos(self, tos):
604 if self.ip_header_offset is None:
605 return
606 self._set_1bytes(self.ip_header_offset + 1, tos)
607 self.match.nw_tos = tos
608
609 def set_nw_ecn(self, ecn):
610 #@todo look up ecn implementation details
611 pass
612
613 def set_tp_src(self, tp_src):
614 if self.tcp_header_offset is None:
615 return
616 if (self.match.nw_proto == socket.IPPROTO_TCP or
617 self.match.nw_proto == socket.IPPROTO_UDP):
618 self._set_2bytes(self.tcp_header_offset, tp_src)
619 elif (self.match.nw_proto == socket.IPPROTO_ICMP):
620 self._set_1bytes(self.tcp_header_offset, tp_src)
621 self._update_l4_checksum()
622 self.match.tp_src = tp_src
623
624 def set_tp_dst(self, tp_dst):
625 if self.tcp_header_offset is None:
626 return
627 if (self.match.nw_proto == socket.IPPROTO_TCP or
628 self.match.nw_proto == socket.IPPROTO_UDP):
629 self._set_2bytes(self.tcp_header_offset +2, tp_dst)
630 elif (self.match.nw_proto == socket.IPPROTO_ICMP):
631 self._set_1bytes(self.tcp_header_offset + 1, tp_dst)
632 self._update_l4_checksum()
633 self.match.tp_dst = tp_dst
634
635 IP_OFFSET_TTL = 8
636
637 def copy_ttl_out(self):
638 if self.mpls_tag_offset is None:
639 # No MPLS tag.
640 return
641 outerTag = struct.unpack("!I", self.data[self.mpls_tag_offset:
642 self.mpls_tag_offset+4])[0]
643 if not (outerTag & MPLS_BOTTOM_OF_STACK):
644 # Payload is another MPLS tag:
645 innerTag = struct.unpack("!I", self.data[self.mpls_tag_offset+4:
646 self.mpls_tag_offset+8])[0]
647 outerTag = (outerTag & 0xFFFFFF00) | (innerTag & 0x000000FF)
648 self._set_4bytes(self.mpls_tag_offset, outerTag)
649 else:
650 # This MPLS tag is the bottom of the stack.
651 # See if the payload looks like it might be IPv4.
652 versionLen = struct.unpack("B",
653 self.data[self.mpls_tag_offset+4])[0]
654 if versionLen >> 4 != 4:
655 # This is not IPv4.
656 return;
657 # This looks like IPv4, so copy the TTL.
658 ipTTL = struct.unpack("B", self.data[self.mpls_tag_offset + 4 +
659 Packet.IP_OFFSET_TTL])[0]
660 outerTag = (outerTag & 0xFFFFFF00) | (ipTTL & 0xFF)
661 self._set_4bytes(self.mpls_tag_offset, outerTag)
662 return
663
664 def copy_ttl_in(self):
665 if self.mpls_tag_offset is None:
666 # No MPLS tag.
667 return
668 outerTag = struct.unpack("!I", self.data[self.mpls_tag_offset:
669 self.mpls_tag_offset+4])[0]
670 if not (outerTag & MPLS_BOTTOM_OF_STACK):
671 # Payload is another MPLS tag:
672 innerTag = struct.unpack("!I", self.data[self.mpls_tag_offset+4:
673 self.mpls_tag_offset+8])[0]
674 innerTag = (innerTag & 0xFFFFFF00) | (outerTag & 0x000000FF)
675 self._set_4bytes(self.mpls_tag_offset+4, innerTag)
676 else:
677 # This MPLS tag is the bottom of the stack.
678 # See if the payload looks like it might be IPv4.
679 versionLen = struct.unpack("B", self.data[self.mpls_tag_offset+4])[0]
680 if versionLen >> 4 != 4:
681 # This is not IPv4.
682 return;
683 # This looks like IPv4, so copy the TTL.
684 self._set_1bytes(self.mpls_tag_offset + 4 + Packet.IP_OFFSET_TTL,
685 outerTag & 0x000000FF)
686 #@todo update checksum
687 return
688
689 def set_mpls_label(self, mpls_label):
690 if self.mpls_tag_offset is None:
691 return
692 tag = struct.unpack("!I", self.data[self.mpls_tag_offset:
693 self.mpls_tag_offset+4])[0]
694 tag = ((mpls_label & 0xfffff) << 12) | (tag & 0x00000fff)
695 self.match.mpls_label = mpls_label
696 self._set_4bytes(self.mpls_tag_offset, tag)
697
698 def set_mpls_tc(self, mpls_tc):
699 if self.mpls_tag_offset is None:
700 return
701 tag = struct.unpack("!I", self.data[self.mpls_tag_offset:
702 self.mpls_tag_offset+4])[0]
703 tag = ((mpls_tc & 0x7) << 9) | (tag & 0xfffff1ff)
704 self.match.mpls_tc = mpls_tc
705 self._set_4bytes(self.mpls_tag_offset, tag)
706
707 def set_mpls_ttl(self, ttl):
708 if self.mpls_tag_offset is None:
709 return
710 self._set_1bytes(self.mpls_tag_offset + 3, ttl)
711
712 def dec_mpls_ttl(self):
713 if self.mpls_tag_offset is None:
714 return
715 ttl = struct.unpack("B", self.data[self.mpls_tag_offset + 3])[0]
716 self.set_mpls_ttl(ttl - 1)
717
718 def push_vlan(self, ethertype):
719 if len(self) < 14:
720 self.logger.error("NOT Pushing a new VLAN tag: packet too short!")
721 pass # invalid ethernet frame, can't add vlan tag
722
723 # from 4.8.1 of the spec, default values are zero
724 # on a push operation if no VLAN tag already exists
725 l2_type = struct.unpack("!H", self.data[12:14])[0]
726 if ((l2_type == ETHERTYPE_VLAN) or (l2_type == ETHERTYPE_VLAN_QinQ)):
727 current_tag = struct.unpack("!H", self.data[14:16])[0]
728 else:
729 current_tag = 0
730 new_tag = struct.pack('!HH',
731 # one of 0x8100 or x88a8
732 # could check to enforce this?
733 ethertype & 0xffff,
734 current_tag
735 )
736 self.data = self.data[0:12] + new_tag + self.data[12:len(self.data)]
737 self.parse()
738
739 def pop_vlan(self):
740 if self.vlan_tag_offset is None:
741 pass
742 self.data = self.data[0:12] + self.data[16:len(self.data)]
743 self.parse()
744
745 def push_mpls(self, ethertype):
746 tag = MplsTag(0, 0, 0)
747 bos = False
748
749 if self.mpls_tag_offset:
750 # The new tag defaults to the old one.
751 packed_tag = struct.unpack("!I", self.data[self.mpls_tag_offset:
752 self.mpls_tag_offset+4])[0]
753 (tag, _) = MplsTag.unpack(packed_tag)
754
755 else:
756 # Pushing a new label stack, set the BoS bit and get TTL from IP.
757 bos = True
758 if self.ip_header_offset:
759 ttl = struct.unpack("B", self.data[self.ip_header_offset + \
760 Packet.IP_OFFSET_TTL])[0]
761 tag = MplsTag(0, 0, ttl)
762
763 self.data = self.data[0:14] + \
764 struct.pack("!I", tag.pack(bos)) + \
765 self.data[14:]
766 self._set_2bytes(12, ethertype)
767 # Reparse to update offsets, ethertype, etc.
768 self.parse()
769
770 def pop_mpls(self, ethertype):
771 # Ignore if no existing tags.
772 if self.mpls_tag_offset:
773 self.data = self.data[0:self.mpls_tag_offset] + \
774 self.data[self.mpls_tag_offset + 4:]
775 self._set_2bytes(12, ethertype)
776
777 # Reparse to update offsets, ethertype, etc.
778 self.parse()
779
780 def set_nw_ttl(self, ttl):
781 if self.ip_header_offset is None:
782 return
783 self._set_1bytes(self.ip_header_offset + Packet.IP_OFFSET_TTL, ttl)
784 self._update_l4_checksum()
785 # don't need to update self.match; no ttl in it
786
787 def dec_nw_ttl(self):
788 if self.ip_header_offset is None:
789 return
790 offset = self.ip_header_offset + Packet.IP_OFFSET_TTL
791 old_ttl = struct.unpack("b",self.data[offset:offset + 1])[0]
792 self.set_nw_ttl( old_ttl - 1)
793
794 #
795 # All action functions need to take the action object for params
796 # These take an action object to facilitate the switch implementation
797 #
798
799 def action_output(self, action, switch):
800 if action.port < ofp.OFPP_MAX:
801 switch.dataplane.send(action.port, self.data,
802 queue_id=self.queue_id)
803 elif action.port == ofp.OFPP_ALL:
804 for of_port in switch.ports.iterkeys():
805 if of_port != self.in_port:
806 switch.dataplane.send(of_port, self.data,
807 queue_id=self.queue_id)
808 elif action.port == ofp.OFPP_IN_PORT:
809 switch.dataplane.send(self.in_port, self.data,
810 queue_id=self.queue_id)
811 else:
812 switch.logger.error("NEED to implement action_output" +
813 " for port %d" % action.port)
814
815 def action_set_queue(self, action, switch):
816 self.set_queue(action.queue_id)
817
818 def action_set_vlan_vid(self, action, switch):
819 self.set_vlan_vid(action.vlan_vid)
820
821 def action_set_vlan_pcp(self, action, switch):
822 self.set_vlan_pcp(action.vlan_pcp)
823
824 def action_set_dl_src(self, action, switch):
825 self.set_dl_src(action.dl_addr)
826
827 def action_set_dl_dst(self, action, switch):
828 self.set_dl_dst(action.dl_addr)
829
830 def action_set_nw_src(self, action, switch):
831 self.set_nw_src(action.nw_addr)
832
833 def action_set_nw_dst(self, action, switch):
834 self.set_nw_dst(action.nw_addr)
835
836 def action_set_nw_tos(self, action, switch):
837 self.set_nw_tos(action.nw_tos)
838
839 def action_set_nw_ecn(self, action, switch):
840 self.set_nw_ecn(action.nw_ecn)
841
842 def action_set_tp_src(self, action, switch):
843 self.set_tp_src(action.tp_port)
844
845 def action_set_tp_dst(self, action, switch):
846 self.set_tp_dst(action.tp_port)
847
848 def action_copy_ttl_out(self, action, switch):
849 self.copy_ttl_out()
850
851 def action_copy_ttl_in(self, action, switch):
852 self.copy_ttl_in()
853
854 def action_set_mpls_label(self, action, switch):
855 self.set_mpls_label(action.mpls_label)
856
857 def action_set_mpls_tc(self, action, switch):
858 self.set_mpls_tc(action.mpls_tc)
859
860 def action_set_mpls_ttl(self, action, switch):
861 self.set_mpls_ttl(action.mpls_ttl)
862
863 def action_dec_mpls_ttl(self, action, switch):
864 self.dec_mpls_ttl()
865
866 def action_push_vlan(self, action, switch):
867 self.push_vlan(action.ethertype)
868
869 def action_pop_vlan(self, action, switch):
870 self.pop_vlan()
871
872 def action_push_mpls(self, action, switch):
873 self.push_mpls(action.ethertype)
874
875 def action_pop_mpls(self, action, switch):
876 self.pop_mpls(action.ethertype)
877
878 def action_experimenter(self, action, switch):
879 pass
880
881 def action_set_nw_ttl(self, action, switch):
882 self.set_nw_ttl(action.nw_ttl)
883
884 def action_dec_nw_ttl(self, action, switch):
885 self.dec_nw_ttl()
886
887 def action_group(self, action, switch):
888 pass
889
890 def execute_action_set(self, switch):
891 """
892 Execute the actions in the action set for the packet
893 according to the order given in ordered_action_list.
894
895 This determines the order in which
896 actions in the packet's action set are executed
897
898 @param switch The parent switch object (for sending pkts out)
899
900 @todo Verify the ordering in this list
901 """
902 cls = action.action_copy_ttl_in
903 if cls in self.action_set.keys():
904 self.logger.debug("Action copy_ttl_in")
905 self.action_copy_ttl_in(self.action_set[cls], switch)
906
907 cls = action.action_pop_mpls
908 if cls in self.action_set.keys():
909 self.logger.debug("Action pop_mpls")
910 self.action_pop_mpls(self.action_set[cls], switch)
911 cls = action.action_pop_vlan
912 if cls in self.action_set.keys():
913 self.logger.debug("Action pop_vlan")
914 self.action_pop_vlan(self.action_set[cls], switch)
915 cls = action.action_push_mpls
916 if cls in self.action_set.keys():
917 self.logger.debug("Action push_mpls")
918 self.action_push_mpls(self.action_set[cls], switch)
919 cls = action.action_push_vlan
920 if cls in self.action_set.keys():
921 self.logger.debug("Action push_vlan")
922 self.action_push_vlan(self.action_set[cls], switch)
923
924 cls = action.action_dec_mpls_ttl
925 if cls in self.action_set.keys():
926 self.logger.debug("Action dec_mpls_ttl")
927 self.action_dec_mpls_ttl(self.action_set[cls], switch)
928 cls = action.action_dec_nw_ttl
929 if cls in self.action_set.keys():
930 self.logger.debug("Action dec_nw_ttl")
931 self.action_dec_nw_ttl(self.action_set[cls], switch)
932 cls = action.action_copy_ttl_out
933 if cls in self.action_set.keys():
934 self.logger.debug("Action copy_ttl_out")
935 self.action_copy_ttl_out(self.action_set[cls], switch)
936
937 cls = action.action_set_dl_dst
938 if cls in self.action_set.keys():
939 self.logger.debug("Action set_dl_dst")
940 self.action_set_dl_dst(self.action_set[cls], switch)
941 cls = action.action_set_dl_src
942 if cls in self.action_set.keys():
943 self.logger.debug("Action set_dl_src")
944 self.action_set_dl_src(self.action_set[cls], switch)
945 cls = action.action_set_mpls_label
946 if cls in self.action_set.keys():
947 self.logger.debug("Action set_mpls_label")
948 self.action_set_mpls_label(self.action_set[cls], switch)
949 cls = action.action_set_mpls_tc
950 if cls in self.action_set.keys():
951 self.logger.debug("Action set_mpls_tc")
952 self.action_set_mpls_tc(self.action_set[cls], switch)
953 cls = action.action_set_mpls_ttl
954 if cls in self.action_set.keys():
955 self.logger.debug("Action set_mpls_ttl")
956 self.action_set_mpls_ttl(self.action_set[cls], switch)
957 cls = action.action_set_nw_dst
958 if cls in self.action_set.keys():
959 self.logger.debug("Action set_nw_dst")
960 self.action_set_nw_dst(self.action_set[cls], switch)
961 cls = action.action_set_nw_ecn
962 if cls in self.action_set.keys():
963 self.logger.debug("Action set_nw_ecn")
964 self.action_set_nw_ecn(self.action_set[cls], switch)
965 cls = action.action_set_nw_src
966 if cls in self.action_set.keys():
967 self.logger.debug("Action set_nw_src")
968 self.action_set_nw_src(self.action_set[cls], switch)
969 cls = action.action_set_nw_tos
970 if cls in self.action_set.keys():
971 self.logger.debug("Action set_nw_tos")
972 self.action_set_nw_tos(self.action_set[cls], switch)
973 cls = action.action_set_nw_ttl
974 if cls in self.action_set.keys():
975 self.logger.debug("Action set_nw_ttl")
976 self.action_set_nw_ttl(self.action_set[cls], switch)
977 cls = action.action_set_queue
978 if cls in self.action_set.keys():
979 self.logger.debug("Action set_queue")
980 self.action_set_queue(self.action_set[cls], switch)
981 cls = action.action_set_tp_dst
982 if cls in self.action_set.keys():
983 self.logger.debug("Action set_tp_dst")
984 self.action_set_tp_dst(self.action_set[cls], switch)
985 cls = action.action_set_tp_src
986 if cls in self.action_set.keys():
987 self.logger.debug("Action set_tp_src")
988 self.action_set_tp_src(self.action_set[cls], switch)
989 cls = action.action_set_vlan_pcp
990 if cls in self.action_set.keys():
991 self.logger.debug("Action set_vlan_pcp")
992 self.action_set_vlan_pcp(self.action_set[cls], switch)
993 cls = action.action_set_vlan_vid
994 if cls in self.action_set.keys():
995 self.logger.debug("Action set_vlan_vid")
996 self.action_set_vlan_vid(self.action_set[cls], switch)
997
998 cls = action.action_group
999 if cls in self.action_set.keys():
1000 self.logger.debug("Action group")
1001 self.action_group(self.action_set[cls], switch)
1002 cls = action.action_experimenter
1003 if cls in self.action_set.keys():
1004 self.logger.debug("Action experimenter")
1005 self.action_experimenter(self.action_set[cls], switch)
1006 cls = action.action_output
1007 if cls in self.action_set.keys():
1008 self.logger.debug("Action set_output")
1009 self.action_output(self.action_set[cls], switch)
1010
1011
1012def ascii_ip_to_bin(ip):
1013 """ Take '192.168.0.1' and return the NBO decimal equivalent 0xc0a80101 """
1014 #Lookup the cleaner, one-line way of doing this
1015 # or if there isn't just a library (!?)
1016 s = ip.split('.')
1017 return struct.unpack("!L", struct.pack("BBBB", int(s[0]),
1018 int(s[1]),
1019 int(s[2]),
1020 int(s[3]) ))[0]
1021
1022
1023class parse_error(Exception):
1024 """
1025 Thrown internally if there is an error in packet parsing
1026 """
1027
1028 def __init__(self, why):
1029 self.why = why
1030
1031 def __str__(self):
1032 return "%s:: %s" % (super.__str__(self), self.why)
1033
1034class packet_test(unittest.TestCase):
1035 """
1036 Unit tests for packet class
1037 """
1038
1039 def ascii_to_data(self, str):
1040 return binascii.unhexlify(str.translate(string.maketrans('',''),
1041 string.whitespace))
1042
1043 def setUp(self):
1044 """
1045 Simple packet data for parsing tests.
1046
1047 Ethernet II, Src: Fujitsu_ef:cd:8d (00:17:42:ef:cd:8d),
1048 Dst: ZhsZeitm_5d:24:00 (00:d0:05:5d:24:00)
1049 Internet Protocol, Src: 172.24.74.96 (172.24.74.96),
1050 Dst: 171.64.74.58 (171.64.74.58)
1051 Transmission Control Protocol, Src Port: 59581 (59581),
1052 Dst Port: ssh (22), Seq: 2694, Ack: 2749, Len: 48
1053 """
1054 pktdata = self.ascii_to_data(
1055 """00 d0 05 5d 24 00 00 17 42 ef cd 8d 08 00 45 10
1056 00 64 65 67 40 00 40 06 e9 29 ac 18 4a 60 ab 40
1057 4a 3a e8 bd 00 16 7c 28 2f 88 f2 bd 7a 03 80 18
1058 00 b5 ec 49 00 00 01 01 08 0a 00 d1 46 8b 32 ed
1059 7c 88 78 4b 8a dc 0a 1f c4 d3 02 a3 ae 1d 3c aa
1060 6f 1a 36 9f 27 11 12 71 5b 5d 88 f2 97 fa e7 f9
1061 99 c1 9f 9c 7f c5 1e 3e 45 c6 a6 ac ec 0b 87 64
1062 98 dd""")
1063 self.pkt = Packet(data=pktdata)
1064
1065 """
1066 MPLS packet data for MPLS parsing tests.
1067
1068 Ethernet II, Src: Fujitsu_ef:cd:8d (00:17:42:ef:cd:8d),
1069 Dst: ZhsZeitm_5d:24:00 (00:d0:05:5d:24:00)
1070 MPLS, Label: 0xFEFEF, TC: 5, S: 1, TTL: 0xAA
1071 Internet Protocol, Src: 172.24.74.96 (172.24.74.96),
1072 Dst: 171.64.74.58 (171.64.74.58)
1073 Transmission Control Protocol, Src Port: 59581 (59581),
1074 Dst Port: ssh (22), Seq: 2694, Ack: 2749, Len: 48
1075 """
1076 mplspktdata = self.ascii_to_data(
1077 """00 d0 05 5d 24 00 00 17 42 ef cd 8d 88 47
1078 FE FE FB AA
1079 45 10 00 64 65 67 40 00 40 06 e9 29 ac 18 4a 60
1080 ab 40 4a 3a
1081 e8 bd 00 16 7c 28 2f 88 f2 bd 7a 03 80 18
1082 00 b5 ec 49 00 00 01 01 08 0a 00 d1 46 8b 32 ed
1083 7c 88 78 4b 8a dc 0a 1f c4 d3 02 a3 ae 1d 3c aa
1084 6f 1a 36 9f 27 11 12 71 5b 5d 88 f2 97 fa e7 f9
1085 99 c1 9f 9c 7f c5 1e 3e 45 c6 a6 ac ec 0b 87 64
1086 98 dd""")
1087 self.mplspkt = Packet(data=mplspktdata)
1088
1089 def runTest(self):
1090 self.assertTrue(self.pkt)
1091
1092class l2_parsing_test(packet_test):
1093 def runTest(self):
1094 match = self.pkt.match
1095 self.assertEqual(match.dl_dst,[0x00,0xd0,0x05,0x5d,0x24,0x00])
1096 self.assertEqual(match.dl_src,[0x00,0x17,0x42,0xef,0xcd,0x8d])
1097 self.assertEqual(match.dl_type,ETHERTYPE_IP)
1098
1099class mpls_parsing_test(packet_test):
1100 def runTest(self):
1101 match = self.mplspkt.match
1102 self.assertEqual(match.mpls_label, 0xFEFEF)
1103 self.assertEqual(match.mpls_tc, 5)
1104
1105class ip_parsing_test(packet_test):
1106 def runTest(self):
1107 match = self.pkt.match
1108 # @todo Verify byte ordering of the following
1109 self.assertEqual(match.nw_dst,ascii_ip_to_bin('171.64.74.58'))
1110 self.assertEqual(match.nw_src,ascii_ip_to_bin('172.24.74.96'))
1111 self.assertEqual(match.nw_proto, socket.IPPROTO_TCP)
1112
1113class mpls_setting_test(packet_test):
1114 def runTest(self):
1115 orig_len = len(self.mplspkt)
1116 label = 0x12345
1117 tc = 6
1118 ttl = 0x78
1119 self.mplspkt.set_mpls_label(label)
1120 self.mplspkt.set_mpls_tc(tc)
1121 self.mplspkt.set_mpls_ttl(ttl)
1122 self.mplspkt.dec_mpls_ttl()
1123 self.mplspkt.parse()
1124
1125 self.assertEqual(len(self.mplspkt), orig_len)
1126 self.assertTrue(self.mplspkt.mpls_tag_offset)
1127 match = self.mplspkt.match
1128
1129 self.assertEqual(match.mpls_label, label)
1130 self.assertEqual(match.mpls_tc, tc)
1131 new_ttl = struct.unpack("B", self.mplspkt.data[self.mplspkt.mpls_tag_offset + 3:
1132 self.mplspkt.mpls_tag_offset + 4])[0]
1133 self.assertEqual(new_ttl, ttl - 1)
1134
1135class mpls_pop_test(packet_test):
1136 def runTest(self):
1137 orig_len = len(self.mplspkt)
1138 self.mplspkt.pop_mpls(ETHERTYPE_IP)
1139 self.mplspkt.parse()
1140
1141 self.assertEqual(len(self.mplspkt), orig_len - 4)
1142 self.assertFalse(self.mplspkt.mpls_tag_offset)
1143 match = self.mplspkt.match
1144
1145 self.assertEqual(match.dl_type,ETHERTYPE_IP)
1146 self.assertEqual(match.nw_dst,ascii_ip_to_bin('171.64.74.58'))
1147 self.assertEqual(match.nw_src,ascii_ip_to_bin('172.24.74.96'))
1148 self.assertEqual(match.nw_proto, socket.IPPROTO_TCP)
1149
1150class mpls_push_test(packet_test):
1151 def runTest(self):
1152 orig_len = len(self.pkt)
1153 self.pkt.push_mpls(ETHERTYPE_MPLS)
1154 self.pkt.parse()
1155
1156 self.assertEqual(len(self.pkt), orig_len + 4)
1157 self.assertTrue(self.pkt.mpls_tag_offset)
1158 match = self.pkt.match
1159
1160 self.assertEqual(match.dl_type, ETHERTYPE_MPLS)
1161 self.assertEqual(match.mpls_label, 0)
1162 self.assertEqual(match.mpls_tc, 0)
1163
1164class ip_setting_test(packet_test):
1165 def runTest(self):
1166 orig_len = len(self.pkt)
1167 ip1 = '11.22.33.44'
1168 ip2 = '55.66.77.88'
1169 self.pkt.set_nw_src(ascii_ip_to_bin(ip1))
1170 self.pkt.set_nw_dst(ascii_ip_to_bin(ip2))
1171 self.pkt.parse()
1172 self.assertEqual(len(self.pkt), orig_len)
1173 match = self.pkt.match
1174
1175 # @todo Verify byte ordering of the following
1176 self.assertEqual(match.nw_src,ascii_ip_to_bin(ip1))
1177 self.assertEqual(match.nw_dst,ascii_ip_to_bin(ip2))
1178
1179
1180
1181
1182class l4_parsing_test(packet_test):
1183 def runTest(self):
1184 match = self.pkt.match
1185 self.assertEqual(match.tp_dst,22)
1186 self.assertEqual(match.tp_src,59581)
1187
1188class l4_setting_test(packet_test):
1189 def runTest(self):
1190 orig_len = len(self.pkt)
1191 self.pkt.set_tp_src(777)
1192 self.pkt.set_tp_dst(666)
1193 self.pkt.parse()
1194 self.assertEqual(len(self.pkt), orig_len)
1195 match = self.pkt.match
1196 self.assertEqual(match.tp_src,777)
1197 self.assertEqual(match.tp_dst,666)
1198
1199class simple_tcp_test(unittest.TestCase):
1200 """ Make sure that simple_tcp_test does what it should
1201 pktlen=100,
1202 dl_dst='00:01:02:03:04:05',
1203 dl_src='00:06:07:08:09:0a',
1204 dl_vlan_enable=False,
1205 dl_vlan=0,
1206 dl_vlan_pcp=0,
1207 dl_vlan_cfi=0,
1208 ip_src='192.168.0.1',
1209 ip_dst='192.168.0.2',
1210 ip_tos=0,
1211 tcp_sport=1234,
1212 tcp_dport=80):
1213 """
1214 def setUp(self):
1215 self.pkt = Packet().simple_tcp_packet()
1216 self.pkt.parse()
1217 logging.basicConfig(filename="", level=logging.DEBUG)
1218 self.logger = logging.getLogger('unittest')
1219
1220 def runTest(self):
1221 match = self.pkt.match
1222 self.assertEqual(match.dl_dst, [0x00, 0x01, 0x02, 0x03, 0x04, 0x05])
1223 self.assertEqual(match.dl_src, [0x00, 0x06, 0x07, 0x08, 0x09, 0x0a])
1224 self.assertEqual(match.dl_type, ETHERTYPE_IP)
1225 self.assertEqual(match.nw_src, ascii_ip_to_bin('192.168.0.1'))
1226 self.assertEqual(match.nw_dst, ascii_ip_to_bin('192.168.0.2'))
1227 self.assertEqual(match.tp_dst, 80)
1228 self.assertEqual(match.tp_src, 1234)
1229
1230class simple_vlan_test(simple_tcp_test):
1231 """ Make sure that simple_tcp_test does what it should with vlans
1232
1233 """
1234
1235 def runTest(self):
1236 self.pkt = Packet().simple_tcp_packet(dl_vlan_enable=True,dl_vlan=0x0abc)
1237 self.pkt.parse()
1238 match = self.pkt.match
1239 #self.logger.debug("Packet=\n%s" % self.pkt.show())
1240 self.assertEqual(match.dl_dst, [0x00, 0x01, 0x02, 0x03, 0x04, 0x05])
1241 self.assertEqual(match.dl_src, [0x00, 0x06, 0x07, 0x08, 0x09, 0x0a])
1242 self.assertEqual(match.dl_type, ETHERTYPE_IP)
1243 self.assertEqual(match.nw_src, ascii_ip_to_bin('192.168.0.1'))
1244 self.assertEqual(match.nw_dst, ascii_ip_to_bin('192.168.0.2'))
1245 self.assertEqual(match.tp_dst, 80)
1246 self.assertEqual(match.tp_src, 1234)
1247 self.assertEqual(match.dl_vlan, 0xabc)
1248
1249class vlan_mod(simple_tcp_test):
1250 """ Start with a packet with no vlan, add one, change it, remove it"""
1251 def runTest(self):
1252 old_len = len(self.pkt)
1253 match = self.pkt.match
1254 self.assertEqual(match.dl_vlan, 0xffff)
1255 self.assertEqual(len(self.pkt), old_len)
1256 #self.logger.debug("PKT=\n" + self.pkt.show())
1257 self.pkt.push_vlan(ETHERTYPE_VLAN) # implicitly pushes vid=0
1258 self.assertEqual(len(self.pkt), old_len + 4)
1259 self.assertEqual(match.dl_vlan, 0)
1260 #self.logger.debug("PKT=\n" + self.pkt.show())
1261 self.assertEqual(match.dl_type,ETHERTYPE_IP)
1262 self.pkt.set_vlan_vid(0xbabe)
1263 self.assertEqual(match.dl_vlan, 0x0abe)
1264
1265class simple_tcp_with_mpls_test(unittest.TestCase):
1266 """ Make sure that simple_tcp_packet does what it should
1267 pktlen=100,
1268 dl_dst='00:01:02:03:04:05',
1269 dl_src='00:06:07:08:09:0a',
1270 dl_vlan_enable=False,
1271 dl_vlan=0,
1272 dl_vlan_pcp=0,
1273 dl_vlan_cfi=0,
1274 ip_src='192.168.0.1',
1275 ip_dst='192.168.0.2',
1276 ip_tos=0,
1277 tcp_sport=1234,
1278 tcp_dport=80):
1279 """
1280 def setUp(self):
1281 tag1 = MplsTag(0xabcde, 0x5, 0xAA)
1282 tag2 = MplsTag(0x54321, 0x2, 0xBB)
1283 mpls_tags = (tag1, tag2)
1284
1285 self.pkt = Packet().simple_tcp_packet(mpls_type=ETHERTYPE_MPLS,
1286 mpls_tags=mpls_tags)
1287 self.pkt.parse()
1288
1289 def runTest(self):
1290 match = self.pkt.match
1291 self.assertEqual(match.dl_dst, [0x00, 0x01, 0x02, 0x03, 0x04, 0x05])
1292 self.assertEqual(match.dl_src, [0x00, 0x06, 0x07, 0x08, 0x09, 0x0a])
1293 self.assertEqual(match.dl_type, ETHERTYPE_MPLS)
1294 self.assertEqual(match.mpls_label, 0xabcde)
1295 self.assertEqual(match.mpls_tc, 0x5)
1296
1297if __name__ == '__main__':
1298 print("Running packet tests\n")
1299 unittest.main()
1300