| 1 |
""" |
|---|
| 2 |
OSPF extension for Scapy <http://www.secdev.org/scapy> |
|---|
| 3 |
|
|---|
| 4 |
This module provides Scapy layers for the Open Shortest Path First |
|---|
| 5 |
routing protocol as defined in RFC 2328. |
|---|
| 6 |
|
|---|
| 7 |
Copyright (c) 2008 Dirk Loss : mail dirk-loss de |
|---|
| 8 |
|
|---|
| 9 |
This program is free software; you can redistribute it and/or |
|---|
| 10 |
modify it under the terms of the GNU General Public License |
|---|
| 11 |
as published by the Free Software Foundation; either version 2 |
|---|
| 12 |
of the License, or (at your option) any later version. |
|---|
| 13 |
|
|---|
| 14 |
This program is distributed in the hope that it will be useful, |
|---|
| 15 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 |
GNU General Public License for more details. |
|---|
| 18 |
""" |
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
from scapy import * |
|---|
| 22 |
|
|---|
| 23 |
EXT_VERSION = "v0.8" |
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
class OSPFOptionsField(FlagsField): |
|---|
| 27 |
def __init__(self, name="options", default=0, size=8, |
|---|
| 28 |
names=["8", "E", "MC", "NP", "L", "DC", "O", "DN"]): |
|---|
| 29 |
FlagsField.__init__(self, name, default, size, names) |
|---|
| 30 |
|
|---|
| 31 |
_OSPF_types = { 1: "Hello", |
|---|
| 32 |
2: "DBDesc", |
|---|
| 33 |
3: "LSReq", |
|---|
| 34 |
4: "LSUpd", |
|---|
| 35 |
5: "LSAck" } |
|---|
| 36 |
|
|---|
| 37 |
class OSPF_Hdr(Packet): |
|---|
| 38 |
name = "OSPF Header" |
|---|
| 39 |
fields_desc = [ |
|---|
| 40 |
ByteField("version", 2), |
|---|
| 41 |
ByteEnumField("type", 1, _OSPF_types), |
|---|
| 42 |
ShortField("len", None), |
|---|
| 43 |
IPField("src", "127.0.0.1"), |
|---|
| 44 |
IPField("area", "0.0.0.0"), |
|---|
| 45 |
XShortField("chksum", None), |
|---|
| 46 |
ShortEnumField("authtype", 0, {0:"Null", 1:"Simple", 2:"Crypto"}), |
|---|
| 47 |
|
|---|
| 48 |
ConditionalField(XLongField("authdata", 0), lambda pkt:pkt.authtype != 2), |
|---|
| 49 |
|
|---|
| 50 |
ConditionalField(XShortField("reserved", 0), lambda pkt:pkt.authtype == 2), |
|---|
| 51 |
ConditionalField(ByteField("keyid", 1), lambda pkt:pkt.authtype == 2), |
|---|
| 52 |
ConditionalField(ByteField("authdatalen", 0), lambda pkt:pkt.authtype == 2), |
|---|
| 53 |
ConditionalField(XIntField("seq", 0), lambda pkt:pkt.authtype == 2) |
|---|
| 54 |
|
|---|
| 55 |
] |
|---|
| 56 |
|
|---|
| 57 |
def post_build(self, p, pay): |
|---|
| 58 |
p += pay |
|---|
| 59 |
l = self.len |
|---|
| 60 |
if l is None: |
|---|
| 61 |
l = len(p) |
|---|
| 62 |
p = p[:2]+struct.pack("!H",l)+p[4:] |
|---|
| 63 |
if self.chksum is None: |
|---|
| 64 |
if self.authtype == 2: |
|---|
| 65 |
ck = 0 |
|---|
| 66 |
else: |
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
ck = checksum(p[:16]+p[24:]) |
|---|
| 70 |
p = p[:12]+chr(ck>>8)+chr(ck&0xff)+p[14:] |
|---|
| 71 |
|
|---|
| 72 |
return p |
|---|
| 73 |
|
|---|
| 74 |
def hashret(self): |
|---|
| 75 |
return struct.pack("H",self.area)+self.payload.hashret() |
|---|
| 76 |
|
|---|
| 77 |
def answers(self, other): |
|---|
| 78 |
if (isinstance(other,OSPF_Hdr) and |
|---|
| 79 |
self.area == other.area and |
|---|
| 80 |
self.type == 5): |
|---|
| 81 |
return self.payload.answers(other.payload) |
|---|
| 82 |
return 0 |
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
class OSPF_Hello(Packet): |
|---|
| 86 |
name = "OSPF Hello" |
|---|
| 87 |
fields_desc = [ IPField("mask", "255.255.255.0"), |
|---|
| 88 |
ShortField("hellointerval", 10), |
|---|
| 89 |
OSPFOptionsField(), |
|---|
| 90 |
ByteField("prio", 1), |
|---|
| 91 |
IntField("deadinterval", 40), |
|---|
| 92 |
IPField("router", "0.0.0.0"), |
|---|
| 93 |
IPField("backup", "0.0.0.0"), |
|---|
| 94 |
ConditionalField(IPField("neighbor", "0.0.0.0"), lambda pkt: pkt.underlayer.len >= 48) |
|---|
| 95 |
] |
|---|
| 96 |
|
|---|
| 97 |
_OSPF_LStypes = { 1: "router", |
|---|
| 98 |
2: "network", |
|---|
| 99 |
3: "summaryIP", |
|---|
| 100 |
4: "summaryASBR", |
|---|
| 101 |
5: "external"} |
|---|
| 102 |
|
|---|
| 103 |
_OSPF_LSclasses = { 1: "OSPF_Router_LSA", |
|---|
| 104 |
2: "OSPF_Network_LSA", |
|---|
| 105 |
3: "OSPF_SummaryIP_LSA", |
|---|
| 106 |
4: "OSPF_SummaryASBR_LSA", |
|---|
| 107 |
5: "OSPF_External_LSA" } |
|---|
| 108 |
|
|---|
| 109 |
def ospf_lsa_checksum(lsa): |
|---|
| 110 |
""" Fletcher checksum for OSPF LSAs, returned as a 2 byte string. |
|---|
| 111 |
|
|---|
| 112 |
Give the whole LSA packet as argument. |
|---|
| 113 |
For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. |
|---|
| 114 |
""" |
|---|
| 115 |
|
|---|
| 116 |
|
|---|
| 117 |
CHKSUM_OFFSET = 16 |
|---|
| 118 |
if len(lsa) < CHKSUM_OFFSET: |
|---|
| 119 |
raise Exception("LSA Packet too short (%s bytes)" % len(lsa)) |
|---|
| 120 |
|
|---|
| 121 |
c0 = c1 = 0 |
|---|
| 122 |
|
|---|
| 123 |
lsa = lsa[:CHKSUM_OFFSET] + "\x00\x00" + lsa[CHKSUM_OFFSET+2:] |
|---|
| 124 |
for char in lsa[2:]: |
|---|
| 125 |
c0 += ord(char) |
|---|
| 126 |
c1 += c0 |
|---|
| 127 |
c0 %= 255 |
|---|
| 128 |
c1 %= 255 |
|---|
| 129 |
|
|---|
| 130 |
x = ((len(lsa) - CHKSUM_OFFSET - 1) * c0 - c1) % 255 |
|---|
| 131 |
if (x <= 0): |
|---|
| 132 |
x += 255 |
|---|
| 133 |
y = 510 - c0 - x |
|---|
| 134 |
if (y > 255): |
|---|
| 135 |
y -= 255 |
|---|
| 136 |
|
|---|
| 137 |
return chr(x) + chr(y) |
|---|
| 138 |
|
|---|
| 139 |
|
|---|
| 140 |
class OSPF_LSA_Hdr(Packet): |
|---|
| 141 |
name = "OSPF LSA Header" |
|---|
| 142 |
fields_desc = [ ShortField("age", 1), |
|---|
| 143 |
OSPFOptionsField(), |
|---|
| 144 |
ByteEnumField("type", 1, _OSPF_LStypes), |
|---|
| 145 |
IPField("id", "0.0.0.0"), |
|---|
| 146 |
IPField("adrouter", "0.0.0.0"), |
|---|
| 147 |
XIntField("seq", 1), |
|---|
| 148 |
XShortField("chksum", 0), |
|---|
| 149 |
ShortField("len", 36)] |
|---|
| 150 |
|
|---|
| 151 |
def extract_padding(self, s): |
|---|
| 152 |
return "", s |
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 |
_OSPF_Router_LSA_types = { 1: "p2p", |
|---|
| 156 |
2: "transit", |
|---|
| 157 |
3: "stub", |
|---|
| 158 |
4: "virtual" } |
|---|
| 159 |
|
|---|
| 160 |
class OSPF_Link(Packet): |
|---|
| 161 |
name = "OSPF Link" |
|---|
| 162 |
fields_desc = [ IPField("id", "0.0.0.0"), |
|---|
| 163 |
IPField("data", "2.2.2.2"), |
|---|
| 164 |
ByteEnumField("type", 1, _OSPF_Router_LSA_types), |
|---|
| 165 |
ByteField("toscount", 0), |
|---|
| 166 |
ShortField("metric", 1), |
|---|
| 167 |
|
|---|
| 168 |
ConditionalField(ByteField("tos", 0), lambda pkt: False), |
|---|
| 169 |
ConditionalField(ByteField("reserved", 0), lambda pkt: False), |
|---|
| 170 |
ConditionalField(ShortField("tosmetric", 0), lambda pkt: False) |
|---|
| 171 |
] |
|---|
| 172 |
|
|---|
| 173 |
def extract_padding(self,s): |
|---|
| 174 |
return "", s |
|---|
| 175 |
|
|---|
| 176 |
|
|---|
| 177 |
def _LSAGuessPayloadClass(p, **kargs): |
|---|
| 178 |
""" Guess the correct LSA class for a given payload """ |
|---|
| 179 |
|
|---|
| 180 |
|
|---|
| 181 |
cls = Raw |
|---|
| 182 |
if len(p) >= 4: |
|---|
| 183 |
typ = struct.unpack("!B", p[3])[0] |
|---|
| 184 |
clsname = _OSPF_LSclasses.get(typ, "Raw") |
|---|
| 185 |
cls = globals()[clsname] |
|---|
| 186 |
return cls(p, **kargs) |
|---|
| 187 |
|
|---|
| 188 |
|
|---|
| 189 |
class OSPF_BaseLSA(Packet): |
|---|
| 190 |
""" An abstract base class for Link State Advertisements """ |
|---|
| 191 |
|
|---|
| 192 |
def post_build(self, p, pay): |
|---|
| 193 |
length = self.len |
|---|
| 194 |
if length is None: |
|---|
| 195 |
length = len(p) |
|---|
| 196 |
p = p[:18] + struct.pack("!H",length) + p[20:] |
|---|
| 197 |
if self.chksum is None: |
|---|
| 198 |
chksum = ospf_lsa_checksum(p) |
|---|
| 199 |
p = p[:16] + chksum + p[18:] |
|---|
| 200 |
return p |
|---|
| 201 |
|
|---|
| 202 |
def extract_padding(self, s): |
|---|
| 203 |
length = self.len |
|---|
| 204 |
return "", s |
|---|
| 205 |
|
|---|
| 206 |
|
|---|
| 207 |
class OSPF_Router_LSA(OSPF_BaseLSA): |
|---|
| 208 |
name = "OSPF Router LSA" |
|---|
| 209 |
|
|---|
| 210 |
fields_desc = [ ShortField("age", 1), |
|---|
| 211 |
OSPFOptionsField(), |
|---|
| 212 |
ByteField("type", 1), |
|---|
| 213 |
IPField("id", "0.0.0.0"), |
|---|
| 214 |
IPField("adrouter", "0.0.0.0"), |
|---|
| 215 |
XIntField("seq", 0x80000001), |
|---|
| 216 |
XShortField("chksum", None), |
|---|
| 217 |
ShortField("len", None), |
|---|
| 218 |
FlagsField("flags", 0, 16, ["16", "15", "14", "13", "12", "V", "E", "B", "8", "7", "6", "5", "4", "3", "2", "1"]), |
|---|
| 219 |
FieldLenField("linkcount", None, count_of="linklist"), |
|---|
| 220 |
PacketListField("linklist", [], OSPF_Link, |
|---|
| 221 |
count_from=lambda pkt:pkt.linkcount, |
|---|
| 222 |
length_from=lambda pkt:pkt.linkcount*12)] |
|---|
| 223 |
|
|---|
| 224 |
|
|---|
| 225 |
class OSPF_Network_LSA(OSPF_BaseLSA): |
|---|
| 226 |
name = "OSPF Network LSA" |
|---|
| 227 |
fields_desc = [ ShortField("age", 1), |
|---|
| 228 |
OSPFOptionsField(), |
|---|
| 229 |
ByteField("type", 2), |
|---|
| 230 |
IPField("id", "0.0.0.0"), |
|---|
| 231 |
IPField("adrouter", "0.0.0.0"), |
|---|
| 232 |
XIntField("seq", 0x80000001), |
|---|
| 233 |
XShortField("chksum", None), |
|---|
| 234 |
ShortField("len", None), |
|---|
| 235 |
IPField("mask", "255.255.255.0"), |
|---|
| 236 |
FieldListField("routerlist", [], IPField("", "0.0.0.1"), |
|---|
| 237 |
length_from=lambda pkt: pkt.len - 24)] |
|---|
| 238 |
|
|---|
| 239 |
|
|---|
| 240 |
class OSPF_SummaryIP_LSA(OSPF_BaseLSA): |
|---|
| 241 |
name = "OSPF Summary LSA (IP Network)" |
|---|
| 242 |
fields_desc = [ ShortField("age", 1), |
|---|
| 243 |
OSPFOptionsField(), |
|---|
| 244 |
ByteField("type", 3), |
|---|
| 245 |
IPField("id", "0.0.0.0"), |
|---|
| 246 |
IPField("advertrouter", "0.0.0.0"), |
|---|
| 247 |
XIntField("seq", 0x80000001), |
|---|
| 248 |
XShortField("chksum", None), |
|---|
| 249 |
ShortField("len", None), |
|---|
| 250 |
IPField("mask", "255.255.255.0"), |
|---|
| 251 |
ByteField("reserved", 0), |
|---|
| 252 |
X3BytesField("metric", 0), |
|---|
| 253 |
|
|---|
| 254 |
ConditionalField(ByteField("tos", 0), lambda pkt:False), |
|---|
| 255 |
ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False) |
|---|
| 256 |
] |
|---|
| 257 |
|
|---|
| 258 |
class OSPF_SummaryASBR_LSA(OSPF_BaseLSA): |
|---|
| 259 |
name = "OSPF Summary LSA (AS Boundary Router)" |
|---|
| 260 |
fields_desc = [ ShortField("age", 1), |
|---|
| 261 |
OSPFOptionsField(), |
|---|
| 262 |
ByteField("type", 4), |
|---|
| 263 |
IPField("id", "0.0.0.0"), |
|---|
| 264 |
IPField("adrouter", "0.0.0.0"), |
|---|
| 265 |
XIntField("seq", 0x80000001), |
|---|
| 266 |
XShortField("chksum", None), |
|---|
| 267 |
ShortField("len", None), |
|---|
| 268 |
IPField("mask", "255.255.255.0"), |
|---|
| 269 |
ByteField("reserved", 0), |
|---|
| 270 |
X3BytesField("metric", 0), |
|---|
| 271 |
|
|---|
| 272 |
ConditionalField(ByteField("tos", 0), lambda pkt:False), |
|---|
| 273 |
ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False) |
|---|
| 274 |
] |
|---|
| 275 |
|
|---|
| 276 |
|
|---|
| 277 |
class OSPF_External_LSA(OSPF_BaseLSA): |
|---|
| 278 |
name = "OSPF External LSA (ASBR)" |
|---|
| 279 |
fields_desc = [ |
|---|
| 280 |
ShortField("age", 1), |
|---|
| 281 |
OSPFOptionsField(), |
|---|
| 282 |
ByteField("type", 5), |
|---|
| 283 |
IPField("id", "0.0.0.0"), |
|---|
| 284 |
IPField("adrouter", "0.0.0.0"), |
|---|
| 285 |
XIntField("seq", 1), |
|---|
| 286 |
XShortField("chksum", None), |
|---|
| 287 |
ShortField("len", None), |
|---|
| 288 |
IPField("mask", "255.255.255.0"), |
|---|
| 289 |
FlagsField("ebit", 0, 1, ["E"]), |
|---|
| 290 |
BitField("reserved", 0, 7), |
|---|
| 291 |
X3BytesField("metric", 1), |
|---|
| 292 |
IPField("fwdaddr", "0.0.0.0"), |
|---|
| 293 |
XIntField("tag", 0), |
|---|
| 294 |
|
|---|
| 295 |
ConditionalField(ByteField("tos", 0), lambda pkt:False), |
|---|
| 296 |
ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False) |
|---|
| 297 |
] |
|---|
| 298 |
|
|---|
| 299 |
class OSPF_DBDesc(Packet): |
|---|
| 300 |
name = "OSPF Database Description" |
|---|
| 301 |
fields_desc = [ ShortField("mtu", 1500), |
|---|
| 302 |
OSPFOptionsField(), |
|---|
| 303 |
FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R", "4", "3", "2", "1"]), |
|---|
| 304 |
IntField("ddseq", 1), |
|---|
| 305 |
PacketListField("lsaheaders", None, OSPF_LSA_Hdr, |
|---|
| 306 |
count_from = lambda pkt:None, |
|---|
| 307 |
length_from = lambda pkt:pkt.underlayer.len - 24 - 8)] |
|---|
| 308 |
|
|---|
| 309 |
class OSPF_LSReq_Item(Packet): |
|---|
| 310 |
name = "OSPF Link State Request (item)" |
|---|
| 311 |
fields_desc = [ IntEnumField("type", 1, _OSPF_LStypes), |
|---|
| 312 |
IPField("id", "0.0.0.0"), |
|---|
| 313 |
IPField("adrouter", "0.0.0.0")] |
|---|
| 314 |
|
|---|
| 315 |
def extract_padding(self, s): |
|---|
| 316 |
return "", s |
|---|
| 317 |
|
|---|
| 318 |
|
|---|
| 319 |
class OSPF_LSReq(Packet): |
|---|
| 320 |
name = "OSPF Link State Request (container)" |
|---|
| 321 |
fields_desc = [ PacketListField("requests", None, OSPF_LSReq_Item, |
|---|
| 322 |
count_from = lambda pkt:None, |
|---|
| 323 |
length_from = lambda pkt:pkt.underlayer.len - 24)] |
|---|
| 324 |
|
|---|
| 325 |
class OSPF_LSUpd(Packet): |
|---|
| 326 |
name = "OSPF Link State Update" |
|---|
| 327 |
fields_desc = [ FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), |
|---|
| 328 |
PacketListField("lsalist", [], _LSAGuessPayloadClass, |
|---|
| 329 |
count_from = lambda pkt:pkt.lsacount, |
|---|
| 330 |
length_from = lambda pkt:pkt.underlayer.len - 24) |
|---|
| 331 |
] |
|---|
| 332 |
|
|---|
| 333 |
|
|---|
| 334 |
|
|---|
| 335 |
class OSPF_LSAck(Packet): |
|---|
| 336 |
name = "OSPF Link State Acknowledgement" |
|---|
| 337 |
fields_desc = [ PacketListField("lsaheaders", None, OSPF_LSA_Hdr, |
|---|
| 338 |
count_from = lambda pkt:None, |
|---|
| 339 |
length_from = lambda pkt:pkt.underlayer.len - 24)] |
|---|
| 340 |
|
|---|
| 341 |
def answers(self, other): |
|---|
| 342 |
if isinstance(other,OSPF_LSUpd): |
|---|
| 343 |
for reqLSA in other.lsalist: |
|---|
| 344 |
for ackLSA in self.lsaheaders: |
|---|
| 345 |
if (reqLSA.type == ackLSA.type and |
|---|
| 346 |
reqLSA.seq == ackLSA.seq): |
|---|
| 347 |
return 1 |
|---|
| 348 |
return 0 |
|---|
| 349 |
|
|---|
| 350 |
|
|---|
| 351 |
bind_layers(IP, OSPF_Hdr, proto=89) |
|---|
| 352 |
bind_layers(OSPF_Hdr, OSPF_Hello, type=1) |
|---|
| 353 |
bind_layers(OSPF_Hdr, OSPF_DBDesc, type=2) |
|---|
| 354 |
bind_layers(OSPF_Hdr, OSPF_LSReq, type=3) |
|---|
| 355 |
bind_layers(OSPF_Hdr, OSPF_LSUpd, type=4) |
|---|
| 356 |
bind_layers(OSPF_Hdr, OSPF_LSAck, type=5) |
|---|
| 357 |
|
|---|
| 358 |
|
|---|
| 359 |
if __name__ == "__main__": |
|---|
| 360 |
interact(mydict=globals(), mybanner="OSPF extension %s" % EXT_VERSION) |
|---|
| 361 |
|
|---|