deb-ryu/ryu/lib/packet/ospf.py
YAMAMOTO Takashi d2bb2205de packet.ospf: workaround a bytearray vs buffer problem
feeding bytearray to struct.unpack() crashes on some environment.
(depends on the interpreter versions?  i'm not sure.)

this fixes the following crash in the unit test.
the crash was seen on travis-ci, too.
https://travis-ci.org/osrg/ryu/jobs/15578909
https://s3.amazonaws.com/archive.travis-ci.org/jobs/15578909/log.txt

======================================================================
ERROR: test_hello (packet.test_ospf.Test_ospf)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/yamamoto/git/ryu/ryu/tests/unit/packet/test_ospf.py", line 68, in test_hello
    msg2, cls, rest = ospf.OSPFMessage.parser(binmsg)
  File "/Users/yamamoto/git/ryu/ryu/lib/packet/ospf.py", line 443, in parser
    kwargs = subcls.parser(binmsg)
  File "/Users/yamamoto/git/ryu/ryu/lib/packet/ospf.py", line 501, in parser
    n = addrconv.ipv4.bin_to_text(n)
  File "/Users/yamamoto/git/ryu/ryu/lib/addrconv.py", line 30, in bin_to_text
    return str(self._addr(self._strat.packed_to_int(bin),
  File "/Users/yamamoto/git/ryu/.venv/lib/python2.7/site-packages/netaddr/strategy/ipv4.py", line 196, in packed_to_int
    return _struct.unpack('>I', packed_int)[0]
error: unpack requires a string argument of length 4

----------------------------------------------------------------------

Signed-off-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
2013-12-18 13:10:07 +09:00

707 lines
24 KiB
Python

# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
RFC 2328 OSPF version 2
"""
import struct
from ryu.lib.stringify import StringifyMixin
from ryu.lib.packet import packet_base
from ryu.lib.packet import packet_utils
from ryu.lib.packet import stream_parser
from ryu.lib import addrconv
_VERSION = 2
OSPF_MSG_UNKNOWN = 0
OSPF_MSG_HELLO = 1
OSPF_MSG_DB_DESC = 2
OSPF_MSG_LS_REQ = 3
OSPF_MSG_LS_UPD = 4
OSPF_MSG_LS_ACK = 5
OSPF_UNKNOWN_LSA = 0
OSPF_ROUTER_LSA = 1
OSPF_NETWORK_LSA = 2
OSPF_SUMMARY_LSA = 3
OSPF_ASBR_SUMMARY_LSA = 4
OSPF_AS_EXTERNAL_LSA = 5
OSPF_AS_NSSA_LSA = 7 # RFC 3101
OSPF_OPAQUE_LINK_LSA = 9 # RFC 5250
OSPF_OPAQUE_AREA_LSA = 10 # RFC 5250
OSPF_OPAQUE_AS_LSA = 11 # RFC 5250
OSPF_OPTION_T = 1 # Obsolete
OSPF_OPTION_E = 1 << 1 # RFC 2328
OSPF_OPTION_MC = 1 << 2 # RFC 1584
OSPF_OPTION_NP = 1 << 3 # RFC 3101
OSPF_OPTION_EA = 1 << 4 # Obsolete
OSPF_OPTION_DC = 1 << 5 # RFC 2370
OSPF_OPTION_DN = 1 << 7 # RFC 2567
LSA_LINK_TYPE_P2P = 1
LSA_LINK_TYPE_TRANSIT = 2
LSA_LINK_TYPE_STUB = 3
LSA_LINK_TYPE_VL = 4
ROUTER_LSA_BORDER = 0x01 # The router is an ABR
ROUTER_LSA_EXTERNAL = 0x02 # The router is an ASBR
ROUTER_LSA_VIRTUAL = 0x04 # The router has a VL in this area
ROUTER_LSA_NT = 0x10 # The router always translates Type-7
ROUTER_LSA_SHORTCUT = 0x20 # Shortcut-ABR specific flag
AS_EXTERNAL_METRIC = 0x80
class InvalidChecksum(Exception):
pass
class _TypeDisp(object):
_TYPES = {}
_REV_TYPES = None
_UNKNOWN_TYPE = None
@classmethod
def register_unknown_type(cls):
def _register_type(subcls):
cls._UNKNOWN_TYPE = subcls
return subcls
return _register_type
@classmethod
def register_type(cls, type_):
cls._TYPES = cls._TYPES.copy()
def _register_type(subcls):
cls._TYPES[type_] = subcls
cls._REV_TYPES = None
return subcls
return _register_type
@classmethod
def _lookup_type(cls, type_):
try:
return cls._TYPES[type_]
except KeyError:
return cls._UNKNOWN_TYPE
@classmethod
def _rev_lookup_type(cls, targ_cls):
if cls._REV_TYPES is None:
rev = dict((v, k) for k, v in cls._TYPES.iteritems())
cls._REV_TYPES = rev
return cls._REV_TYPES[targ_cls]
class LSAHeader(StringifyMixin):
_HDR_PACK_STR = '!HBB4s4sIHH'
_HDR_LEN = struct.calcsize(_HDR_PACK_STR)
def __init__(self, ls_age=0, options=0, type_=OSPF_UNKNOWN_LSA,
id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
checksum=None, length=None):
self.ls_age = ls_age
self.options = options
self.type_ = type_
self.id_ = id_
self.adv_router = adv_router
self.ls_seqnum = ls_seqnum
self.checksum = checksum
self.length = length
@classmethod
def parser(cls, buf):
if len(buf) < cls._HDR_LEN:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), cls._HDR_LEN))
(ls_age, options, type_, id_, adv_router, ls_seqnum, checksum,
length,) = struct.unpack_from(cls._HDR_PACK_STR, buffer(buf))
id_ = addrconv.ipv4.bin_to_text(id_)
adv_router = addrconv.ipv4.bin_to_text(adv_router)
rest = buf[cls._HDR_LEN:]
return {
"ls_age": ls_age,
"options": options,
"type_": type_,
"id_": id_,
"adv_router": adv_router,
"ls_seqnum": ls_seqnum,
"checksum": checksum,
"length": length,
}, rest
def serialize(self):
id_ = addrconv.ipv4.text_to_bin(self.id_)
adv_router = addrconv.ipv4.text_to_bin(self.adv_router)
return bytearray(struct.pack(self._HDR_PACK_STR, self.ls_age,
self.options, self.type_, id_, adv_router,
self.ls_seqnum, self.checksum, self.length))
class LSA(_TypeDisp, StringifyMixin):
def __init__(self, ls_age=0, options=0, type_=OSPF_UNKNOWN_LSA,
id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
checksum=None, length=None):
self.header = LSAHeader(ls_age, options, type_, id_, adv_router,
ls_seqnum, 0, 0)
if not (checksum or length):
tail = self.serialize_tail()
length = self.header._HDR_LEN + len(tail)
if not checksum:
head = self.header.serialize()
checksum = packet_utils.fletcher_checksum(head[2:], 14)
self.header.length = length
self.header.checksum = checksum
@classmethod
def parser(cls, buf):
hdr, rest = LSAHeader.parser(buf)
#exclude ls_age for checksum calculation
csum = packet_utils.fletcher_checksum(buf[2:hdr['length']], 14)
if csum != hdr['checksum']:
raise InvalidChecksum("header has %d, but calculated value is %d"
% (hdr['checksum'], csum))
subcls = cls._lookup_type(hdr['type_'])
body = rest[:hdr['length']-LSAHeader._HDR_LEN]
rest = rest[hdr['length']-LSAHeader._HDR_LEN:]
kwargs = subcls.parser(body)
kwargs.update(hdr)
return subcls(**kwargs), subcls, rest
def serialize(self):
tail = self.serialize_tail()
self.header.length = self.header._HDR_LEN + len(tail)
head = self.header.serialize()
#exclude ls_age for checksum calculation
csum = packet_utils.fletcher_checksum(head[2:]+tail, 14)
self.header.checksum = csum
struct.pack_into("!H", head, 16, csum)
return head + tail
@LSA.register_type(OSPF_ROUTER_LSA)
class RouterLSA(LSA):
_PACK_STR = '!BBH'
_PACK_LEN = struct.calcsize(_PACK_STR) # 4bytes
class Link(StringifyMixin):
_PACK_STR = '!4s4sBBH'
_PACK_LEN = struct.calcsize(_PACK_STR) # 12bytes
def __init__(self, id_='0.0.0.0', data='0.0.0.0',
type_=LSA_LINK_TYPE_STUB, tos=0, metric=10):
self.id_ = id_
self.data = data
self.type_ = type_
self.tos = tos
self.metric = metric
@classmethod
def parser(cls, buf):
if len(buf) < cls._PACK_LEN:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), cls._PACK_LEN))
link = buf[:cls._PACK_LEN]
rest = buf[cls._PACK_LEN:]
(id_, data, type_, tos, metric) = \
struct.unpack_from(cls._PACK_STR, buffer(link))
id_ = addrconv.ipv4.bin_to_text(id_)
data = addrconv.ipv4.bin_to_text(data)
return cls(id_, data, type_, tos, metric), rest
def serialize(self):
id_ = addrconv.ipv4.text_to_bin(self.id_)
data = addrconv.ipv4.text_to_bin(self.data)
return bytearray(struct.pack(self._PACK_STR, id_, data, self.type_,
self.tos, self.metric))
def __init__(self, ls_age=0, options=0, type_=OSPF_ROUTER_LSA,
id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
checksum=None, length=None, flags=0, links=[]):
self.flags = flags
self.links = links
super(RouterLSA, self).__init__(ls_age, options, type_, id_,
adv_router, ls_seqnum, checksum,
length)
@classmethod
def parser(cls, buf):
links = []
hdr = buf[:cls._PACK_LEN]
buf = buf[cls._PACK_LEN:]
(flags, padding, num) = struct.unpack_from(cls._PACK_STR, buffer(hdr))
while buf:
link, buf = cls.Link.parser(buf)
links.append(link)
assert(len(links) == num)
return {
"flags": flags,
"links": links,
}
def serialize_tail(self):
head = bytearray(struct.pack(self._PACK_STR, self.flags, 0,
len(self.links)))
try:
return head + reduce(lambda a, b: a+b,
(link.serialize() for link in self.links))
except TypeError:
return head
@LSA.register_type(OSPF_NETWORK_LSA)
class NetworkLSA(LSA):
_PACK_STR = '!4s'
_PACK_LEN = struct.calcsize(_PACK_STR)
def __init__(self, ls_age=0, options=0, type_=OSPF_NETWORK_LSA,
id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
checksum=None, length=None, mask='0.0.0.0', routers=[]):
self.mask = mask
self.routers = routers
super(NetworkLSA, self).__init__(ls_age, options, type_, id_,
adv_router, ls_seqnum, checksum,
length)
@classmethod
def parser(cls, buf):
if len(buf) < cls._PACK_LEN:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), cls._PACK_LEN))
binmask = buf[:cls._PACK_LEN]
(mask,) = struct.unpack_from(cls._PACK_STR, buffer(binmask))
mask = addrconv.ipv4.bin_to_text(mask)
buf = buf[cls._PACK_LEN:]
routers = []
while buf:
binrouter = buf[:cls._PACK_LEN]
(router,) = struct.unpack_from(cls._PACK_STR, buffer(binrouter))
router = addrconv.ipv4.bin_to_text(router)
routers.append(router)
buf = buf[cls._PACK_LEN:]
return {
"mask": mask,
"routers": routers,
}
def serialize_tail(self):
mask = addrconv.ipv4.text_to_bin(self.mask)
routers = [addrconv.ipv4.text_to_bin(
router) for router in self.routers]
return bytearray(struct.pack("!"+"4s"*(1+len(routers)), mask,
*routers))
@LSA.register_type(OSPF_SUMMARY_LSA)
class SummaryLSA(LSA):
pass
@LSA.register_type(OSPF_ASBR_SUMMARY_LSA)
class ASBRSummaryLSA(LSA):
pass
@LSA.register_type(OSPF_AS_EXTERNAL_LSA)
class ASExternalLSA(LSA):
class ExternalNetwork(StringifyMixin):
_PACK_STR = '!4sBBH4sI'
_PACK_LEN = struct.calcsize(_PACK_STR)
def __init__(self, mask='0.0.0.0', flags=0, metric=0,
fwd_addr='0.0.0.0', tag=0):
self.mask = mask
self.flags = flags
self.metric = metric
self.fwd_addr = fwd_addr
self.tag = tag
@classmethod
def parser(cls, buf):
if len(buf) < cls._PACK_LEN:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), cls._PACK_LEN))
ext_nw = buf[:cls._PACK_LEN]
rest = buf[cls._PACK_LEN:]
(mask, flags, metric_fst, metric_lst, fwd_addr,
tag) = struct.unpack_from(cls._PACK_STR, buffer(ext_nw))
mask = addrconv.ipv4.bin_to_text(mask)
metric = metric_fst << 16 | (metric_lst & 0xffff)
fwd_addr = addrconv.ipv4.bin_to_text(fwd_addr)
return cls(mask, flags, metric, fwd_addr, tag), rest
def serialize(self):
mask = addrconv.ipv4.text_to_bin(self.mask)
metric_fst = (self.metric >> 16) & 0xff
metric_lst = self.metric & 0xffff
fwd_addr = addrconv.ipv4.text_to_bin(self.fwd_addr)
return bytearray(struct.pack(self._PACK_STR, mask, self.flags,
metric_fst, metric_lst, fwd_addr,
self.tag))
def __init__(self, ls_age=0, options=0, type_=OSPF_AS_EXTERNAL_LSA,
id_='0.0.0.0', adv_router='0.0.0.0', ls_seqnum=0,
checksum=None, length=None, extnws=[]):
self.extnws = extnws
super(ASExternalLSA, self).__init__(ls_age, options, type_, id_,
adv_router, ls_seqnum, checksum,
length)
@classmethod
def parser(cls, buf):
extnws = []
while buf:
extnw, buf = cls.ExternalNetwork.parser(buf)
extnws.append(extnw)
return {
"extnws": extnws,
}
def serialize_tail(self):
return reduce(lambda a, b: a+b,
(extnw.serialize() for extnw in self.extnws))
@LSA.register_type(OSPF_AS_NSSA_LSA)
class NSSAExternalLSA(LSA):
pass
@LSA.register_type(OSPF_OPAQUE_LINK_LSA)
class LocalOpaqueLSA(LSA):
pass
@LSA.register_type(OSPF_OPAQUE_AREA_LSA)
class AreaOpaqueLSA(LSA):
pass
@LSA.register_type(OSPF_OPAQUE_AS_LSA)
class ASOpaqueLSA(LSA):
pass
class OSPFMessage(packet_base.PacketBase, _TypeDisp):
"""Base class for OSPF version 2 messages.
"""
_HDR_PACK_STR = '!BBH4s4sHHQ'
_HDR_LEN = struct.calcsize(_HDR_PACK_STR)
def __init__(self, type_, length=None, router_id='0.0.0.0',
area_id='0.0.0.0', au_type=1, authentication=0, checksum=None,
version=_VERSION):
self.version = version
self.type_ = type_
self.length = length
self.router_id = router_id
self.area_id = area_id
self.checksum = checksum
self.au_type = au_type
self.authentication = authentication
@classmethod
def parser(cls, buf):
if len(buf) < cls._HDR_LEN:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), cls._HDR_LEN))
(version, type_, length, router_id, area_id, checksum, au_type,
authentication) = struct.unpack_from(cls._HDR_PACK_STR, buffer(buf))
#Exclude checksum and authentication field for checksum validation.
if packet_utils.checksum(buf[:12]+buf[14:16]+buf[cls._HDR_LEN:]) \
!= checksum:
raise InvalidChecksum
if len(buf) < length:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), length))
router_id = addrconv.ipv4.bin_to_text(router_id)
area_id = addrconv.ipv4.bin_to_text(area_id)
binmsg = buf[cls._HDR_LEN:length]
rest = buf[length:]
subcls = cls._lookup_type(type_)
kwargs = subcls.parser(binmsg)
return subcls(length, router_id, area_id, au_type, authentication,
checksum, version, **kwargs), None, rest
def serialize(self):
tail = self.serialize_tail()
self.length = self._HDR_LEN + len(tail)
head = bytearray(struct.pack(self._HDR_PACK_STR, self.version,
self.type_, self.length,
addrconv.ipv4.text_to_bin(self.router_id),
addrconv.ipv4.text_to_bin(self.area_id), 0,
self.au_type, self.authentication))
buf = head + tail
csum = packet_utils.checksum(buf[:12]+buf[14:16]+buf[self._HDR_LEN:])
self.checksum = csum
struct.pack_into("!H", buf, 12, csum)
return buf
#alias
ospf = OSPFMessage
@OSPFMessage.register_type(OSPF_MSG_HELLO)
class OSPFHello(OSPFMessage):
_PACK_STR = '!4sHBBI4s4s' # + neighbors
_PACK_LEN = struct.calcsize(_PACK_STR)
_MIN_LEN = OSPFMessage._HDR_LEN + _PACK_LEN
def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
au_type=1, authentication=0, checksum=None, version=_VERSION,
mask='0.0.0.0', hello_interval=10, options=0, priority=1,
dead_interval=40, designated_router='0.0.0.0',
backup_router='0.0.0.0', neighbors=[]):
super(OSPFHello, self).__init__(OSPF_MSG_HELLO, length, router_id,
area_id, au_type, authentication,
checksum, version)
self.mask = mask
self.hello_interval = hello_interval
self.options = options
self.priority = priority
self.dead_interval = dead_interval
self.designated_router = designated_router
self.backup_router = backup_router
self.neighbors = neighbors
@classmethod
def parser(cls, buf):
(mask, hello_interval, options, priority, dead_interval,
designated_router, backup_router) = struct.unpack_from(cls._PACK_STR,
buffer(buf))
mask = addrconv.ipv4.bin_to_text(mask)
designated_router = addrconv.ipv4.bin_to_text(designated_router)
backup_router = addrconv.ipv4.bin_to_text(backup_router)
neighbors = []
binneighbors = buf[cls._PACK_LEN:len(buf)]
while binneighbors:
n = binneighbors[:4]
n = addrconv.ipv4.bin_to_text(buffer(n))
binneighbors = binneighbors[4:]
neighbors.append(n)
return {
"mask": mask,
"hello_interval": hello_interval,
"options": options,
"priority": priority,
"dead_interval": dead_interval,
"designated_router": designated_router,
"backup_router": backup_router,
"neighbors": neighbors,
}
def serialize_tail(self):
head = bytearray(struct.pack(self._PACK_STR,
addrconv.ipv4.text_to_bin(self.mask),
self.hello_interval, self.options, self.priority,
self.dead_interval,
addrconv.ipv4.text_to_bin(self.designated_router),
addrconv.ipv4.text_to_bin(self.backup_router)))
try:
return head + reduce(lambda a, b: a+b,
(addrconv.ipv4.text_to_bin(
n) for n in self.neighbors))
except TypeError:
return head
@OSPFMessage.register_type(OSPF_MSG_DB_DESC)
class OSPFDBDesc(OSPFMessage):
_PACK_STR = '!HBBI' # + LSA_HEADERS
_PACK_LEN = struct.calcsize(_PACK_STR)
_MIN_LEN = OSPFMessage._HDR_LEN + _PACK_LEN
def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
au_type=1, authentication=0, checksum=None, version=_VERSION,
mtu=1500, options=0, i_flag=0, m_flag=0, ms_flag=0,
sequence_number=0, lsa_headers=[]):
super(OSPFDBDesc, self).__init__(OSPF_MSG_DB_DESC, length, router_id,
area_id, au_type, authentication,
checksum, version)
self.mtu = mtu
self.options = options
self.i_flag = i_flag
self.m_flag = m_flag
self.ms_flag = ms_flag
self.sequence_number = sequence_number
self.lsa_headers = lsa_headers
@classmethod
def parser(cls, buf):
(mtu, options, flags,
sequence_number) = struct.unpack_from(cls._PACK_STR, buffer(buf))
i_flag = (flags >> 2) & 0x1
m_flag = (flags >> 1) & 0x1
ms_flag = flags & 0x1
lsahdrs = []
buf = buf[cls._PACK_LEN:]
while buf:
kwargs, buf = LSAHeader.parser(buf)
lsahdrs.append(LSAHeader(**kwargs))
return {
"mtu": mtu,
"options": options,
"i_flag": i_flag,
"m_flag": m_flag,
"ms_flag": ms_flag,
"sequence_number": sequence_number,
"lsa_headers": lsahdrs,
}
def serialize_tail(self):
flags = ((self.i_flag & 0x1) << 2) ^ \
((self.m_flag & 0x1) << 1) ^ \
(self.ms_flag & 0x1)
head = bytearray(struct.pack(self._PACK_STR, self.mtu,
self.options, flags,
self.sequence_number))
try:
return head + reduce(lambda a, b: a+b,
(hdr.serialize() for hdr in self.lsa_headers))
except TypeError:
return head
@OSPFMessage.register_type(OSPF_MSG_LS_REQ)
class OSPFLSReq(OSPFMessage):
_MIN_LEN = OSPFMessage._HDR_LEN
class Request(StringifyMixin):
_PACK_STR = '!I4s4s'
_PACK_LEN = struct.calcsize(_PACK_STR)
def __init__(self, type_=OSPF_UNKNOWN_LSA, id_='0.0.0.0',
adv_router='0.0.0.0'):
self.type_ = type_
self.id = id_
self.adv_router = adv_router
@classmethod
def parser(cls, buf):
if len(buf) < cls._PACK_LEN:
raise stream_parser.StreamParser.TooSmallException(
'%d < %d' % (len(buf), cls._PACK_LEN))
link = buf[:cls._PACK_LEN]
rest = buf[cls._PACK_LEN:]
(type_, id_, adv_router) = struct.unpack_from(cls._PACK_STR,
buffer(link))
id_ = addrconv.ipv4.bin_to_text(id_)
adv_router = addrconv.ipv4.bin_to_text(adv_router)
return cls(type_, id_, adv_router), rest
def serialize(self):
id_ = addrconv.ipv4.text_to_bin(self.id)
adv_router = addrconv.ipv4.text_to_bin(self.adv_router)
return bytearray(struct.pack(self._PACK_STR, self.type_,
id_, adv_router))
def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
au_type=1, authentication=0, checksum=None, version=_VERSION,
lsa_requests=[]):
super(OSPFLSReq, self).__init__(OSPF_MSG_LS_REQ, length, router_id,
area_id, au_type, authentication,
checksum, version)
self.lsa_requests = lsa_requests
@classmethod
def parser(cls, buf):
reqs = []
while buf:
req, buf = cls.Request.parser(buf)
reqs.append(req)
return {
"lsa_requests": reqs,
}
def serialize_tail(self):
return reduce(lambda a, b: a+b,
(req.serialize() for req in self.lsa_requests))
@OSPFMessage.register_type(OSPF_MSG_LS_UPD)
class OSPFLSUpd(OSPFMessage):
_PACK_STR = '!I'
_PACK_LEN = struct.calcsize(_PACK_STR)
_MIN_LEN = OSPFMessage._HDR_LEN + _PACK_LEN
def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
au_type=1, authentication=0, checksum=None, version=_VERSION,
lsas=[]):
super(OSPFLSUpd, self).__init__(OSPF_MSG_LS_UPD, length, router_id,
area_id, au_type, authentication,
checksum, version)
self.lsas = lsas
@classmethod
def parser(cls, buf):
binnum = buf[:cls._PACK_LEN]
(num,) = struct.unpack_from(cls._PACK_STR, buffer(binnum))
buf = buf[cls._PACK_LEN:]
lsas = []
while buf:
lsa, cls, buf = LSA.parser(buf)
lsas.append(lsa)
assert(len(lsas) == num)
return {
"lsas": lsas,
}
def serialize_tail(self):
head = bytearray(struct.pack(self._PACK_STR, len(self.lsas)))
try:
return head + reduce(lambda a, b: a+b,
(lsa.serialize() for lsa in self.lsas))
except TypeError:
return head
@OSPFMessage.register_type(OSPF_MSG_LS_ACK)
class OSPFLSAck(OSPFMessage):
_MIN_LEN = OSPFMessage._HDR_LEN
def __init__(self, length=None, router_id='0.0.0.0', area_id='0.0.0.0',
au_type=1, authentication=0, checksum=None, version=_VERSION,
lsa_headers=[]):
super(OSPFLSAck, self).__init__(OSPF_MSG_LS_ACK, length, router_id,
area_id, au_type, authentication,
checksum, version)
self.lsa_headers = lsa_headers
@classmethod
def parser(cls, buf):
lsahdrs = []
while buf:
kwargs, buf = LSAHeader.parser(buf)
lsahdrs.append(LSAHeader(**kwargs))
return {
"lsa_headers": lsahdrs,
}
def serialize_tail(self):
return reduce(lambda a, b: a+b,
(hdr.serialize() for hdr in self.lsa_headers))