1# $Id: ip.py 87 2013-03-05 19:41:04Z andrewflnr@gmail.com $
2# -*- coding: utf-8 -*-
3"""Internet Protocol."""
4from __future__ import print_function
5from __future__ import absolute_import
6
7from . import dpkt
8from .compat import iteritems
9
10
11class IP(dpkt.Packet):
12    """Internet Protocol.
13
14    TODO: Longer class information....
15
16    Attributes:
17        __hdr__: Header fields of IP.
18        TODO.
19    """
20
21    __hdr__ = (
22        ('_v_hl', 'B', (4 << 4) | (20 >> 2)),
23        ('tos', 'B', 0),
24        ('len', 'H', 20),
25        ('id', 'H', 0),
26        ('off', 'H', 0),
27        ('ttl', 'B', 64),
28        ('p', 'B', 0),
29        ('sum', 'H', 0),
30        ('src', '4s', b'\x00' * 4),
31        ('dst', '4s', b'\x00' * 4)
32    )
33    _protosw = {}
34    opts = b''
35
36    def __init__(self, *args, **kwargs):
37        super(IP, self).__init__(*args, **kwargs)
38
39        # If IP packet is not initialized by string and the len field has
40        # been rewritten.
41        if not args and 'len' not in kwargs:
42            self.len = self.__len__()
43
44    @property
45    def v(self):
46        return self._v_hl >> 4
47
48    @v.setter
49    def v(self, v):
50        self._v_hl = (v << 4) | (self._v_hl & 0xf)
51
52    @property
53    def hl(self):
54        return self._v_hl & 0xf
55
56    @hl.setter
57    def hl(self, hl):
58        self._v_hl = (self._v_hl & 0xf0) | hl
59
60    @property
61    def rf(self):
62        return (self.off >> 15) & 0x1
63
64    @rf.setter
65    def rf(self, rf):
66        self.off = (self.off & ~IP_RF) | (rf << 15)
67
68    @property
69    def df(self):
70        return (self.off >> 14) & 0x1
71
72    @df.setter
73    def df(self, df):
74        self.off = (self.off & ~IP_DF) | (df << 14)
75
76    @property
77    def mf(self):
78        return (self.off >> 13) & 0x1
79
80    @mf.setter
81    def mf(self, mf):
82        self.off = (self.off & ~IP_MF) | (mf << 13)
83
84    @property
85    def offset(self):
86        return (self.off & IP_OFFMASK) << 3
87
88    @offset.setter
89    def offset(self, offset):
90        self.off = (self.off & ~IP_OFFMASK) | (offset >> 3)
91
92    def __len__(self):
93        return self.__hdr_len__ + len(self.opts) + len(self.data)
94
95    def __bytes__(self):
96        self.len = self.__len__()
97        if self.sum == 0:
98            self.sum = dpkt.in_cksum(self.pack_hdr() + bytes(self.opts))
99            if (self.p == 6 or self.p == 17) and (self.off & (IP_MF | IP_OFFMASK)) == 0 and \
100                    isinstance(self.data, dpkt.Packet) and self.data.sum == 0:
101                # Set zeroed TCP and UDP checksums for non-fragments.
102                p = bytes(self.data)
103                s = dpkt.struct.pack('>4s4sxBH', self.src, self.dst,
104                                     self.p, len(p))
105                s = dpkt.in_cksum_add(0, s)
106                s = dpkt.in_cksum_add(s, p)
107                self.data.sum = dpkt.in_cksum_done(s)
108
109                # RFC 768 (Fields):
110                # If the computed checksum is zero, it is transmitted as all
111                # ones (the equivalent in one's complement arithmetic). An all
112                # zero transmitted checksum value means that the transmitter
113                # generated no checksum (for debugging or for higher level
114                # protocols that don't care).
115                if self.p == 17 and self.data.sum == 0:
116                    self.data.sum = 0xffff
117                    # XXX - skip transports which don't need the pseudoheader
118        return self.pack_hdr() + bytes(self.opts) + bytes(self.data)
119
120    def unpack(self, buf):
121        dpkt.Packet.unpack(self, buf)
122        ol = ((self._v_hl & 0xf) << 2) - self.__hdr_len__
123        if ol < 0:
124            raise dpkt.UnpackError('invalid header length')
125        self.opts = buf[self.__hdr_len__:self.__hdr_len__ + ol]
126        if self.len:
127            buf = buf[self.__hdr_len__ + ol:self.len]
128        else:  # very likely due to TCP segmentation offload
129            buf = buf[self.__hdr_len__ + ol:]
130        try:
131            self.data = self._protosw[self.p](buf) if self.offset == 0 else buf
132            setattr(self, self.data.__class__.__name__.lower(), self.data)
133        except (KeyError, dpkt.UnpackError):
134            self.data = buf
135
136    @classmethod
137    def set_proto(cls, p, pktclass):
138        cls._protosw[p] = pktclass
139
140    @classmethod
141    def get_proto(cls, p):
142        return cls._protosw[p]
143
144
145# IP Headers
146IP_ADDR_LEN = 0x04
147IP_ADDR_BITS = 0x20
148
149IP_HDR_LEN = 0x14
150IP_OPT_LEN = 0x02
151IP_OPT_LEN_MAX = 0x28
152IP_HDR_LEN_MAX = IP_HDR_LEN + IP_OPT_LEN_MAX
153
154IP_LEN_MAX = 0xffff
155IP_LEN_MIN = IP_HDR_LEN
156
157# Reserved Addresses
158IP_ADDR_ANY = "\x00\x00\x00\x00"    # 0.0.0.0
159IP_ADDR_BROADCAST = "\xff\xff\xff\xff"    # 255.255.255.255
160IP_ADDR_LOOPBACK = "\x7f\x00\x00\x01"    # 127.0.0.1
161IP_ADDR_MCAST_ALL = "\xe0\x00\x00\x01"    # 224.0.0.1
162IP_ADDR_MCAST_LOCAL = "\xe0\x00\x00\xff"    # 224.0.0.255
163
164# Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474")
165IP_TOS_DEFAULT = 0x00  # default
166IP_TOS_LOWDELAY = 0x10  # low delay
167IP_TOS_THROUGHPUT = 0x08  # high throughput
168IP_TOS_RELIABILITY = 0x04  # high reliability
169IP_TOS_LOWCOST = 0x02  # low monetary cost - XXX
170IP_TOS_ECT = 0x02  # ECN-capable transport
171IP_TOS_CE = 0x01  # congestion experienced
172
173# IP precedence (high 3 bits of ip_tos), hopefully unused
174IP_TOS_PREC_ROUTINE = 0x00
175IP_TOS_PREC_PRIORITY = 0x20
176IP_TOS_PREC_IMMEDIATE = 0x40
177IP_TOS_PREC_FLASH = 0x60
178IP_TOS_PREC_FLASHOVERRIDE = 0x80
179IP_TOS_PREC_CRITIC_ECP = 0xa0
180IP_TOS_PREC_INTERNETCONTROL = 0xc0
181IP_TOS_PREC_NETCONTROL = 0xe0
182
183# Fragmentation flags (ip_off)
184IP_RF = 0x8000  # reserved
185IP_DF = 0x4000  # don't fragment
186IP_MF = 0x2000  # more fragments (not last frag)
187IP_OFFMASK = 0x1fff  # mask for fragment offset
188
189# Time-to-live (ip_ttl), seconds
190IP_TTL_DEFAULT = 64  # default ttl, RFC 1122, RFC 1340
191IP_TTL_MAX = 255  # maximum ttl
192
193# Protocol (ip_p) - http://www.iana.org/assignments/protocol-numbers
194IP_PROTO_IP = 0  # dummy for IP
195IP_PROTO_HOPOPTS = IP_PROTO_IP  # IPv6 hop-by-hop options
196IP_PROTO_ICMP = 1  # ICMP
197IP_PROTO_IGMP = 2  # IGMP
198IP_PROTO_GGP = 3  # gateway-gateway protocol
199IP_PROTO_IPIP = 4  # IP in IP
200IP_PROTO_ST = 5  # ST datagram mode
201IP_PROTO_TCP = 6  # TCP
202IP_PROTO_CBT = 7  # CBT
203IP_PROTO_EGP = 8  # exterior gateway protocol
204IP_PROTO_IGP = 9  # interior gateway protocol
205IP_PROTO_BBNRCC = 10  # BBN RCC monitoring
206IP_PROTO_NVP = 11  # Network Voice Protocol
207IP_PROTO_PUP = 12  # PARC universal packet
208IP_PROTO_ARGUS = 13  # ARGUS
209IP_PROTO_EMCON = 14  # EMCON
210IP_PROTO_XNET = 15  # Cross Net Debugger
211IP_PROTO_CHAOS = 16  # Chaos
212IP_PROTO_UDP = 17  # UDP
213IP_PROTO_MUX = 18  # multiplexing
214IP_PROTO_DCNMEAS = 19  # DCN measurement
215IP_PROTO_HMP = 20  # Host Monitoring Protocol
216IP_PROTO_PRM = 21  # Packet Radio Measurement
217IP_PROTO_IDP = 22  # Xerox NS IDP
218IP_PROTO_TRUNK1 = 23  # Trunk-1
219IP_PROTO_TRUNK2 = 24  # Trunk-2
220IP_PROTO_LEAF1 = 25  # Leaf-1
221IP_PROTO_LEAF2 = 26  # Leaf-2
222IP_PROTO_RDP = 27  # "Reliable Datagram" proto
223IP_PROTO_IRTP = 28  # Inet Reliable Transaction
224IP_PROTO_TP = 29  # ISO TP class 4
225IP_PROTO_NETBLT = 30  # Bulk Data Transfer
226IP_PROTO_MFPNSP = 31  # MFE Network Services
227IP_PROTO_MERITINP = 32  # Merit Internodal Protocol
228IP_PROTO_SEP = 33  # Sequential Exchange proto
229IP_PROTO_3PC = 34  # Third Party Connect proto
230IP_PROTO_IDPR = 35  # Interdomain Policy Route
231IP_PROTO_XTP = 36  # Xpress Transfer Protocol
232IP_PROTO_DDP = 37  # Datagram Delivery Proto
233IP_PROTO_CMTP = 38  # IDPR Ctrl Message Trans
234IP_PROTO_TPPP = 39  # TP++ Transport Protocol
235IP_PROTO_IL = 40  # IL Transport Protocol
236IP_PROTO_IP6 = 41  # IPv6
237IP_PROTO_SDRP = 42  # Source Demand Routing
238IP_PROTO_ROUTING = 43  # IPv6 routing header
239IP_PROTO_FRAGMENT = 44  # IPv6 fragmentation header
240IP_PROTO_RSVP = 46  # Reservation protocol
241IP_PROTO_GRE = 47  # General Routing Encap
242IP_PROTO_MHRP = 48  # Mobile Host Routing
243IP_PROTO_ENA = 49  # ENA
244IP_PROTO_ESP = 50  # Encap Security Payload
245IP_PROTO_AH = 51  # Authentication Header
246IP_PROTO_INLSP = 52  # Integated Net Layer Sec
247IP_PROTO_SWIPE = 53  # SWIPE
248IP_PROTO_NARP = 54  # NBMA Address Resolution
249IP_PROTO_MOBILE = 55  # Mobile IP, RFC 2004
250IP_PROTO_TLSP = 56  # Transport Layer Security
251IP_PROTO_SKIP = 57  # SKIP
252IP_PROTO_ICMP6 = 58  # ICMP for IPv6
253IP_PROTO_NONE = 59  # IPv6 no next header
254IP_PROTO_DSTOPTS = 60  # IPv6 destination options
255IP_PROTO_ANYHOST = 61  # any host internal proto
256IP_PROTO_CFTP = 62  # CFTP
257IP_PROTO_ANYNET = 63  # any local network
258IP_PROTO_EXPAK = 64  # SATNET and Backroom EXPAK
259IP_PROTO_KRYPTOLAN = 65  # Kryptolan
260IP_PROTO_RVD = 66  # MIT Remote Virtual Disk
261IP_PROTO_IPPC = 67  # Inet Pluribus Packet Core
262IP_PROTO_DISTFS = 68  # any distributed fs
263IP_PROTO_SATMON = 69  # SATNET Monitoring
264IP_PROTO_VISA = 70  # VISA Protocol
265IP_PROTO_IPCV = 71  # Inet Packet Core Utility
266IP_PROTO_CPNX = 72  # Comp Proto Net Executive
267IP_PROTO_CPHB = 73  # Comp Protocol Heart Beat
268IP_PROTO_WSN = 74  # Wang Span Network
269IP_PROTO_PVP = 75  # Packet Video Protocol
270IP_PROTO_BRSATMON = 76  # Backroom SATNET Monitor
271IP_PROTO_SUNND = 77  # SUN ND Protocol
272IP_PROTO_WBMON = 78  # WIDEBAND Monitoring
273IP_PROTO_WBEXPAK = 79  # WIDEBAND EXPAK
274IP_PROTO_EON = 80  # ISO CNLP
275IP_PROTO_VMTP = 81  # Versatile Msg Transport
276IP_PROTO_SVMTP = 82  # Secure VMTP
277IP_PROTO_VINES = 83  # VINES
278IP_PROTO_TTP = 84  # TTP
279IP_PROTO_NSFIGP = 85  # NSFNET-IGP
280IP_PROTO_DGP = 86  # Dissimilar Gateway Proto
281IP_PROTO_TCF = 87  # TCF
282IP_PROTO_EIGRP = 88  # EIGRP
283IP_PROTO_OSPF = 89  # Open Shortest Path First
284IP_PROTO_SPRITERPC = 90  # Sprite RPC Protocol
285IP_PROTO_LARP = 91  # Locus Address Resolution
286IP_PROTO_MTP = 92  # Multicast Transport Proto
287IP_PROTO_AX25 = 93  # AX.25 Frames
288IP_PROTO_IPIPENCAP = 94  # yet-another IP encap
289IP_PROTO_MICP = 95  # Mobile Internet Ctrl
290IP_PROTO_SCCSP = 96  # Semaphore Comm Sec Proto
291IP_PROTO_ETHERIP = 97  # Ethernet in IPv4
292IP_PROTO_ENCAP = 98  # encapsulation header
293IP_PROTO_ANYENC = 99  # private encryption scheme
294IP_PROTO_GMTP = 100  # GMTP
295IP_PROTO_IFMP = 101  # Ipsilon Flow Mgmt Proto
296IP_PROTO_PNNI = 102  # PNNI over IP
297IP_PROTO_PIM = 103  # Protocol Indep Multicast
298IP_PROTO_ARIS = 104  # ARIS
299IP_PROTO_SCPS = 105  # SCPS
300IP_PROTO_QNX = 106  # QNX
301IP_PROTO_AN = 107  # Active Networks
302IP_PROTO_IPCOMP = 108  # IP Payload Compression
303IP_PROTO_SNP = 109  # Sitara Networks Protocol
304IP_PROTO_COMPAQPEER = 110  # Compaq Peer Protocol
305IP_PROTO_IPXIP = 111  # IPX in IP
306IP_PROTO_VRRP = 112  # Virtual Router Redundancy
307IP_PROTO_PGM = 113  # PGM Reliable Transport
308IP_PROTO_ANY0HOP = 114  # 0-hop protocol
309IP_PROTO_L2TP = 115  # Layer 2 Tunneling Proto
310IP_PROTO_DDX = 116  # D-II Data Exchange (DDX)
311IP_PROTO_IATP = 117  # Interactive Agent Xfer
312IP_PROTO_STP = 118  # Schedule Transfer Proto
313IP_PROTO_SRP = 119  # SpectraLink Radio Proto
314IP_PROTO_UTI = 120  # UTI
315IP_PROTO_SMP = 121  # Simple Message Protocol
316IP_PROTO_SM = 122  # SM
317IP_PROTO_PTP = 123  # Performance Transparency
318IP_PROTO_ISIS = 124  # ISIS over IPv4
319IP_PROTO_FIRE = 125  # FIRE
320IP_PROTO_CRTP = 126  # Combat Radio Transport
321IP_PROTO_CRUDP = 127  # Combat Radio UDP
322IP_PROTO_SSCOPMCE = 128  # SSCOPMCE
323IP_PROTO_IPLT = 129  # IPLT
324IP_PROTO_SPS = 130  # Secure Packet Shield
325IP_PROTO_PIPE = 131  # Private IP Encap in IP
326IP_PROTO_SCTP = 132  # Stream Ctrl Transmission
327IP_PROTO_FC = 133  # Fibre Channel
328IP_PROTO_RSVPIGN = 134  # RSVP-E2E-IGNORE
329IP_PROTO_RAW = 255  # Raw IP packets
330IP_PROTO_RESERVED = IP_PROTO_RAW  # Reserved
331IP_PROTO_MAX = 255
332
333# XXX - auto-load IP dispatch table from IP_PROTO_* definitions
334
335
336def __load_protos():
337    g = globals()
338    for k, v in iteritems(g):
339        if k.startswith('IP_PROTO_'):
340            name = k[9:].lower()
341            try:
342                mod = __import__(name, g, level=1)
343                IP.set_proto(v, getattr(mod, name.upper()))
344            except (ImportError, AttributeError):
345                continue
346
347
348def _mod_init():
349    """Post-initialization called when all dpkt modules are fully loaded"""
350    if not IP._protosw:
351        __load_protos()
352
353
354def test_ip():
355    from . import udp
356
357    s = b'E\x00\x00"\x00\x00\x00\x00@\x11r\xc0\x01\x02\x03\x04\x01\x02\x03\x04\x00o\x00\xde\x00\x0e\xbf5foobar'
358    ip = IP(id=0, src=b'\x01\x02\x03\x04', dst=b'\x01\x02\x03\x04', p=17)
359    u = udp.UDP(sport=111, dport=222)
360    u.data = b'foobar'
361    u.ulen += len(u.data)
362    ip.data = u
363    ip.len += len(u)
364    assert (bytes(ip) == s)
365    assert (ip.v == 4)
366    assert (ip.hl == 5)
367
368    ip = IP(s)
369    assert (bytes(ip) == s)
370    assert (ip.udp.sport == 111)
371    assert (ip.udp.data == b'foobar')
372
373
374def test_dict():
375    ip = IP(id=0, src=b'\x01\x02\x03\x04', dst=b'\x01\x02\x03\x04', p=17)
376    d = dict(ip)
377
378    assert (d['src'] == b'\x01\x02\x03\x04')
379    assert (d['dst'] == b'\x01\x02\x03\x04')
380    assert (d['id'] == 0)
381    assert (d['p'] == 17)
382
383
384def test_hl():  # Todo chack this test method
385    s = (b'BB\x03\x00\x00\x00\x00\x00\x00\x00\xd0\x00\xec\xbc\xa5\x00\x00\x00\x03\x80\x00\x00\xd0'
386         b'\x01\xf2\xac\xa5"0\x01\x00\x14\x00\x02\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00')
387    try:
388        IP(s)
389    except dpkt.UnpackError:
390        pass
391
392
393def test_opt():
394    s = (b'\x4f\x00\x00\x3c\xae\x08\x00\x00\x40\x06\x18\x10\xc0\xa8\x0a\x26\xc0\xa8\x0a\x01\x07\x27'
395         b'\x08\x01\x02\x03\x04\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
396         b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
397    ip = IP(s)
398    ip.sum = 0
399    assert (bytes(ip) == s)
400
401
402def test_zerolen():
403    from . import tcp
404    d = b'X' * 2048
405    s = (b'\x45\x00\x00\x00\x34\xce\x40\x00\x80\x06\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\xcc\x4e'
406         b'\x0c\x38\x60\xff\xc6\x4e\x5f\x8a\x12\x98\x50\x18\x40\x29\x3a\xa3\x00\x00') + d
407    ip = IP(s)
408    assert (isinstance(ip.data, tcp.TCP))
409    assert (ip.tcp.data == d)
410
411
412def test_constuctor():
413    ip1 = IP(data=b"Hello world!")
414    ip2 = IP(data=b"Hello world!", len=0)
415    ip3 = IP(bytes(ip1))
416    ip4 = IP(bytes(ip2))
417    assert (bytes(ip1) == bytes(ip3))
418    assert (bytes(ip1) == b'E\x00\x00 \x00\x00\x00\x00@\x00z\xdf\x00\x00\x00\x00\x00\x00\x00\x00Hello world!')
419    assert (bytes(ip2) == bytes(ip4))
420    assert (bytes(ip2) == b'E\x00\x00 \x00\x00\x00\x00@\x00z\xdf\x00\x00\x00\x00\x00\x00\x00\x00Hello world!')
421
422
423def test_frag():
424    from . import ethernet
425    s = (b'\x00\x23\x20\xd4\x2a\x8c\x00\x23\x20\xd4\x2a\x8c\x08\x00\x45\x00\x00\x54\x00\x00\x40\x00'
426         b'\x40\x01\x25\x8d\x0a\x00\x00\x8f\x0a\x00\x00\x8e\x08\x00\x2e\xa0\x01\xff\x23\x73\x20\x48'
427         b'\x4a\x4d\x00\x00\x00\x00\x78\x85\x02\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17'
428         b'\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d'
429         b'\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37')
430    ip = ethernet.Ethernet(s).ip
431    assert (ip.rf == 0)
432    assert (ip.df == 1)
433    assert (ip.mf == 0)
434    assert (ip.offset == 0)
435
436    # test setters of fragmentation related attributes.
437    ip.rf = 1
438    ip.df = 0
439    ip.mf = 1
440    ip.offset = 1480
441    assert (ip.rf == 1)
442    assert (ip.df == 0)
443    assert (ip.mf == 1)
444    assert (ip.offset == 1480)
445
446
447def test_property_setters():
448    ip = IP()
449    assert ip.v == 4
450    ip.v = 6
451    assert ip.v == 6
452
453    assert ip.hl == 5
454    ip.hl = 7
455    assert ip.hl == 7
456
457
458def test_default_udp_checksum():
459    from dpkt.udp import UDP
460
461    udp = UDP(sport=1, dport=0xffdb)
462    ip = IP(src=b'\x00\x00\x00\x01', dst=b'\x00\x00\x00\x01', p=17, data=udp)
463    assert ip.p == 17
464    assert ip.data.sum == 0
465
466    # this forces recalculation of the data layer checksum
467    bytes(ip)
468
469    # during calculation the checksum was evaluated to 0x0000
470    # this was then conditionally set to 0xffff per RFC768
471    assert ip.data.sum == 0xffff
472