1# scapy.contrib.description = Label Distribution Protocol (LDP)
2# scapy.contrib.status = loads
3
4# http://git.savannah.gnu.org/cgit/ldpscapy.git/snapshot/ldpscapy-5285b81d6e628043df2a83301b292f24a95f0ba1.tar.gz
5
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19# Copyright (C) 2010 Florian Duraffourg
20
21from __future__ import absolute_import
22import struct
23
24from scapy.packet import *
25from scapy.fields import *
26from scapy.ansmachine import *
27from scapy.layers.inet import UDP
28from scapy.layers.inet import TCP
29from scapy.base_classes import Net
30from scapy.modules.six.moves import range
31
32
33# Guess payload
34def guess_payload(p):
35    LDPTypes = {
36        0x0001: LDPNotification,
37        0x0100: LDPHello,
38        0x0200: LDPInit,
39        0x0201: LDPKeepAlive,
40        0x0300: LDPAddress,
41        0x0301: LDPAddressWM,
42        0x0400: LDPLabelMM,
43        0x0401: LDPLabelReqM,
44        0x0404: LDPLabelARM,
45        0x0402: LDPLabelWM,
46        0x0403: LDPLabelRelM,
47        }
48    type = struct.unpack("!H",p[0:2])[0]
49    type = type & 0x7fff
50    if type == 0x0001 and struct.unpack("!H",p[2:4])[0] > 20:
51        return LDP
52    if type in LDPTypes:
53        return LDPTypes[type]
54    else:
55        return conf.raw_layer
56
57## Fields ##
58
59# 3.4.1. FEC TLV
60
61class FecTLVField(StrField):
62    islist=1
63    def m2i(self, pkt, x):
64        nbr = struct.unpack("!H",x[2:4])[0]
65        used = 0
66        x=x[4:]
67        list=[]
68        while x:
69            #if x[0] == 1:
70            #   list.append('Wildcard')
71            #else:
72            #mask=ord(x[8*i+3])
73            #add=inet_ntoa(x[8*i+4:8*i+8])
74            mask=ord(x[3])
75            nbroctets = mask / 8
76            if mask % 8:
77                nbroctets += 1
78            add=inet_ntoa(x[4:4+nbroctets]+b"\x00"*(4-nbroctets))
79            list.append( (add, mask) )
80            used += 4 + nbroctets
81            x=x[4+nbroctets:]
82        return list
83    def i2m(self, pkt, x):
84        if isinstance(x, str):
85            return x
86        s = b"\x01\x00"
87        l = 0
88        fec = ""
89        for o in x:
90            fec += b"\x02\x00\x01"
91            # mask length
92            fec += struct.pack("!B",o[1])
93            # Prefix
94            fec += inet_aton(o[0])
95            l += 8
96        s += struct.pack("!H",l)
97        s += fec
98        return s
99    def size(self, s):
100        """Get the size of this field"""
101        l = 4 + struct.unpack("!H",s[2:4])[0]
102        return l
103    def getfield(self, pkt, s):
104        l = self.size(s)
105        return s[l:],self.m2i(pkt, s[:l])
106
107
108# 3.4.2.1. Generic Label TLV
109
110class LabelTLVField(StrField):
111    def m2i(self, pkt, x):
112        return struct.unpack("!I",x[4:8])[0]
113    def i2m(self, pkt, x):
114        if isinstance(x, str):
115            return x
116        s = b"\x02\x00\x00\x04"
117        s += struct.pack("!I",x)
118        return s
119    def size(self, s):
120        """Get the size of this field"""
121        l = 4 + struct.unpack("!H",s[2:4])[0]
122        return l
123    def getfield(self, pkt, s):
124        l = self.size(s)
125        return s[l:],self.m2i(pkt, s[:l])
126
127
128# 3.4.3. Address List TLV
129
130class AddressTLVField(StrField):
131    islist=1
132    def m2i(self, pkt, x):
133        nbr = struct.unpack("!H",x[2:4])[0] - 2
134        nbr /= 4
135        x=x[6:]
136        list=[]
137        for i in range(0, nbr):
138            add = x[4*i:4*i+4]
139            list.append(inet_ntoa(add))
140        return list
141    def i2m(self, pkt, x):
142        if isinstance(x, str):
143            return x
144        l=2+len(x)*4
145        s = b"\x01\x01"+struct.pack("!H",l)+b"\x00\x01"
146        for o in x:
147            s += inet_aton(o)
148        return s
149    def size(self, s):
150        """Get the size of this field"""
151        l = 4 + struct.unpack("!H",s[2:4])[0]
152        return l
153    def getfield(self, pkt, s):
154        l = self.size(s)
155        return s[l:],self.m2i(pkt, s[:l])
156
157
158# 3.4.6. Status TLV
159
160class StatusTLVField(StrField):
161    islist=1
162    def m2i(self, pkt, x):
163        l = []
164        statuscode = struct.unpack("!I",x[4:8])[0]
165        l.append( (statuscode & 2**31) >> 31)
166        l.append( (statuscode & 2**30) >> 30)
167        l.append( statuscode & 0x3FFFFFFF )
168        l.append( struct.unpack("!I", x[8:12])[0] )
169        l.append( struct.unpack("!H", x[12:14])[0] )
170        return l
171    def i2m(self, pkt, x):
172        if isinstance(x, str):
173            return x
174        s = b"\x03\x00" + struct.pack("!H",10)
175        statuscode = 0
176        if x[0] != 0:
177            statuscode += 2**31
178        if x[1] != 0:
179            statuscode += 2**30
180        statuscode += x[2]
181        s += struct.pack("!I",statuscode)
182        if len(x) > 3:
183            s += struct.pack("!I",x[3])
184        else:
185            s += b"\x00\x00\x00\x00"
186        if len(x) > 4:
187            s += struct.pack("!H",x[4])
188        else:
189            s += b"\x00\x00"
190        return s
191    def getfield(self, pkt, s):
192        l = 14
193        return s[l:],self.m2i(pkt, s[:l])
194
195
196# 3.5.2 Common Hello Parameters TLV
197class CommonHelloTLVField(StrField):
198    islist = 1
199    def m2i(self, pkt, x):
200        list = []
201        v = struct.unpack("!H",x[4:6])[0]
202        list.append(v)
203        flags = struct.unpack("B",x[6])[0]
204        v = ( flags & 0x80 ) >> 7
205        list.append(v)
206        v = ( flags & 0x40 ) >> 7
207        list.append(v)
208        return list
209    def i2m(self, pkt, x):
210        if isinstance(x, str):
211            return x
212        s = b"\x04\x00\x00\x04"
213        s += struct.pack("!H",x[0])
214        byte = 0
215        if x[1] == 1:
216            byte += 0x80
217        if x[2] == 1:
218            byte += 0x40
219        s += struct.pack("!B",byte)
220        s += b"\x00"
221        return s
222    def getfield(self, pkt, s):
223        l = 8
224        return s[l:],self.m2i(pkt, s[:l])
225
226
227# 3.5.3 Common Session Parameters TLV
228class CommonSessionTLVField(StrField):
229    islist = 1
230    def m2i(self, pkt, x):
231        l = [struct.unpack("!H", x[6:8])[0]]
232        octet = struct.unpack("B",x[8:9])[0]
233        l.append( (octet & 2**7 ) >> 7 )
234        l.append( (octet & 2**6 ) >> 6 )
235        l.append( struct.unpack("B",x[9:10])[0] )
236        l.append( struct.unpack("!H",x[10:12])[0] )
237        l.append( inet_ntoa(x[12:16]) )
238        l.append( struct.unpack("!H",x[16:18])[0] )
239        return l
240    def i2m(self, pkt, x):
241        if isinstance(x, str):
242            return x
243        s = b"\x05\x00\x00\x0E\x00\x01"
244        s += struct.pack("!H",x[0])
245        octet = 0
246        if x[1] != 0:
247            octet += 2**7
248        if x[2] != 0:
249            octet += 2**6
250        s += struct.pack("!B",octet)
251        s += struct.pack("!B",x[3])
252        s += struct.pack("!H",x[4])
253        s += inet_aton(x[5])
254        s += struct.pack("!H",x[6])
255        return s
256    def getfield(self, pkt, s):
257        l = 18
258        return s[l:],self.m2i(pkt, s[:l])
259
260
261
262## Messages ##
263
264# 3.5.1. Notification Message
265class LDPNotification(Packet):
266    name = "LDPNotification"
267    fields_desc = [ BitField("u",0,1),
268                    BitField("type", 0x0001, 15),
269                    ShortField("len", None),
270                    IntField("id", 0) ,
271                    StatusTLVField("status",(0,0,0,0,0)) ]
272    def post_build(self, p, pay):
273        if self.len is None:
274            l = len(p) - 4
275            p = p[:2]+struct.pack("!H", l)+p[4:]
276        return p+pay
277    def guess_payload_class(self, p):
278        return guess_payload(p)
279
280
281# 3.5.2. Hello Message
282class LDPHello(Packet):
283    name = "LDPHello"
284    fields_desc = [ BitField("u",0,1),
285                    BitField("type", 0x0100, 15),
286                    ShortField("len", None),
287                    IntField("id", 0) ,
288                    CommonHelloTLVField("params",[180,0,0]) ]
289    def post_build(self, p, pay):
290        if self.len is None:
291            l = len(p) - 4
292            p = p[:2]+struct.pack("!H", l)+p[4:]
293        return p+pay
294    def guess_payload_class(self, p):
295        return guess_payload(p)
296
297
298# 3.5.3. Initialization Message
299class LDPInit(Packet):
300    name = "LDPInit"
301    fields_desc = [ BitField("u",0,1),
302                    XBitField("type", 0x0200, 15),
303                    ShortField("len", None),
304                    IntField("id", 0),
305                    CommonSessionTLVField("params",None)]
306    def post_build(self, p, pay):
307        if self.len is None:
308            l = len(p) - 4
309            p = p[:2]+struct.pack("!H", l)+p[4:]
310        return p+pay
311    def guess_payload_class(self, p):
312        return guess_payload(p)
313
314
315# 3.5.4. KeepAlive Message
316class LDPKeepAlive(Packet):
317    name = "LDPKeepAlive"
318    fields_desc = [ BitField("u",0,1),
319                    XBitField("type", 0x0201, 15),
320                    ShortField("len", None),
321                    IntField("id", 0)]
322    def post_build(self, p, pay):
323        if self.len is None:
324            l = len(p) - 4
325            p = p[:2]+struct.pack("!H", l)+p[4:]
326        return p+pay
327    def guess_payload_class(self, p):
328        return guess_payload(p)
329
330
331# 3.5.5. Address Message
332
333class LDPAddress(Packet):
334    name = "LDPAddress"
335    fields_desc = [ BitField("u",0,1),
336                    XBitField("type", 0x0300, 15),
337                    ShortField("len", None),
338                    IntField("id", 0),
339                    AddressTLVField("address",None) ]
340    def post_build(self, p, pay):
341        if self.len is None:
342            l = len(p) - 4
343            p = p[:2]+struct.pack("!H", l)+p[4:]
344        return p+pay
345    def guess_payload_class(self, p):
346        return guess_payload(p)
347
348
349# 3.5.6. Address Withdraw Message
350
351class LDPAddressWM(Packet):
352    name = "LDPAddressWM"
353    fields_desc = [ BitField("u",0,1),
354                    XBitField("type", 0x0301, 15),
355                    ShortField("len", None),
356                    IntField("id", 0),
357                    AddressTLVField("address",None) ]
358    def post_build(self, p, pay):
359        if self.len is None:
360            l = len(p) - 4
361            p = p[:2]+struct.pack("!H", l)+p[4:]
362        return p+pay
363    def guess_payload_class(self, p):
364        return guess_payload(p)
365
366
367# 3.5.7. Label Mapping Message
368
369class LDPLabelMM(Packet):
370    name = "LDPLabelMM"
371    fields_desc = [ BitField("u",0,1),
372                    XBitField("type", 0x0400, 15),
373                    ShortField("len", None),
374                    IntField("id", 0),
375                    FecTLVField("fec",None),
376                    LabelTLVField("label",0)]
377    def post_build(self, p, pay):
378        if self.len is None:
379            l = len(p) - 4
380            p = p[:2]+struct.pack("!H", l)+p[4:]
381        return p+pay
382    def guess_payload_class(self, p):
383        return guess_payload(p)
384
385# 3.5.8. Label Request Message
386
387class LDPLabelReqM(Packet):
388    name = "LDPLabelReqM"
389    fields_desc = [ BitField("u",0,1),
390                    XBitField("type", 0x0401, 15),
391                    ShortField("len", None),
392                    IntField("id", 0),
393                    FecTLVField("fec",None)]
394    def post_build(self, p, pay):
395        if self.len is None:
396            l = len(p) - 4
397            p = p[:2]+struct.pack("!H", l)+p[4:]
398        return p+pay
399    def guess_payload_class(self, p):
400        return guess_payload(p)
401
402
403# 3.5.9. Label Abort Request Message
404
405class LDPLabelARM(Packet):
406    name = "LDPLabelARM"
407    fields_desc = [ BitField("u",0,1),
408                    XBitField("type", 0x0404, 15),
409                    ShortField("len", None),
410                    IntField("id", 0),
411                    FecTLVField("fec",None),
412                    IntField("labelRMid",0)]
413    def post_build(self, p, pay):
414        if self.len is None:
415            l = len(p) - 4
416            p = p[:2]+struct.pack("!H", l)+p[4:]
417        return p+pay
418    def guess_payload_class(self, p):
419        return guess_payload(p)
420
421
422# 3.5.10. Label Withdraw Message
423
424class LDPLabelWM(Packet):
425    name = "LDPLabelWM"
426    fields_desc = [ BitField("u",0,1),
427                    XBitField("type", 0x0402, 15),
428                    ShortField("len", None),
429                    IntField("id", 0),
430                    FecTLVField("fec",None),
431                    LabelTLVField("label",0)]
432    def post_build(self, p, pay):
433        if self.len is None:
434            l = len(p) - 4
435            p = p[:2]+struct.pack("!H", l)+p[4:]
436        return p+pay
437    def guess_payload_class(self, p):
438        return guess_payload(p)
439
440
441# 3.5.11. Label Release Message
442
443class LDPLabelRelM(Packet):
444    name = "LDPLabelRelM"
445    fields_desc = [ BitField("u",0,1),
446                    XBitField("type", 0x0403, 15),
447                    ShortField("len", None),
448                    IntField("id", 0),
449                    FecTLVField("fec",None),
450                    LabelTLVField("label",0)]
451    def post_build(self, p, pay):
452        if self.len is None:
453            l = len(p) - 4
454            p = p[:2]+struct.pack("!H", l)+p[4:]
455        return p+pay
456    def guess_payload_class(self, p):
457        return guess_payload(p)
458
459
460# 3.1. LDP PDUs
461class LDP(Packet):
462    name = "LDP"
463    fields_desc = [ ShortField("version",1),
464                    ShortField("len", None),
465                    IPField("id","127.0.0.1"),
466                    ShortField("space",0) ]
467    def post_build(self, p, pay):
468        if self.len is None:
469            l = len(p)+len(pay)-4
470            p = p[:2]+struct.pack("!H", l)+p[4:]
471        return p+pay
472    def guess_payload_class(self, p):
473        return guess_payload(p)
474
475bind_layers( TCP, LDP, sport=646, dport=646 )
476bind_layers( UDP, LDP, sport=646, dport=646 )
477