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