mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-08 22:06:10 +02:00
packet/bgp: add route distinguisher classes and refine codes
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
44725bce37
commit
49dcfa5c0d
@ -142,6 +142,72 @@ BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION = 7
|
||||
BGP_ERROR_SUB_OUT_OF_RESOURCES = 8
|
||||
|
||||
|
||||
class _Value(object):
|
||||
_VALUE_PACK_STR = None
|
||||
_VALUE_FIELDS = ['value']
|
||||
|
||||
@staticmethod
|
||||
def do_init(cls, self, kwargs, **extra_kwargs):
|
||||
ourfields = {}
|
||||
for f in cls._VALUE_FIELDS:
|
||||
v = kwargs[f]
|
||||
del kwargs[f]
|
||||
ourfields[f] = v
|
||||
kwargs.update(extra_kwargs)
|
||||
super(cls, self).__init__(**kwargs)
|
||||
self.__dict__.update(ourfields)
|
||||
|
||||
@classmethod
|
||||
def parse_value(cls, buf):
|
||||
values = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf))
|
||||
return dict(zip(cls._VALUE_FIELDS, values))
|
||||
|
||||
def serialize_value(self):
|
||||
args = []
|
||||
for f in self._VALUE_FIELDS:
|
||||
args.append(getattr(self, f))
|
||||
buf = bytearray()
|
||||
msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
|
||||
return buf
|
||||
|
||||
|
||||
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 BgpExc(Exception):
|
||||
"""Base bgp exception."""
|
||||
|
||||
@ -505,12 +571,116 @@ RF_IPv4_VPN = RouteFamily(afi.IP, safi.MPLS_VPN)
|
||||
RF_IPv6_VPN = RouteFamily(afi.IP6, safi.MPLS_VPN)
|
||||
RF_RTC_UC = RouteFamily(afi.IP, safi.ROUTE_TARGET_CONSTRTAINS)
|
||||
|
||||
_rf_map = {
|
||||
(afi.IP, safi.UNICAST): RF_IPv4_UC,
|
||||
(afi.IP6, safi.UNICAST): RF_IPv6_UC,
|
||||
(afi.IP, safi.MPLS_VPN): RF_IPv4_VPN,
|
||||
(afi.IP6, safi.MPLS_VPN): RF_IPv6_VPN,
|
||||
(afi.IP, safi.ROUTE_TARGET_CONSTRTAINS): RF_RTC_UC
|
||||
}
|
||||
|
||||
|
||||
def pad(bin, len_):
|
||||
assert len(bin) <= len_
|
||||
return bin + (len_ - len(bin)) * '\0'
|
||||
|
||||
|
||||
class _RouteDistinguisher(StringifyMixin, _TypeDisp, _Value):
|
||||
_PACK_STR = '!H'
|
||||
TWO_OCTET_AS = 0
|
||||
IPV4_ADDRESS = 1
|
||||
FOUR_OCTET_AS = 2
|
||||
|
||||
def __init__(self, type_, admin=0, assigned=0):
|
||||
self.type = type_
|
||||
self.admin = admin
|
||||
self.assigned = assigned
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
assert len(buf) == 8
|
||||
(type_,) = struct.unpack_from(cls._PACK_STR, buffer(buf))
|
||||
rest = buf[struct.calcsize(cls._PACK_STR):]
|
||||
subcls = cls._lookup_type(type_)
|
||||
return subcls(type_=type_, **subcls.parse_value(rest))
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, str_):
|
||||
assert isinstance(str_, str)
|
||||
|
||||
first, second = str_.split(':')
|
||||
if '.' in first:
|
||||
type_ = cls.IPV4_ADDRESS
|
||||
elif int(first) > (1 << 16):
|
||||
type_ = cls.FOUR_OCTET_AS
|
||||
first = int(first)
|
||||
else:
|
||||
type_ = cls.TWO_OCTET_AS
|
||||
first = int(first)
|
||||
subcls = cls._lookup_type(type_)
|
||||
return subcls(type_=type_, admin=first, assigned=int(second))
|
||||
|
||||
def serialize(self):
|
||||
value = self.serialize_value()
|
||||
buf = bytearray()
|
||||
msg_pack_into(self._PACK_STR, buf, 0, self.type)
|
||||
return buf + value
|
||||
|
||||
@property
|
||||
def formatted_str(self):
|
||||
return "%s:%s" % (str(self.admin), str(self.assigned))
|
||||
|
||||
|
||||
@_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS)
|
||||
class BGPTwoOctetAsRD(_RouteDistinguisher):
|
||||
_VALUE_PACK_STR = '!HI'
|
||||
_VALUE_FIELDS = ['admin', 'assigned']
|
||||
|
||||
def __init__(self, type_=_RouteDistinguisher.TWO_OCTET_AS, **kwargs):
|
||||
self.do_init(BGPTwoOctetAsRD, self, kwargs, type_=type_)
|
||||
|
||||
|
||||
@_RouteDistinguisher.register_type(_RouteDistinguisher.IPV4_ADDRESS)
|
||||
class BGPIPv4AddressRD(_RouteDistinguisher):
|
||||
_VALUE_PACK_STR = '!4sH'
|
||||
_VALUE_FIELDS = ['admin', 'assigned']
|
||||
_TYPE = {
|
||||
'ascii': [
|
||||
'admin'
|
||||
]
|
||||
}
|
||||
|
||||
def __init__(self, type_=_RouteDistinguisher.IPV4_ADDRESS, **kwargs):
|
||||
self.do_init(BGPIPv4AddressRD, self, kwargs, type_=type_)
|
||||
|
||||
@classmethod
|
||||
def parse_value(cls, buf):
|
||||
d_ = super(BGPIPv4AddressRD, cls).parse_value(buf)
|
||||
d_['admin'] = addrconv.ipv4.bin_to_text(d_['admin'])
|
||||
return d_
|
||||
|
||||
def serialize_value(self):
|
||||
args = []
|
||||
for f in self._VALUE_FIELDS:
|
||||
v = getattr(self, f)
|
||||
if f == 'admin':
|
||||
v = bytes(addrconv.ipv4.text_to_bin(v))
|
||||
args.append(v)
|
||||
buf = bytearray()
|
||||
msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
|
||||
return buf
|
||||
|
||||
|
||||
@_RouteDistinguisher.register_type(_RouteDistinguisher.FOUR_OCTET_AS)
|
||||
class BGPFourOctetAsRD(_RouteDistinguisher):
|
||||
_VALUE_PACK_STR = '!IH'
|
||||
_VALUE_FIELDS = ['admin', 'assigned']
|
||||
|
||||
def __init__(self, type_=_RouteDistinguisher.FOUR_OCTET_AS,
|
||||
**kwargs):
|
||||
self.do_init(BGPFourOctetAsRD, self, kwargs, type_=type_)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class _AddrPrefix(StringifyMixin):
|
||||
_PACK_STR = '!B' # length
|
||||
@ -690,6 +860,10 @@ class _VPNAddrPrefix(_AddrPrefix):
|
||||
addr = addr[1:]
|
||||
else:
|
||||
length += struct.calcsize(self._RD_PACK_STR) * 8
|
||||
|
||||
if isinstance(route_dist, str):
|
||||
route_dist = _RouteDistinguisher.from_str(route_dist)
|
||||
|
||||
prefixes = prefixes + (route_dist,)
|
||||
super(_VPNAddrPrefix, self).__init__(prefixes=prefixes,
|
||||
length=length,
|
||||
@ -699,15 +873,14 @@ class _VPNAddrPrefix(_AddrPrefix):
|
||||
def _prefix_to_bin(cls, addr):
|
||||
rd = addr[0]
|
||||
rest = addr[1:]
|
||||
binrd = bytearray()
|
||||
msg_pack_into(cls._RD_PACK_STR, binrd, 0, rd)
|
||||
binrd = rd.serialize()
|
||||
return binrd + super(_VPNAddrPrefix, cls)._prefix_to_bin(rest)
|
||||
|
||||
@classmethod
|
||||
def _prefix_from_bin(cls, binaddr):
|
||||
binrd = binaddr[:8]
|
||||
binrest = binaddr[8:]
|
||||
(rd,) = struct.unpack_from(cls._RD_PACK_STR, buffer(binrd))
|
||||
rd = _RouteDistinguisher.parser(binrd)
|
||||
return (rd,) + super(_VPNAddrPrefix, cls)._prefix_from_bin(binrest)
|
||||
|
||||
|
||||
@ -741,11 +914,13 @@ class LabelledVPNIPAddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self.addr[-1] + '/{0}'.format(self.length)
|
||||
masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
|
||||
- struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
|
||||
return self.addr[-1] + '/{0}'.format(masklen)
|
||||
|
||||
@property
|
||||
def route_disc(self):
|
||||
return self.addr[-2]
|
||||
def route_dist(self):
|
||||
return self.addr[-2].formatted_str
|
||||
|
||||
@property
|
||||
def label_list(self):
|
||||
@ -762,11 +937,13 @@ class LabelledVPNIP6AddrPrefix(_LabelledAddrPrefix, _VPNAddrPrefix,
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
return self.addr[-1] + '/{0}'.format(self.length)
|
||||
masklen = self.length - struct.calcsize(self._RD_PACK_STR) * 8 \
|
||||
- struct.calcsize(self._LABEL_PACK_STR) * 8 * len(self.addr[:-2])
|
||||
return self.addr[-1] + '/{0}'.format(masklen)
|
||||
|
||||
@property
|
||||
def route_disc(self):
|
||||
return self.addr[-2]
|
||||
def route_dist(self):
|
||||
return self.addr[-2].formatted_str
|
||||
|
||||
@property
|
||||
def label_list(self):
|
||||
@ -861,27 +1038,7 @@ class RouteTargetMembershipNLRI(StringifyMixin):
|
||||
idx += 4
|
||||
|
||||
# Extract route target.
|
||||
route_target = ''
|
||||
etype, esubtype, payload = struct.unpack_from('BB6s', buf, idx)
|
||||
# RFC says: The value of the high-order octet of the Type field for the
|
||||
# Route Target Community can be 0x00, 0x01, or 0x02. The value of the
|
||||
# low-order octet of the Type field for this community is 0x02.
|
||||
# TODO(PH): Remove this exception when it breaks something Here we make
|
||||
# exception as Routem packs lower-order octet as 0x00
|
||||
if etype in (0, 2) and esubtype in (0, 2):
|
||||
# If we have route target community in AS number format.
|
||||
asnum, i = struct.unpack('!HI', payload)
|
||||
route_target = ('%s:%s' % (asnum, i))
|
||||
elif etype == 1 and esubtype == 2:
|
||||
# If we have route target community in IP address format.
|
||||
ip_addr, i = struct.unpack('!4sH', payload)
|
||||
ip_addr = socket.inet_ntoa(ip_addr)
|
||||
route_target = ('%s:%s' % (ip_addr, i))
|
||||
elif etype == 0 and esubtype == 1:
|
||||
# TODO(PH): Parsing of RtNlri 1:1:100:1
|
||||
asnum, i = struct.unpack('!HI', payload)
|
||||
route_target = ('%s:%s' % (asnum, i))
|
||||
|
||||
route_target = _ExtendedCommunity(buf[idx:])
|
||||
return cls(origin_as, route_target)
|
||||
|
||||
def serialize(self):
|
||||
@ -889,12 +1046,7 @@ class RouteTargetMembershipNLRI(StringifyMixin):
|
||||
if not self.is_default_rtnlri():
|
||||
rt_nlri += struct.pack('!I', self.origin_as)
|
||||
# Encode route target
|
||||
first, second = self.route_target.split(':')
|
||||
if '.' in first:
|
||||
ip_addr = socket.inet_aton(first)
|
||||
rt_nlri += struct.pack('!BB4sH', 1, 2, ip_addr, int(second))
|
||||
else:
|
||||
rt_nlri += struct.pack('!BBHI', 0, 2, int(first), int(second))
|
||||
rt_nlri += self.route_target.serialize()
|
||||
|
||||
# RT Nlri is 12 octets
|
||||
return struct.pack('B', (8 * 12)) + rt_nlri
|
||||
@ -917,72 +1069,6 @@ def _get_addr_class(afi, safi):
|
||||
return _BinAddrPrefix
|
||||
|
||||
|
||||
class _Value(object):
|
||||
_VALUE_PACK_STR = None
|
||||
_VALUE_FIELDS = ['value']
|
||||
|
||||
@staticmethod
|
||||
def do_init(cls, self, kwargs, **extra_kwargs):
|
||||
ourfields = {}
|
||||
for f in cls._VALUE_FIELDS:
|
||||
v = kwargs[f]
|
||||
del kwargs[f]
|
||||
ourfields[f] = v
|
||||
kwargs.update(extra_kwargs)
|
||||
super(cls, self).__init__(**kwargs)
|
||||
self.__dict__.update(ourfields)
|
||||
|
||||
@classmethod
|
||||
def parse_value(cls, buf):
|
||||
values = struct.unpack_from(cls._VALUE_PACK_STR, buffer(buf))
|
||||
return dict(zip(cls._VALUE_FIELDS, values))
|
||||
|
||||
def serialize_value(self):
|
||||
args = []
|
||||
for f in self._VALUE_FIELDS:
|
||||
args.append(getattr(self, f))
|
||||
buf = bytearray()
|
||||
msg_pack_into(self._VALUE_PACK_STR, buf, 0, *args)
|
||||
return buf
|
||||
|
||||
|
||||
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 _OptParam(StringifyMixin, _TypeDisp, _Value):
|
||||
_PACK_STR = '!BB' # type, length
|
||||
|
||||
@ -1549,6 +1635,26 @@ class BGPPathAttributeExtendedCommunities(_PathAttribute):
|
||||
buf += comm.serialize()
|
||||
return buf
|
||||
|
||||
def _community_list(self, subtype):
|
||||
_list = []
|
||||
for comm in (c for c in self.communities
|
||||
if hasattr(c, "subtype") and c.subtype == subtype):
|
||||
if comm.type == 0 or comm.type == 2:
|
||||
_list.append('%d:%d' % (comm.as_number,
|
||||
comm.local_administrator))
|
||||
elif comm.type == 1:
|
||||
_list.append('%s:%d' % (comm.ipv4_address,
|
||||
comm.local_administrator))
|
||||
return _list
|
||||
|
||||
@property
|
||||
def rt_list(self):
|
||||
return self._community_list(2)
|
||||
|
||||
@property
|
||||
def soo_list(self):
|
||||
return self._community_list(3)
|
||||
|
||||
|
||||
class _ExtendedCommunity(StringifyMixin, _TypeDisp, _Value):
|
||||
_PACK_STR = '!B7s' # type high (+ type low) + value
|
||||
@ -1716,6 +1822,10 @@ class BGPPathAttributeMpReachNLRI(_PathAttribute):
|
||||
buf += binnlri
|
||||
return buf
|
||||
|
||||
@property
|
||||
def route_family(self):
|
||||
return _rf_map[(self.afi, self.safi)]
|
||||
|
||||
|
||||
@_PathAttribute.register_type(BGP_ATTR_TYPE_MP_UNREACH_NLRI)
|
||||
class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
|
||||
@ -1759,6 +1869,10 @@ class BGPPathAttributeMpUnreachNLRI(_PathAttribute):
|
||||
buf += binnlri
|
||||
return buf
|
||||
|
||||
@property
|
||||
def route_family(self):
|
||||
return _rf_map[(self.afi, self.safi)]
|
||||
|
||||
|
||||
class BGPNLRI(IPAddrPrefix):
|
||||
pass
|
||||
|
||||
@ -765,7 +765,7 @@ class Path(object):
|
||||
if extcomm_attr is None:
|
||||
rts = []
|
||||
else:
|
||||
rts = extcomm_attr.rt_list[:]
|
||||
rts = extcomm_attr.rt_list
|
||||
return rts
|
||||
|
||||
def has_rts_in(self, interested_rts):
|
||||
|
||||
@ -80,10 +80,10 @@ class Test_bgp(unittest.TestCase):
|
||||
addr='192.0.2.13')]
|
||||
mp_nlri = [
|
||||
bgp.LabelledVPNIPAddrPrefix(24, '192.0.9.0',
|
||||
route_dist=100,
|
||||
route_dist='100:100',
|
||||
labels=[1, 2, 3]),
|
||||
bgp.LabelledVPNIPAddrPrefix(26, '192.0.10.192',
|
||||
route_dist=4000000000,
|
||||
route_dist='10.0.0.1:10000',
|
||||
labels=[5, 6, 7, 8]),
|
||||
]
|
||||
communities = [
|
||||
@ -223,10 +223,10 @@ class Test_bgp(unittest.TestCase):
|
||||
addr='192.0.2.13')]
|
||||
mp_nlri = [
|
||||
bgp.LabelledVPNIPAddrPrefix(24, '192.0.9.0',
|
||||
route_dist=100,
|
||||
route_dist='100:100',
|
||||
labels=[1, 2, 3]),
|
||||
bgp.LabelledVPNIPAddrPrefix(26, '192.0.10.192',
|
||||
route_dist=4000000000,
|
||||
route_dist='10.0.0.1:10000',
|
||||
labels=[5, 6, 7, 8]),
|
||||
]
|
||||
communities = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user