backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / lib / packet / ospf.py
1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17 RFC 2328 OSPF version 2
18 """
19
20 from functools import reduce
21 import logging
22 import struct
23
24 import six
25
26 from ryu.lib import addrconv
27 from ryu.lib.packet import packet_base
28 from ryu.lib.packet import packet_utils
29 from ryu.lib.packet import stream_parser
30 from ryu.lib.stringify import StringifyMixin
31 from ryu.lib import type_desc
32
33
34 LOG = logging.getLogger(__name__)
35
36 _VERSION = 2
37
38 OSPF_MSG_UNKNOWN = 0
39 OSPF_MSG_HELLO = 1
40 OSPF_MSG_DB_DESC = 2
41 OSPF_MSG_LS_REQ = 3
42 OSPF_MSG_LS_UPD = 4
43 OSPF_MSG_LS_ACK = 5
44
45 OSPF_UNKNOWN_LSA = 0
46 OSPF_ROUTER_LSA = 1
47 OSPF_NETWORK_LSA = 2
48 OSPF_SUMMARY_LSA = 3
49 OSPF_ASBR_SUMMARY_LSA = 4
50 OSPF_AS_EXTERNAL_LSA = 5
51 OSPF_AS_NSSA_LSA = 7       # RFC 3101
52 OSPF_OPAQUE_LINK_LSA = 9   # RFC 5250
53 OSPF_OPAQUE_AREA_LSA = 10  # RFC 5250
54 OSPF_OPAQUE_AS_LSA = 11    # RFC 5250
55
56 OSPF_OPTION_T = 1        # Obsolete
57 OSPF_OPTION_E = 1 << 1   # RFC 2328
58 OSPF_OPTION_MC = 1 << 2  # RFC 1584
59 OSPF_OPTION_NP = 1 << 3  # RFC 3101
60 OSPF_OPTION_EA = 1 << 4  # Obsolete
61 OSPF_OPTION_DC = 1 << 5  # RFC 2370
62 OSPF_OPTION_DN = 1 << 7  # RFC 2567
63
64 LSA_LINK_TYPE_P2P = 1
65 LSA_LINK_TYPE_TRANSIT = 2
66 LSA_LINK_TYPE_STUB = 3
67 LSA_LINK_TYPE_VL = 4
68
69 ROUTER_LSA_BORDER = 0x01  # The router is an ABR
70 ROUTER_LSA_EXTERNAL = 0x02  # The router is an ASBR
71 ROUTER_LSA_VIRTUAL = 0x04  # The router has a VL in this area
72 ROUTER_LSA_NT = 0x10  # The router always translates Type-7
73 ROUTER_LSA_SHORTCUT = 0x20  # Shortcut-ABR specific flag
74
75 AS_EXTERNAL_METRIC = 0x80
76
77 OSPF_OPAQUE_TYPE_UNKNOWN = 0
78 OSPF_OPAQUE_TYPE_EXTENDED_PREFIX_LSA = 7
79 OSPF_OPAQUE_TYPE_EXTENDED_LINK_LSA = 8
80
81 OSPF_EXTENDED_PREFIX_TLV = 1
82 OSPF_EXTENDED_PREFIX_SID_SUBTLV = 2
83
84
85 class InvalidChecksum(Exception):
86     pass
87
88
89 class LSAHeader(StringifyMixin):
90     _HDR_PACK_STR = '!HBB4s4sIHH'
91     _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
92
93     def __init__(self, ls_age=0, options=0, type_=OSPF_UNKNOWN_LSA,
94                  id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
95                  checksum=0, length=0, opaque_type=OSPF_OPAQUE_TYPE_UNKNOWN,
96                  opaque_id=0):
97         self.ls_age = ls_age
98         self.options = options
99         self.type_ = type_
100         if self.type_ < OSPF_OPAQUE_LINK_LSA:
101             self.id_ = id_
102         else:
103             self.opaque_type = opaque_type
104             self.opaque_id = opaque_id
105         self.adv_router = adv_router
106         self.ls_seqnum = ls_seqnum
107         self.checksum = checksum
108         self.length = length
109
110     @classmethod
111     def parser(cls, buf):
112         if len(buf) < cls._HDR_LEN:
113             raise stream_parser.StreamParser.TooSmallException(
114                 '%d < %d' % (len(buf), cls._HDR_LEN))
115         (ls_age, options, type_, id_, adv_router, ls_seqnum, checksum,
116          length,) = struct.unpack_from(cls._HDR_PACK_STR, six.binary_type(buf))
117         adv_router = addrconv.ipv4.bin_to_text(adv_router)
118         rest = buf[cls._HDR_LEN:]
119         lsacls = LSA._lookup_type(type_)
120
121         value = {
122             "ls_age": ls_age,
123             "options": options,
124             "type_": type_,
125             "adv_router": adv_router,
126             "ls_seqnum": ls_seqnum,
127             "checksum": checksum,
128             "length": length,
129         }
130
131         if issubclass(lsacls, OpaqueLSA):
132             (id_,) = struct.unpack_from('!I', id_)
133             value['opaque_type'] = (id_ & 0xff000000) >> 24
134             value['opaque_id'] = (id_ & 0xffffff)
135         else:
136             value['id_'] = addrconv.ipv4.bin_to_text(id_)
137
138         return value, rest
139
140     def serialize(self):
141         if self.type_ < OSPF_OPAQUE_LINK_LSA:
142             id_ = addrconv.ipv4.text_to_bin(self.id_)
143         else:
144             id_ = (self.opaque_type << 24) + self.opaque_id
145             (id_,) = struct.unpack_from('4s', struct.pack('!I', id_))
146
147         adv_router = addrconv.ipv4.text_to_bin(self.adv_router)
148         return bytearray(
149             struct.pack(self._HDR_PACK_STR, self.ls_age,
150                         self.options, self.type_, id_, adv_router,
151                         self.ls_seqnum, self.checksum, self.length))
152
153
154 class LSA(type_desc.TypeDisp, StringifyMixin):
155     def __init__(self, ls_age=0, options=0, type_=OSPF_UNKNOWN_LSA,
156                  id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
157                  checksum=0, length=0, opaque_type=OSPF_OPAQUE_TYPE_UNKNOWN,
158                  opaque_id=0):
159         if type_ < OSPF_OPAQUE_LINK_LSA:
160             self.header = LSAHeader(
161                 ls_age=ls_age,
162                 options=options,
163                 type_=type_,
164                 id_=id_,
165                 adv_router=adv_router,
166                 ls_seqnum=ls_seqnum)
167         else:
168             self.header = LSAHeader(
169                 ls_age=ls_age,
170                 options=options,
171                 type_=type_,
172                 adv_router=adv_router,
173                 ls_seqnum=ls_seqnum,
174                 opaque_type=opaque_type,
175                 opaque_id=opaque_id)
176
177         if not (checksum or length):
178             tail = self.serialize_tail()
179             length = self.header._HDR_LEN + len(tail)
180         if not checksum:
181             head = self.header.serialize()
182             checksum = packet_utils.fletcher_checksum(head[2:], 14)
183         self.header.length = length
184         self.header.checksum = checksum
185
186     @classmethod
187     def parser(cls, buf):
188         hdr, rest = LSAHeader.parser(buf)
189         if len(buf) < hdr['length']:
190             raise stream_parser.StreamParser.TooSmallException(
191                 '%d < %d' % (len(buf), hdr['length']))
192         # exclude ls_age for checksum calculation
193         csum = packet_utils.fletcher_checksum(buf[2:hdr['length']], 14)
194         if csum != hdr['checksum']:
195             raise InvalidChecksum("header has %d, but calculated value is %d"
196                                   % (hdr['checksum'], csum))
197         subcls = cls._lookup_type(hdr['type_'])
198         body = rest[:hdr['length'] - LSAHeader._HDR_LEN]
199         rest = rest[hdr['length'] - LSAHeader._HDR_LEN:]
200         if issubclass(subcls, OpaqueLSA):
201             kwargs = subcls.parser(body, hdr['opaque_type'])
202         else:
203             kwargs = subcls.parser(body)
204         kwargs.update(hdr)
205         return subcls(**kwargs), subcls, rest
206
207     def serialize(self):
208         tail = self.serialize_tail()
209         self.header.length = self.header._HDR_LEN + len(tail)
210         head = self.header.serialize()
211         # exclude ls_age for checksum calculation
212         csum = packet_utils.fletcher_checksum(head[2:] + tail, 14)
213         self.header.checksum = csum
214         struct.pack_into("!H", head, 16, csum)
215         return head + tail
216
217     def serialize_tail(self):
218         # should be implemented in subclass
219         return b''
220
221
222 @LSA.register_type(OSPF_ROUTER_LSA)
223 class RouterLSA(LSA):
224     _PACK_STR = '!BBH'
225     _PACK_LEN = struct.calcsize(_PACK_STR)  # 4bytes
226
227     class Link(StringifyMixin):
228         _PACK_STR = '!4s4sBBH'
229         _PACK_LEN = struct.calcsize(_PACK_STR)  # 12bytes
230
231         def __init__(self, id_='0.0.0.0', data='0.0.0.0',
232                      type_=LSA_LINK_TYPE_STUB, tos=0, metric=10):
233             self.id_ = id_
234             self.data = data
235             self.type_ = type_
236             self.tos = tos
237             self.metric = metric
238
239         @classmethod
240         def parser(cls, buf):
241             if len(buf) < cls._PACK_LEN:
242                 raise stream_parser.StreamParser.TooSmallException(
243                     '%d < %d' % (len(buf), cls._PACK_LEN))
244             link = buf[:cls._PACK_LEN]
245             rest = buf[cls._PACK_LEN:]
246             (id_, data, type_, tos, metric) = \
247                 struct.unpack_from(cls._PACK_STR, six.binary_type(link))
248             id_ = addrconv.ipv4.bin_to_text(id_)
249             data = addrconv.ipv4.bin_to_text(data)
250             return cls(id_, data, type_, tos, metric), rest
251
252         def serialize(self):
253             id_ = addrconv.ipv4.text_to_bin(self.id_)
254             data = addrconv.ipv4.text_to_bin(self.data)
255             return bytearray(
256                 struct.pack(self._PACK_STR, id_, data, self.type_, self.tos,
257                             self.metric))
258
259     def __init__(self, ls_age=0, options=0, type_=OSPF_ROUTER_LSA,
260                  id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
261                  checksum=None, length=None, flags=0, links=None):
262         links = links if links else []
263         self.flags = flags
264         self.links = links
265         super(RouterLSA, self).__init__(ls_age, options, type_, id_,
266                                         adv_router, ls_seqnum, checksum,
267                                         length)
268
269     @classmethod
270     def parser(cls, buf):
271         links = []
272         hdr = buf[:cls._PACK_LEN]
273         buf = buf[cls._PACK_LEN:]
274         (flags, _, num) = struct.unpack_from(cls._PACK_STR,
275                                              six.binary_type(hdr))
276         while buf:
277             link, buf = cls.Link.parser(buf)
278             links.append(link)
279         assert len(links) == num
280         return {
281             "flags": flags,
282             "links": links,
283         }
284
285     def serialize_tail(self):
286         head = bytearray(
287             struct.pack(self._PACK_STR, self.flags, 0, len(self.links)))
288         try:
289             return head + reduce(lambda a, b: a + b,
290                                  (link.serialize() for link in self.links))
291         except TypeError:
292             return head
293
294
295 @LSA.register_type(OSPF_NETWORK_LSA)
296 class NetworkLSA(LSA):
297     _PACK_STR = '!4s'
298     _PACK_LEN = struct.calcsize(_PACK_STR)
299
300     def __init__(self, ls_age=0, options=0, type_=OSPF_NETWORK_LSA,
301                  id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
302                  checksum=None, length=None, mask='0.0.0.0', routers=None):
303         routers = routers if routers else []
304         self.mask = mask
305         self.routers = routers
306         super(NetworkLSA, self).__init__(ls_age, options, type_, id_,
307                                          adv_router, ls_seqnum, checksum,
308                                          length)
309
310     @classmethod
311     def parser(cls, buf):
312         if len(buf) < cls._PACK_LEN:
313             raise stream_parser.StreamParser.TooSmallException(
314                 '%d < %d' % (len(buf), cls._PACK_LEN))
315         binmask = buf[:cls._PACK_LEN]
316         (mask,) = struct.unpack_from(cls._PACK_STR, six.binary_type(binmask))
317         mask = addrconv.ipv4.bin_to_text(mask)
318         buf = buf[cls._PACK_LEN:]
319         routers = []
320         while buf:
321             binrouter = buf[:cls._PACK_LEN]
322             (router,) = struct.unpack_from(cls._PACK_STR,
323                                            six.binary_type(binrouter))
324             router = addrconv.ipv4.bin_to_text(router)
325             routers.append(router)
326             buf = buf[cls._PACK_LEN:]
327         return {
328             "mask": mask,
329             "routers": routers,
330         }
331
332     def serialize_tail(self):
333         mask = addrconv.ipv4.text_to_bin(self.mask)
334         routers = [addrconv.ipv4.text_to_bin(router)
335                    for router in self.routers]
336         return bytearray(
337             struct.pack("!" + "4s" * (1 + len(routers)), mask, *routers))
338
339
340 @LSA.register_type(OSPF_SUMMARY_LSA)
341 class SummaryLSA(LSA):
342     _PACK_STR = '!4sB3s'
343     _PACK_LEN = struct.calcsize(_PACK_STR)
344
345     def __init__(self, ls_age=0, options=0, type_=OSPF_SUMMARY_LSA,
346                  id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
347                  checksum=None, length=None, mask='0.0.0.0', tos=0, metric=0):
348         self.mask = mask
349         self.tos = tos
350         self.metric = metric
351         super(SummaryLSA, self).__init__(ls_age, options, type_, id_,
352                                          adv_router, ls_seqnum, checksum,
353                                          length)
354
355     @classmethod
356     def parser(cls, buf):
357         if len(buf) < cls._PACK_LEN:
358             raise stream_parser.StreamParser.TooSmallException(
359                 '%d < %d' % (len(buf), cls._PACK_LEN))
360         buf = buf[:cls._PACK_LEN]
361         (mask, tos, metric) = struct.unpack_from(
362             cls._PACK_STR, six.binary_type(buf))
363         mask = addrconv.ipv4.bin_to_text(mask)
364         metric = type_desc.Int3.to_user(metric)
365         return {
366             "mask": mask,
367             "tos": tos,
368             "metric": metric,
369         }
370
371     def serialize_tail(self):
372         mask = addrconv.ipv4.text_to_bin(self.mask)
373         metric = type_desc.Int3.from_user(self.metric)
374         return bytearray(struct.pack(self._PACK_STR, mask, self.tos, metric))
375
376
377 @LSA.register_type(OSPF_ASBR_SUMMARY_LSA)
378 class ASBRSummaryLSA(LSA):
379     pass
380
381
382 @LSA.register_type(OSPF_AS_EXTERNAL_LSA)
383 class ASExternalLSA(LSA):
384     class ExternalNetwork(StringifyMixin):
385         _PACK_STR = '!4sB3s4sI'
386         _PACK_LEN = struct.calcsize(_PACK_STR)
387
388         def __init__(self, mask='0.0.0.0', flags=0, metric=0,
389                      fwd_addr='0.0.0.0', tag=0):
390             self.mask = mask
391             self.flags = flags
392             self.metric = metric
393             self.fwd_addr = fwd_addr
394             self.tag = tag
395
396         @classmethod
397         def parser(cls, buf):
398             if len(buf) < cls._PACK_LEN:
399                 raise stream_parser.StreamParser.TooSmallException(
400                     '%d < %d' % (len(buf), cls._PACK_LEN))
401             ext_nw = buf[:cls._PACK_LEN]
402             rest = buf[cls._PACK_LEN:]
403             (mask, flags, metric, fwd_addr,
404              tag) = struct.unpack_from(cls._PACK_STR, six.binary_type(ext_nw))
405             mask = addrconv.ipv4.bin_to_text(mask)
406             metric = type_desc.Int3.to_user(metric)
407             fwd_addr = addrconv.ipv4.bin_to_text(fwd_addr)
408             return cls(mask, flags, metric, fwd_addr, tag), rest
409
410         def serialize(self):
411             mask = addrconv.ipv4.text_to_bin(self.mask)
412             metric = type_desc.Int3.from_user(self.metric)
413             fwd_addr = addrconv.ipv4.text_to_bin(self.fwd_addr)
414             return bytearray(
415                 struct.pack(self._PACK_STR, mask, self.flags, metric,
416                             fwd_addr, self.tag))
417
418     def __init__(self, ls_age=0, options=0, type_=OSPF_AS_EXTERNAL_LSA,
419                  id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
420                  checksum=None, length=None, extnws=None):
421         extnws = extnws if extnws else []
422         self.extnws = extnws
423         super(ASExternalLSA, self).__init__(ls_age, options, type_, id_,
424                                             adv_router, ls_seqnum, checksum,
425                                             length)
426
427     @classmethod
428     def parser(cls, buf):
429         extnws = []
430         while buf:
431             extnw, buf = cls.ExternalNetwork.parser(buf)
432             extnws.append(extnw)
433         return {
434             "extnws": extnws,
435         }
436
437     def serialize_tail(self):
438         return reduce(lambda a, b: a + b,
439                       (extnw.serialize() for extnw in self.extnws))
440
441
442 @LSA.register_type(OSPF_AS_NSSA_LSA)
443 class NSSAExternalLSA(LSA):
444     pass
445
446
447 class ExtendedPrefixTLV(StringifyMixin, type_desc.TypeDisp):
448     pass
449
450
451 @ExtendedPrefixTLV.register_type(OSPF_EXTENDED_PREFIX_TLV)
452 class ExtendedPrefixTLV(ExtendedPrefixTLV):
453     _VALUE_PACK_STR = '!HHBBBB4s'
454     _VALUE_PACK_LEN = struct.calcsize(_VALUE_PACK_STR)
455     _VALUE_FIELDS = ['route_type', 'prefix_length', 'address_family', '_pad'
456                      'prefix']
457
458     def __init__(self, type_=OSPF_EXTENDED_PREFIX_TLV, length=0, route_type=0,
459                  address_family=0, prefix='0.0.0.0/0'):
460         self.type_ = type_
461         self.length = length
462         self.route_type = route_type
463         self.address_family = address_family
464         self.prefix = prefix
465
466     @classmethod
467     def parser(cls, buf):
468         rest = buf[cls._VALUE_PACK_LEN:]
469         buf = buf[:cls._VALUE_PACK_LEN]
470         (type_, length, route_type, prefix_length, address_family, _pad,
471          prefix) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
472
473         prefix = addrconv.ipv4.bin_to_text(prefix)
474         prefix = "%s/%d" % (prefix, prefix_length)
475         return cls(type_, length, route_type, address_family, prefix), rest
476
477     def serialize(self):
478         prefix, prefix_length = self.prefix.split('/')
479         prefix = addrconv.ipv4.text_to_bin(prefix)
480         prefix_length = int(prefix_length)
481         return struct.pack(self._VALUE_PACK_STR, OSPF_EXTENDED_PREFIX_TLV,
482                            self._VALUE_PACK_LEN - 4, self.route_type,
483                            prefix_length, self.address_family, 0, prefix)
484
485
486 @ExtendedPrefixTLV.register_type(OSPF_EXTENDED_PREFIX_SID_SUBTLV)
487 class PrefixSIDSubTLV(ExtendedPrefixTLV):
488     _VALUE_PACK_STR = '!HHBBBBHHI'
489     _VALUE_PACK_LEN = struct.calcsize(_VALUE_PACK_STR)
490     _VALUE_FIELDS = ['flags', 'mt_id', 'algorithm', '_pad', 'range_size',
491                      '_pad', 'index']
492
493     def __init__(self, type_=OSPF_EXTENDED_PREFIX_SID_SUBTLV, length=0,
494                  flags=0, mt_id=0, algorithm=0, range_size=0, index=0):
495         super(PrefixSIDSubTLV, self).__init__()
496         self.type_ = type_
497         self.length = length
498         self.flags = flags
499         self.mt_id = mt_id
500         self.algorithm = algorithm
501         self.range_size = range_size
502         self.index = index
503
504     @classmethod
505     def parser(cls, buf):
506         rest = buf[cls._VALUE_PACK_LEN:]
507         buf = buf[:cls._VALUE_PACK_LEN]
508         (type_, length, flags, mt_id, algorithm, _pad, range_size, _pad,
509          index) = struct.unpack_from(cls._VALUE_PACK_STR, buf)
510
511         return cls(type_, length, flags, mt_id, algorithm, range_size,
512                    index), rest
513
514     def serialize(self):
515         return struct.pack(self._VALUE_PACK_STR,
516                            OSPF_EXTENDED_PREFIX_SID_SUBTLV,
517                            self._VALUE_PACK_LEN - 4, self.flags, self.mt_id,
518                            self.algorithm, 0, self.range_size, 0, self.index)
519
520
521 class ExtendedLinkTLV(StringifyMixin, type_desc.TypeDisp):
522     pass
523
524
525 class OpaqueBody(StringifyMixin, type_desc.TypeDisp):
526     def __init__(self, tlvs=None):
527         tlvs = tlvs if tlvs else []
528         self.tlvs = tlvs
529
530     def serialize(self):
531         return reduce(lambda a, b: a + b,
532                       (tlv.serialize() for tlv in self.tlvs))
533
534
535 @OpaqueBody.register_type(OSPF_OPAQUE_TYPE_EXTENDED_PREFIX_LSA)
536 class ExtendedPrefixOpaqueBody(OpaqueBody):
537     @classmethod
538     def parser(cls, buf):
539         buf = six.binary_type(buf)
540         tlvs = []
541         while buf:
542             (type_, length) = struct.unpack_from('!HH', buf)
543             if len(buf[struct.calcsize('!HH'):]) < length:
544                 raise stream_parser.StreamParser.TooSmallException(
545                     '%d < %d' % (len(buf), length))
546             tlvcls = ExtendedPrefixTLV._lookup_type(type_)
547             if tlvcls:
548                 tlv, buf = tlvcls.parser(buf)
549                 tlvs.append(tlv)
550
551         return cls(tlvs)
552
553
554 @OpaqueBody.register_type(OSPF_OPAQUE_TYPE_EXTENDED_LINK_LSA)
555 class ExtendedLinkOpaqueBody(OpaqueBody):
556     @classmethod
557     def parser(cls, buf):
558         buf = six.binary_type(buf)
559         tlvs = []
560         while buf:
561             (type_, length) = struct.unpack_from('!HH', buf)
562             if len(buf[struct.calcsize('!HH'):]) < length:
563                 raise stream_parser.StreamParser.TooSmallException(
564                     '%d < %d' % (len(buf), length))
565             tlvcls = ExtendedLinkTLV._lookup_type(type_)
566             if tlvcls:
567                 tlv, buf = tlvcls.parser(buf)
568                 tlvs.append(tlv)
569
570         return cls(tlvs)
571
572
573 class OpaqueLSA(LSA):
574
575     def __init__(self, data, *args, **kwargs):
576         super(OpaqueLSA, self).__init__(*args, **kwargs)
577         self.data = data
578
579     @classmethod
580     def parser(cls, buf, opaque_type=OSPF_OPAQUE_TYPE_UNKNOWN):
581         opaquecls = OpaqueBody._lookup_type(opaque_type)
582         if opaquecls:
583             data = opaquecls.parser(buf)
584         else:
585             data = buf
586         return {'data': data}
587
588     def serialize_tail(self):
589         if isinstance(self.data, OpaqueBody):
590             return self.data.serialize()
591         else:
592             return self.data
593
594
595 @LSA.register_type(OSPF_OPAQUE_LINK_LSA)
596 class LocalOpaqueLSA(OpaqueLSA):
597     def __init__(self, ls_age=0, options=0, type_=OSPF_OPAQUE_LINK_LSA,
598                  adv_router='0.0.0.0', ls_seqnum=0, checksum=0, length=0,
599                  opaque_type=OSPF_OPAQUE_TYPE_UNKNOWN, opaque_id=0, data=None):
600         self.data = data
601         super(LocalOpaqueLSA, self).__init__(ls_age, options, type_, 0,
602                                              adv_router, ls_seqnum, checksum,
603                                              length, opaque_type, opaque_id)
604
605
606 @LSA.register_type(OSPF_OPAQUE_AREA_LSA)
607 class AreaOpaqueLSA(OpaqueLSA):
608     def __init__(self, ls_age=0, options=0, type_=OSPF_OPAQUE_AREA_LSA,
609                  adv_router='0.0.0.0', ls_seqnum=0, checksum=0, length=0,
610                  opaque_type=OSPF_OPAQUE_TYPE_UNKNOWN, opaque_id=0, data=None):
611         self.data = data
612         super(AreaOpaqueLSA, self).__init__(ls_age, options, type_, 0,
613                                             adv_router, ls_seqnum, checksum,
614                                             length, opaque_type, opaque_id)
615
616
617 @LSA.register_type(OSPF_OPAQUE_AS_LSA)
618 class ASOpaqueLSA(OpaqueLSA):
619     def __init__(self, ls_age=0, options=0, type_=OSPF_OPAQUE_AS_LSA,
620                  adv_router='0.0.0.0', ls_seqnum=0, checksum=0, length=0,
621                  opaque_type=OSPF_OPAQUE_TYPE_UNKNOWN, opaque_id=0, data=None):
622         self.data = data
623         super(ASOpaqueLSA, self).__init__(ls_age, options, type_, 0,
624                                           adv_router, ls_seqnum, checksum,
625                                           length, opaque_type, opaque_id)
626
627
628 class OSPFMessage(packet_base.PacketBase, type_desc.TypeDisp):
629     """Base class for OSPF version 2 messages.
630     """
631
632     _HDR_PACK_STR = '!BBH4s4sHHQ'
633     _HDR_LEN = struct.calcsize(_HDR_PACK_STR)
634
635     def __init__(self, type_, length=None, router_id='0.0.0.0',
636                  area_id='0.0.0.0', au_type=1, authentication=0, checksum=None,
637                  version=_VERSION):
638         super(OSPFMessage, self).__init__()
639         self.version = version
640         self.type_ = type_
641         self.length = length
642         self.router_id = router_id
643         self.area_id = area_id
644         self.checksum = checksum
645         self.au_type = au_type
646         self.authentication = authentication
647
648     @classmethod
649     def _parser(cls, buf):
650         if len(buf) < cls._HDR_LEN:
651             raise stream_parser.StreamParser.TooSmallException(
652                 '%d < %d' % (len(buf), cls._HDR_LEN))
653         (version, type_, length, router_id, area_id, checksum, au_type,
654          authentication) = struct.unpack_from(cls._HDR_PACK_STR,
655                                               six.binary_type(buf))
656
657         # Exclude checksum and authentication field for checksum validation.
658         if packet_utils.checksum(buf[:12] + buf[14:16] + buf[cls._HDR_LEN:]) \
659                 != checksum:
660             raise InvalidChecksum
661
662         if len(buf) < length:
663             raise stream_parser.StreamParser.TooSmallException(
664                 '%d < %d' % (len(buf), length))
665
666         router_id = addrconv.ipv4.bin_to_text(router_id)
667         area_id = addrconv.ipv4.bin_to_text(area_id)
668         binmsg = buf[cls._HDR_LEN:length]
669         rest = buf[length:]
670         subcls = cls._lookup_type(type_)
671         kwargs = subcls.parser(binmsg)
672         return subcls(length, router_id, area_id, au_type, int(authentication),
673                       checksum, version, **kwargs), None, rest
674
675     @classmethod
676     def parser(cls, buf):
677         try:
678             return cls._parser(buf)
679         except:
680             return None, None, buf
681
682     def serialize(self, payload=None, prev=None):
683         tail = self.serialize_tail()
684         self.length = self._HDR_LEN + len(tail)
685         head = bytearray(
686             struct.pack(self._HDR_PACK_STR, self.version,
687                         self.type_, self.length,
688                         addrconv.ipv4.text_to_bin(self.router_id),
689                         addrconv.ipv4.text_to_bin(self.area_id), 0,
690                         self.au_type, self.authentication))
691         buf = head + tail
692         csum = packet_utils.checksum(buf[:12] + buf[14:16] +
693                                      buf[self._HDR_LEN:])
694         self.checksum = csum
695         struct.pack_into("!H", buf, 12, csum)
696         return buf
697
698
699 # alias
700 ospf = OSPFMessage
701
702
703 @OSPFMessage.register_type(OSPF_MSG_HELLO)
704 class OSPFHello(OSPFMessage):
705
706     _PACK_STR = '!4sHBBI4s4s'  # + neighbors
707     _PACK_LEN = struct.calcsize(_PACK_STR)
708     _MIN_LEN = OSPFMessage._HDR_LEN + _PACK_LEN
709
710     def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
711                  au_type=1, authentication=0, checksum=None, version=_VERSION,
712                  mask='0.0.0.0', hello_interval=10, options=0, priority=1,
713                  dead_interval=40, designated_router='0.0.0.0',
714                  backup_router='0.0.0.0', neighbors=None):
715         neighbors = neighbors if neighbors else []
716         super(OSPFHello, self).__init__(OSPF_MSG_HELLO, length, router_id,
717                                         area_id, au_type, authentication,
718                                         checksum, version)
719         self.mask = mask
720         self.hello_interval = hello_interval
721         self.options = options
722         self.priority = priority
723         self.dead_interval = dead_interval
724         self.designated_router = designated_router
725         self.backup_router = backup_router
726         self.neighbors = neighbors
727
728     @classmethod
729     def parser(cls, buf):
730         (mask, hello_interval, options, priority, dead_interval,
731          designated_router, backup_router) = struct.unpack_from(cls._PACK_STR,
732                                                                 six.binary_type(buf))
733         mask = addrconv.ipv4.bin_to_text(mask)
734         designated_router = addrconv.ipv4.bin_to_text(designated_router)
735         backup_router = addrconv.ipv4.bin_to_text(backup_router)
736         neighbors = []
737         binneighbors = buf[cls._PACK_LEN:len(buf)]
738         while binneighbors:
739             n = binneighbors[:4]
740             n = addrconv.ipv4.bin_to_text(six.binary_type(n))
741             binneighbors = binneighbors[4:]
742             neighbors.append(n)
743         return {
744             "mask": mask,
745             "hello_interval": hello_interval,
746             "options": options,
747             "priority": priority,
748             "dead_interval": dead_interval,
749             "designated_router": designated_router,
750             "backup_router": backup_router,
751             "neighbors": neighbors,
752         }
753
754     def serialize_tail(self):
755         head = bytearray(
756             struct.pack(self._PACK_STR,
757                         addrconv.ipv4.text_to_bin(self.mask),
758                         self.hello_interval, self.options, self.priority,
759                         self.dead_interval,
760                         addrconv.ipv4.text_to_bin(self.designated_router),
761                         addrconv.ipv4.text_to_bin(self.backup_router)))
762         try:
763             return head + reduce(lambda a, b: a + b,
764                                  (addrconv.ipv4.text_to_bin(n)
765                                   for n in self.neighbors))
766         except TypeError:
767             return head
768
769
770 @OSPFMessage.register_type(OSPF_MSG_DB_DESC)
771 class OSPFDBDesc(OSPFMessage):
772
773     _PACK_STR = '!HBBI'  # + LSA_HEADERS
774     _PACK_LEN = struct.calcsize(_PACK_STR)
775     _MIN_LEN = OSPFMessage._HDR_LEN + _PACK_LEN
776
777     def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
778                  au_type=1, authentication=0, checksum=None, version=_VERSION,
779                  mtu=1500, options=0, i_flag=0, m_flag=0, ms_flag=0,
780                  sequence_number=0, lsa_headers=None):
781         lsa_headers = lsa_headers if lsa_headers else []
782         super(OSPFDBDesc, self).__init__(OSPF_MSG_DB_DESC, length, router_id,
783                                          area_id, au_type, authentication,
784                                          checksum, version)
785         self.mtu = mtu
786         self.options = options
787         self.i_flag = i_flag
788         self.m_flag = m_flag
789         self.ms_flag = ms_flag
790         self.sequence_number = sequence_number
791         self.lsa_headers = lsa_headers
792
793     @classmethod
794     def parser(cls, buf):
795         (mtu, options, flags,
796          sequence_number) = struct.unpack_from(cls._PACK_STR, six.binary_type(buf))
797         i_flag = (flags >> 2) & 0x1
798         m_flag = (flags >> 1) & 0x1
799         ms_flag = flags & 0x1
800         lsahdrs = []
801         buf = buf[cls._PACK_LEN:]
802         while buf:
803             kwargs, buf = LSAHeader.parser(buf)
804             lsahdrs.append(LSAHeader(**kwargs))
805         return {
806             "mtu": mtu,
807             "options": options,
808             "i_flag": i_flag,
809             "m_flag": m_flag,
810             "ms_flag": ms_flag,
811             "sequence_number": sequence_number,
812             "lsa_headers": lsahdrs,
813         }
814
815     def serialize_tail(self):
816         flags = ((self.i_flag & 0x1) << 2) ^ \
817                 ((self.m_flag & 0x1) << 1) ^ \
818                 (self.ms_flag & 0x1)
819         head = bytearray(
820             struct.pack(self._PACK_STR, self.mtu, self.options, flags,
821                         self.sequence_number))
822         try:
823             return head + reduce(lambda a, b: a + b,
824                                  (hdr.serialize() for hdr in self.lsa_headers))
825         except TypeError:
826             return head
827
828
829 @OSPFMessage.register_type(OSPF_MSG_LS_REQ)
830 class OSPFLSReq(OSPFMessage):
831     _MIN_LEN = OSPFMessage._HDR_LEN
832
833     class Request(StringifyMixin):
834         _PACK_STR = '!I4s4s'
835         _PACK_LEN = struct.calcsize(_PACK_STR)
836
837         def __init__(self, type_=OSPF_UNKNOWN_LSA, id_='0.0.0.0',
838                      adv_router='0.0.0.0'):
839             self.type_ = type_
840             self.id = id_
841             self.adv_router = adv_router
842
843         @classmethod
844         def parser(cls, buf):
845             if len(buf) < cls._PACK_LEN:
846                 raise stream_parser.StreamParser.TooSmallException(
847                     '%d < %d' % (len(buf), cls._PACK_LEN))
848             link = buf[:cls._PACK_LEN]
849             rest = buf[cls._PACK_LEN:]
850             (type_, id_, adv_router) = struct.unpack_from(cls._PACK_STR,
851                                                           six.binary_type(link))
852             id_ = addrconv.ipv4.bin_to_text(id_)
853             adv_router = addrconv.ipv4.bin_to_text(adv_router)
854             return cls(type_, id_, adv_router), rest
855
856         def serialize(self):
857             id_ = addrconv.ipv4.text_to_bin(self.id)
858             adv_router = addrconv.ipv4.text_to_bin(self.adv_router)
859             return struct.pack(self._PACK_STR, self.type_, id_, adv_router)
860
861     def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
862                  au_type=1, authentication=0, checksum=None, version=_VERSION,
863                  lsa_requests=None):
864         lsa_requests = lsa_requests if lsa_requests else []
865         super(OSPFLSReq, self).__init__(OSPF_MSG_LS_REQ, length, router_id,
866                                         area_id, au_type, authentication,
867                                         checksum, version)
868         self.lsa_requests = lsa_requests
869
870     @classmethod
871     def parser(cls, buf):
872         reqs = []
873         while buf:
874             req, buf = cls.Request.parser(buf)
875             reqs.append(req)
876         return {
877             "lsa_requests": reqs,
878         }
879
880     def serialize_tail(self):
881         return reduce(lambda a, b: a + b,
882                       (req.serialize() for req in self.lsa_requests))
883
884
885 @OSPFMessage.register_type(OSPF_MSG_LS_UPD)
886 class OSPFLSUpd(OSPFMessage):
887     _PACK_STR = '!I'
888     _PACK_LEN = struct.calcsize(_PACK_STR)
889     _MIN_LEN = OSPFMessage._HDR_LEN + _PACK_LEN
890
891     def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
892                  au_type=1, authentication=0, checksum=None, version=_VERSION,
893                  lsas=None):
894         lsas = lsas if lsas else []
895         super(OSPFLSUpd, self).__init__(OSPF_MSG_LS_UPD, length, router_id,
896                                         area_id, au_type, authentication,
897                                         checksum, version)
898         self.lsas = lsas
899
900     @classmethod
901     def parser(cls, buf):
902         binnum = buf[:cls._PACK_LEN]
903         (num,) = struct.unpack_from(cls._PACK_STR, six.binary_type(binnum))
904
905         buf = buf[cls._PACK_LEN:]
906         lsas = []
907         while buf:
908             lsa, _cls, buf = LSA.parser(buf)
909             lsas.append(lsa)
910         assert len(lsas) == num
911         return {
912             "lsas": lsas,
913         }
914
915     def serialize_tail(self):
916         head = bytearray(struct.pack(self._PACK_STR, len(self.lsas)))
917         try:
918             return head + reduce(lambda a, b: a + b,
919                                  (lsa.serialize() for lsa in self.lsas))
920         except TypeError:
921             return head
922
923
924 @OSPFMessage.register_type(OSPF_MSG_LS_ACK)
925 class OSPFLSAck(OSPFMessage):
926     _MIN_LEN = OSPFMessage._HDR_LEN
927
928     def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
929                  au_type=1, authentication=0, checksum=None, version=_VERSION,
930                  lsa_headers=None):
931         lsa_headers = lsa_headers if lsa_headers else []
932         super(OSPFLSAck, self).__init__(OSPF_MSG_LS_ACK, length, router_id,
933                                         area_id, au_type, authentication,
934                                         checksum, version)
935         self.lsa_headers = lsa_headers
936
937     @classmethod
938     def parser(cls, buf):
939         lsahdrs = []
940         while buf:
941             kwargs, buf = LSAHeader.parser(buf)
942             lsahdrs.append(LSAHeader(**kwargs))
943         return {
944             "lsa_headers": lsahdrs,
945         }
946
947     def serialize_tail(self):
948         return reduce(lambda a, b: a + b,
949                       (hdr.serialize() for hdr in self.lsa_headers))