blob: f98e70915ca677409b1c35c258dd11abcd320ff0 [file] [log] [blame]
Kris Michielsen91074432017-06-22 13:00:20 +02001diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
2--- a/scapy/layers/inet6.py 2017-06-01 14:04:18.160881034 +0200
3+++ b/scapy/layers/inet6.py 2017-06-02 09:08:40.133800208 +0200
4@@ -369,6 +369,8 @@
5 return Raw
6 elif self.nh == 135 and len(p) > 3: # Mobile IPv6
7 return _mip6_mhtype2cls.get(ord(p[2]), MIP6MH_Generic)
8+ elif self.nh == 43 and ord(p[2]) == 4: # Segment Routing header
9+ return IPv6ExtHdrSegmentRouting
10 else:
11 return get_cls(ipv6nhcls.get(self.nh,"Raw"), "Raw")
12
13@@ -430,6 +432,14 @@
14 sd = strxor(sd, a)
15 sd = inet_ntop(socket.AF_INET6, sd)
16
17+ if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrSegmentRouting):
18+ # With segment routing header (rh == 4), the destination is
19+ # the first address of the IPv6 addresses list
20+ try:
21+ sd = self.addresses[0]
22+ except IndexError:
23+ sd = self.dst
24+
25 if self.nh == 44 and isinstance(self.payload, IPv6ExtHdrFragment):
26 nh = self.payload.nh
27
28@@ -489,6 +499,8 @@
29 return self.payload.answers(other.payload.payload)
30 elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrRouting):
31 return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting
32+ elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrSegmentRouting):
33+ return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting
34 elif other.nh == 60 and isinstance(other.payload, IPv6ExtHdrDestOpt):
35 return self.payload.payload.answers(other.payload.payload)
36 elif self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): # BU in reply to BRR, for instance
37@@ -919,6 +931,148 @@
38 pkt = pkt[:3]+struct.pack("B", len(self.addresses))+pkt[4:]
39 return _IPv6ExtHdr.post_build(self, pkt, pay)
40
41+######################### Segment Routing Header ############################
42+
43+# This implementation is based on draft 06, available at:
44+# https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-06
45+
46+class IPv6ExtHdrSegmentRoutingTLV(Packet):
47+ name = "IPv6 Option Header Segment Routing - Generic TLV"
48+ fields_desc = [ ByteField("type", 0),
49+ ByteField("len", 0),
50+ ByteField("reserved", 0),
51+ ByteField("flags", 0),
52+ StrLenField("value", "", length_from=lambda pkt: pkt.len) ]
53+
54+ def extract_padding(self, p):
55+ return "",p
56+
57+ registered_sr_tlv = {}
58+ @classmethod
59+ def register_variant(cls):
60+ cls.registered_sr_tlv[cls.type.default] = cls
61+
62+ @classmethod
63+ def dispatch_hook(cls, pkt=None, *args, **kargs):
64+ if pkt:
65+ tmp_type = ord(pkt[0])
66+ return cls.registered_sr_tlv.get(tmp_type, cls)
67+ return cls
68+
69+
70+class IPv6ExtHdrSegmentRoutingTLVIngressNode(IPv6ExtHdrSegmentRoutingTLV):
71+ name = "IPv6 Option Header Segment Routing - Ingress Node TLV"
72+ fields_desc = [ ByteField("type", 1),
73+ ByteField("len", 18),
74+ ByteField("reserved", 0),
75+ ByteField("flags", 0),
76+ IP6Field("ingress_node", "::1") ]
77+
78+
79+class IPv6ExtHdrSegmentRoutingTLVEgressNode(IPv6ExtHdrSegmentRoutingTLV):
80+ name = "IPv6 Option Header Segment Routing - Egress Node TLV"
81+ fields_desc = [ ByteField("type", 2),
82+ ByteField("len", 18),
83+ ByteField("reserved", 0),
84+ ByteField("flags", 0),
85+ IP6Field("egress_node", "::1") ]
86+
87+
88+class IPv6ExtHdrSegmentRoutingTLVPadding(IPv6ExtHdrSegmentRoutingTLV):
89+ name = "IPv6 Option Header Segment Routing - Padding TLV"
90+ fields_desc = [ ByteField("type", 4),
91+ FieldLenField("len", None, length_of="padding", fmt="B"),
92+ StrLenField("padding", b"\x00", length_from=lambda pkt: pkt.len) ]
93+
94+
95+class IPv6ExtHdrSegmentRouting(_IPv6ExtHdr):
96+ # 0 1 2 3
97+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
98+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99+ #| Next Header | Hdr Ext Len | Routing Type | Segments Left |
100+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101+ #| Last Entry | Flags | Tag |
102+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
103+ #| |
104+ #| Segment List[0] (128 bits IPv6 address) |
105+ #| |
106+ #| |
107+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
108+ #| |
109+ #| |
110+ # ...
111+ #| |
112+ #| |
113+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
114+ #| |
115+ #| Segment List[n] (128 bits IPv6 address) |
116+ #| |
117+ #| |
118+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119+ #// //
120+ #// Optional Type Length Value objects (variable) //
121+ #// //
122+ #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
123+ #
124+ # 0 1 2 3 4 5 6 7
125+ # +-+-+-+-+-+-+-+-+
126+ # |U|P|O|A|H| U |
127+ # +-+-+-+-+-+-+-+-+
128+
129+ name = "IPv6 Segment Routing Extension Header"
130+ fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
131+ ByteField("len", None),
132+ ByteField("type", 4),
133+ ByteField("segleft", None),
134+ ByteField("lastentry", None),
135+ BitField("unused1", 0, 1),
136+ BitField("protected", 0, 1),
137+ BitField("oam", 0, 1),
138+ BitField("alert", 0, 1),
139+ BitField("hmac", 0, 1),
140+ BitField("unused2", 0, 3),
141+ ShortField("tag", 0),
142+ IP6ListField("addresses", ["::1"],
143+ count_from=lambda pkt: pkt.lastentry+1),
144+ PacketListField("tlv_objects", [], IPv6ExtHdrSegmentRoutingTLV,
145+ length_from=lambda pkt: 8*pkt.len - 16*(pkt.lastentry+1)) ]
146+
147+ overload_fields = { IPv6: { "nh": 43 } }
148+
149+ def post_build(self, pkt, pay):
150+
151+ if self.len is None:
152+
153+ # The extension must be align on 8 bytes
154+ tmp_mod = (len(pkt) - 8) % 8
155+ if tmp_mod == 1:
156+ warning("IPv6ExtHdrSegmentRouting(): can't pad 1 byte !")
157+ elif tmp_mod >= 2:
158+ #Add the padding extension
159+ tmp_pad = b"\x00" * (tmp_mod-2)
160+ tlv = IPv6ExtHdrSegmentRoutingTLVPadding(padding=tmp_pad)
161+ pkt += str(tlv)
162+
163+ tmp_len = (len(pkt) - 8) / 8
164+ pkt = pkt[:1] + struct.pack("B", tmp_len)+ pkt[2:]
165+
166+ if self.segleft is None:
167+ tmp_len = len(self.addresses)
168+ if tmp_len:
169+ tmp_len -= 1
170+ pkt = pkt[:3] + struct.pack("B", tmp_len) + pkt[4:]
171+
172+ if self.lastentry is None:
173+ #km - changed to contain n-1
174+ tmp_len = len(self.addresses)
175+ if tmp_len:
176+ tmp_len -= 1
177+ #pkt = pkt[:4] + struct.pack("B", len(self.addresses)) + pkt[5:]
178+ pkt = pkt[:4] + struct.pack("B", tmp_len) + pkt[5:]
179+
180+ return _IPv6ExtHdr.post_build(self, pkt, pay)
181+
182+
183 ########################### Fragmentation Header ############################
184
185 class IPv6ExtHdrFragment(_IPv6ExtHdr):