Ticket #117: scapy_eigrp.py

File scapy_eigrp.py, 8.6 kB (added by lobo@c3a.de, 4 months ago)
Line 
1 #!/usr/bin/env python
2 """
3     EIGRP Scapy Extension
4     ~~~~~~~~~~~~~~~~~~~~~
5
6     :version:   2008-04-27
7     :copyright: 2008 by Jochen Bartl
8     :e-mail:    lobo@c3a.de
9     :license:   GPL v2
10
11         This program is free software; you can redistribute it and/or
12         modify it under the terms of the GNU General Public License
13         as published by the Free Software Foundation; either version 2
14         of the License, or (at your option) any later version.
15
16         This program is distributed in the hope that it will be useful,
17         but WITHOUT ANY WARRANTY; without even the implied warranty of
18         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19         GNU General Public License for more details.
20
21     :Thanks:
22
23     - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard)
24         http://trac.secdev.org/scapy/ticket/18
25     - IOS / EIGRP Version Representation FIX by Dirk Loss
26 """
27
28 from scapy import *
29
30 class EigrpIPField(StrField):
31     """
32     This is a special field type for handling ip addresses of destination networks in internal and
33     external route updates.
34
35     EIGRP removes zeros from the host portion of the ip address if the netmask is 8, 16 or 24 bits.
36     """
37
38     def __init__(self, name, default, fld=None, length_from=None, shift=0):
39         StrField.__init__(self, name, default, shift=shift)
40         self.length_from = length_from
41         if fld is not None or shift != 0:
42             FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__)
43             self.length_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift
44
45     def h2i(self, pkt, x):
46         if type(x) is str:
47             try:
48                 inet_aton(x)
49             except socket.error:
50                 print x
51                 x = Net(x)
52         elif type(x) is list:
53             x = [self.h2i(pkt, n) for n in x]
54         return x
55
56     def i2m(self, pkt, x):
57         x = inet_aton(x)
58         l = self.length_from(pkt)
59         if l <= 8:
60             return x[:1]
61         elif l <= 16:
62             return x[:2]
63         elif l <= 24:
64             return x[:3]
65         else:
66             return x
67
68     def m2i(self, pkt, x):
69         l = self.length_from(pkt)
70         if l <= 8:
71             x += "\x00\x00\x00"
72         elif l <= 16:
73             x += "\x00\x00"
74         elif l <= 24:
75             x += "\x00"
76
77         return inet_ntoa(x)
78
79     def getfield(self, pkt, s):
80         l = self.length_from(pkt)
81         if l <= 8:
82             l = 8
83         elif l <= 16:
84             l = 16
85         elif l <= 24:
86             l = 24
87         else:
88             l = 32
89
90         return s[l:], self.m2i(pkt,s[:l])
91
92 class EIGRPParam(Packet):
93     name = "EIGRP Parameters"
94     fields_desc = [ XShortField("type", 0x0001),
95             ShortField("len", 12),
96             ByteField("k1", 1),
97             ByteField("k2", 0),
98             ByteField("k3", 1),
99             ByteField("k4", 0),
100             ByteField("k5", 0),
101             ByteField("reserved", 0),
102             ShortField("holdtime", 15)
103             ]
104
105     def guess_payload_class(self, p):
106         return Padding
107
108 class EIGRPAuthData(Packet):
109     name = "EIGRP Authentication Data"
110     fields_desc = [ XShortField("type", 0x0002),
111             FieldLenField("len", None, "authdata", "!H", adjust=lambda pkt,x: x + 4),
112             ShortField("authtype", 2),
113             ShortField("keysize", 16),
114             IntField("keyid", 1),
115             StrFixedLenField("nullpad", "\x00" * 12, 12),
116             StrLenField("authdata", RandString(36), length_from=lambda pkt: pkt.len - 24),
117             ]
118
119     def guess_payload_class(self, p):
120         return Padding
121
122 class EIGRPSeq(Packet):
123     name = "EIGRP Sequence"
124     fields_desc = [ XShortField("type", 0x0003),
125             ShortField("len", 9),
126             ByteField("addrlen", 4),
127             IPField("ipaddr", "192.168.0.1")
128             ]
129
130     def guess_payload_class(self, p):
131         return Padding
132
133 class ShortVersionField(ShortField):
134     def i2repr(self, pkt, x):
135         try:
136             minor = x & 0xff
137             major = (x >> 8) & 0xff
138         except TypeError:
139             return "unknown"
140         else:
141             # We print a leading 'v' so that these values don't look like floats
142             return "v%s.%s" % (major, minor)
143
144
145 class EIGRPSwVer(Packet):
146     name = "EIGRP Software Version"
147     fields_desc = [ XShortField("type", 0x0004),
148             ShortField("len", 8),
149             ShortVersionField("ios", 3072), # v12.0
150             ShortVersionField("eigrp", 258) # v1.2
151             ]
152
153     def guess_payload_class(self, p):
154         return Padding
155
156 class EIGRPNms(Packet):
157     name = "EIGRP Next Multicast Sequence"
158     fields_desc = [ XShortField("type", 0x0005),
159             ShortField("len", 8),
160             IntField("nms", 144)
161             ]
162
163     def guess_payload_class(self, p):
164         return Padding
165
166 class EIGRPIntRoute(Packet):
167     name = "EIGRP Internal Route"
168     fields_desc = [ XShortField("type", 0x0102),
169             FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 25),
170             IPField("nexthop", "192.168.0.0"),
171             IntField("delay", 0),
172             IntField("bandwidth", 0),
173             X3BytesField("MTU", 1500),
174             ByteField("hopcount", 0),
175             ByteField("reliability", 0),
176             ByteField("load", 0),
177             XShortField("reserved", 0),
178             ByteField("prefixlen", 24),
179             EigrpIPField("dst", "", length_from=lambda pkt: pkt.prefixlen),
180             ]
181
182     def guess_payload_class(self, p):
183         return Padding
184
185 class EIGRPExtRoute(Packet):
186     name = "EIGRP External Route"
187     fields_desc = [ XShortField("type", 0x0103),
188             FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 25),
189             IPField("nexthop", "192.168.0.0"),
190             IPField("originrouter", "192.168.0.1"),
191             IntField("originasn", 0),
192             IntField("tag", 0),
193             IntField("externalmetric", 0),
194             ShortField("reserved", 0),
195             ByteField("extprotocolid", 3),
196             XByteField("flags", 0x0),
197             IntField("delay", 0),
198             IntField("bandwidth", 256),
199             X3BytesField("MTU", 1500),
200             ByteField("hopcount", 0),
201             ByteField("reliability", 0),
202             ByteField("load", 0),
203             XShortField("reserved", 0),
204             ByteField("prefixlen", 24),
205             EigrpIPField("dst", "", length_from=lambda pkt: pkt.prefixlen)
206             ]
207
208     def guess_payload_class(self, p):
209         return Padding
210
211 # TODO: Stuck-In-Active Messgeas
212
213 _eigrp_tlv_cls = {
214                     0x0001: "EIGRPParam",
215                     0x0002: "EIGRPAuthData",
216                     0x0003: "EIGRPSeq",
217                     0x0004: "EIGRPSwVer",
218                     0x0005: "EIGRPNms",
219                     0x0102: "EIGRPIntRoute",
220                     0x0103: "EIGRPExtRoute"
221                    }
222
223 class RepeatedTlvListField(PacketListField):
224     def __init__(self, name, default, cls):
225         PacketField.__init__(self, name, default, cls)
226
227     def getfield(self, pkt, s):
228         lst = []
229         remain = s
230         while len(remain) > 0:
231             p = self.m2i(pkt,remain)
232             if Padding in p:
233                 pad = p[Padding]
234                 remain = pad.load
235                 del(pad.underlayer.payload)
236             else:
237                 remain = ""
238             lst.append(p)
239         return remain,lst
240
241     def addfield(self, pkt, s, val):
242         return s+reduce(str.__add__, map(str, val),"")
243
244 def _EIGRPGuessPayloadClass(p, **kargs):
245     cls = Raw
246     if len(p) >= 2:
247         t = struct.unpack("!H", p[:2])[0]
248         clsname = _eigrp_tlv_cls.get(t, "Raw")
249         cls = globals()[clsname]
250         return cls(p, **kargs)
251
252 _EIGRP_OPCODES = { 1 : "Update",
253                    2 : "Request",
254                    3 : "Query",
255                    4 : "Replay",
256                    5 : "Hello" }
257
258 class EIGRP(Packet):
259     name = "EIGRP"
260     fields_desc = [ ByteField("ver", 2),
261                     ByteEnumField("opcode", 5, _EIGRP_OPCODES),
262                     XShortField("chksum", None),
263                     XIntField("flags", 0),
264                     IntField("seq", 0),
265                     IntField("ack", 0),
266                     IntField("asn", 100),
267                     RepeatedTlvListField("tlvlist", [], _EIGRPGuessPayloadClass)
268                  ]
269
270     def post_build(self, p, pay):
271         p += pay
272         if self.chksum is None:
273             c = checksum(p)
274             p = p[:2] + chr((c>>8)&0xff)+chr(c&0xff) + p[4:]
275         return p
276
277     def mysummary(self):
278         return self.sprintf("EIGRP (AS=%EIGRP.asn% Opcode=%EIGRP.opcode% Flags=%EIGRP.flags%)")
279
280
281 bind_layers( IP, EIGRP, proto=88)
282
283 if __name__ == "__main__":
284     interact(mydict=globals(), mybanner="EIGRP")
285