1# Copyright (C) 2017 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"""
17Zebra protocol parser/serializer
18
19Zebra Protocol is used to communicate with the zebra daemon.
20"""
21
22import abc
23import socket
24import struct
25import logging
26from distutils.version import LooseVersion
27
28import netaddr
29import six
30
31from ryu import flags as cfg_flags  # For loading 'zapi' option definition
32from ryu.cfg import CONF
33from ryu.lib import addrconv
34from ryu.lib import ip
35from ryu.lib import stringify
36from ryu.lib import type_desc
37from . import packet_base
38from . import bgp
39from . import safi as packet_safi
40
41
42LOG = logging.getLogger(__name__)
43
44# Default Zebra protocol version
45_DEFAULT_VERSION = 3
46_DEFAULT_FRR_VERSION = 4
47
48_FRR_VERSION_2_0 = LooseVersion('2.0')
49_FRR_VERSION_3_0 = LooseVersion('3.0')
50
51# Constants in quagga/lib/zebra.h
52
53# Default Zebra TCP port
54ZEBRA_PORT = 2600
55
56# Zebra message types
57ZEBRA_INTERFACE_ADD = 1
58ZEBRA_INTERFACE_DELETE = 2
59ZEBRA_INTERFACE_ADDRESS_ADD = 3
60ZEBRA_INTERFACE_ADDRESS_DELETE = 4
61ZEBRA_INTERFACE_UP = 5
62ZEBRA_INTERFACE_DOWN = 6
63ZEBRA_IPV4_ROUTE_ADD = 7
64ZEBRA_IPV4_ROUTE_DELETE = 8
65ZEBRA_IPV6_ROUTE_ADD = 9
66ZEBRA_IPV6_ROUTE_DELETE = 10
67ZEBRA_REDISTRIBUTE_ADD = 11
68ZEBRA_REDISTRIBUTE_DELETE = 12
69ZEBRA_REDISTRIBUTE_DEFAULT_ADD = 13
70ZEBRA_REDISTRIBUTE_DEFAULT_DELETE = 14
71ZEBRA_IPV4_NEXTHOP_LOOKUP = 15
72ZEBRA_IPV6_NEXTHOP_LOOKUP = 16
73ZEBRA_IPV4_IMPORT_LOOKUP = 17
74ZEBRA_IPV6_IMPORT_LOOKUP = 18
75ZEBRA_INTERFACE_RENAME = 19
76ZEBRA_ROUTER_ID_ADD = 20
77ZEBRA_ROUTER_ID_DELETE = 21
78ZEBRA_ROUTER_ID_UPDATE = 22
79ZEBRA_HELLO = 23
80ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB = 24
81ZEBRA_VRF_UNREGISTER = 25
82ZEBRA_INTERFACE_LINK_PARAMS = 26
83ZEBRA_NEXTHOP_REGISTER = 27
84ZEBRA_NEXTHOP_UNREGISTER = 28
85ZEBRA_NEXTHOP_UPDATE = 29
86ZEBRA_MESSAGE_MAX = 30
87
88# Zebra message types on FRRouting
89FRR_ZEBRA_INTERFACE_ADD = 0
90FRR_ZEBRA_INTERFACE_DELETE = 1
91FRR_ZEBRA_INTERFACE_ADDRESS_ADD = 2
92FRR_ZEBRA_INTERFACE_ADDRESS_DELETE = 3
93FRR_ZEBRA_INTERFACE_UP = 4
94FRR_ZEBRA_INTERFACE_DOWN = 5
95FRR_ZEBRA_IPV4_ROUTE_ADD = 6
96FRR_ZEBRA_IPV4_ROUTE_DELETE = 7
97FRR_ZEBRA_IPV6_ROUTE_ADD = 8
98FRR_ZEBRA_IPV6_ROUTE_DELETE = 9
99FRR_ZEBRA_REDISTRIBUTE_ADD = 10
100FRR_ZEBRA_REDISTRIBUTE_DELETE = 11
101FRR_ZEBRA_REDISTRIBUTE_DEFAULT_ADD = 12
102FRR_ZEBRA_REDISTRIBUTE_DEFAULT_DELETE = 13
103FRR_ZEBRA_ROUTER_ID_ADD = 14
104FRR_ZEBRA_ROUTER_ID_DELETE = 15
105FRR_ZEBRA_ROUTER_ID_UPDATE = 16
106FRR_ZEBRA_HELLO = 17
107FRR_ZEBRA_NEXTHOP_REGISTER = 18
108FRR_ZEBRA_NEXTHOP_UNREGISTER = 19
109FRR_ZEBRA_NEXTHOP_UPDATE = 20
110FRR_ZEBRA_INTERFACE_NBR_ADDRESS_ADD = 21
111FRR_ZEBRA_INTERFACE_NBR_ADDRESS_DELETE = 22
112FRR_ZEBRA_INTERFACE_BFD_DEST_UPDATE = 23
113FRR_ZEBRA_IMPORT_ROUTE_REGISTER = 24
114FRR_ZEBRA_IMPORT_ROUTE_UNREGISTER = 25
115FRR_ZEBRA_IMPORT_CHECK_UPDATE = 26
116FRR_ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD = 27
117FRR_ZEBRA_BFD_DEST_REGISTER = 28
118FRR_ZEBRA_BFD_DEST_DEREGISTER = 29
119FRR_ZEBRA_BFD_DEST_UPDATE = 30
120FRR_ZEBRA_BFD_DEST_REPLAY = 31
121FRR_ZEBRA_REDISTRIBUTE_IPV4_ADD = 32
122FRR_ZEBRA_REDISTRIBUTE_IPV4_DEL = 33
123FRR_ZEBRA_REDISTRIBUTE_IPV6_ADD = 34
124FRR_ZEBRA_REDISTRIBUTE_IPV6_DEL = 35
125FRR_ZEBRA_VRF_UNREGISTER = 36
126FRR_ZEBRA_VRF_ADD = 37
127FRR_ZEBRA_VRF_DELETE = 38
128FRR_ZEBRA_INTERFACE_VRF_UPDATE = 39
129FRR_ZEBRA_BFD_CLIENT_REGISTER = 40
130FRR_ZEBRA_INTERFACE_ENABLE_RADV = 41
131FRR_ZEBRA_INTERFACE_DISABLE_RADV = 42
132FRR_ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB = 43
133FRR_ZEBRA_INTERFACE_LINK_PARAMS = 44
134FRR_ZEBRA_MPLS_LABELS_ADD = 45
135FRR_ZEBRA_MPLS_LABELS_DELETE = 46
136FRR_ZEBRA_IPV4_NEXTHOP_ADD = 47
137FRR_ZEBRA_IPV4_NEXTHOP_DELETE = 48
138FRR_ZEBRA_IPV6_NEXTHOP_ADD = 49
139FRR_ZEBRA_IPV6_NEXTHOP_DELETE = 50
140
141# Zebra route types
142ZEBRA_ROUTE_SYSTEM = 0
143ZEBRA_ROUTE_KERNEL = 1
144ZEBRA_ROUTE_CONNECT = 2
145ZEBRA_ROUTE_STATIC = 3
146ZEBRA_ROUTE_RIP = 4
147ZEBRA_ROUTE_RIPNG = 5
148ZEBRA_ROUTE_OSPF = 6
149ZEBRA_ROUTE_OSPF6 = 7
150ZEBRA_ROUTE_ISIS = 8
151ZEBRA_ROUTE_BGP = 9
152ZEBRA_ROUTE_PIM = 10
153ZEBRA_ROUTE_HSLS = 11
154ZEBRA_ROUTE_OLSR = 12
155ZEBRA_ROUTE_BABEL = 13
156ZEBRA_ROUTE_MAX = 14
157
158# Zebra route types on FRRouting
159FRR_ZEBRA_ROUTE_SYSTEM = 0
160FRR_ZEBRA_ROUTE_KERNEL = 1
161FRR_ZEBRA_ROUTE_CONNECT = 2
162FRR_ZEBRA_ROUTE_STATIC = 3
163FRR_ZEBRA_ROUTE_RIP = 4
164FRR_ZEBRA_ROUTE_RIPNG = 5
165FRR_ZEBRA_ROUTE_OSPF = 6
166FRR_ZEBRA_ROUTE_OSPF6 = 7
167FRR_ZEBRA_ROUTE_ISIS = 8
168FRR_ZEBRA_ROUTE_BGP = 9
169FRR_ZEBRA_ROUTE_PIM = 10
170FRR_ZEBRA_ROUTE_HSLS = 11
171FRR_ZEBRA_ROUTE_OLSR = 12
172FRR_ZEBRA_ROUTE_TABLE = 13
173FRR_ZEBRA_ROUTE_LDP = 14
174FRR_ZEBRA_ROUTE_VNC = 15
175FRR_ZEBRA_ROUTE_VNC_DIRECT = 16
176FRR_ZEBRA_ROUTE_VNC_DIRECT_RH = 17
177FRR_ZEBRA_ROUTE_BGP_DIRECT = 18
178FRR_ZEBRA_ROUTE_BGP_DIRECT_EXT = 19
179FRR_ZEBRA_ROUTE_ALL = 20
180FRR_ZEBRA_ROUTE_MAX = 21
181
182# Zebra message flags
183ZEBRA_FLAG_INTERNAL = 0x01
184ZEBRA_FLAG_SELFROUTE = 0x02
185ZEBRA_FLAG_BLACKHOLE = 0x04
186ZEBRA_FLAG_IBGP = 0x08
187ZEBRA_FLAG_SELECTED = 0x10
188ZEBRA_FLAG_FIB_OVERRIDE = 0x20
189ZEBRA_FLAG_STATIC = 0x40
190ZEBRA_FLAG_REJECT = 0x80
191
192# Zebra message flags on FRRouting
193FRR_ZEBRA_FLAG_INTERNAL = 0x01
194FRR_ZEBRA_FLAG_SELFROUTE = 0x02
195FRR_ZEBRA_FLAG_BLACKHOLE = 0x04
196FRR_ZEBRA_FLAG_IBGP = 0x08
197FRR_ZEBRA_FLAG_SELECTED = 0x10
198FRR_ZEBRA_FLAG_STATIC = 0x40
199FRR_ZEBRA_FLAG_REJECT = 0x80
200FRR_ZEBRA_FLAG_SCOPE_LINK = 0x100
201FRR_ZEBRA_FLAG_FIB_OVERRIDE = 0x200
202
203# Zebra nexthop flags
204ZEBRA_NEXTHOP_IFINDEX = 1
205ZEBRA_NEXTHOP_IFNAME = 2
206ZEBRA_NEXTHOP_IPV4 = 3
207ZEBRA_NEXTHOP_IPV4_IFINDEX = 4
208ZEBRA_NEXTHOP_IPV4_IFNAME = 5
209ZEBRA_NEXTHOP_IPV6 = 6
210ZEBRA_NEXTHOP_IPV6_IFINDEX = 7
211ZEBRA_NEXTHOP_IPV6_IFNAME = 8
212ZEBRA_NEXTHOP_BLACKHOLE = 9
213
214# Zebra nexthop flags on FRRouting
215FRR_ZEBRA_NEXTHOP_IFINDEX = 1
216FRR_ZEBRA_NEXTHOP_IPV4 = 2
217FRR_ZEBRA_NEXTHOP_IPV4_IFINDEX = 3
218FRR_ZEBRA_NEXTHOP_IPV6 = 4
219FRR_ZEBRA_NEXTHOP_IPV6_IFINDEX = 5
220FRR_ZEBRA_NEXTHOP_BLACKHOLE = 6
221
222# Constants in quagga/lib/zclient.h
223
224# Zebra API message flags
225ZAPI_MESSAGE_NEXTHOP = 0x01
226ZAPI_MESSAGE_IFINDEX = 0x02
227ZAPI_MESSAGE_DISTANCE = 0x04
228ZAPI_MESSAGE_METRIC = 0x08
229ZAPI_MESSAGE_MTU = 0x10
230ZAPI_MESSAGE_TAG = 0x20
231
232# Zebra API message flags on FRRouting.
233# Note: Constants for TAG/MTU is inverted from Quagga version.
234FRR_ZAPI_MESSAGE_NEXTHOP = 0x01
235FRR_ZAPI_MESSAGE_IFINDEX = 0x02
236FRR_ZAPI_MESSAGE_DISTANCE = 0x04
237FRR_ZAPI_MESSAGE_METRIC = 0x08
238FRR_ZAPI_MESSAGE_TAG = 0x10
239FRR_ZAPI_MESSAGE_MTU = 0x20
240FRR_ZAPI_MESSAGE_SRCPFX = 0x40
241FRR_ZAPI_MESSAGE_LABEL = 0x80
242
243# Constants in quagga/lib/if.h
244
245# Interface name length
246#   Linux define value in /usr/include/linux/if.h.
247#   #define IFNAMSIZ        16
248#   FreeBSD define value in /usr/include/net/if.h.
249#   #define IFNAMSIZ        16
250INTERFACE_NAMSIZE = 20
251INTERFACE_HWADDR_MAX = 20
252
253# Zebra internal interface status
254ZEBRA_INTERFACE_ACTIVE = 1 << 0
255ZEBRA_INTERFACE_SUB = 1 << 1
256ZEBRA_INTERFACE_LINKDETECTION = 1 << 2
257# Followings are extended on FRRouting
258ZEBRA_INTERFACE_VRF_LOOPBACK = 1 << 3
259
260# Zebra interface connected address flags
261ZEBRA_IFA_SECONDARY = 1 << 0
262ZEBRA_IFA_PEER = 1 << 1
263ZEBRA_IFA_UNNUMBERED = 1 << 2
264
265# Zebra link layer types
266ZEBRA_LLT_UNKNOWN = 0
267ZEBRA_LLT_ETHER = 1
268ZEBRA_LLT_EETHER = 2
269ZEBRA_LLT_AX25 = 3
270ZEBRA_LLT_PRONET = 4
271ZEBRA_LLT_IEEE802 = 5
272ZEBRA_LLT_ARCNET = 6
273ZEBRA_LLT_APPLETLK = 7
274ZEBRA_LLT_DLCI = 8
275ZEBRA_LLT_ATM = 9
276ZEBRA_LLT_METRICOM = 10
277ZEBRA_LLT_IEEE1394 = 11
278ZEBRA_LLT_EUI64 = 12
279ZEBRA_LLT_INFINIBAND = 13
280ZEBRA_LLT_SLIP = 14
281ZEBRA_LLT_CSLIP = 15
282ZEBRA_LLT_SLIP6 = 16
283ZEBRA_LLT_CSLIP6 = 17
284ZEBRA_LLT_RSRVD = 18
285ZEBRA_LLT_ADAPT = 19
286ZEBRA_LLT_ROSE = 20
287ZEBRA_LLT_X25 = 21
288ZEBRA_LLT_PPP = 22
289ZEBRA_LLT_CHDLC = 23
290ZEBRA_LLT_LAPB = 24
291ZEBRA_LLT_RAWHDLC = 25
292ZEBRA_LLT_IPIP = 26
293ZEBRA_LLT_IPIP6 = 27
294ZEBRA_LLT_FRAD = 28
295ZEBRA_LLT_SKIP = 29
296ZEBRA_LLT_LOOPBACK = 30
297ZEBRA_LLT_LOCALTLK = 31
298ZEBRA_LLT_FDDI = 32
299ZEBRA_LLT_SIT = 33
300ZEBRA_LLT_IPDDP = 34
301ZEBRA_LLT_IPGRE = 35
302ZEBRA_LLT_IP6GRE = 36
303ZEBRA_LLT_PIMREG = 37
304ZEBRA_LLT_HIPPI = 38
305ZEBRA_LLT_ECONET = 39
306ZEBRA_LLT_IRDA = 40
307ZEBRA_LLT_FCPP = 41
308ZEBRA_LLT_FCAL = 42
309ZEBRA_LLT_FCPL = 43
310ZEBRA_LLT_FCFABRIC = 44
311ZEBRA_LLT_IEEE802_TR = 45
312ZEBRA_LLT_IEEE80211 = 46
313ZEBRA_LLT_IEEE80211_RADIOTAP = 47
314ZEBRA_LLT_IEEE802154 = 48
315ZEBRA_LLT_IEEE802154_PHY = 49
316
317# Link Parameters Status
318LP_UNSET = 0x0000
319LP_TE = 0x0001
320LP_MAX_BW = 0x0002
321LP_MAX_RSV_BW = 0x0004
322LP_UNRSV_BW = 0x0008
323LP_ADM_GRP = 0x0010
324LP_RMT_AS = 0x0020
325LP_DELAY = 0x0040
326LP_MM_DELAY = 0x0080
327LP_DELAY_VAR = 0x0100
328LP_PKT_LOSS = 0x0200
329LP_RES_BW = 0x0400
330LP_AVA_BW = 0x0800
331LP_USE_BW = 0x1000
332LP_TE_METRIC = 0x2000
333
334# "non-official" architectural constants
335MAX_CLASS_TYPE = 8
336
337# Constants in frr/zebra/zebra_ptm.h
338
339# Interface PTM Enable configuration
340ZEBRA_IF_PTM_ENABLE_OFF = 0
341ZEBRA_IF_PTM_ENABLE_ON = 1
342ZEBRA_IF_PTM_ENABLE_UNSPEC = 2
343
344# PTM status
345ZEBRA_PTM_STATUS_DOWN = 0
346ZEBRA_PTM_STATUS_UP = 1
347ZEBRA_PTM_STATUS_UNKNOWN = 2
348
349# Constants in frr/lib/bfd.h
350
351# BFD status
352BFD_STATUS_UNKNOWN = 1 << 0
353BFD_STATUS_DOWN = 1 << 1
354BFD_STATUS_UP = 1 << 2
355
356# Constants in frr/lib/vrf.h
357
358# VRF name length
359VRF_NAMSIZ = 36
360
361# Constants in frr/lib/mpls.h
362
363# Reserved MPLS label values
364MPLS_V4_EXP_NULL_LABEL = 0
365MPLS_RA_LABEL = 1
366MPLS_V6_EXP_NULL_LABEL = 2
367MPLS_IMP_NULL_LABEL = 3
368MPLS_ENTROPY_LABEL_INDICATOR = 7
369MPLS_GAL_LABEL = 13
370MPLS_OAM_ALERT_LABEL = 14
371MPLS_EXTENSION_LABEL = 15
372MPLS_MIN_RESERVED_LABEL = 0
373MPLS_MAX_RESERVED_LABEL = 15
374MPLS_MIN_UNRESERVED_LABEL = 16
375MPLS_MAX_UNRESERVED_LABEL = 1048575
376
377
378# Utility functions/classes
379
380IPv4Prefix = bgp.IPAddrPrefix
381IPv6Prefix = bgp.IP6AddrPrefix
382
383
384def _parse_ip_prefix(family, buf):
385    if family == socket.AF_INET:
386        prefix, rest = bgp.IPAddrPrefix.parser(buf)
387    elif family == socket.AF_INET6:
388        prefix, rest = IPv6Prefix.parser(buf)
389    else:
390        raise struct.error('Unsupported family: %d' % family)
391
392    return prefix.prefix, rest
393
394
395def _serialize_ip_prefix(prefix):
396    if ip.valid_ipv4(prefix):
397        prefix_addr, prefix_num = prefix.split('/')
398        return bgp.IPAddrPrefix(int(prefix_num), prefix_addr).serialize()
399    elif ip.valid_ipv6(prefix):
400        prefix_addr, prefix_num = prefix.split('/')
401        return IPv6Prefix(int(prefix_num), prefix_addr).serialize()
402    else:
403        raise ValueError('Invalid prefix: %s' % prefix)
404
405
406# Family and Zebra Prefix format:
407#  0                   1                   2                   3
408#  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
409# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410# | Family        |
411# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
412# | IPv4/v6 prefix (4 bytes or 16 bytes)                          |
413# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
414# | Prefix len    |
415# +-+-+-+-+-+-+-+-+
416_ZEBRA_FAMILY_FMT = '!B'  # family
417_ZEBRA_FAMILY_SIZE = struct.calcsize(_ZEBRA_FAMILY_FMT)
418_ZEBRA_IPV4_PREFIX_FMT = '!4sB'  # prefix, prefix_len
419_ZEBRA_IPV6_PREFIX_FMT = '!16sB'
420_ZEBRA_IPV4_PREFIX_SIZE = struct.calcsize(_ZEBRA_IPV4_PREFIX_FMT)
421_ZEBRA_IPV6_PREFIX_SIZE = struct.calcsize(_ZEBRA_IPV6_PREFIX_FMT)
422_ZEBRA_FAMILY_IPV4_PREFIX_FMT = '!B4sB'  # family, prefix, prefix_len
423_ZEBRA_FAMILY_IPV6_PREFIX_FMT = '!B16sB'  # family, prefix, prefix_len
424
425
426def _parse_zebra_family_prefix(buf):
427    """
428    Parses family and prefix in Zebra format.
429    """
430    (family,) = struct.unpack_from(_ZEBRA_FAMILY_FMT, buf)
431    rest = buf[_ZEBRA_FAMILY_SIZE:]
432
433    if socket.AF_INET == family:
434        (prefix, p_len) = struct.unpack_from(_ZEBRA_IPV4_PREFIX_FMT, rest)
435        prefix = '%s/%d' % (addrconv.ipv4.bin_to_text(prefix), p_len)
436        rest = rest[_ZEBRA_IPV4_PREFIX_SIZE:]
437    elif socket.AF_INET6 == family:
438        (prefix, p_len) = struct.unpack_from(_ZEBRA_IPV6_PREFIX_FMT, rest)
439        prefix = '%s/%d' % (addrconv.ipv6.bin_to_text(prefix), p_len)
440        rest = rest[_ZEBRA_IPV6_PREFIX_SIZE:]
441    else:
442        raise struct.error('Unsupported family: %d' % family)
443
444    return family, prefix, rest
445
446
447def _serialize_zebra_family_prefix(prefix):
448    """
449    Serializes family and prefix in Zebra format.
450    """
451    if ip.valid_ipv4(prefix):
452        family = socket.AF_INET  # fixup
453        prefix_addr, prefix_num = prefix.split('/')
454        return family, struct.pack(
455            _ZEBRA_FAMILY_IPV4_PREFIX_FMT,
456            family,
457            addrconv.ipv4.text_to_bin(prefix_addr),
458            int(prefix_num))
459    elif ip.valid_ipv6(prefix):
460        family = socket.AF_INET6  # fixup
461        prefix_addr, prefix_num = prefix.split('/')
462        return family, struct.pack(
463            _ZEBRA_FAMILY_IPV6_PREFIX_FMT,
464            family,
465            addrconv.ipv6.text_to_bin(prefix_addr),
466            int(prefix_num))
467
468    raise ValueError('Invalid prefix: %s' % prefix)
469
470
471def _is_frr_version_ge(compared_version):
472    return CONF['zapi'].frr_version >= compared_version
473
474
475class InterfaceLinkParams(stringify.StringifyMixin):
476    """
477    Interface Link Parameters class for if_link_params structure.
478    """
479    # Interface Link Parameters structure:
480    #  0                   1                   2                   3
481    #  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
482    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
483    # | Status of Link Parameters                                     |
484    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
485    # | Traffic Engineering metric                                    |
486    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
487    # | (float) Maximum Bandwidth                                     |
488    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
489    # | (float) Maximum Reservable Bandwidth                          |
490    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
491    # | Number of Unreserved Bandwidth Classes (max is MAX_CLASS_TYPE)|
492    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
493    # | (float) Unreserved Bandwidth per Class Type                   |
494    # |  ...  repeats Number of Unreserved Bandwidth Classes times    |
495    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
496    # | Administrative group                                          |
497    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
498    # | Remote AS number                                              |
499    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
500    # | Remote IP address                                             |
501    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
502    # | Link Average Delay                                            |
503    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
504    # | Link Min Delay                                                |
505    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
506    # | Link Max Delay                                                |
507    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
508    # | Link Delay Variation                                          |
509    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
510    # | (float) Link Packet Loss                                      |
511    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
512    # | (float) Residual Bandwidth                                    |
513    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
514    # | (float) Available Bandwidth                                   |
515    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
516    # | (float) Utilized Bandwidth                                    |
517    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
518    # lp_status, te_metric, max_bw, max_reserved_bw, bw_cls_num
519    _HEADER_FMT = '!IIffI'
520    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
521    _REPEATED_FMT = '!f'
522    REPEATED_SIZE = struct.calcsize(_REPEATED_FMT)
523    # admin_group, remote_as, remote_ip,
524    # average_delay, min_delay, max_delay, delay_var,
525    #  pkt_loss, residual_bw, average_bw, utilized_bw
526    _FOOTER_FMT = '!II4sIIIIffff'
527    FOOTER_SIZE = struct.calcsize(_FOOTER_FMT)
528
529    def __init__(self, lp_status, te_metric, max_bw, max_reserved_bw,
530                 unreserved_bw, admin_group, remote_as, remote_ip,
531                 average_delay, min_delay, max_delay, delay_var, pkt_loss,
532                 residual_bw, average_bw, utilized_bw):
533        super(InterfaceLinkParams, self).__init__()
534        self.lp_status = lp_status
535        self.te_metric = te_metric
536        self.max_bw = max_bw
537        self.max_reserved_bw = max_reserved_bw
538        assert isinstance(unreserved_bw, (list, tuple))
539        assert len(unreserved_bw) == MAX_CLASS_TYPE
540        self.unreserved_bw = unreserved_bw
541        self.admin_group = admin_group
542        self.remote_as = remote_as
543        assert ip.valid_ipv4(remote_ip)
544        self.remote_ip = remote_ip
545        self.average_delay = average_delay
546        self.min_delay = min_delay
547        self.max_delay = max_delay
548        self.delay_var = delay_var
549        self.pkt_loss = pkt_loss
550        self.residual_bw = residual_bw
551        self.average_bw = average_bw
552        self.utilized_bw = utilized_bw
553
554    @classmethod
555    def parse(cls, buf):
556        (lp_status, te_metric, max_bw, max_reserved_bw,
557         bw_cls_num) = struct.unpack_from(cls._HEADER_FMT, buf)
558        if MAX_CLASS_TYPE < bw_cls_num:
559            bw_cls_num = MAX_CLASS_TYPE
560        offset = cls.HEADER_SIZE
561
562        unreserved_bw = []
563        for _ in range(bw_cls_num):
564            (u_bw,) = struct.unpack_from(cls._REPEATED_FMT, buf, offset)
565            unreserved_bw.append(u_bw)
566            offset += cls.REPEATED_SIZE
567
568        (admin_group, remote_as, remote_ip, average_delay, min_delay,
569         max_delay, delay_var, pkt_loss, residual_bw, average_bw,
570         utilized_bw) = struct.unpack_from(
571             cls._FOOTER_FMT, buf, offset)
572        offset += cls.FOOTER_SIZE
573
574        remote_ip = addrconv.ipv4.bin_to_text(remote_ip)
575
576        return cls(lp_status, te_metric, max_bw, max_reserved_bw,
577                   unreserved_bw, admin_group, remote_as, remote_ip,
578                   average_delay, min_delay, max_delay, delay_var, pkt_loss,
579                   residual_bw, average_bw, utilized_bw), buf[offset:]
580
581    def serialize(self):
582        buf = struct.pack(
583            self._HEADER_FMT, self.lp_status, self.te_metric, self.max_bw,
584            self.max_reserved_bw, len(self.unreserved_bw))
585
586        for u_bw in self.unreserved_bw:
587            buf += struct.pack(self._REPEATED_FMT, u_bw)
588
589        remote_ip = addrconv.ipv4.text_to_bin(self.remote_ip)
590
591        buf += struct.pack(
592            self._FOOTER_FMT, self.admin_group, self.remote_as, remote_ip,
593            self.average_delay, self.min_delay, self.max_delay,
594            self.delay_var, self.pkt_loss, self.residual_bw, self.average_bw,
595            self.utilized_bw)
596
597        return buf
598
599
600@six.add_metaclass(abc.ABCMeta)
601class _NextHop(type_desc.TypeDisp, stringify.StringifyMixin):
602    """
603    Base class for Zebra Nexthop structure.
604    """
605    # Zebra Nexthop structure:
606    #  0                   1                   2                   3
607    #  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
608    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609    # | Nexthop Type  |
610    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
611    # | IPv4/v6 address or Interface Index number (Variable)          |
612    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
613    _HEADER_FMT = '!B'
614    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
615
616    def __init__(self, ifindex=None, ifname=None, addr=None, type_=None):
617        super(_NextHop, self).__init__()
618        self.ifindex = ifindex
619        self.ifname = ifname
620        self.addr = addr
621        self.type = type_
622
623    @classmethod
624    @abc.abstractmethod
625    def parse(cls, buf):
626        (type_,) = struct.unpack_from(cls._HEADER_FMT, buf)
627        rest = buf[cls.HEADER_SIZE:]
628
629        subcls = cls._lookup_type(type_)
630        if subcls is None:
631            raise struct.error('unsupported Nexthop type: %d' % type_)
632
633        nexthop, rest = subcls.parse(rest)
634        nexthop.type = type_
635        return nexthop, rest
636
637    @abc.abstractmethod
638    def _serialize(self):
639        return b''
640
641    def serialize(self, version=_DEFAULT_VERSION):
642        if self.type is None:
643            if version <= 3:
644                nh_cls = _NextHop
645            elif version == 4:
646                nh_cls = _FrrNextHop
647            else:
648                raise ValueError(
649                    'Unsupported Zebra protocol version: %d' % version)
650            self.type = nh_cls._rev_lookup_type(self.__class__)
651        return struct.pack(self._HEADER_FMT, self.type) + self._serialize()
652
653
654@six.add_metaclass(abc.ABCMeta)
655class _FrrNextHop(_NextHop):
656    """
657    Base class for Zebra Nexthop structure for translating nexthop types
658    on FRRouting.
659    """
660
661
662_NEXTHOP_COUNT_FMT = '!B'  # nexthop_count
663_NEXTHOP_COUNT_SIZE = struct.calcsize(_NEXTHOP_COUNT_FMT)
664
665
666def _parse_nexthops(buf, version=_DEFAULT_VERSION):
667    (nexthop_count,) = struct.unpack_from(_NEXTHOP_COUNT_FMT, buf)
668    rest = buf[_NEXTHOP_COUNT_SIZE:]
669
670    if version <= 3:
671        nh_cls = _NextHop
672    elif version == 4:
673        nh_cls = _FrrNextHop
674    else:
675        raise struct.error(
676            'Unsupported Zebra protocol version: %d' % version)
677
678    nexthops = []
679    for _ in range(nexthop_count):
680        nexthop, rest = nh_cls.parse(rest)
681        nexthops.append(nexthop)
682
683    return nexthops, rest
684
685
686def _serialize_nexthops(nexthops, version=_DEFAULT_VERSION):
687    nexthop_count = len(nexthops)
688    buf = struct.pack(_NEXTHOP_COUNT_FMT, nexthop_count)
689
690    if nexthop_count == 0:
691        return buf
692
693    for nexthop in nexthops:
694        buf += nexthop.serialize(version=version)
695
696    return buf
697
698
699@_FrrNextHop.register_type(FRR_ZEBRA_NEXTHOP_IFINDEX)
700@_NextHop.register_type(ZEBRA_NEXTHOP_IFINDEX)
701class NextHopIFIndex(_NextHop):
702    """
703    Nexthop class for ZEBRA_NEXTHOP_IFINDEX type.
704    """
705    _BODY_FMT = '!I'  # ifindex
706    BODY_SIZE = struct.calcsize(_BODY_FMT)
707
708    @classmethod
709    def parse(cls, buf):
710        (ifindex,) = struct.unpack_from(cls._BODY_FMT, buf)
711        rest = buf[cls.BODY_SIZE:]
712
713        return cls(ifindex=ifindex), rest
714
715    def _serialize(self):
716        return struct.pack(self._BODY_FMT, self.ifindex)
717
718
719@_NextHop.register_type(ZEBRA_NEXTHOP_IFNAME)
720class NextHopIFName(_NextHop):
721    """
722    Nexthop class for ZEBRA_NEXTHOP_IFNAME type.
723    """
724    _BODY_FMT = '!I'  # ifindex
725    BODY_SIZE = struct.calcsize(_BODY_FMT)
726
727    @classmethod
728    def parse(cls, buf):
729        (ifindex,) = struct.unpack_from(cls._BODY_FMT, buf)
730        rest = buf[cls.BODY_SIZE:]
731
732        return cls(ifindex=ifindex), rest
733
734    def _serialize(self):
735        return struct.pack(self._BODY_FMT, self.ifindex)
736
737
738@_FrrNextHop.register_type(FRR_ZEBRA_NEXTHOP_IPV4)
739@_NextHop.register_type(ZEBRA_NEXTHOP_IPV4)
740class NextHopIPv4(_NextHop):
741    """
742    Nexthop class for ZEBRA_NEXTHOP_IPV4 type.
743    """
744    _BODY_FMT = '!4s'  # addr(IPv4)
745    BODY_SIZE = struct.calcsize(_BODY_FMT)
746    _BODY_FMT_FRR_V3 = '!4sI'  # addr(IPv4), ifindex
747    BODY_SIZE_FRR_V3 = struct.calcsize(_BODY_FMT_FRR_V3)
748
749    @classmethod
750    def parse(cls, buf):
751        if _is_frr_version_ge(_FRR_VERSION_3_0):
752            (addr, ifindex) = struct.unpack_from(cls._BODY_FMT_FRR_V3, buf)
753            addr = addrconv.ipv4.bin_to_text(addr)
754            rest = buf[cls.BODY_SIZE_FRR_V3:]
755            return cls(ifindex=ifindex, addr=addr), rest
756
757        addr = addrconv.ipv4.bin_to_text(buf[:cls.BODY_SIZE])
758        rest = buf[cls.BODY_SIZE:]
759
760        return cls(addr=addr), rest
761
762    def _serialize(self):
763        if _is_frr_version_ge(_FRR_VERSION_3_0) and self.ifindex:
764            addr = addrconv.ipv4.text_to_bin(self.addr)
765            return struct.pack(self._BODY_FMT_FRR_V3, addr, self.ifindex)
766
767        return addrconv.ipv4.text_to_bin(self.addr)
768
769
770@_FrrNextHop.register_type(FRR_ZEBRA_NEXTHOP_IPV4_IFINDEX)
771@_NextHop.register_type(ZEBRA_NEXTHOP_IPV4_IFINDEX)
772class NextHopIPv4IFIndex(_NextHop):
773    """
774    Nexthop class for ZEBRA_NEXTHOP_IPV4_IFINDEX type.
775    """
776    _BODY_FMT = '!4sI'  # addr(IPv4), ifindex
777    BODY_SIZE = struct.calcsize(_BODY_FMT)
778
779    @classmethod
780    def parse(cls, buf):
781        (addr, ifindex) = struct.unpack_from(cls._BODY_FMT, buf)
782        addr = addrconv.ipv4.bin_to_text(addr)
783        rest = buf[cls.BODY_SIZE:]
784
785        return cls(ifindex=ifindex, addr=addr), rest
786
787    def _serialize(self):
788        addr = addrconv.ipv4.text_to_bin(self.addr)
789
790        return struct.pack(self._BODY_FMT, addr, self.ifindex)
791
792
793@_NextHop.register_type(ZEBRA_NEXTHOP_IPV4_IFNAME)
794class NextHopIPv4IFName(_NextHop):
795    """
796    Nexthop class for ZEBRA_NEXTHOP_IPV4_IFNAME type.
797    """
798    _BODY_FMT = '!4sI'  # addr(IPv4), ifindex
799    BODY_SIZE = struct.calcsize(_BODY_FMT)
800
801    @classmethod
802    def parse(cls, buf):
803        (addr, ifindex) = struct.unpack_from(cls._BODY_FMT, buf)
804        addr = addrconv.ipv4.bin_to_text(addr)
805        rest = buf[cls.BODY_SIZE:]
806
807        return cls(ifindex=ifindex, addr=addr), rest
808
809    def _serialize(self):
810        addr = addrconv.ipv4.text_to_bin(self.addr)
811
812        return struct.pack(self._BODY_FMT, addr, self.ifindex)
813
814
815@_FrrNextHop.register_type(FRR_ZEBRA_NEXTHOP_IPV6)
816@_NextHop.register_type(ZEBRA_NEXTHOP_IPV6)
817class NextHopIPv6(_NextHop):
818    """
819    Nexthop class for ZEBRA_NEXTHOP_IPV6 type.
820    """
821    _BODY_FMT = '!16s'  # addr(IPv6)
822    BODY_SIZE = struct.calcsize(_BODY_FMT)
823    _BODY_FMT_FRR_V3 = '!16sI'  # addr(IPv6), ifindex
824    BODY_SIZE_FRR_V3 = struct.calcsize(_BODY_FMT_FRR_V3)
825
826    @classmethod
827    def parse(cls, buf):
828        if _is_frr_version_ge(_FRR_VERSION_3_0):
829            (addr, ifindex) = struct.unpack_from(cls._BODY_FMT_FRR_V3, buf)
830            addr = addrconv.ipv4.bin_to_text(addr)
831            rest = buf[cls.BODY_SIZE_FRR_V3:]
832            return cls(ifindex=ifindex, addr=addr), rest
833
834        addr = addrconv.ipv6.bin_to_text(buf[:cls.BODY_SIZE])
835        rest = buf[cls.BODY_SIZE:]
836
837        return cls(addr=addr), rest
838
839    def _serialize(self):
840        if _is_frr_version_ge(_FRR_VERSION_3_0) and self.ifindex:
841            addr = addrconv.ipv4.text_to_bin(self.addr)
842            return struct.pack(self._BODY_FMT_FRR_V3, addr, self.ifindex)
843
844        return addrconv.ipv6.text_to_bin(self.addr)
845
846
847@_FrrNextHop.register_type(FRR_ZEBRA_NEXTHOP_IPV6_IFINDEX)
848@_NextHop.register_type(ZEBRA_NEXTHOP_IPV6_IFINDEX)
849class NextHopIPv6IFIndex(_NextHop):
850    """
851    Nexthop class for ZEBRA_NEXTHOP_IPV6_IFINDEX type.
852    """
853    _BODY_FMT = '!16sI'  # addr(IPv6), ifindex
854    BODY_SIZE = struct.calcsize(_BODY_FMT)
855
856    @classmethod
857    def parse(cls, buf):
858        (addr, ifindex) = struct.unpack_from(cls._BODY_FMT, buf)
859        addr = addrconv.ipv6.bin_to_text(addr)
860        rest = buf[cls.BODY_SIZE:]
861
862        return cls(ifindex=ifindex, addr=addr), rest
863
864    def _serialize(self):
865        addr = addrconv.ipv6.text_to_bin(self.addr)
866
867        return struct.pack(self._BODY_FMT, addr, self.ifindex)
868
869
870@_NextHop.register_type(ZEBRA_NEXTHOP_IPV6_IFNAME)
871class NextHopIPv6IFName(_NextHop):
872    """
873    Nexthop class for ZEBRA_NEXTHOP_IPV6_IFNAME type.
874    """
875    _BODY_FMT = '!16sI'  # addr(IPv6), ifindex
876    BODY_SIZE = struct.calcsize(_BODY_FMT)
877
878    @classmethod
879    def parse(cls, buf):
880        (addr, ifindex) = struct.unpack_from(cls._BODY_FMT, buf)
881        addr = addrconv.ipv6.bin_to_text(addr)
882        rest = buf[cls.BODY_SIZE:]
883
884        return cls(ifindex=ifindex, addr=addr), rest
885
886    def _serialize(self):
887        addr = addrconv.ipv6.text_to_bin(self.addr)
888
889        return struct.pack(self._BODY_FMT, addr, self.ifindex)
890
891
892@_FrrNextHop.register_type(FRR_ZEBRA_NEXTHOP_BLACKHOLE)
893@_NextHop.register_type(ZEBRA_NEXTHOP_BLACKHOLE)
894class NextHopBlackhole(_NextHop):
895    """
896    Nexthop class for ZEBRA_NEXTHOP_BLACKHOLE type.
897    """
898
899    @classmethod
900    def parse(cls, buf):
901        return cls(), buf
902
903    def _serialize(self):
904        return b''
905
906
907class RegisteredNexthop(stringify.StringifyMixin):
908    """
909    Unit of ZEBRA_NEXTHOP_REGISTER message body.
910    """
911    # Unit of Zebra Nexthop Register message body:
912    #  0                   1                   2                   3
913    #  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
914    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
915    # | Connected     | Family                        |
916    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
917    # | IPv4/v6 Prefix (Variable)                                     |
918    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
919    _HEADER_FMT = '!?H'
920    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
921    # Note: connected is renamed to flags on FRRouting.
922
923    def __init__(self, connected, family, prefix):
924        super(RegisteredNexthop, self).__init__()
925        self.connected = connected
926        self.family = family
927        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
928            prefix = prefix.prefix
929        self.prefix = prefix
930
931    @property
932    def flags(self):
933        return self.connected
934
935    @flags.setter
936    def flags(self, v):
937        self.connected = v
938
939    @classmethod
940    def parse(cls, buf):
941        (connected, family) = struct.unpack_from(cls._HEADER_FMT, buf)
942        rest = buf[cls.HEADER_SIZE:]
943
944        prefix, rest = _parse_ip_prefix(family, rest)
945
946        return cls(connected, family, prefix), rest
947
948    def serialize(self):
949        buf = struct.pack(self._HEADER_FMT, self.connected, self.family)
950
951        return buf + _serialize_ip_prefix(self.prefix)
952
953
954# Zebra message class
955
956class ZebraMessage(packet_base.PacketBase):
957    """
958    Zebra protocol parser/serializer class.
959
960    An instance has the following attributes at least.
961    Most of them are same to the on-wire counterparts but in host byte order.
962    __init__ takes the corresponding args in this order.
963
964    ============== ==========================================================
965    Attribute      Description
966    ============== ==========================================================
967    length         Total packet length including this header.
968                   The minimum length is 3 bytes for version 0 messages,
969                   6 bytes for version 1/2 messages and 8 bytes for version
970                   3 messages.
971    version        Version number of the Zebra protocol message.
972                   To instantiate messages with other than the default
973                   version, ``version`` must be specified.
974    vrf_id         VRF ID for the route contained in message.
975                   Not present in version 0/1/2 messages in the on-wire
976                   structure, and always 0 for theses version.
977    command        Zebra Protocol command, which denotes message type.
978    body           Messages body.
979                   An instance of subclass of ``_ZebraMessageBody`` named
980                   like "Zebra + <message name>" (e.g., ``ZebraHello``).
981                   Or ``None`` if message does not contain any body.
982    ============== ==========================================================
983
984    .. Note::
985
986        To instantiate Zebra messages, ``command`` can be omitted when the
987        valid ``body`` is specified.
988
989        ::
990
991            >>> from ryu.lib.packet import zebra
992            >>> zebra.ZebraMessage(body=zebra.ZebraHello())
993            ZebraMessage(body=ZebraHello(route_type=14),command=23,
994            length=None,version=3,vrf_id=0)
995
996        On the other hand, if ``body`` is omitted, ``command`` must be
997        specified.
998
999        ::
1000
1001            >>> zebra.ZebraMessage(command=zebra.ZEBRA_INTERFACE_ADD)
1002            ZebraMessage(body=None,command=1,length=None,version=3,vrf_id=0)
1003    """
1004
1005    # Zebra Protocol Common Header (version 0):
1006    #  0                   1                   2                   3
1007    #  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
1008    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1009    # | Length                        | Command       |
1010    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1011    _V0_HEADER_FMT = '!HB'
1012    V0_HEADER_SIZE = struct.calcsize(_V0_HEADER_FMT)
1013    _MIN_LEN = V0_HEADER_SIZE
1014
1015    # Zebra Protocol Common Header (version 1):
1016    #  0                   1                   2                   3
1017    #  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
1018    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1019    # | Length                        | Marker        | Version       |
1020    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1021    # | Command                       |
1022    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1023    _V1_HEADER_FMT = '!HBBH'
1024    V1_HEADER_SIZE = struct.calcsize(_V1_HEADER_FMT)
1025
1026    # Zebra Protocol Common Header (version 3):
1027    #  0                   1                   2                   3
1028    #  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
1029    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1030    # | Length                        | Marker        | Version       |
1031    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1032    # | VRF ID                        | Command                       |
1033    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1034    _V3_HEADER_FMT = '!HBBHH'
1035    V3_HEADER_SIZE = struct.calcsize(_V3_HEADER_FMT)
1036
1037    # Note: Marker should be 0xff(=255) in the version>=1 header.
1038    # Also, FRRouting uses the different marker value.
1039    _MARKER = 0xff
1040    _LT_MARKER = 0xfe
1041
1042    def __init__(self, length=None, version=_DEFAULT_VERSION,
1043                 vrf_id=0, command=None, body=None):
1044        super(ZebraMessage, self).__init__()
1045        self.length = length
1046        self.version = version
1047        self.vrf_id = vrf_id
1048        self.command = command
1049        self.body = body
1050
1051    def _fill_command(self):
1052        assert isinstance(self.body, _ZebraMessageBody)
1053        body_base_cls = _ZebraMessageBody
1054        if self.version == 4:
1055            body_base_cls = _FrrZebraMessageBody
1056        self.command = body_base_cls.rev_lookup_command(self.body.__class__)
1057
1058    @classmethod
1059    def get_header_size(cls, version):
1060        if version == 0:
1061            return cls.V0_HEADER_SIZE
1062        elif version in [1, 2]:
1063            return cls.V1_HEADER_SIZE
1064        elif version in [3, 4]:
1065            return cls.V3_HEADER_SIZE
1066        else:
1067            raise ValueError(
1068                'Unsupported Zebra protocol version: %d'
1069                % version)
1070
1071    @classmethod
1072    def parse_header(cls, buf):
1073        (length, marker) = struct.unpack_from(cls._V0_HEADER_FMT, buf)
1074        if marker not in [cls._MARKER, cls._LT_MARKER]:
1075            command = marker
1076            body_buf = buf[cls.V0_HEADER_SIZE:length]
1077            # version=0, vrf_id=0
1078            return length, 0, 0, command, body_buf
1079
1080        (length, marker, version, command) = struct.unpack_from(
1081            cls._V1_HEADER_FMT, buf)
1082        if version in [1, 2]:
1083            body_buf = buf[cls.V1_HEADER_SIZE:length]
1084            # vrf_id=0
1085            return length, version, 0, command, body_buf
1086
1087        (length, marker, version, vrf_id, command) = struct.unpack_from(
1088            cls._V3_HEADER_FMT, buf)
1089        if version == 3 or (version == 4 and marker == cls._LT_MARKER):
1090            body_buf = buf[cls.V3_HEADER_SIZE:length]
1091            return length, version, vrf_id, command, body_buf
1092
1093        raise struct.error(
1094            'Failed to parse Zebra protocol header: '
1095            'marker=%d, version=%d' % (marker, version))
1096
1097    @classmethod
1098    def get_body_class(cls, version, command):
1099        if version == 4:
1100            return _FrrZebraMessageBody.lookup_command(command)
1101        else:
1102            return _ZebraMessageBody.lookup_command(command)
1103
1104    @classmethod
1105    def _parser_impl(cls, buf, from_zebra=False):
1106        buf = six.binary_type(buf)
1107        (length, version, vrf_id, command,
1108         body_buf) = cls.parse_header(buf)
1109
1110        if body_buf:
1111            body_cls = cls.get_body_class(version, command)
1112            if from_zebra:
1113                body = body_cls.parse_from_zebra(body_buf, version=version)
1114            else:
1115                body = body_cls.parse(body_buf, version=version)
1116        else:
1117            body = None
1118
1119        rest = buf[length:]
1120
1121        if from_zebra:
1122            return (cls(length, version, vrf_id, command, body),
1123                    _ZebraMessageFromZebra, rest)
1124
1125        return cls(length, version, vrf_id, command, body), cls, rest
1126
1127    @classmethod
1128    def parser(cls, buf):
1129        return cls._parser_impl(buf)
1130
1131    def serialize_header(self, body_len):
1132        if self.version == 0:
1133            self.length = self.V0_HEADER_SIZE + body_len  # fixup
1134            return struct.pack(
1135                self._V0_HEADER_FMT,
1136                self.length, self.command)
1137        elif self.version in [1, 2]:
1138            self.length = self.V1_HEADER_SIZE + body_len  # fixup
1139            return struct.pack(
1140                self._V1_HEADER_FMT,
1141                self.length, self._MARKER, self.version,
1142                self.command)
1143        elif self.version in [3, 4]:
1144            if self.version == 3:
1145                _marker = self._MARKER
1146            else:  # self.version == 4
1147                _marker = self._LT_MARKER
1148            self.length = self.V3_HEADER_SIZE + body_len  # fixup
1149            return struct.pack(
1150                self._V3_HEADER_FMT,
1151                self.length, _marker, self.version,
1152                self.vrf_id, self.command)
1153        else:
1154            raise ValueError(
1155                'Unsupported Zebra protocol version: %d'
1156                % self.version)
1157
1158    def serialize(self, _payload=None, _prev=None):
1159        if self.body is None:
1160            assert self.command is not None
1161            body = b''
1162        else:
1163            assert isinstance(self.body, _ZebraMessageBody)
1164            self._fill_command()  # fixup
1165            body = self.body.serialize(version=self.version)
1166
1167        return self.serialize_header(len(body)) + body
1168
1169
1170class _ZebraMessageFromZebra(ZebraMessage):
1171    """
1172    This class is corresponding to the message sent from Zebra daemon.
1173    """
1174
1175    @classmethod
1176    def parser(cls, buf):
1177        return ZebraMessage._parser_impl(buf, from_zebra=True)
1178
1179
1180# Alias
1181zebra = ZebraMessage
1182
1183
1184# Zebra message body classes
1185
1186class _ZebraMessageBody(type_desc.TypeDisp, stringify.StringifyMixin):
1187    """
1188    Base class for Zebra message body.
1189    """
1190
1191    @classmethod
1192    def lookup_command(cls, command):
1193        return cls._lookup_type(command)
1194
1195    @classmethod
1196    def rev_lookup_command(cls, body_cls):
1197        return cls._rev_lookup_type(body_cls)
1198
1199    @classmethod
1200    def parse(cls, buf, version=_DEFAULT_VERSION):
1201        return cls()
1202
1203    @classmethod
1204    def parse_from_zebra(cls, buf, version=_DEFAULT_VERSION):
1205        return cls.parse(buf, version=version)
1206
1207    def serialize(self, version=_DEFAULT_VERSION):
1208        return b''
1209
1210
1211class _FrrZebraMessageBody(_ZebraMessageBody):
1212    """
1213    Pseudo message body class for translating message types on FRRouting.
1214    """
1215
1216
1217@_FrrZebraMessageBody.register_unknown_type()
1218@_ZebraMessageBody.register_unknown_type()
1219class ZebraUnknownMessage(_ZebraMessageBody):
1220    """
1221    Message body class for Unknown command.
1222    """
1223
1224    def __init__(self, buf):
1225        super(ZebraUnknownMessage, self).__init__()
1226        self.buf = buf
1227
1228    @classmethod
1229    def parse(cls, buf, version=_DEFAULT_VERSION):
1230        return cls(buf)
1231
1232    def serialize(self, version=_DEFAULT_VERSION):
1233        return self.buf
1234
1235
1236@six.add_metaclass(abc.ABCMeta)
1237class _ZebraInterface(_ZebraMessageBody):
1238    """
1239    Base class for ZEBRA_INTERFACE_ADD, ZEBRA_INTERFACE_DELETE,
1240    ZEBRA_INTERFACE_UP and ZEBRA_INTERFACE_DOWN message body.
1241    """
1242    # Zebra Interface Add/Delete message body:
1243    #  0                   1                   2                   3
1244    #  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
1245    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1246    # | Interface Name (INTERFACE_NAMSIZE bytes length)               |
1247    # |                                                               |
1248    # |                                                               |
1249    # |                                                               |
1250    # |                                                               |
1251    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1252    # | Interface index                                               |
1253    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1254    # | Status        |
1255    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1256    # | Interface flags                                               |
1257    # |                                                               |
1258    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1259    # | (PTM Enable)  | (PTM Status)  | v4(FRRouting)
1260    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1261    # | Metric                                                        |
1262    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1263    # | (Speed): v4(FRRouting v3.0 or later)                          |
1264    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1265    # | Interface's MTU for IPv4                                      |
1266    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1267    # | Interface's MTU for IPv6                                      |
1268    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1269    # | Bandwidth                                                     |
1270    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1271    # | (Link Layer Type): v3 or later                                |
1272    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1273    # | Hardware Address Length                                       |
1274    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1275    # | Hardware Address    if HW length different from 0             |
1276    # |  ...                max is INTERFACE_HWADDR_MAX               |
1277    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1278    # | link_params?  |  Whether a link-params follows: 1 or 0.
1279    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1280    # | Link params    0 or 1 INTERFACE_LINK_PARAMS_SIZE sized        |
1281    # |  ....          (struct if_link_params).                       |
1282    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1283    # ifname, ifindex, status, if_flags, metric, ifmtu, ifmtu6, bandwidth,
1284    # hw_addr_len
1285    _HEADER_FMT = '!%dsIBQIIIII' % INTERFACE_NAMSIZE
1286    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1287    # ifname, ifindex, status, if_flags, metric, ifmtu, ifmtu6, bandwidth,
1288    # ll_type, hw_addr_len
1289    _V3_HEADER_FMT = '!%dsIBQIIIIII' % INTERFACE_NAMSIZE
1290    V3_HEADER_SIZE = struct.calcsize(_V3_HEADER_FMT)
1291    # ifname, ifindex, status, if_flags, ptm_enable, ptm_status, metric,
1292    # ifmtu, ifmtu6, bandwidth, ll_type, hw_addr_len
1293    _V4_HEADER_FMT_2_0 = '!%dsIBQBBIIIIII' % INTERFACE_NAMSIZE
1294    V4_HEADER_SIZE_2_0 = struct.calcsize(_V4_HEADER_FMT_2_0)
1295    # ifname, ifindex, status, if_flags, ptm_enable, ptm_status, metric,
1296    # speed, ifmtu, ifmtu6, bandwidth, ll_type, hw_addr_len
1297    _V4_HEADER_FMT_3_0 = '!%dsIBQBBIIIIIII' % INTERFACE_NAMSIZE
1298    V4_HEADER_SIZE_3_0 = struct.calcsize(_V4_HEADER_FMT_3_0)
1299
1300    # link_params_state (whether a link-params follows)
1301    _LP_STATE_FMT = '!?'
1302    LP_STATE_SIZE = struct.calcsize(_LP_STATE_FMT)
1303    # See InterfaceLinkParams class for Link params structure
1304
1305    def __init__(self, ifname=None, ifindex=None, status=None, if_flags=None,
1306                 ptm_enable=None, ptm_status=None,
1307                 metric=None, speed=None, ifmtu=None, ifmtu6=None,
1308                 bandwidth=None, ll_type=None, hw_addr_len=0, hw_addr=None,
1309                 link_params=None):
1310        super(_ZebraInterface, self).__init__()
1311        self.ifname = ifname
1312        self.ifindex = ifindex
1313        self.status = status
1314        self.if_flags = if_flags
1315        self.ptm_enable = ptm_enable
1316        self.ptm_status = ptm_status
1317        self.metric = metric
1318        self.speed = speed
1319        self.ifmtu = ifmtu
1320        self.ifmtu6 = ifmtu6
1321        self.bandwidth = bandwidth
1322        self.ll_type = ll_type
1323        self.hw_addr_lenght = hw_addr_len
1324        hw_addr = hw_addr or b''
1325        self.hw_addr = hw_addr
1326        assert (isinstance(link_params, InterfaceLinkParams)
1327                or link_params is None)
1328        self.link_params = link_params
1329
1330    @classmethod
1331    def parse(cls, buf, version=_DEFAULT_VERSION):
1332        ptm_enable = None
1333        ptm_status = None
1334        speed = None
1335        ll_type = None
1336        if version <= 2:
1337            (ifname, ifindex, status, if_flags, metric,
1338             ifmtu, ifmtu6, bandwidth,
1339             hw_addr_len) = struct.unpack_from(cls._HEADER_FMT, buf)
1340            rest = buf[cls.HEADER_SIZE:]
1341        elif version == 3:
1342            (ifname, ifindex, status, if_flags, metric,
1343             ifmtu, ifmtu6, bandwidth, ll_type,
1344             hw_addr_len) = struct.unpack_from(cls._V3_HEADER_FMT, buf)
1345            rest = buf[cls.V3_HEADER_SIZE:]
1346        elif version == 4:
1347            if _is_frr_version_ge(_FRR_VERSION_3_0):
1348                (ifname, ifindex, status, if_flags, ptm_enable, ptm_status,
1349                 metric, speed, ifmtu, ifmtu6, bandwidth, ll_type,
1350                 hw_addr_len) = struct.unpack_from(cls._V4_HEADER_FMT_3_0, buf)
1351                rest = buf[cls.V4_HEADER_SIZE_3_0:]
1352            elif _is_frr_version_ge(_FRR_VERSION_2_0):
1353                (ifname, ifindex, status, if_flags, ptm_enable, ptm_status,
1354                 metric, ifmtu, ifmtu6, bandwidth, ll_type,
1355                 hw_addr_len) = struct.unpack_from(cls._V4_HEADER_FMT_2_0, buf)
1356                rest = buf[cls.V4_HEADER_SIZE_2_0:]
1357            else:
1358                raise struct.error(
1359                    'Unsupported FRRouting version: %s'
1360                    % CONF['zapi'].frr_version)
1361        else:
1362            raise struct.error(
1363                'Unsupported Zebra protocol version: %d'
1364                % version)
1365        ifname = str(six.text_type(ifname.strip(b'\x00'), 'ascii'))
1366
1367        hw_addr_len = min(hw_addr_len, INTERFACE_HWADDR_MAX)
1368        hw_addr_bin = rest[:hw_addr_len]
1369        rest = rest[hw_addr_len:]
1370        if 0 < hw_addr_len < 7:
1371            # Assuming MAC address
1372            hw_addr = addrconv.mac.bin_to_text(
1373                hw_addr_bin + b'\x00' * (6 - hw_addr_len))
1374        else:
1375            # Unknown hardware address
1376            hw_addr = hw_addr_bin
1377
1378        if not rest:
1379            return cls(ifname, ifindex, status, if_flags,
1380                       ptm_enable, ptm_status, metric, speed, ifmtu, ifmtu6,
1381                       bandwidth, ll_type, hw_addr_len, hw_addr)
1382
1383        (link_param_state,) = struct.unpack_from(cls._LP_STATE_FMT, rest)
1384        rest = rest[cls.LP_STATE_SIZE:]
1385
1386        if link_param_state:
1387            link_params, rest = InterfaceLinkParams.parse(rest)
1388        else:
1389            link_params = None
1390
1391        return cls(ifname, ifindex, status, if_flags,
1392                   ptm_enable, ptm_status, metric, speed, ifmtu, ifmtu6,
1393                   bandwidth, ll_type, hw_addr_len, hw_addr,
1394                   link_params)
1395
1396    def serialize(self, version=_DEFAULT_VERSION):
1397        if self.ifname is None:
1398            # Case for sending message to Zebra
1399            return b''
1400        # fixup
1401        if netaddr.valid_mac(self.hw_addr):
1402            # MAC address
1403            hw_addr_len = 6
1404            hw_addr = addrconv.mac.text_to_bin(self.hw_addr)
1405        else:
1406            # Unknown hardware address
1407            hw_addr_len = len(self.hw_addr)
1408            hw_addr = self.hw_addr
1409
1410        if version <= 2:
1411            return struct.pack(
1412                self._HEADER_FMT,
1413                self.ifname.encode('ascii'), self.ifindex, self.status,
1414                self.if_flags, self.metric, self.ifmtu, self.ifmtu6,
1415                self.bandwidth, hw_addr_len) + hw_addr
1416        elif version == 3:
1417            buf = struct.pack(
1418                self._V3_HEADER_FMT,
1419                self.ifname.encode('ascii'), self.ifindex, self.status,
1420                self.if_flags, self.metric, self.ifmtu, self.ifmtu6,
1421                self.bandwidth, self.ll_type, hw_addr_len) + hw_addr
1422        elif version == 4:
1423            if _is_frr_version_ge(_FRR_VERSION_3_0):
1424                buf = struct.pack(
1425                    self._V4_HEADER_FMT_3_0,
1426                    self.ifname.encode('ascii'), self.ifindex, self.status,
1427                    self.if_flags, self.ptm_enable, self.ptm_status,
1428                    self.metric, self.speed, self.ifmtu, self.ifmtu6,
1429                    self.bandwidth, self.ll_type, hw_addr_len) + hw_addr
1430            elif _is_frr_version_ge(_FRR_VERSION_2_0):
1431                buf = struct.pack(
1432                    self._V4_HEADER_FMT_2_0,
1433                    self.ifname.encode('ascii'), self.ifindex, self.status,
1434                    self.if_flags, self.ptm_enable, self.ptm_status,
1435                    self.metric, self.ifmtu, self.ifmtu6,
1436                    self.bandwidth, self.ll_type, hw_addr_len) + hw_addr
1437            else:
1438                raise ValueError(
1439                    'Unsupported FRRouting version: %s'
1440                    % CONF['zapi'].frr_version)
1441        else:
1442            raise ValueError(
1443                'Unsupported Zebra protocol version: %d'
1444                % version)
1445
1446        if isinstance(self.link_params, InterfaceLinkParams):
1447            buf += struct.pack(self._LP_STATE_FMT, True)
1448            buf += self.link_params.serialize()
1449        else:
1450            buf += struct.pack(self._LP_STATE_FMT, False)
1451
1452        return buf
1453
1454
1455@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_ADD)
1456@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_ADD)
1457class ZebraInterfaceAdd(_ZebraInterface):
1458    """
1459    Message body class for ZEBRA_INTERFACE_ADD.
1460    """
1461
1462
1463@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_DELETE)
1464@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_DELETE)
1465class ZebraInterfaceDelete(_ZebraInterface):
1466    """
1467    Message body class for ZEBRA_INTERFACE_DELETE.
1468    """
1469
1470
1471@six.add_metaclass(abc.ABCMeta)
1472class _ZebraInterfaceAddress(_ZebraMessageBody):
1473    """
1474    Base class for ZEBRA_INTERFACE_ADDRESS_ADD and
1475    ZEBRA_INTERFACE_ADDRESS_DELETE message body.
1476    """
1477    # Zebra Interface Address Add/Delete message body:
1478    #  0                   1                   2                   3
1479    #  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
1480    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1481    # | Interface index                                               |
1482    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1483    # | IFC Flags     |  flags for connected address
1484    # +-+-+-+-+-+-+-+-+
1485    # | Family        |
1486    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1487    # | IPv4/v6 Prefix (Variable)                                     |
1488    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1489    # | Prefix len    |
1490    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1491    # | IPv4/v6 Destination Address (Variable)                        |
1492    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1493    _HEADER_FMT = '!IB'  # ifindex, ifc_flags
1494    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1495
1496    def __init__(self, ifindex, ifc_flags, family, prefix, dest):
1497        super(_ZebraInterfaceAddress, self).__init__()
1498        self.ifindex = ifindex
1499        self.ifc_flags = ifc_flags
1500        self.family = family
1501        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
1502            prefix = prefix.prefix
1503        self.prefix = prefix
1504        assert ip.valid_ipv4(dest) or ip.valid_ipv6(dest)
1505        self.dest = dest
1506
1507    @classmethod
1508    def parse(cls, buf, version=_DEFAULT_VERSION):
1509        (ifindex, ifc_flags) = struct.unpack_from(cls._HEADER_FMT, buf)
1510        rest = buf[cls.HEADER_SIZE:]
1511
1512        (family, prefix, rest) = _parse_zebra_family_prefix(rest)
1513
1514        if socket.AF_INET == family:
1515            dest = addrconv.ipv4.bin_to_text(rest)
1516        elif socket.AF_INET6 == family:
1517            dest = addrconv.ipv6.bin_to_text(rest)
1518        else:
1519            raise struct.error('Unsupported family: %d' % family)
1520
1521        return cls(ifindex, ifc_flags, family, prefix, dest)
1522
1523    def serialize(self, version=_DEFAULT_VERSION):
1524        (self.family,  # fixup
1525         body_bin) = _serialize_zebra_family_prefix(self.prefix)
1526
1527        if ip.valid_ipv4(self.dest):
1528            body_bin += addrconv.ipv4.text_to_bin(self.dest)
1529        elif ip.valid_ipv6(self.prefix):
1530            body_bin += addrconv.ipv6.text_to_bin(self.dest)
1531        else:
1532            raise ValueError(
1533                'Invalid destination address: %s' % self.dest)
1534
1535        return struct.pack(self._HEADER_FMT,
1536                           self.ifindex, self.ifc_flags) + body_bin
1537
1538
1539@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_ADDRESS_ADD)
1540@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_ADDRESS_ADD)
1541class ZebraInterfaceAddressAdd(_ZebraInterfaceAddress):
1542    """
1543    Message body class for ZEBRA_INTERFACE_ADDRESS_ADD.
1544    """
1545
1546
1547@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_ADDRESS_DELETE)
1548@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_ADDRESS_DELETE)
1549class ZebraInterfaceAddressDelete(_ZebraInterfaceAddress):
1550    """
1551    Message body class for ZEBRA_INTERFACE_ADDRESS_DELETE.
1552    """
1553
1554
1555@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_UP)
1556@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_UP)
1557class ZebraInterfaceUp(_ZebraInterface):
1558    """
1559    Message body class for ZEBRA_INTERFACE_UP.
1560    """
1561
1562
1563@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_DOWN)
1564@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_DOWN)
1565class ZebraInterfaceDown(_ZebraInterface):
1566    """
1567    Message body class for ZEBRA_INTERFACE_DOWN.
1568    """
1569
1570
1571@six.add_metaclass(abc.ABCMeta)
1572class _ZebraIPRoute(_ZebraMessageBody):
1573    """
1574    Base class for ZEBRA_IPV4_ROUTE_* and ZEBRA_IPV6_ROUTE_*
1575    message body.
1576
1577    .. Note::
1578
1579        Zebra IPv4/IPv6 Route message have asymmetric structure.
1580        If the message sent from Zebra Daemon, set 'from_zebra=True' to
1581        create an instance of this class.
1582    """
1583    # Zebra IPv4/IPv6 Route message body (Protocol Daemons -> Zebra Daemon):
1584    #  0                   1                   2                   3
1585    #  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
1586    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1587    # | Route Type    | Flags         | Message       |
1588    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1589    # | SAFI                          |
1590    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1591    # | IPv4/v6 Prefix (Variable)                                     |
1592    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1593    # | Nexthop Num   |
1594    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1595    # | Nexthops (Variable)                                           |
1596    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1597    # | (Distance)    |
1598    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1599    # | (Metric)                                                      |
1600    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1601    # | (MTU)                                                         |
1602    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1603    # | (TAG)                                                         |
1604    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1605    #
1606    # Zebra IPv4/IPv6 Route message body on FRRouting
1607    # (Protocol Daemons -> Zebra Daemon):
1608    #  0                   1                   2                   3
1609    #  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
1610    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1611    # | Route Type    | Instance                      |
1612    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1613    # | Flags                                                         |
1614    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1615    # | Message       | SAFI                          |
1616    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1617    # | IPv4/v6 Prefix (Variable)                                     |
1618    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1619    # | (IPv4/v6 Source Prefix): v4(FRRouting v3.0 or later)          |
1620    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1621    # | Nexthop Num   |
1622    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1623    # | Nexthops (Variable)                                           |
1624    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1625    # | (Distance)    |
1626    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1627    # | (Metric)                                                      |
1628    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1629    # | (TAG)                                                         |
1630    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1631    # | (MTU)                                                         |
1632    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1633    #
1634    # Zebra IPv4/IPv6 Route message body (Zebra Daemon -> Protocol Daemons):
1635    #  0                   1                   2                   3
1636    #  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
1637    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1638    # | Route Type    | Flags         | Message       |
1639    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1640    # | IPv4/v6 Prefix (Variable)                                     |
1641    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1642    # | (Nexthop Num) |
1643    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1644    # | (Nexthops (Variable))                                         |
1645    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1646    # | (IFIndex Num) |
1647    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1648    # | (Interface indexes)                                           |
1649    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1650    # | (Distance)    |
1651    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1652    # | (Metric)                                                      |
1653    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1654    # | (MTU)                                                         |
1655    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1656    # | (TAG)                                                         |
1657    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1658    #
1659    # Zebra IPv4/IPv6 Route message body on FRRouting
1660    # (Zebra Daemon -> Protocol Daemons):
1661    #  0                   1                   2                   3
1662    #  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
1663    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1664    # | Route Type    | Instance                      |
1665    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1666    # | Flags                                                         |
1667    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1668    # | Message       |
1669    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1670    # | IPv4/v6 Prefix (Variable)                                     |
1671    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1672    # | (IPv4/v6 Source Prefix): v4(FRRouting v3.0 or later)          |
1673    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1674    # | (Nexthop Num) |
1675    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1676    # | (Nexthops (Variable))                                         |
1677    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1678    # | (IFIndex Num) |
1679    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1680    # | (Interface indexes)                                           |
1681    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1682    # | (Distance)    |
1683    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1684    # | (Metric)                                                      |
1685    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1686    # | (TAG)                                                         |
1687    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1688    _HEADER_FMT = '!BBB'  # type, flags, message
1689    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1690    _V4_HEADER_FMT = '!BHIB'  # type, instance, flags, message
1691    V4_HEADER_SIZE = struct.calcsize(_V4_HEADER_FMT)
1692    _SAFI_FMT = '!H'  # safi
1693    SAFI_SIZE = struct.calcsize(_SAFI_FMT)
1694    _NUM_FMT = '!B'  # nexthop_num or ifindex_num
1695    NUM_SIZE = struct.calcsize(_NUM_FMT)
1696    _IFINDEX_FMT = '!I'  # ifindex
1697    IFINDEX_SIZE = struct.calcsize(_IFINDEX_FMT)
1698
1699    # API type specific constants
1700    _FAMILY = None  # either socket.AF_INET or socket.AF_INET6
1701
1702    def __init__(self, route_type, flags, message, safi=None,
1703                 prefix=None, src_prefix=None,
1704                 nexthops=None, ifindexes=None,
1705                 distance=None, metric=None, mtu=None, tag=None,
1706                 instance=None, from_zebra=False):
1707        super(_ZebraIPRoute, self).__init__()
1708        self.route_type = route_type
1709        self.instance = instance
1710        self.flags = flags
1711        self.message = message
1712
1713        # SAFI should be included if this message sent to Zebra.
1714        if from_zebra:
1715            self.safi = None
1716        else:
1717            self.safi = safi or packet_safi.UNICAST
1718
1719        assert prefix is not None
1720        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
1721            prefix = prefix.prefix
1722        self.prefix = prefix
1723
1724        if isinstance(src_prefix, (IPv4Prefix, IPv6Prefix)):
1725            src_prefix = src_prefix.prefix
1726        self.src_prefix = src_prefix
1727
1728        # Nexthops should be a list of str representations of IP address
1729        # if this message sent from Zebra, otherwise a list of _Nexthop
1730        # subclasses.
1731        nexthops = nexthops or []
1732        if from_zebra:
1733            for nexthop in nexthops:
1734                assert ip.valid_ipv4(nexthop) or ip.valid_ipv6(nexthop)
1735        else:
1736            for nexthop in nexthops:
1737                assert isinstance(nexthop, _NextHop)
1738        self.nexthops = nexthops
1739
1740        # Interface indexes should be included if this message sent from
1741        # Zebra.
1742        if from_zebra:
1743            ifindexes = ifindexes or []
1744            for ifindex in ifindexes:
1745                assert isinstance(ifindex, six.integer_types)
1746            self.ifindexes = ifindexes
1747        else:
1748            self.ifindexes = None
1749
1750        self.distance = distance
1751        self.metric = metric
1752        self.mtu = mtu
1753        self.tag = tag
1754
1755        # is this message sent from Zebra message or not.
1756        self.from_zebra = from_zebra
1757
1758    @classmethod
1759    def _parse_message_option(cls, message, flag, fmt, buf):
1760        if message & flag:
1761            (option,) = struct.unpack_from(fmt, buf)
1762            return option, buf[struct.calcsize(fmt):]
1763
1764        return None, buf
1765
1766    @classmethod
1767    def _parse_impl(cls, buf, version=_DEFAULT_VERSION, from_zebra=False):
1768        instance = None
1769        if version <= 3:
1770            (route_type, flags, message,) = struct.unpack_from(
1771                cls._HEADER_FMT, buf)
1772            rest = buf[cls.HEADER_SIZE:]
1773        elif version == 4:
1774            (route_type, instance, flags, message,) = struct.unpack_from(
1775                cls._V4_HEADER_FMT, buf)
1776            rest = buf[cls.V4_HEADER_SIZE:]
1777        else:
1778            raise struct.error(
1779                'Unsupported Zebra protocol version: %d'
1780                % version)
1781
1782        if from_zebra:
1783            safi = None
1784        else:
1785            (safi,) = struct.unpack_from(cls._SAFI_FMT, rest)
1786            rest = rest[cls.SAFI_SIZE:]
1787
1788        prefix, rest = _parse_ip_prefix(cls._FAMILY, rest)
1789
1790        src_prefix = None
1791        if version == 4 and message & FRR_ZAPI_MESSAGE_SRCPFX:
1792            src_prefix, rest = _parse_ip_prefix(cls._FAMILY, rest)
1793
1794        if from_zebra and message & ZAPI_MESSAGE_NEXTHOP:
1795            nexthops = []
1796            (nexthop_num,) = struct.unpack_from(cls._NUM_FMT, rest)
1797            rest = rest[cls.NUM_SIZE:]
1798            if cls._FAMILY == socket.AF_INET:
1799                for _ in range(nexthop_num):
1800                    nexthop = addrconv.ipv4.bin_to_text(rest[:4])
1801                    nexthops.append(nexthop)
1802                    rest = rest[4:]
1803            else:  # cls._FAMILY == socket.AF_INET6:
1804                for _ in range(nexthop_num):
1805                    nexthop = addrconv.ipv6.bin_to_text(rest[:16])
1806                    nexthops.append(nexthop)
1807                    rest = rest[16:]
1808        else:
1809            nexthops, rest = _parse_nexthops(rest, version)
1810
1811        ifindexes = []
1812        if from_zebra and message & ZAPI_MESSAGE_IFINDEX:
1813            (ifindex_num,) = struct.unpack_from(cls._NUM_FMT, rest)
1814            rest = rest[cls.NUM_SIZE:]
1815            for _ in range(ifindex_num):
1816                (ifindex,) = struct.unpack_from(cls._IFINDEX_FMT, rest)
1817                ifindexes.append(ifindex)
1818                rest = rest[cls.IFINDEX_SIZE:]
1819
1820        if version <= 3:
1821            distance, rest = cls._parse_message_option(
1822                message, ZAPI_MESSAGE_DISTANCE, '!B', rest)
1823            metric, rest = cls._parse_message_option(
1824                message, ZAPI_MESSAGE_METRIC, '!I', rest)
1825            mtu, rest = cls._parse_message_option(
1826                message, ZAPI_MESSAGE_MTU, '!I', rest)
1827            tag, rest = cls._parse_message_option(
1828                message, ZAPI_MESSAGE_TAG, '!I', rest)
1829        elif version == 4:
1830            distance, rest = cls._parse_message_option(
1831                message, FRR_ZAPI_MESSAGE_DISTANCE, '!B', rest)
1832            metric, rest = cls._parse_message_option(
1833                message, FRR_ZAPI_MESSAGE_METRIC, '!I', rest)
1834            tag, rest = cls._parse_message_option(
1835                message, FRR_ZAPI_MESSAGE_TAG, '!I', rest)
1836            mtu, rest = cls._parse_message_option(
1837                message, FRR_ZAPI_MESSAGE_MTU, '!I', rest)
1838        else:
1839            raise struct.error(
1840                'Unsupported Zebra protocol version: %d'
1841                % version)
1842
1843        return cls(route_type, flags, message, safi, prefix, src_prefix,
1844                   nexthops, ifindexes,
1845                   distance, metric, mtu, tag,
1846                   instance, from_zebra=from_zebra)
1847
1848    @classmethod
1849    def parse(cls, buf, version=_DEFAULT_VERSION):
1850        return cls._parse_impl(buf, version=version)
1851
1852    @classmethod
1853    def parse_from_zebra(cls, buf, version=_DEFAULT_VERSION):
1854        return cls._parse_impl(buf, version=version, from_zebra=True)
1855
1856    def _serialize_message_option(self, option, flag, fmt):
1857        if option is None:
1858            return b''
1859
1860        # fixup
1861        self.message |= flag
1862
1863        return struct.pack(fmt, option)
1864
1865    def serialize(self, version=_DEFAULT_VERSION):
1866        prefix = _serialize_ip_prefix(self.prefix)
1867        if version == 4 and self.src_prefix:
1868            self.message |= FRR_ZAPI_MESSAGE_SRCPFX  # fixup
1869            prefix += _serialize_ip_prefix(self.src_prefix)
1870
1871        nexthops = b''
1872        if self.from_zebra and self.nexthops:
1873            self.message |= ZAPI_MESSAGE_NEXTHOP  # fixup
1874            nexthops += struct.pack(self._NUM_FMT, len(self.nexthops))
1875            for nexthop in self.nexthops:
1876                nexthops += ip.text_to_bin(nexthop)
1877        else:
1878            self.message |= ZAPI_MESSAGE_NEXTHOP  # fixup
1879            nexthops = _serialize_nexthops(self.nexthops, version=version)
1880
1881        ifindexes = b''
1882        if self.ifindexes and self.from_zebra:
1883            self.message |= ZAPI_MESSAGE_IFINDEX  # fixup
1884            ifindexes += struct.pack(self._NUM_FMT, len(self.ifindexes))
1885            for ifindex in self.ifindexes:
1886                ifindexes += struct.pack(self._IFINDEX_FMT, ifindex)
1887
1888        if version <= 3:
1889            options = self._serialize_message_option(
1890                self.distance, ZAPI_MESSAGE_DISTANCE, '!B')
1891            options += self._serialize_message_option(
1892                self.metric, ZAPI_MESSAGE_METRIC, '!I')
1893            options += self._serialize_message_option(
1894                self.mtu, ZAPI_MESSAGE_MTU, '!I')
1895            options += self._serialize_message_option(
1896                self.tag, ZAPI_MESSAGE_TAG, '!I')
1897            header = struct.pack(
1898                self._HEADER_FMT,
1899                self.route_type, self.flags, self.message)
1900        elif version == 4:
1901            options = self._serialize_message_option(
1902                self.distance, FRR_ZAPI_MESSAGE_DISTANCE, '!B')
1903            options += self._serialize_message_option(
1904                self.metric, FRR_ZAPI_MESSAGE_METRIC, '!I')
1905            options += self._serialize_message_option(
1906                self.tag, FRR_ZAPI_MESSAGE_TAG, '!I')
1907            options += self._serialize_message_option(
1908                self.mtu, FRR_ZAPI_MESSAGE_MTU, '!I')
1909            header = struct.pack(
1910                self._V4_HEADER_FMT,
1911                self.route_type, self.instance, self.flags, self.message)
1912        else:
1913            raise ValueError(
1914                'Unsupported Zebra protocol version: %d'
1915                % version)
1916
1917        if not self.from_zebra:
1918            header += struct.pack(self._SAFI_FMT, self.safi)
1919
1920        return header + prefix + nexthops + ifindexes + options
1921
1922
1923class _ZebraIPv4Route(_ZebraIPRoute):
1924    """
1925    Base class for ZEBRA_IPV4_ROUTE_* message body.
1926    """
1927    _FAMILY = socket.AF_INET
1928
1929
1930@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV4_ROUTE_ADD)
1931@_ZebraMessageBody.register_type(ZEBRA_IPV4_ROUTE_ADD)
1932class ZebraIPv4RouteAdd(_ZebraIPv4Route):
1933    """
1934    Message body class for ZEBRA_IPV4_ROUTE_ADD.
1935    """
1936
1937
1938@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV4_ROUTE_DELETE)
1939@_ZebraMessageBody.register_type(ZEBRA_IPV4_ROUTE_DELETE)
1940class ZebraIPv4RouteDelete(_ZebraIPv4Route):
1941    """
1942    Message body class for ZEBRA_IPV4_ROUTE_DELETE.
1943    """
1944
1945
1946class _ZebraIPv6Route(_ZebraIPRoute):
1947    """
1948    Base class for ZEBRA_IPV6_ROUTE_* message body.
1949    """
1950    _FAMILY = socket.AF_INET6
1951
1952
1953@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV6_ROUTE_ADD)
1954@_ZebraMessageBody.register_type(ZEBRA_IPV6_ROUTE_ADD)
1955class ZebraIPv6RouteAdd(_ZebraIPv6Route):
1956    """
1957    Message body class for ZEBRA_IPV6_ROUTE_ADD.
1958    """
1959
1960
1961@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV6_ROUTE_DELETE)
1962@_ZebraMessageBody.register_type(ZEBRA_IPV6_ROUTE_DELETE)
1963class ZebraIPv6RouteDelete(_ZebraIPv6Route):
1964    """
1965    Message body class for ZEBRA_IPV6_ROUTE_DELETE.
1966    """
1967
1968
1969@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD)
1970class ZebraIPv4RouteIPv6NexthopAdd(_ZebraIPv4Route):
1971    """
1972    Message body class for FRR_ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD.
1973    """
1974
1975
1976@six.add_metaclass(abc.ABCMeta)
1977class _ZebraRedistribute(_ZebraMessageBody):
1978    """
1979    Base class for ZEBRA_REDISTRIBUTE_ADD and ZEBRA_REDISTRIBUTE_DELETE
1980    message body.
1981    """
1982    # Zebra Redistribute message body:
1983    #  0                   1                   2                   3
1984    #  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
1985    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1986    # | Route Type    |
1987    # +-+-+-+-+-+-+-+-+
1988    #
1989    # Zebra Redistribute message body on FRRouting:
1990    #  0                   1                   2                   3
1991    #  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
1992    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1993    # | AFI           | Route Type    | Instance                      |
1994    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-++-+-+-+-+-+-+
1995    _HEADER_FMT = '!B'  # route_type
1996    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
1997    _V4_HEADER_FMT = '!BBH'  # afi, route_type, instance
1998    V4_HEADER_SIZE = struct.calcsize(_V4_HEADER_FMT)
1999
2000    def __init__(self, route_type, afi=None, instance=None):
2001        super(_ZebraRedistribute, self).__init__()
2002        self.afi = afi
2003        self.route_type = route_type
2004        self.instance = instance
2005
2006    @classmethod
2007    def parse(cls, buf, version=_DEFAULT_VERSION):
2008        afi = None
2009        instance = None
2010        if version <= 3:
2011            (route_type,) = struct.unpack_from(cls._HEADER_FMT, buf)
2012        elif version == 4:
2013            (afi, route_type,
2014             instance) = struct.unpack_from(cls._V4_HEADER_FMT, buf)
2015        else:
2016            raise struct.error(
2017                'Unsupported Zebra protocol version: %d'
2018                % version)
2019
2020        return cls(route_type, afi, instance)
2021
2022    def serialize(self, version=_DEFAULT_VERSION):
2023        if version <= 3:
2024            return struct.pack(self._HEADER_FMT, self.route_type)
2025        elif version == 4:
2026            return struct.pack(self._V4_HEADER_FMT,
2027                               self.afi, self.route_type, self.instance)
2028        else:
2029            raise ValueError(
2030                'Unsupported Zebra protocol version: %d'
2031                % version)
2032
2033
2034@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_ADD)
2035@_ZebraMessageBody.register_type(ZEBRA_REDISTRIBUTE_ADD)
2036class ZebraRedistributeAdd(_ZebraRedistribute):
2037    """
2038    Message body class for ZEBRA_REDISTRIBUTE_ADD.
2039    """
2040
2041
2042@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_DELETE)
2043@_ZebraMessageBody.register_type(ZEBRA_REDISTRIBUTE_DELETE)
2044class ZebraRedistributeDelete(_ZebraRedistribute):
2045    """
2046    Message body class for ZEBRA_REDISTRIBUTE_DELETE.
2047    """
2048
2049
2050@six.add_metaclass(abc.ABCMeta)
2051class _ZebraRedistributeDefault(_ZebraMessageBody):
2052    """
2053    Base class for ZEBRA_REDISTRIBUTE_DEFAULT_ADD and
2054    ZEBRA_REDISTRIBUTE_DEFAULT_DELETE message body.
2055    """
2056
2057
2058@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_DEFAULT_ADD)
2059@_ZebraMessageBody.register_type(ZEBRA_REDISTRIBUTE_DEFAULT_ADD)
2060class ZebraRedistributeDefaultAdd(_ZebraRedistribute):
2061    """
2062    Message body class for ZEBRA_REDISTRIBUTE_DEFAULT_ADD.
2063    """
2064
2065
2066@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_DEFAULT_DELETE)
2067@_ZebraMessageBody.register_type(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE)
2068class ZebraRedistributeDefaultDelete(_ZebraRedistribute):
2069    """
2070    Message body class for ZEBRA_REDISTRIBUTE_DEFAULT_DELETE.
2071    """
2072
2073
2074@six.add_metaclass(abc.ABCMeta)
2075class _ZebraIPNexthopLookup(_ZebraMessageBody):
2076    """
2077    Base class for ZEBRA_IPV4_NEXTHOP_LOOKUP and
2078    ZEBRA_IPV6_NEXTHOP_LOOKUP message body.
2079    """
2080    # Zebra IPv4/v6 Nexthop Lookup message body:
2081    #  0                   1                   2                   3
2082    #  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
2083    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2084    # | IPv4/v6 address                                               |
2085    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2086    # | Metric                                                        |
2087    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2088    # | Nexthop Num   |
2089    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2090    # | Nexthops (Variable)                                           |
2091    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2092    _METRIC_FMT = '!I'  # metric
2093    METRIC_SIZE = struct.calcsize(_METRIC_FMT)
2094
2095    # Message type specific constants
2096    ADDR_CLS = None  # either addrconv.ipv4 or addrconv.ipv6
2097    ADDR_LEN = None  # IP address length in bytes
2098
2099    def __init__(self, addr, metric=None, nexthops=None):
2100        super(_ZebraIPNexthopLookup, self).__init__()
2101        assert ip.valid_ipv4(addr) or ip.valid_ipv6(addr)
2102        self.addr = addr
2103        self.metric = metric
2104        nexthops = nexthops or []
2105        for nexthop in nexthops:
2106            assert isinstance(nexthop, _NextHop)
2107        self.nexthops = nexthops
2108
2109    @classmethod
2110    def parse(cls, buf, version=_DEFAULT_VERSION):
2111        addr = cls.ADDR_CLS.bin_to_text(buf[:cls.ADDR_LEN])
2112        rest = buf[cls.ADDR_LEN:]
2113
2114        metric = None
2115        if rest:
2116            # Note: Case for ZEBRA_IPV4_NEXTHOP_LOOKUP request
2117            (metric,) = struct.unpack_from(cls._METRIC_FMT, rest)
2118            rest = rest[cls.METRIC_SIZE:]
2119
2120        nexthops = None
2121        if rest:
2122            nexthops, rest = _parse_nexthops(rest, version)
2123
2124        return cls(addr, metric, nexthops)
2125
2126    def serialize(self, version=_DEFAULT_VERSION):
2127        buf = self.ADDR_CLS.text_to_bin(self.addr)
2128
2129        if self.metric is None:
2130            return buf
2131
2132        buf += struct.pack(self._METRIC_FMT, self.metric)
2133
2134        return buf + _serialize_nexthops(self.nexthops, version=version)
2135
2136
2137@_ZebraMessageBody.register_type(ZEBRA_IPV4_NEXTHOP_LOOKUP)
2138class ZebraIPv4NexthopLookup(_ZebraIPNexthopLookup):
2139    """
2140    Message body class for ZEBRA_IPV4_NEXTHOP_LOOKUP.
2141    """
2142    ADDR_CLS = addrconv.ipv4
2143    ADDR_LEN = 4
2144
2145
2146@_ZebraMessageBody.register_type(ZEBRA_IPV6_NEXTHOP_LOOKUP)
2147class ZebraIPv6NexthopLookup(_ZebraIPNexthopLookup):
2148    """
2149    Message body class for ZEBRA_IPV6_NEXTHOP_LOOKUP.
2150    """
2151    ADDR_CLS = addrconv.ipv6
2152    ADDR_LEN = 16
2153
2154
2155@six.add_metaclass(abc.ABCMeta)
2156class _ZebraIPImportLookup(_ZebraMessageBody):
2157    """
2158    Base class for ZEBRA_IPV4_IMPORT_LOOKUP and
2159    ZEBRA_IPV6_IMPORT_LOOKUP message body.
2160
2161    .. Note::
2162
2163        Zebra IPv4/v6 Import Lookup message have asymmetric structure.
2164        If the message sent from Zebra Daemon, set 'from_zebra=True' to
2165        create an instance of this class.
2166    """
2167    # Zebra IPv4/v6 Import Lookup message body
2168    # (Protocol Daemons -> Zebra Daemon):
2169    #  0                   1                   2                   3
2170    #  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
2171    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2172    # | Prefix Len    |
2173    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2174    # | IPv4/v6 Prefix (4 bytes or 16 bytes)                          |
2175    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2176    #
2177    # Zebra IPv4/v6 Import Lookup message body
2178    # (Zebra Daemons -> Protocol Daemon):
2179    #  0                   1                   2                   3
2180    #  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
2181    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2182    # | IPv4/v6 Prefix (4 bytes or 16 bytes)                          |
2183    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2184    # | Metric                                                        |
2185    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2186    # | Nexthop Num   |
2187    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2188    # | Nexthops (Variable)                                           |
2189    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2190    _PREFIX_LEN_FMT = '!B'  # prefix_len
2191    PREFIX_LEN_SIZE = struct.calcsize(_PREFIX_LEN_FMT)
2192    _METRIC_FMT = '!I'  # metric
2193    METRIC_SIZE = struct.calcsize(_METRIC_FMT)
2194
2195    # Message type specific constants
2196    PREFIX_CLS = None  # either addrconv.ipv4 or addrconv.ipv6
2197    PREFIX_LEN = None  # IP prefix length in bytes
2198
2199    def __init__(self, prefix, metric=None, nexthops=None,
2200                 from_zebra=False):
2201        super(_ZebraIPImportLookup, self).__init__()
2202        if not from_zebra:
2203            assert ip.valid_ipv4(prefix) or ip.valid_ipv6(prefix)
2204        else:
2205            if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
2206                prefix = prefix.prefix
2207            else:
2208                assert ip.valid_ipv4(prefix) or ip.valid_ipv6(prefix)
2209        self.prefix = prefix
2210        self.metric = metric
2211        nexthops = nexthops or []
2212        for nexthop in nexthops:
2213            assert isinstance(nexthop, _NextHop)
2214        self.nexthops = nexthops
2215        self.from_zebra = from_zebra
2216
2217    @classmethod
2218    def parse_impl(cls, buf, version=_DEFAULT_VERSION, from_zebra=False):
2219        if not from_zebra:
2220            (prefix_len,) = struct.unpack_from(cls._PREFIX_LEN_FMT, buf)
2221            rest = buf[cls.PREFIX_LEN_SIZE:]
2222            prefix = cls.PREFIX_CLS.bin_to_text(rest[:cls.PREFIX_LEN])
2223            return cls('%s/%d' % (prefix, prefix_len), from_zebra=False)
2224
2225        prefix = cls.PREFIX_CLS.bin_to_text(buf[:cls.PREFIX_LEN])
2226        rest = buf[4:]
2227
2228        (metric,) = struct.unpack_from(cls._METRIC_FMT, rest)
2229        rest = rest[cls.METRIC_SIZE:]
2230
2231        nexthops, rest = _parse_nexthops(rest, version)
2232
2233        return cls(prefix, metric, nexthops, from_zebra=True)
2234
2235    @classmethod
2236    def parse(cls, buf, version=_DEFAULT_VERSION):
2237        return cls.parse_impl(buf, version=version, from_zebra=False)
2238
2239    @classmethod
2240    def parse_from_zebra(cls, buf, version=_DEFAULT_VERSION):
2241        return cls.parse_impl(buf, version=version, from_zebra=True)
2242
2243    def serialize(self, version=_DEFAULT_VERSION):
2244        if not self.from_zebra:
2245            if ip.valid_ipv4(self.prefix) or ip.valid_ipv6(self.prefix):
2246                prefix, prefix_len = self.prefix.split('/')
2247                return struct.pack(
2248                    self._PREFIX_LEN_FMT,
2249                    int(prefix_len)) + self.PREFIX_CLS.text_to_bin(prefix)
2250            else:
2251                raise ValueError('Invalid prefix: %s' % self.prefix)
2252
2253        if ip.valid_ipv4(self.prefix) or ip.valid_ipv6(self.prefix):
2254            buf = self.PREFIX_CLS.text_to_bin(self.prefix)
2255        else:
2256            raise ValueError('Invalid prefix: %s' % self.prefix)
2257
2258        buf += struct.pack(self._METRIC_FMT, self.metric)
2259
2260        return buf + _serialize_nexthops(self.nexthops, version=version)
2261
2262
2263@_ZebraMessageBody.register_type(ZEBRA_IPV4_IMPORT_LOOKUP)
2264class ZebraIPv4ImportLookup(_ZebraIPImportLookup):
2265    """
2266    Message body class for ZEBRA_IPV4_IMPORT_LOOKUP.
2267    """
2268    PREFIX_CLS = addrconv.ipv4
2269    PREFIX_LEN = 4
2270
2271
2272@_ZebraMessageBody.register_type(ZEBRA_IPV6_IMPORT_LOOKUP)
2273class ZebraIPv6ImportLookup(_ZebraIPImportLookup):
2274    """
2275    Message body class for ZEBRA_IPV6_IMPORT_LOOKUP.
2276    """
2277    PREFIX_CLS = addrconv.ipv6
2278    PREFIX_LEN = 16
2279
2280
2281# Note: Not implemented in quagga/zebra/zserv.c
2282# @_ZebraMessageBody.register_type(ZEBRA_INTERFACE_RENAME)
2283# class ZebraInterfaceRename(_ZebraMessageBody):
2284
2285
2286@_FrrZebraMessageBody.register_type(FRR_ZEBRA_ROUTER_ID_ADD)
2287@_ZebraMessageBody.register_type(ZEBRA_ROUTER_ID_ADD)
2288class ZebraRouterIDAdd(_ZebraMessageBody):
2289    """
2290    Message body class for ZEBRA_ROUTER_ID_ADD.
2291    """
2292
2293
2294@_FrrZebraMessageBody.register_type(FRR_ZEBRA_ROUTER_ID_DELETE)
2295@_ZebraMessageBody.register_type(ZEBRA_ROUTER_ID_DELETE)
2296class ZebraRouterIDDelete(_ZebraMessageBody):
2297    """
2298    Message body class for ZEBRA_ROUTER_ID_DELETE.
2299    """
2300
2301
2302@_FrrZebraMessageBody.register_type(FRR_ZEBRA_ROUTER_ID_UPDATE)
2303@_ZebraMessageBody.register_type(ZEBRA_ROUTER_ID_UPDATE)
2304class ZebraRouterIDUpdate(_ZebraMessageBody):
2305    """
2306    Message body class for ZEBRA_ROUTER_ID_UPDATE.
2307    """
2308    # Zebra Router ID Update message body:
2309    #  0                   1                   2                   3
2310    #  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
2311    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2312    # | Family        |
2313    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2314    # | IPv4/v6 prefix                                                |
2315    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2316    # | Prefix len    |
2317    # +-+-+-+-+-+-+-+-+
2318
2319    def __init__(self, family, prefix):
2320        super(ZebraRouterIDUpdate, self).__init__()
2321        self.family = family
2322        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
2323            prefix = prefix.prefix
2324        self.prefix = prefix
2325
2326    @classmethod
2327    def parse(cls, buf, version=_DEFAULT_VERSION):
2328        (family, prefix, _) = _parse_zebra_family_prefix(buf)
2329
2330        return cls(family, prefix)
2331
2332    def serialize(self, version=_DEFAULT_VERSION):
2333        (self.family,  # fixup
2334         buf) = _serialize_zebra_family_prefix(self.prefix)
2335
2336        return buf
2337
2338
2339@_FrrZebraMessageBody.register_type(FRR_ZEBRA_HELLO)
2340@_ZebraMessageBody.register_type(ZEBRA_HELLO)
2341class ZebraHello(_ZebraMessageBody):
2342    """
2343    Message body class for ZEBRA_HELLO.
2344    """
2345    # Zebra Hello message body:
2346    #  0                   1                   2                   3
2347    #  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
2348    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2349    # | Route Type    | (Instance): v4(FRRouting)     |
2350    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2351    _HEADER_FMT = '!B'  # route_type
2352    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
2353    _V4_HEADER_FMT = '!BH'  # route_type, instance
2354    V4_HEADER_SIZE = struct.calcsize(_V4_HEADER_FMT)
2355
2356    def __init__(self, route_type, instance=None):
2357        super(ZebraHello, self).__init__()
2358        self.route_type = route_type
2359        self.instance = instance
2360
2361    @classmethod
2362    def parse(cls, buf, version=_DEFAULT_VERSION):
2363        instance = None
2364        if version <= 3:
2365            (route_type,) = struct.unpack_from(cls._HEADER_FMT, buf)
2366        elif version == 4:
2367            (route_type,
2368             instance) = struct.unpack_from(cls._V4_HEADER_FMT, buf)
2369        else:
2370            raise struct.error(
2371                'Unsupported Zebra protocol version: %d'
2372                % version)
2373
2374        return cls(route_type, instance)
2375
2376    def serialize(self, version=_DEFAULT_VERSION):
2377        if version <= 3:
2378            return struct.pack(self._HEADER_FMT, self.route_type)
2379        elif version == 4:
2380            return struct.pack(self._V4_HEADER_FMT,
2381                               self.route_type, self.instance)
2382        else:
2383            raise ValueError(
2384                'Unsupported Zebra protocol version: %d'
2385                % version)
2386
2387
2388@six.add_metaclass(abc.ABCMeta)
2389class _ZebraIPNexthopLookupMRib(_ZebraMessageBody):
2390    """
2391    Base class for ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB (and
2392    ZEBRA_IPV6_NEXTHOP_LOOKUP_MRIB) message body.
2393    """
2394    # Zebra IPv4/v6 Nexthop Lookup MRIB message body:
2395    #  0                   1                   2                   3
2396    #  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
2397    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2398    # | IPv4/v6 address                                               |
2399    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2400    # | Distance      |
2401    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2402    # | Metric                                                        |
2403    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2404    # | Nexthop Num   |
2405    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2406    # | Nexthops (Variable)                                           |
2407    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2408    _DISTANCE_METRIC_FMT = '!BI'  # distance, metric
2409    DISTANCE_METRIC_SIZE = struct.calcsize(_DISTANCE_METRIC_FMT)
2410
2411    # Message type specific constants
2412    ADDR_CLS = None  # either addrconv.ipv4 or addrconv.ipv6
2413    ADDR_LEN = None  # IP address length in bytes
2414
2415    def __init__(self, addr, distance=None, metric=None, nexthops=None):
2416        super(_ZebraIPNexthopLookupMRib, self).__init__()
2417        assert ip.valid_ipv4(addr) or ip.valid_ipv6(addr)
2418        self.addr = addr
2419        self.distance = distance
2420        self.metric = metric
2421        nexthops = nexthops or []
2422        for nexthop in nexthops:
2423            assert isinstance(nexthop, _NextHop)
2424        self.nexthops = nexthops
2425
2426    @classmethod
2427    def parse(cls, buf, version=_DEFAULT_VERSION):
2428        addr = cls.ADDR_CLS.bin_to_text(buf[:cls.ADDR_LEN])
2429        rest = buf[cls.ADDR_LEN:]
2430
2431        if not rest:
2432            return cls(addr)
2433
2434        (distance,
2435         metric) = struct.unpack_from(cls._DISTANCE_METRIC_FMT, rest)
2436        rest = rest[cls.DISTANCE_METRIC_SIZE:]
2437
2438        nexthops, rest = _parse_nexthops(rest, version)
2439
2440        return cls(addr, distance, metric, nexthops)
2441
2442    def serialize(self, version=_DEFAULT_VERSION):
2443        buf = self.ADDR_CLS.text_to_bin(self.addr)
2444
2445        if self.distance is None or self.metric is None:
2446            return buf
2447
2448        buf += struct.pack(
2449            self._DISTANCE_METRIC_FMT, self.distance, self.metric)
2450
2451        return buf + _serialize_nexthops(self.nexthops, version=version)
2452
2453
2454@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB)
2455@_ZebraMessageBody.register_type(ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB)
2456class ZebraIPv4NexthopLookupMRib(_ZebraIPNexthopLookupMRib):
2457    """
2458    Message body class for ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB.
2459    """
2460    ADDR_CLS = addrconv.ipv4
2461    ADDR_LEN = 4
2462
2463
2464@_FrrZebraMessageBody.register_type(FRR_ZEBRA_VRF_UNREGISTER)
2465@_ZebraMessageBody.register_type(ZEBRA_VRF_UNREGISTER)
2466class ZebraVrfUnregister(_ZebraMessageBody):
2467    """
2468    Message body class for ZEBRA_VRF_UNREGISTER.
2469    """
2470
2471
2472@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_LINK_PARAMS)
2473@_ZebraMessageBody.register_type(ZEBRA_INTERFACE_LINK_PARAMS)
2474class ZebraInterfaceLinkParams(_ZebraMessageBody):
2475    """
2476    Message body class for ZEBRA_INTERFACE_LINK_PARAMS.
2477    """
2478    # Zebra Interface Link Parameters message body:
2479    #  0                   1                   2                   3
2480    #  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
2481    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2482    # | Interface Index                                               |
2483    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2484    # | Interface Link Parameters                                     |
2485    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2486    _HEADER_FMT = '!I'  # ifindex
2487    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
2488    # See InterfaceLinkParams class for Interface Link Parameters structure
2489
2490    def __init__(self, ifindex, link_params):
2491        super(ZebraInterfaceLinkParams, self).__init__()
2492        self.ifindex = ifindex
2493        assert isinstance(link_params, InterfaceLinkParams)
2494        self.link_params = link_params
2495
2496    @classmethod
2497    def parse(cls, buf, version=_DEFAULT_VERSION):
2498        (ifindex,) = struct.unpack_from(cls._HEADER_FMT, buf)
2499        rest = buf[cls.HEADER_SIZE:]
2500
2501        link_params, rest = InterfaceLinkParams.parse(rest)
2502
2503        return cls(ifindex, link_params)
2504
2505    def serialize(self, version=_DEFAULT_VERSION):
2506        buf = struct.pack(self._HEADER_FMT, self.ifindex)
2507
2508        return buf + self.link_params.serialize()
2509
2510
2511class _ZebraNexthopRegister(_ZebraMessageBody):
2512    """
2513    Base class for ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER
2514    message body.
2515    """
2516    # Zebra Nexthop Register message body:
2517    # (Repeat of RegisteredNexthop class)
2518
2519    def __init__(self, nexthops):
2520        super(_ZebraNexthopRegister, self).__init__()
2521        nexthops = nexthops or []
2522        for nexthop in nexthops:
2523            assert isinstance(nexthop, RegisteredNexthop)
2524        self.nexthops = nexthops
2525
2526    @classmethod
2527    def parse(cls, buf, version=_DEFAULT_VERSION):
2528        nexthops = []
2529        while buf:
2530            nexthop, buf = RegisteredNexthop.parse(buf)
2531            nexthops.append(nexthop)
2532
2533        return cls(nexthops)
2534
2535    def serialize(self, version=_DEFAULT_VERSION):
2536        buf = b''
2537        for nexthop in self.nexthops:
2538            buf += nexthop.serialize()
2539
2540        return buf
2541
2542
2543@_FrrZebraMessageBody.register_type(FRR_ZEBRA_NEXTHOP_REGISTER)
2544@_ZebraMessageBody.register_type(ZEBRA_NEXTHOP_REGISTER)
2545class ZebraNexthopRegister(_ZebraNexthopRegister):
2546    """
2547    Message body class for ZEBRA_NEXTHOP_REGISTER.
2548    """
2549
2550
2551@_FrrZebraMessageBody.register_type(FRR_ZEBRA_NEXTHOP_UNREGISTER)
2552@_ZebraMessageBody.register_type(ZEBRA_NEXTHOP_UNREGISTER)
2553class ZebraNexthopUnregister(_ZebraNexthopRegister):
2554    """
2555    Message body class for ZEBRA_NEXTHOP_UNREGISTER.
2556    """
2557
2558
2559@_FrrZebraMessageBody.register_type(FRR_ZEBRA_NEXTHOP_UPDATE)
2560@_ZebraMessageBody.register_type(ZEBRA_NEXTHOP_UPDATE)
2561class ZebraNexthopUpdate(_ZebraMessageBody):
2562    """
2563    Message body class for ZEBRA_NEXTHOP_UPDATE.
2564    """
2565    # Zebra IPv4/v6 Nexthop Update message body:
2566    #  0                   1                   2                   3
2567    #  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
2568    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2569    # | Family                        |
2570    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2571    # | IPv4/v6 prefix                                                |
2572    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2573    # | (Distance)    | v4(FRRouting v3.0 or later)
2574    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2575    # | Metric                                                        |
2576    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2577    # | Nexthop Num   |
2578    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2579    # | Nexthops (Variable)                                           |
2580    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2581    _FAMILY_FMT = '!H'  # family
2582    FAMILY_SIZE = struct.calcsize(_FAMILY_FMT)
2583    _DISTANCE_FMT = '!B'  # metric
2584    DISTANCE_SIZE = struct.calcsize(_DISTANCE_FMT)
2585    _METRIC_FMT = '!I'  # metric
2586    METRIC_SIZE = struct.calcsize(_METRIC_FMT)
2587
2588    def __init__(self, family, prefix, distance=None, metric=None,
2589                 nexthops=None):
2590        super(ZebraNexthopUpdate, self).__init__()
2591        self.family = family
2592        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
2593            prefix = prefix.prefix
2594        self.prefix = prefix
2595        if _is_frr_version_ge(_FRR_VERSION_3_0):
2596            assert distance is not None
2597        self.distance = distance
2598        assert metric is not None
2599        self.metric = metric
2600        nexthops = nexthops or []
2601        for nexthop in nexthops:
2602            assert isinstance(nexthop, _NextHop)
2603        self.nexthops = nexthops
2604
2605    @classmethod
2606    def parse(cls, buf, version=_DEFAULT_VERSION):
2607        (family,) = struct.unpack_from(cls._FAMILY_FMT, buf)
2608        rest = buf[cls.FAMILY_SIZE:]
2609
2610        prefix, rest = _parse_ip_prefix(family, rest)
2611
2612        distance = None
2613        if _is_frr_version_ge(_FRR_VERSION_3_0):
2614            (distance,) = struct.unpack_from(cls._DISTANCE_FMT, rest)
2615            rest = rest[cls.DISTANCE_SIZE:]
2616
2617        (metric,) = struct.unpack_from(cls._METRIC_FMT, rest)
2618        rest = rest[cls.METRIC_SIZE:]
2619
2620        nexthops, rest = _parse_nexthops(rest, version)
2621
2622        return cls(family, prefix, distance, metric, nexthops)
2623
2624    def serialize(self, version=_DEFAULT_VERSION):
2625        # fixup
2626        if ip.valid_ipv4(self.prefix):
2627            self.family = socket.AF_INET
2628        elif ip.valid_ipv6(self.prefix):
2629            self.family = socket.AF_INET6
2630        else:
2631            raise ValueError('Invalid prefix: %s' % self.prefix)
2632
2633        buf = struct.pack(self._FAMILY_FMT, self.family)
2634
2635        buf += _serialize_ip_prefix(self.prefix)
2636
2637        if _is_frr_version_ge(_FRR_VERSION_3_0):
2638            buf += struct.pack(self._DISTANCE_FMT, self.distance)
2639
2640        buf += struct.pack(self._METRIC_FMT, self.metric)
2641
2642        return buf + _serialize_nexthops(self.nexthops, version=version)
2643
2644
2645class _ZebraInterfaceNbrAddress(_ZebraMessageBody):
2646    """
2647    Base class for FRR_ZEBRA_INTERFACE_NBR_ADDRESS_* message body.
2648    """
2649    # Zebra Interface Neighbor Address message body:
2650    #  0                   1                   2                   3
2651    #  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
2652    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2653    # | Interface index                                               |
2654    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2655    # | Family        |
2656    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2657    # | IPv4/v6 prefix                                                |
2658    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2659    # | Prefix len    |
2660    # +-+-+-+-+-+-+-+-+
2661    _HEADER_FMT = '!I'  # ifindex
2662    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
2663
2664    def __init__(self, ifindex, family, prefix):
2665        super(_ZebraInterfaceNbrAddress, self).__init__()
2666        self.ifindex = ifindex
2667        self.family = family
2668        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
2669            prefix = prefix.prefix
2670        self.prefix = prefix
2671
2672    @classmethod
2673    def parse(cls, buf, version=_DEFAULT_VERSION):
2674        (ifindex,) = struct.unpack_from(cls._HEADER_FMT, buf)
2675        rest = buf[cls.HEADER_SIZE:]
2676
2677        (family, prefix, _) = _parse_zebra_family_prefix(rest)
2678
2679        return cls(ifindex, family, prefix)
2680
2681    def serialize(self, version=_DEFAULT_VERSION):
2682        (self.family,  # fixup
2683         body_bin) = _serialize_zebra_family_prefix(self.prefix)
2684
2685        return struct.pack(self._HEADER_FMT, self.ifindex) + body_bin
2686
2687
2688@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_NBR_ADDRESS_ADD)
2689class ZebraInterfaceNbrAddressAdd(_ZebraInterfaceNbrAddress):
2690    """
2691    Message body class for FRR_ZEBRA_INTERFACE_NBR_ADDRESS_ADD.
2692    """
2693
2694
2695@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_NBR_ADDRESS_DELETE)
2696class ZebraInterfaceNbrAddressDelete(_ZebraInterfaceNbrAddress):
2697    """
2698    Message body class for FRR_ZEBRA_INTERFACE_NBR_ADDRESS_DELETE.
2699    """
2700
2701
2702@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_BFD_DEST_UPDATE)
2703class ZebraInterfaceBfdDestinationUpdate(_ZebraMessageBody):
2704    """
2705    Message body class for FRR_ZEBRA_INTERFACE_BFD_DEST_UPDATE.
2706    """
2707    # Zebra Interface BFD Destination Update message body:
2708    #  0                   1                   2                   3
2709    #  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
2710    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2711    # | Interface index                                               |
2712    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2713    # | Dst Family    |
2714    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2715    # | Dst IPv4/v6 prefix                                            |
2716    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2717    # | Dst Plen      |
2718    # +-+-+-+-+-+-+-+-+
2719    # | Status        |
2720    # +-+-+-+-+-+-+-+-+
2721    # | Src Family    |
2722    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2723    # | Source IPv4/v6 prefix                                         |
2724    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2725    # | Src Plen      |
2726    # +-+-+-+-+-+-+-+-+
2727    _HEADER_FMT = '!I'  # ifindex
2728    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
2729    _STATUS_FMT = '!B'  # status
2730    STATUS_SIZE = struct.calcsize(_STATUS_FMT)
2731
2732    def __init__(self, ifindex, dst_family, dst_prefix, status,
2733                 src_family, src_prefix):
2734        super(ZebraInterfaceBfdDestinationUpdate, self).__init__()
2735        self.ifindex = ifindex
2736        self.dst_family = dst_family
2737        if isinstance(dst_prefix, (IPv4Prefix, IPv6Prefix)):
2738            dst_prefix = dst_prefix.prefix
2739        self.dst_prefix = dst_prefix
2740        self.status = status
2741        self.src_family = src_family
2742        if isinstance(src_prefix, (IPv4Prefix, IPv6Prefix)):
2743            src_prefix = src_prefix.prefix
2744        self.src_prefix = src_prefix
2745
2746    @classmethod
2747    def parse(cls, buf, version=_DEFAULT_VERSION):
2748        (ifindex,) = struct.unpack_from(cls._HEADER_FMT, buf)
2749        rest = buf[cls.HEADER_SIZE:]
2750
2751        (dst_family, dst_prefix,
2752         rest) = _parse_zebra_family_prefix(rest)
2753
2754        (status,) = struct.unpack_from(cls._STATUS_FMT, rest)
2755        rest = rest[cls.STATUS_SIZE:]
2756
2757        (src_family, src_prefix,
2758         _) = _parse_zebra_family_prefix(rest)
2759
2760        return cls(ifindex, dst_family, dst_prefix, status,
2761                   src_family, src_prefix)
2762
2763    def serialize(self, version=_DEFAULT_VERSION):
2764        (self.dst_family,  # fixup
2765         dst_bin) = _serialize_zebra_family_prefix(self.dst_prefix)
2766
2767        status_bin = struct.pack(
2768            self._STATUS_FMT, self.status)
2769
2770        (self.src_family,  # fixup
2771         src_bin) = _serialize_zebra_family_prefix(self.src_prefix)
2772
2773        return struct.pack(
2774            self._HEADER_FMT,
2775            self.ifindex) + dst_bin + status_bin + src_bin
2776
2777
2778@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IMPORT_ROUTE_REGISTER)
2779class ZebraImportRouteRegister(_ZebraNexthopRegister):
2780    """
2781    Message body class for FRR_ZEBRA_IMPORT_ROUTE_REGISTER.
2782    """
2783
2784
2785@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IMPORT_ROUTE_UNREGISTER)
2786class ZebraImportRouteUnregister(_ZebraNexthopRegister):
2787    """
2788    Message body class for FRR_ZEBRA_IMPORT_ROUTE_UNREGISTER.
2789    """
2790
2791
2792@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IMPORT_CHECK_UPDATE)
2793class ZebraImportCheckUpdate(ZebraNexthopUpdate):
2794    """
2795    Message body class for FRR_ZEBRA_IMPORT_CHECK_UPDATE.
2796    """
2797
2798
2799class _ZebraBfdDestination(_ZebraMessageBody):
2800    """
2801    Base class for FRR_ZEBRA_BFD_DEST_REGISTER and
2802    FRR_ZEBRA_BFD_DEST_UPDATE message body.
2803    """
2804    # Zebra BFD Destination message body:
2805    #  0                   1                   2                   3
2806    #  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
2807    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2808    # | PID                                                           |
2809    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2810    # | Destination Family            |
2811    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2812    # | Destination IPv4/v6 prefix (4 bytes or 16 bytes)              |
2813    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2814    # | Min RX Timer                                                  |
2815    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2816    # | Min TX Timer                                                  |
2817    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2818    # | Detect Mult   |
2819    # +-+-+-+-+-+-+-+-+
2820    # | Multi Hop     |
2821    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2822    # | Source Family                 |
2823    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2824    # | Source IPv4/v6 prefix  (4 bytes or 16 bytes)                  |
2825    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2826    # | (MultiHopCnt) | if Multi Hop enabled
2827    # +-+-+-+-+-+-+-+-+
2828    # | (IFName Len)  | if Multi Hop disabled
2829    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2830    # | (Interface Name (Variable)) if Multi Hop disabled             |
2831    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2832    _HEADER_FMT = '!I'  # pid
2833    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
2834    _FAMILY_FMT = '!H'
2835    FAMILY_SIZE = struct.calcsize(_FAMILY_FMT)
2836    _BODY_FMT = '!IIBB'  # min_rx_timer, min_tx_timer, detect_mult, multi_hop
2837    BODY_SIZE = struct.calcsize(_BODY_FMT)
2838    _FOOTER_FMT = '!B'  # multi_hop_count or ifname_len
2839    FOOTER_SIZE = struct.calcsize(_FOOTER_FMT)
2840
2841    def __init__(self, pid, dst_family, dst_prefix,
2842                 min_rx_timer, min_tx_timer, detect_mult,
2843                 multi_hop, src_family, src_prefix,
2844                 multi_hop_count=None, ifname=None):
2845        super(_ZebraBfdDestination, self).__init__()
2846        self.pid = pid
2847        self.dst_family = dst_family
2848        assert ip.valid_ipv4(dst_prefix) or ip.valid_ipv6(dst_prefix)
2849        self.dst_prefix = dst_prefix
2850        self.min_rx_timer = min_rx_timer
2851        self.min_tx_timer = min_tx_timer
2852        self.detect_mult = detect_mult
2853        self.multi_hop = multi_hop
2854        self.src_family = src_family
2855        assert ip.valid_ipv4(src_prefix) or ip.valid_ipv6(src_prefix)
2856        self.src_prefix = src_prefix
2857        self.multi_hop_count = multi_hop_count
2858        self.ifname = ifname
2859
2860    @classmethod
2861    def _parse_family_prefix(cls, buf):
2862        (family,) = struct.unpack_from(cls._FAMILY_FMT, buf)
2863        rest = buf[cls.FAMILY_SIZE:]
2864
2865        if socket.AF_INET == family:
2866            return family, addrconv.ipv4.bin_to_text(rest[:4]), rest[4:]
2867        elif socket.AF_INET6 == family:
2868            return family, addrconv.ipv6.bin_to_text(rest[:16]), rest[16:]
2869
2870        raise struct.error('Unsupported family: %d' % family)
2871
2872    @classmethod
2873    def parse(cls, buf, version=_DEFAULT_VERSION):
2874        (pid,) = struct.unpack_from(cls._HEADER_FMT, buf)
2875        rest = buf[cls.HEADER_SIZE:]
2876
2877        (dst_family, dst_prefix,
2878         rest) = cls._parse_family_prefix(rest)
2879
2880        (min_rx_timer, min_tx_timer, detect_mult,
2881         multi_hop) = struct.unpack_from(cls._BODY_FMT, rest)
2882        rest = rest[cls.BODY_SIZE:]
2883
2884        (src_family, src_prefix,
2885         rest) = cls._parse_family_prefix(rest)
2886
2887        multi_hop_count = None
2888        ifname = None
2889        if multi_hop:
2890            (multi_hop_count,) = struct.unpack_from(cls._FOOTER_FMT, rest)
2891        else:
2892            (ifname_len,) = struct.unpack_from(cls._FOOTER_FMT, rest)
2893            ifname_bin = rest[cls.FOOTER_SIZE:cls.FOOTER_SIZE + ifname_len]
2894            ifname = str(six.text_type(ifname_bin.strip(b'\x00'), 'ascii'))
2895
2896        return cls(pid, dst_family, dst_prefix,
2897                   min_rx_timer, min_tx_timer, detect_mult,
2898                   multi_hop, src_family, src_prefix,
2899                   multi_hop_count, ifname)
2900
2901    def _serialize_family_prefix(self, prefix):
2902        if ip.valid_ipv4(prefix):
2903            family = socket.AF_INET
2904            return (family,
2905                    struct.pack(self._FAMILY_FMT, family)
2906                    + addrconv.ipv4.text_to_bin(prefix))
2907        elif ip.valid_ipv6(prefix):
2908            family = socket.AF_INET6
2909            return (family,
2910                    struct.pack(self._FAMILY_FMT, family)
2911                    + addrconv.ipv6.text_to_bin(prefix))
2912
2913        raise ValueError('Invalid prefix: %s' % prefix)
2914
2915    def serialize(self, version=_DEFAULT_VERSION):
2916        (self.dst_family,  # fixup
2917         dst_bin) = self._serialize_family_prefix(self.dst_prefix)
2918
2919        body_bin = struct.pack(
2920            self._BODY_FMT,
2921            self.min_rx_timer, self.min_tx_timer, self.detect_mult,
2922            self.multi_hop)
2923
2924        (self.src_family,  # fixup
2925         src_bin) = self._serialize_family_prefix(self.src_prefix)
2926
2927        if self.multi_hop:
2928            footer_bin = struct.pack(
2929                self._FOOTER_FMT, self.multi_hop_count)
2930        else:
2931            ifname_bin = self.ifname.encode('ascii')
2932            footer_bin = struct.pack(
2933                self._FOOTER_FMT, len(ifname_bin)) + ifname_bin
2934
2935        return struct.pack(
2936            self._HEADER_FMT,
2937            self.pid) + dst_bin + body_bin + src_bin + footer_bin
2938
2939
2940@_FrrZebraMessageBody.register_type(FRR_ZEBRA_BFD_DEST_REGISTER)
2941class ZebraBfdDestinationRegister(_ZebraBfdDestination):
2942    """
2943    Message body class for FRR_ZEBRA_BFD_DEST_REGISTER.
2944    """
2945
2946
2947@_FrrZebraMessageBody.register_type(FRR_ZEBRA_BFD_DEST_DEREGISTER)
2948class ZebraBfdDestinationDeregister(_ZebraMessageBody):
2949    """
2950    Message body class for FRR_ZEBRA_BFD_DEST_DEREGISTER.
2951    """
2952    #  0                   1                   2                   3
2953    #  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
2954    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2955    # | PID                                                           |
2956    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2957    # | Family                        |
2958    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2959    # | Destination IPv4/v6 prefix (4 bytes or 16 bytes)              |
2960    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2961    # | Multi Hop     |
2962    # +-+-+-+-+-+-+-+-+
2963    # | Family        |
2964    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2965    # | Source IPv4/v6 prefix  (4 bytes or 16 bytes)                  |
2966    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2967    # | (MultiHopCnt) | if Multi Hop enabled
2968    # +-+-+-+-+-+-+-+-+
2969    # | (IF Name Len) | if Multi Hop disabled
2970    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2971    # | (IF Name (Variable)) if Multi Hop disabled                    |
2972    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2973    _HEADER_FMT = '!I'  # pid
2974    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
2975    _FAMILY_FMT = '!H'
2976    FAMILY_SIZE = struct.calcsize(_FAMILY_FMT)
2977    _BODY_FMT = '!B'  # multi_hop
2978    BODY_SIZE = struct.calcsize(_BODY_FMT)
2979    _FOOTER_FMT = '!B'  # multi_hop_count or ifname_len
2980    FOOTER_SIZE = struct.calcsize(_FOOTER_FMT)
2981
2982    def __init__(self, pid, dst_family, dst_prefix,
2983                 multi_hop, src_family, src_prefix,
2984                 multi_hop_count=None, ifname=None):
2985        super(ZebraBfdDestinationDeregister, self).__init__()
2986        self.pid = pid
2987        self.dst_family = dst_family
2988        assert ip.valid_ipv4(dst_prefix) or ip.valid_ipv6(dst_prefix)
2989        self.dst_prefix = dst_prefix
2990        self.multi_hop = multi_hop
2991        self.src_family = src_family
2992        assert ip.valid_ipv4(src_prefix) or ip.valid_ipv6(src_prefix)
2993        self.src_prefix = src_prefix
2994        self.multi_hop_count = multi_hop_count
2995        self.ifname = ifname
2996
2997    @classmethod
2998    def _parse_family_prefix(cls, buf):
2999        (family,) = struct.unpack_from(cls._FAMILY_FMT, buf)
3000        rest = buf[cls.FAMILY_SIZE:]
3001
3002        if socket.AF_INET == family:
3003            return family, addrconv.ipv4.bin_to_text(rest[:4]), rest[4:]
3004        elif socket.AF_INET6 == family:
3005            return family, addrconv.ipv6.bin_to_text(rest[:16]), rest[16:]
3006
3007        raise struct.error('Unsupported family: %d' % family)
3008
3009    @classmethod
3010    def parse(cls, buf, version=_DEFAULT_VERSION):
3011        (pid,) = struct.unpack_from(cls._HEADER_FMT, buf)
3012        rest = buf[cls.HEADER_SIZE:]
3013
3014        (dst_family, dst_prefix,
3015         rest) = cls._parse_family_prefix(rest)
3016
3017        (multi_hop,) = struct.unpack_from(cls._BODY_FMT, rest)
3018        rest = rest[cls.BODY_SIZE:]
3019
3020        (src_family, src_prefix,
3021         rest) = cls._parse_family_prefix(rest)
3022
3023        multi_hop_count = None
3024        ifname = None
3025        if multi_hop:
3026            (multi_hop_count,) = struct.unpack_from(cls._FOOTER_FMT, rest)
3027        else:
3028            (ifname_len,) = struct.unpack_from(cls._FOOTER_FMT, rest)
3029            ifname_bin = rest[cls.FOOTER_SIZE:cls.FOOTER_SIZE + ifname_len]
3030            ifname = str(six.text_type(ifname_bin.strip(b'\x00'), 'ascii'))
3031
3032        return cls(pid, dst_family, dst_prefix,
3033                   multi_hop, src_family, src_prefix,
3034                   multi_hop_count, ifname)
3035
3036    def _serialize_family_prefix(self, prefix):
3037        if ip.valid_ipv4(prefix):
3038            family = socket.AF_INET
3039            return (family,
3040                    struct.pack(self._FAMILY_FMT, family)
3041                    + addrconv.ipv4.text_to_bin(prefix))
3042        elif ip.valid_ipv6(prefix):
3043            family = socket.AF_INET6
3044            return (family,
3045                    struct.pack(self._FAMILY_FMT, family)
3046                    + addrconv.ipv6.text_to_bin(prefix))
3047
3048        raise ValueError('Invalid prefix: %s' % prefix)
3049
3050    def serialize(self, version=_DEFAULT_VERSION):
3051        (self.dst_family,  # fixup
3052         dst_bin) = self._serialize_family_prefix(self.dst_prefix)
3053
3054        body_bin = struct.pack(self._BODY_FMT, self.multi_hop)
3055
3056        (self.src_family,  # fixup
3057         src_bin) = self._serialize_family_prefix(self.src_prefix)
3058
3059        if self.multi_hop:
3060            footer_bin = struct.pack(
3061                self._FOOTER_FMT, self.multi_hop_count)
3062        else:
3063            ifname_bin = self.ifname.encode('ascii')
3064            footer_bin = struct.pack(
3065                self._FOOTER_FMT, len(ifname_bin)) + ifname_bin
3066
3067        return struct.pack(
3068            self._HEADER_FMT,
3069            self.pid) + dst_bin + body_bin + src_bin + footer_bin
3070
3071
3072@_FrrZebraMessageBody.register_type(FRR_ZEBRA_BFD_DEST_UPDATE)
3073class ZebraBfdDestinationUpdate(_ZebraBfdDestination):
3074    """
3075    Message body class for FRR_ZEBRA_BFD_DEST_UPDATE.
3076    """
3077
3078
3079@_FrrZebraMessageBody.register_type(FRR_ZEBRA_BFD_DEST_REPLAY)
3080class ZebraBfdDestinationReply(_ZebraMessageBody):
3081    """
3082    Message body class for FRR_ZEBRA_BFD_DEST_REPLAY.
3083    """
3084
3085
3086class _ZebraRedistributeIPv4(_ZebraIPRoute):
3087    """
3088    Base class for FRR_ZEBRA_REDISTRIBUTE_IPV4_* message body.
3089    """
3090    _FAMILY = socket.AF_INET
3091
3092
3093@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_IPV4_ADD)
3094class ZebraRedistributeIPv4Add(_ZebraRedistributeIPv4):
3095    """
3096    Message body class for FRR_ZEBRA_IPV4_ROUTE_ADD.
3097    """
3098
3099
3100@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_IPV4_DEL)
3101class ZebraRedistributeIPv4Delete(_ZebraRedistributeIPv4):
3102    """
3103    Message body class for FRR_ZEBRA_IPV4_ROUTE_DELETE.
3104    """
3105
3106
3107class _ZebraRedistributeIPv6(_ZebraIPRoute):
3108    """
3109    Base class for FRR_ZEBRA_REDISTRIBUTE_IPV6_* message body.
3110    """
3111    _FAMILY = socket.AF_INET6
3112
3113
3114@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_IPV6_ADD)
3115class ZebraRedistributeIPv6Add(_ZebraRedistributeIPv6):
3116    """
3117    Message body class for FRR_ZEBRA_REDISTRIBUTE_IPV6_ADD.
3118    """
3119
3120
3121@_FrrZebraMessageBody.register_type(FRR_ZEBRA_REDISTRIBUTE_IPV6_DEL)
3122class ZebraRedistributeIPv6Delete(_ZebraRedistributeIPv6):
3123    """
3124    Message body class for FRR_ZEBRA_REDISTRIBUTE_IPV6_DEL.
3125    """
3126
3127
3128class _ZebraVrf(_ZebraMessageBody):
3129    """
3130    Base class for FRR_ZEBRA_VRF_ADD and FRR_ZEBRA_VRF_DELETE message body.
3131    """
3132    # Zebra VRF Add/Delete message body:
3133    #  0                   1                   2                   3
3134    #  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
3135    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3136    # | VRF Name (VRF_NAMSIZ bytes length)                            |
3137    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3138    _HEADER_FMT = '!%ds' % VRF_NAMSIZ
3139
3140    def __init__(self, vrf_name):
3141        super(_ZebraVrf, self).__init__()
3142        self.vrf_name = vrf_name
3143
3144    @classmethod
3145    def parse(cls, buf, version=_DEFAULT_FRR_VERSION):
3146        vrf_name_bin = buf[:VRF_NAMSIZ]
3147        vrf_name = str(six.text_type(vrf_name_bin.strip(b'\x00'), 'ascii'))
3148
3149        return cls(vrf_name)
3150
3151    def serialize(self, version=_DEFAULT_FRR_VERSION):
3152        return struct.pack(self._HEADER_FMT, self.vrf_name.encode('ascii'))
3153
3154
3155@_FrrZebraMessageBody.register_type(FRR_ZEBRA_VRF_ADD)
3156class ZebraVrfAdd(_ZebraVrf):
3157    """
3158    Message body class for FRR_ZEBRA_VRF_ADD.
3159    """
3160
3161
3162@_FrrZebraMessageBody.register_type(FRR_ZEBRA_VRF_DELETE)
3163class ZebraVrfDelete(_ZebraVrf):
3164    """
3165    Message body class for FRR_ZEBRA_VRF_DELETE.
3166    """
3167
3168
3169@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_VRF_UPDATE)
3170class ZebraInterfaceVrfUpdate(_ZebraMessageBody):
3171    """
3172    Message body class for FRR_ZEBRA_INTERFACE_VRF_UPDATE.
3173    """
3174    # Zebra Interface VRF Update message body:
3175    #  0                   1                   2                   3
3176    #  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
3177    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3178    # | Interface Index                                               |
3179    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3180    # | VRF ID                        |
3181    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3182    _HEADER_FMT = '!IH'  # ifindex, vrf_id
3183
3184    def __init__(self, ifindex, vrf_id):
3185        super(ZebraInterfaceVrfUpdate, self).__init__()
3186        self.ifindex = ifindex
3187        self.vrf_id = vrf_id
3188
3189    @classmethod
3190    def parse(cls, buf, version=_DEFAULT_FRR_VERSION):
3191        (ifindex, vrf_id) = struct.unpack_from(cls._HEADER_FMT, buf)
3192
3193        return cls(ifindex, vrf_id)
3194
3195    def serialize(self, version=_DEFAULT_FRR_VERSION):
3196        return struct.pack(self._HEADER_FMT, self.ifindex, self.vrf_id)
3197
3198
3199class _ZebraBfdClient(_ZebraMessageBody):
3200    """
3201    Base class for FRR_ZEBRA_BFD_CLIENT_*.
3202    """
3203    # Zebra BFD Client message body:
3204    #  0                   1                   2                   3
3205    #  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
3206    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3207    # | PID                                                           |
3208    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3209    _HEADER_FMT = '!I'  # pid
3210    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
3211
3212    def __init__(self, pid):
3213        super(_ZebraBfdClient, self).__init__()
3214        self.pid = pid
3215
3216    @classmethod
3217    def parse(cls, buf, version=_DEFAULT_FRR_VERSION):
3218        (pid,) = struct.unpack_from(cls._HEADER_FMT, buf)
3219
3220        return cls(pid)
3221
3222    def serialize(self, version=_DEFAULT_FRR_VERSION):
3223        return struct.pack(self._HEADER_FMT, self.pid)
3224
3225
3226@_FrrZebraMessageBody.register_type(FRR_ZEBRA_BFD_CLIENT_REGISTER)
3227class ZebraBfdClientRegister(_ZebraBfdClient):
3228    """
3229    Message body class for FRR_ZEBRA_BFD_CLIENT_REGISTER.
3230    """
3231
3232
3233class _ZebraInterfaceRadv(_ZebraMessageBody):
3234    """
3235    Base class for FRR_ZEBRA_INTERFACE_*_RADV message body.
3236    """
3237    # Zebra interface Router Advertisement message body:
3238    #  0                   1                   2                   3
3239    #  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
3240    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3241    # | Interface Index                                               |
3242    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3243    # | RA Interval                                                   |
3244    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3245    _HEADER_FMT = '!II'  # ifindex, interval
3246    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
3247
3248    def __init__(self, ifindex, interval):
3249        super(_ZebraInterfaceRadv, self).__init__()
3250        self.ifindex = ifindex
3251        self.interval = interval
3252
3253    @classmethod
3254    def parse(cls, buf, version=_DEFAULT_FRR_VERSION):
3255        (ifindex, interval,) = struct.unpack_from(cls._HEADER_FMT, buf)
3256
3257        return cls(ifindex, interval)
3258
3259    def serialize(self, version=_DEFAULT_FRR_VERSION):
3260        return struct.pack(self._HEADER_FMT, self.ifindex, self.interval)
3261
3262
3263@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_ENABLE_RADV)
3264class ZebraInterfaceEnableRadv(_ZebraInterfaceRadv):
3265    """
3266    Message body class for FRR_ZEBRA_INTERFACE_ENABLE_RADV.
3267    """
3268
3269
3270@_FrrZebraMessageBody.register_type(FRR_ZEBRA_INTERFACE_DISABLE_RADV)
3271class ZebraInterfaceDisableRadv(_ZebraInterfaceRadv):
3272    """
3273    Message body class for FRR_ZEBRA_INTERFACE_DISABLE_RADV.
3274    """
3275
3276
3277class _ZebraMplsLabels(_ZebraMessageBody):
3278    """
3279    Base class for ZEBRA_MPLS_LABELS_* message body.
3280    """
3281    # Zebra MPLS Labels message body:
3282    #  0                   1                   2                   3
3283    #  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
3284    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3285    # | Route Type    |
3286    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3287    # | Family                                                        |
3288    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3289    # | IPv4/v6 Prefix (4 bytes/16 bytes)                             |
3290    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3291    # | Prefix Len    |
3292    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3293    # | Gate IPv4/v6 Address (4 bytes/16 bytes)                       |
3294    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3295    # | Interface Index: v4(FRRouting v3.0 or later)                  |
3296    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3297    # | Distance      |
3298    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3299    # | In Label                                                      |
3300    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3301    # | Out Label                                                     |
3302    # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3303    _HEADER_FMT = '!B'  # route_type
3304    HEADER_SIZE = struct.calcsize(_HEADER_FMT)
3305    _FAMILY_FMT = '!I'
3306    FAMILY_SIZE = struct.calcsize(_FAMILY_FMT)
3307    _IPV4_PREFIX_FMT = '!4sB'  # prefix, prefix_len
3308    _IPV6_PREFIX_FMT = '!16sB'
3309    IPV4_PREFIX_SIZE = struct.calcsize(_IPV4_PREFIX_FMT)
3310    IPV6_PREFIX_SIZE = struct.calcsize(_IPV6_PREFIX_FMT)
3311    _FAMILY_IPV4_PREFIX_FMT = '!I4sB'
3312    _FAMILY_IPV6_PREFIX_FMT = '!I16sB'
3313    _IFINDEX_FMT = '!I'
3314    IFINDEX_SIZE = struct.calcsize(_IFINDEX_FMT)
3315    _BODY_FMT = '!BII'  # distance, in_label, out_label
3316
3317    def __init__(self, route_type, family, prefix, gate_addr, ifindex=None,
3318                 distance=None, in_label=None, out_label=None):
3319        super(_ZebraMplsLabels, self).__init__()
3320        self.route_type = route_type
3321        self.family = family
3322        if isinstance(prefix, (IPv4Prefix, IPv6Prefix)):
3323            prefix = prefix.prefix
3324        self.prefix = prefix
3325        assert ip.valid_ipv4(gate_addr) or ip.valid_ipv6(gate_addr)
3326        self.gate_addr = gate_addr
3327        if _is_frr_version_ge(_FRR_VERSION_3_0):
3328            assert ifindex is not None
3329        self.ifindex = ifindex
3330        assert distance is not None
3331        self.distance = distance
3332        assert in_label is not None
3333        self.in_label = in_label
3334        assert out_label is not None
3335        self.out_label = out_label
3336
3337    @classmethod
3338    def _parse_family_prefix(cls, buf):
3339        (family,) = struct.unpack_from(cls._FAMILY_FMT, buf)
3340        rest = buf[cls.FAMILY_SIZE:]
3341
3342        if socket.AF_INET == family:
3343            (prefix, p_len) = struct.unpack_from(cls._IPV4_PREFIX_FMT, rest)
3344            prefix = '%s/%d' % (addrconv.ipv4.bin_to_text(prefix), p_len)
3345            rest = rest[cls.IPV4_PREFIX_SIZE:]
3346        elif socket.AF_INET6 == family:
3347            (prefix, p_len) = struct.unpack_from(cls._IPV6_PREFIX_FMT, rest)
3348            prefix = '%s/%d' % (addrconv.ipv6.bin_to_text(prefix), p_len)
3349            rest = rest[cls.IPV6_PREFIX_SIZE:]
3350        else:
3351            raise struct.error('Unsupported family: %d' % family)
3352
3353        return family, prefix, rest
3354
3355    @classmethod
3356    def parse(cls, buf, version=_DEFAULT_FRR_VERSION):
3357        (route_type,) = struct.unpack_from(cls._HEADER_FMT, buf)
3358        rest = buf[cls.HEADER_SIZE:]
3359
3360        (family, prefix, rest) = cls._parse_family_prefix(rest)
3361
3362        if family == socket.AF_INET:
3363            gate_addr = addrconv.ipv4.bin_to_text(rest[:4])
3364            rest = rest[4:]
3365        elif family == socket.AF_INET6:
3366            gate_addr = addrconv.ipv6.bin_to_text(rest[:16])
3367            rest = rest[16:]
3368        else:
3369            raise struct.error('Unsupported family: %d' % family)
3370
3371        ifindex = None
3372        if _is_frr_version_ge(_FRR_VERSION_3_0):
3373            (ifindex,) = struct.unpack_from(cls._IFINDEX_FMT, rest)
3374            rest = rest[cls.IFINDEX_SIZE:]
3375
3376        (distance, in_label,
3377         out_label) = struct.unpack_from(cls._BODY_FMT, rest)
3378
3379        return cls(route_type, family, prefix, gate_addr, ifindex,
3380                   distance, in_label, out_label)
3381
3382    def _serialize_family_prefix(self, prefix):
3383        if ip.valid_ipv4(prefix):
3384            family = socket.AF_INET  # fixup
3385            prefix_addr, prefix_num = prefix.split('/')
3386            return family, struct.pack(
3387                self._FAMILY_IPV4_PREFIX_FMT,
3388                family,
3389                addrconv.ipv4.text_to_bin(prefix_addr),
3390                int(prefix_num))
3391        elif ip.valid_ipv6(prefix):
3392            family = socket.AF_INET6  # fixup
3393            prefix_addr, prefix_num = prefix.split('/')
3394            return family, struct.pack(
3395                self._FAMILY_IPV6_PREFIX_FMT,
3396                family,
3397                addrconv.ipv6.text_to_bin(prefix_addr),
3398                int(prefix_num))
3399
3400        raise ValueError('Invalid prefix: %s' % prefix)
3401
3402    def serialize(self, version=_DEFAULT_FRR_VERSION):
3403        (self.family,  # fixup
3404         prefix_bin) = self._serialize_family_prefix(self.prefix)
3405
3406        if self.family == socket.AF_INET:
3407            gate_addr_bin = addrconv.ipv4.text_to_bin(self.gate_addr)
3408        elif self.family == socket.AF_INET6:
3409            gate_addr_bin = addrconv.ipv6.text_to_bin(self.gate_addr)
3410        else:
3411            raise ValueError('Unsupported family: %d' % self.family)
3412
3413        body_bin = b''
3414        if _is_frr_version_ge(_FRR_VERSION_3_0):
3415            body_bin = struct.pack(self._IFINDEX_FMT, self.ifindex)
3416
3417        body_bin += struct.pack(
3418            self._BODY_FMT, self.distance, self.in_label, self.out_label)
3419
3420        return struct.pack(
3421            self._HEADER_FMT,
3422            self.route_type) + prefix_bin + gate_addr_bin + body_bin
3423
3424
3425@_FrrZebraMessageBody.register_type(FRR_ZEBRA_MPLS_LABELS_ADD)
3426class ZebraMplsLabelsAdd(_ZebraMplsLabels):
3427    """
3428    Message body class for FRR_ZEBRA_MPLS_LABELS_ADD.
3429    """
3430
3431
3432@_FrrZebraMessageBody.register_type(FRR_ZEBRA_MPLS_LABELS_DELETE)
3433class ZebraMplsLabelsDelete(_ZebraMplsLabels):
3434    """
3435    Message body class for FRR_ZEBRA_MPLS_LABELS_DELETE.
3436    """
3437
3438
3439class _ZebraIPv4Nexthop(_ZebraIPRoute):
3440    """
3441    Base class for FRR_ZEBRA_IPV4_NEXTHOP_* message body.
3442    """
3443    _FAMILY = socket.AF_INET
3444
3445
3446@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV4_NEXTHOP_ADD)
3447class ZebraIPv4NexthopAdd(_ZebraIPv4Nexthop):
3448    """
3449    Message body class for FRR_ZEBRA_IPV4_NEXTHOP_ADD.
3450    """
3451
3452
3453@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV4_NEXTHOP_DELETE)
3454class ZebraIPv4NexthopDelete(_ZebraIPv4Nexthop):
3455    """
3456    Message body class for FRR_ZEBRA_IPV4_NEXTHOP_DELETE.
3457    """
3458
3459
3460class _ZebraIPv6Nexthop(_ZebraIPRoute):
3461    """
3462    Base class for FRR_ZEBRA_IPV6_NEXTHOP_* message body.
3463    """
3464    _FAMILY = socket.AF_INET6
3465
3466
3467@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV6_NEXTHOP_ADD)
3468class ZebraIPv6NexthopAdd(_ZebraIPv6Nexthop):
3469    """
3470    Message body class for FRR_ZEBRA_IPV6_NEXTHOP_ADD.
3471    """
3472
3473
3474@_FrrZebraMessageBody.register_type(FRR_ZEBRA_IPV6_NEXTHOP_DELETE)
3475class ZebraIPv6NexthopDelete(_ZebraIPv6Nexthop):
3476    """
3477    Message body class for FRR_ZEBRA_IPV6_NEXTHOP_DELETE.
3478    """
3479