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