packet/zebra: Support IP_ROUTE message from Zebra

Zebra IPv4/IPv6 route message have asymmetric structure, in other words,
the message structure from Zebra to the protocol daemons and that from
the protocol daemons to Zebra have the different structures.

This patch introduces _ZebraMessageFromZebra in order to distinguish
which daemon sent the message and fixes _ZebraIPRoute to decode IPv4/IPv6
route message from Zebra.

Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
IWASE Yusuke 2017-04-04 14:21:03 +09:00 committed by FUJITA Tomonori
parent 6d35bb8d01
commit 2354bd7df3
3 changed files with 164 additions and 23 deletions

View File

@ -120,7 +120,9 @@ class tcp(packet_base.PacketBase):
elif(src_port in [OFP_TCP_PORT, OFP_SSL_PORT_OLD] or
dst_port in [OFP_TCP_PORT, OFP_SSL_PORT_OLD]):
return openflow.openflow
elif zebra.ZEBRA_PORT in [src_port, dst_port]:
elif src_port == zebra.ZEBRA_PORT:
return zebra._ZebraMessageFromZebra
elif dst_port == zebra.ZEBRA_PORT:
return zebra.ZebraMessage
else:
return None

View File

@ -33,6 +33,7 @@ from ryu.lib import stringify
from ryu.lib import type_desc
from . import packet_base
from . import bgp
from . import safi as packet_safi
LOG = logging.getLogger(__name__)
@ -774,14 +775,15 @@ class ZebraMessage(packet_base.PacketBase):
'marker=%d, version=%d' % (marker, version))
@classmethod
def parser(cls, buf):
def _parser_impl(cls, buf, body_parser='parse'):
buf = six.binary_type(buf)
(length, version, vrf_id, command,
body_buf) = cls.parse_header(buf)
if body_buf:
body_cls = _ZebraMessageBody.lookup_command(command)
body = body_cls.parse(body_buf)
_parser = getattr(body_cls, body_parser)
body = _parser(body_buf)
else:
body = None
@ -789,6 +791,10 @@ class ZebraMessage(packet_base.PacketBase):
return cls(length, version, vrf_id, command, body), cls, rest
@classmethod
def parser(cls, buf):
return cls._parser_impl(buf)
def serialize_header(self, body_len):
if self.version == 0:
self.length = self.V0_HEADER_SIZE + body_len # fixup
@ -821,6 +827,16 @@ class ZebraMessage(packet_base.PacketBase):
return self.serialize_header(len(body)) + body
class _ZebraMessageFromZebra(ZebraMessage):
"""
This class is corresponding to the message sent from Zebra daemon.
"""
@classmethod
def parser(cls, buf):
return cls._parser_impl(buf, body_parser='parse_from_zebra')
# Alias
zebra = ZebraMessage
@ -844,6 +860,10 @@ class _ZebraMessageBody(type_desc.TypeDisp, stringify.StringifyMixin):
def parse(cls, buf):
return cls()
@classmethod
def parse_from_zebra(cls, buf):
return cls.parse(buf)
def serialize(self):
return b''
@ -1155,8 +1175,14 @@ class _ZebraIPRoute(_ZebraMessageBody):
"""
Base class for ZEBRA_IPV4_ROUTE_* and ZEBRA_IPV6_ROUTE_*
message body.
.. Note::
Zebra IPv4/IPv6 Route message have asymmetric structure.
If the message sent from Zebra Daemon, set 'from_zebra=True' to
create an instance of this class.
"""
# Zebra IPv4/IPv6 Route message body:
# Zebra IPv4/IPv6 Route message body (Protocol Daemons -> Zebra Daemon):
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -1178,33 +1204,93 @@ class _ZebraIPRoute(_ZebraMessageBody):
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (TAG) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
_HEADER_FMT = '!BBBH' # type, flags, message, safi
#
# Zebra IPv4/IPv6 Route message body (Zebra Daemon -> Protocol Daemons):
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Route Type | Flags | Message |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | IPv4/v6 Prefix (Variable) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (Nexthop Num) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (Nexthops (Variable)) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (IFIndex Num) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (Interface indexes) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (Distance) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (Metric) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (MTU) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | (TAG) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
_HEADER_FMT = '!BBB' # type, flags, message
HEADER_SIZE = struct.calcsize(_HEADER_FMT)
_SAFI_FMT = '!H' # safi
SAFI_SIZE = struct.calcsize(_SAFI_FMT)
_NUM_FMT = '!B' # nexthop_num or ifindex_num
NUM_SIZE = struct.calcsize(_NUM_FMT)
_IFINDEX_FMT = '!I' # ifindex
IFINDEX_SIZE = struct.calcsize(_IFINDEX_FMT)
# API type specific constants
_FAMILY = None # either socket.AF_INET or socket.AF_INET6
def __init__(self, route_type, flags, message, safi, prefix,
nexthops=None,
def __init__(self, route_type, flags, message, safi=None, prefix=None,
nexthops=None, ifindexes=None,
distance=None, metric=None, mtu=None, tag=None,
_tail=None):
from_zebra=False):
super(_ZebraIPRoute, self).__init__()
self.route_type = route_type
self.flags = flags
self.message = message
self.safi = safi
# SAFI should be included if this message sent to Zebra.
if from_zebra:
self.safi = None
else:
self.safi = safi or packet_safi.UNICAST
assert prefix is not None
if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
prefix = prefix.prefix
self.prefix = prefix
# Nexthops should be a list of str representations of IP address
# if this message sent from Zebra, otherwise a list of _Nexthop
# subclasses.
nexthops = nexthops or []
for nexthop in nexthops:
assert isinstance(nexthop, _NextHop)
if from_zebra:
for nexthop in nexthops:
assert (netaddr.valid_ipv4(nexthop)
or netaddr.valid_ipv6(nexthop))
else:
for nexthop in nexthops:
assert isinstance(nexthop, _NextHop)
self.nexthops = nexthops
# Interface indexes should be included if this message sent from
# Zebra.
if from_zebra:
ifindexes = ifindexes or []
for ifindex in ifindexes:
assert isinstance(ifindex, six.integer_types)
self.ifindexes = ifindexes
else:
self.ifindexes = None
self.distance = distance
self.metric = metric
self.mtu = mtu
self.tag = tag
self._tail = _tail or b''
# is this message sent from Zebra message or not.
self.from_zebra = from_zebra
@classmethod
def _parse_message_option(cls, message, flag, fmt, buf):
@ -1215,14 +1301,44 @@ class _ZebraIPRoute(_ZebraMessageBody):
return None, buf
@classmethod
def parse(cls, buf):
(route_type, flags, message, safi) = struct.unpack_from(
def _parse_impl(cls, buf, from_zebra=False):
(route_type, flags, message,) = struct.unpack_from(
cls._HEADER_FMT, buf)
rest = buf[cls.HEADER_SIZE:]
if from_zebra:
safi = None
else:
(safi,) = struct.unpack_from(cls._SAFI_FMT, rest)
rest = rest[cls.SAFI_SIZE:]
prefix, rest = _parse_ip_prefix(cls._FAMILY, rest)
nexthops, rest = _parse_nexthops(rest)
if from_zebra and message & ZAPI_MESSAGE_NEXTHOP:
nexthops = []
(nexthop_num,) = struct.unpack_from(cls._NUM_FMT, rest)
rest = rest[cls.NUM_SIZE:]
if cls._FAMILY == socket.AF_INET:
for _ in range(nexthop_num):
nexthop = addrconv.ipv4.bin_to_text(rest[:4])
nexthops.append(nexthop)
rest = rest[4:]
else: # cls._FAMILY == socket.AF_INET6:
for _ in range(nexthop_num):
nexthop = addrconv.ipv6.bin_to_text(rest[:16])
nexthops.append(nexthop)
rest = rest[16:]
else:
nexthops, rest = _parse_nexthops(rest)
ifindexes = []
if from_zebra and message & ZAPI_MESSAGE_IFINDEX:
(ifindex_num,) = struct.unpack_from(cls._NUM_FMT, rest)
rest = rest[cls.NUM_SIZE:]
for _ in range(ifindex_num):
(ifindex,) = struct.unpack_from(cls._IFINDEX_FMT, rest)
ifindexes.append(ifindex)
rest = rest[cls.IFINDEX_SIZE:]
distance, rest = cls._parse_message_option(
message, ZAPI_MESSAGE_DISTANCE, '!B', rest)
@ -1233,9 +1349,18 @@ class _ZebraIPRoute(_ZebraMessageBody):
tag, rest = cls._parse_message_option(
message, ZAPI_MESSAGE_TAG, '!I', rest)
return cls(route_type, flags, message, safi,
prefix, nexthops,
distance, metric, mtu, tag, _tail=rest)
return cls(route_type, flags, message, safi, prefix,
nexthops, ifindexes,
distance, metric, mtu, tag,
from_zebra=from_zebra)
@classmethod
def parse(cls, buf):
return cls._parse_impl(buf)
@classmethod
def parse_from_zebra(cls, buf):
return cls._parse_impl(buf, from_zebra=True)
def _serialize_message_option(self, option, flag, fmt):
if option is None:
@ -1249,9 +1374,22 @@ class _ZebraIPRoute(_ZebraMessageBody):
def serialize(self):
prefix = _serialize_ip_prefix(self.prefix)
nexthops = _serialize_nexthops(self.nexthops)
nexthops = b''
if self.nexthops:
self.message |= ZAPI_MESSAGE_NEXTHOP # fixup
if self.from_zebra:
nexthops += struct.pack(self._NUM_FMT, len(self.nexthops))
for nexthop in self.nexthops:
nexthops += ip.text_to_bin(nexthop)
else:
nexthops = _serialize_nexthops(self.nexthops)
ifindexes = b''
if self.ifindexes and self.from_zebra:
self.message |= ZAPI_MESSAGE_IFINDEX # fixup
ifindexes += struct.pack(self._NUM_FMT, len(self.ifindexes))
for ifindex in self.ifindexes:
ifindexes += struct.pack(self._IFINDEX_FMT, ifindex)
options = self._serialize_message_option(
self.distance, ZAPI_MESSAGE_DISTANCE, '!B')
@ -1263,10 +1401,11 @@ class _ZebraIPRoute(_ZebraMessageBody):
self.tag, ZAPI_MESSAGE_TAG, '!I')
header = struct.pack(
self._HEADER_FMT,
self.route_type, self.flags, self.message, self.safi)
self._HEADER_FMT, self.route_type, self.flags, self.message)
if not self.from_zebra:
header += struct.pack(self._SAFI_FMT, self.safi)
return header + prefix + nexthops + options + self._tail
return header + prefix + nexthops + ifindexes + options
class _ZebraIPv4Route(_ZebraIPRoute):

View File

@ -171,7 +171,7 @@ class ZServer(object):
recv_len = length - len(buf)
break
msg, _, buf = zebra.ZebraMessage.parser(buf)
msg, _, buf = zebra._ZebraMessageFromZebra.parser(buf)
ev = event.message_to_event(self.client, msg)
if ev: