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