mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-27 08:32:12 +00:00
commit
7cfaf4b339
@ -147,6 +147,7 @@ include staticd/subdir.am
|
||||
include bfdd/subdir.am
|
||||
include yang/subdir.am
|
||||
include yang/libyang_plugins/subdir.am
|
||||
include vrrpd/subdir.am
|
||||
|
||||
include vtysh/subdir.am
|
||||
include tests/subdir.am
|
||||
@ -188,7 +189,6 @@ EXTRA_DIST += \
|
||||
snapcraft/defaults \
|
||||
snapcraft/helpers \
|
||||
snapcraft/snap \
|
||||
\
|
||||
babeld/Makefile \
|
||||
bgpd/Makefile \
|
||||
bgpd/rfp-example/librfp/Makefile \
|
||||
@ -218,6 +218,7 @@ EXTRA_DIST += \
|
||||
vtysh/Makefile \
|
||||
watchfrr/Makefile \
|
||||
zebra/Makefile \
|
||||
vrrpd/Makefile \
|
||||
# end
|
||||
|
||||
noinst_HEADERS += defaults.h
|
||||
|
@ -443,6 +443,8 @@ AC_ARG_ENABLE([fabricd],
|
||||
AS_HELP_STRING([--disable-fabricd], [do not build fabricd]))
|
||||
AC_ARG_ENABLE([bgp-announce],
|
||||
AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))
|
||||
AC_ARG_ENABLE([vrrpd],
|
||||
AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd]))
|
||||
AC_ARG_ENABLE([bgp-vnc],
|
||||
AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support]))
|
||||
AC_ARG_ENABLE([snmp],
|
||||
@ -1602,6 +1604,7 @@ AM_CONDITIONAL([PBRD], [test "${enable_pbrd}" != "no"])
|
||||
AM_CONDITIONAL([SHARPD], [test "${enable_sharpd}" = "yes"])
|
||||
AM_CONDITIONAL([STATICD], [test "${enable_staticd}" != "no"])
|
||||
AM_CONDITIONAL([FABRICD], [test "${enable_fabricd}" != "no"])
|
||||
AM_CONDITIONAL([VRRPD], [test "${enable_vrrpd}" != "no"])
|
||||
|
||||
if test "${enable_bgp_announce}" = "no";then
|
||||
AC_DEFINE([DISABLE_BGP_ANNOUNCE], [1], [Disable BGP installation to zebra])
|
||||
|
@ -80,6 +80,9 @@ IP
|
||||
iptables
|
||||
ipv
|
||||
IPv
|
||||
IPvX
|
||||
IPv4
|
||||
IPv6
|
||||
isis
|
||||
isisd
|
||||
lan
|
||||
@ -99,6 +102,8 @@ LSAs
|
||||
Masaki
|
||||
Mbit
|
||||
Mbits
|
||||
macvlan
|
||||
macvlans
|
||||
mib
|
||||
motd
|
||||
mpls
|
||||
@ -227,6 +232,7 @@ VN
|
||||
VNC
|
||||
vrf
|
||||
vrfs
|
||||
vrrp
|
||||
vty
|
||||
Vty
|
||||
vtysh
|
||||
|
@ -126,6 +126,7 @@ These following options control the daemon's VTY (interactive command line) inte
|
||||
staticd 2616
|
||||
bfdd 2617
|
||||
fabricd 2618
|
||||
vrrpd 2619
|
||||
|
||||
Port 2607 is used for ospfd's Opaque LSA API.
|
||||
|
||||
|
@ -334,6 +334,7 @@ man_pages = [
|
||||
('frr', 'frr', 'a systemd interaction script', [], 1),
|
||||
('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8),
|
||||
('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8),
|
||||
('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8),
|
||||
]
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
@ -1,3 +1,3 @@
|
||||
.. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
|
||||
.. |synopsis-options-hv| replace:: [-h] [-v]
|
||||
.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), mtracebis(8)
|
||||
.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8)
|
||||
|
@ -26,4 +26,5 @@
|
||||
watchfrr
|
||||
zebra
|
||||
vtysh
|
||||
vrrpd
|
||||
frr
|
||||
|
@ -30,6 +30,7 @@ man_RSTFILES = \
|
||||
doc/manpages/zebra.rst \
|
||||
doc/manpages/bfdd.rst \
|
||||
doc/manpages/bfd-options.rst \
|
||||
doc/manpages/vrrpd.rst \
|
||||
# end
|
||||
|
||||
EXTRA_DIST += $(man_RSTFILES)
|
||||
|
40
doc/manpages/vrrpd.rst
Normal file
40
doc/manpages/vrrpd.rst
Normal file
@ -0,0 +1,40 @@
|
||||
*****
|
||||
VRRPD
|
||||
*****
|
||||
|
||||
.. include:: defines.rst
|
||||
.. |DAEMON| replace:: vrrpd
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|DAEMON| |synopsis-options-hv|
|
||||
|
||||
|DAEMON| |synopsis-options|
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|DAEMON| is a routing component that works with the FRRouting routing engine.
|
||||
It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2
|
||||
and VRRPv3 is present.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
OPTIONS available for the |DAEMON| command:
|
||||
|
||||
.. include:: common-options.rst
|
||||
|
||||
FILES
|
||||
=====
|
||||
|
||||
|INSTALL_PREFIX_SBIN|/|DAEMON|
|
||||
The default location of the |DAEMON| binary.
|
||||
|
||||
|INSTALL_PREFIX_ETC|/|DAEMON|.conf
|
||||
The default location of the |DAEMON| config file.
|
||||
|
||||
$(PWD)/|DAEMON|.log
|
||||
If the |DAEMON| process is configured to output logs to a file, then you
|
||||
will find this file in the directory where you started |DAEMON|.
|
||||
|
||||
.. include:: epilogue.rst
|
||||
|
@ -56,6 +56,7 @@ Protocols
|
||||
sharp
|
||||
static
|
||||
vnc
|
||||
vrrp
|
||||
|
||||
########
|
||||
Appendix
|
||||
|
@ -37,6 +37,7 @@ user_RSTFILES = \
|
||||
doc/user/snmptrap.rst \
|
||||
doc/user/static.rst \
|
||||
doc/user/vnc.rst \
|
||||
doc/user/vrrp.rst \
|
||||
doc/user/vtysh.rst \
|
||||
doc/user/zebra.rst \
|
||||
doc/user/bfd.rst \
|
||||
|
506
doc/user/vrrp.rst
Normal file
506
doc/user/vrrp.rst
Normal file
@ -0,0 +1,506 @@
|
||||
.. _vrrp:
|
||||
|
||||
****
|
||||
VRRP
|
||||
****
|
||||
|
||||
:abbr:`VRRP` stands for Virtual Router Redundancy Protocol. This protocol is
|
||||
used to allow multiple backup routers on the same segment to take over
|
||||
operation of each others' IP addresses if the primary router fails. This is
|
||||
typically used to provide fault-tolerant gateways to hosts on the segment.
|
||||
|
||||
FRR implements VRRPv2 (:rfc:`3768`) and VRRPv3 (:rfc:`5798`). For VRRPv2, no
|
||||
authentication methods are supported; these are deprecated in the VRRPv2
|
||||
specification as they do not provide any additional security over the base
|
||||
protocol.
|
||||
|
||||
.. note::
|
||||
|
||||
- VRRP is supported on Linux 5.1+
|
||||
- VRRP does not implement Accept_Mode
|
||||
|
||||
.. _vrrp-starting:
|
||||
|
||||
Starting VRRP
|
||||
=============
|
||||
|
||||
The configuration file for *vrrpd* is :file:`vrrpd.conf`. The typical location
|
||||
of :file:`vrrpd.conf` is |INSTALL_PREFIX_ETC|/vrrpd.conf.
|
||||
|
||||
If using integrated config, then :file:`vrrpd.conf` need not be present and
|
||||
:file:`frr.conf` is read instead.
|
||||
|
||||
.. program:: vrrpd
|
||||
|
||||
:abbr:`VRRP` supports all the common FRR daemon start options which are
|
||||
documented elsewhere.
|
||||
|
||||
.. _vrrp-protocol-overview:
|
||||
|
||||
Protocol Overview
|
||||
=================
|
||||
|
||||
From :rfc:`5798`:
|
||||
|
||||
VRRP specifies an election protocol that dynamically assigns responsibility
|
||||
for a virtual router to one of the VRRP routers on a LAN. The VRRP router
|
||||
controlling the IPv4 or IPv6 address(es) associated with a virtual router is
|
||||
called the Master, and it forwards packets sent to these IPv4 or IPv6
|
||||
addresses. VRRP Master routers are configured with virtual IPv4 or IPv6
|
||||
addresses, and VRRP Backup routers infer the address family of the virtual
|
||||
addresses being carried based on the transport protocol. Within a VRRP
|
||||
router, the virtual routers in each of the IPv4 and IPv6 address families
|
||||
are a domain unto themselves and do not overlap. The election process
|
||||
provides dynamic failover in the forwarding responsibility should the Master
|
||||
become unavailable. For IPv4, the advantage gained from using VRRP is a
|
||||
higher-availability default path without requiring configuration of dynamic
|
||||
routing or router discovery protocols on every end-host. For IPv6, the
|
||||
advantage gained from using VRRP for IPv6 is a quicker switchover to Backup
|
||||
routers than can be obtained with standard IPv6 Neighbor Discovery
|
||||
mechanisms.
|
||||
|
||||
VRRP accomplishes these goals primarily by using a virtual MAC address shared
|
||||
between the physical routers participating in a VRRP virtual router. This
|
||||
reduces churn in the neighbor tables of hosts and downstream switches and makes
|
||||
router failover theoretically transparent to these devices.
|
||||
|
||||
FRR implements the election protocol and handles changing the operating system
|
||||
interface configuration in response to protocol state changes.
|
||||
|
||||
As a consequence of the shared virtual MAC requirement, VRRP is currently
|
||||
supported only on Linux, as Linux is the only operating system that provides
|
||||
the necessary features in its network stack to make implementing this protocol
|
||||
feasible.
|
||||
|
||||
When a VRRP router is acting as the Master router, FRR allows the interface(s)
|
||||
with the backed-up IP addresses to remain up and functional. When the router
|
||||
transitions to Backup state, these interfaces are set into ``protodown`` mode.
|
||||
This is an interface mode that is functionally equivalent to ``NO-CARRIER``.
|
||||
Physical drivers typically use this state indication to drop traffic on an
|
||||
interface. In the case of VRRP, the interfaces in question are macvlan devices,
|
||||
which are virtual interfaces. Since the IP addresses managed by VRRP are on
|
||||
these interfaces, this has the same effect as removing these addresses from the
|
||||
interface, but is implemented as a state flag.
|
||||
|
||||
.. _vrrp-configuration:
|
||||
|
||||
Configuring VRRP
|
||||
================
|
||||
|
||||
VRRP is configured on a per-interface basis, with some global defaults
|
||||
accessible outside the interface context.
|
||||
|
||||
.. _vrrp-system-configuration:
|
||||
|
||||
System Configuration
|
||||
--------------------
|
||||
|
||||
FRR's VRRP implementation uses Linux macvlan devices to to implement the shared
|
||||
virtual MAC feature of the protocol. Currently, it does not create those system
|
||||
interfaces - they must be configured outside of FRR before VRRP can be enabled
|
||||
on them.
|
||||
|
||||
Each interface on which VRRP will be enabled must have at least one macvlan
|
||||
device configured with the virtual MAC and placed in the proper operation mode.
|
||||
The addresses backed up by VRRP are assigned to these interfaces.
|
||||
|
||||
Suppose you have an interface ``eth0`` with the following configuration:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ip link show eth0
|
||||
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
|
||||
link/ether 02:17:45:00:aa:aa brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
|
||||
valid_lft 72532sec preferred_lft 72532sec
|
||||
inet 10.0.2.16/24 brd 10.0.2.255 scope global dynamic eth0
|
||||
valid_lft 72532sec preferred_lft 72532sec
|
||||
inet6 fe80::17:45ff:fe00:aaaa/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
Suppose the address you want to back up is ``10.0.2.16``, and will be managed
|
||||
by the virtual router with id ``5``. A macvlan device with the appropriate MAC
|
||||
address must be created before VRRP can begin to operate.
|
||||
|
||||
If you are using ``ifupdown2``, the configuration is as follows:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
iface eth0
|
||||
...
|
||||
vrrp 5 10.0.2.16/24 2001:0db8::0370:7334/64
|
||||
|
||||
Applying this configuration with ``ifreload -a`` will create the appropriate
|
||||
macvlan device. If you are using ``iproute2``, the equivalent configuration is:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
ip link add vrrp4-2-1 link eth0 addrgenmode random type macvlan mode bridge
|
||||
ip link set dev vrrp4-2-1 address 00:00:5e:00:01:05
|
||||
ip addr add 10.0.2.16/24 dev vrrp4-2-1
|
||||
ip link set dev vrrp4-2-1 up
|
||||
|
||||
ip link add vrrp6-2-1 link eth0 addrgenmode random type macvlan mode bridge
|
||||
ip link set dev vrrp4-2-1 address 00:00:5e:00:02:05
|
||||
ip addr add 2001:db8::370:7334/64 dev vrrp6-2-1
|
||||
ip link set dev vrrp6-2-1 up
|
||||
|
||||
In either case, the created interfaces will look like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ip addr show vrrp4-2-1
|
||||
5: vrrp4-2-1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
|
||||
link/ether 00:00:5e:00:01:05 brd ff:ff:ff:ff:ff:ff
|
||||
inet 10.0.2.16/24 scope global vrrp4-2-1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::dc56:d11a:e69d:ea72/64 scope link stable-privacy
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
$ ip addr show vrrp6-2-1
|
||||
8: vrrp6-2-1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
|
||||
link/ether 00:00:5e:00:02:05 brd ff:ff:ff:ff:ff:ff
|
||||
inet6 2001:db8::370:7334/64 scope global
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::f8b7:c9dd:a1e8:9844/64 scope link stable-privacy
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
Using ``vrrp4-2-1`` as an example, a few things to note about this interface:
|
||||
|
||||
- It is slaved to ``eth0``; any packets transmitted on this interface will
|
||||
egress via ``eth0``
|
||||
- Its MAC address is set to the VRRP IPv4 virtual MAC specified by the RFC for
|
||||
:abbr:`VRID (Virtual Router ID)` ``5``
|
||||
- The link local address on the interface is not derived from the interface
|
||||
MAC
|
||||
|
||||
First to note is that packets transmitted on this interface will egress via
|
||||
``eth0``, but with their Ethernet source MAC set to the VRRP virtual MAC. This
|
||||
is how FRR's VRRP implementation accomplishes the virtual MAC requirement on
|
||||
real hardware.
|
||||
|
||||
Ingress traffic is a more complicated matter. Macvlan devices have multiple
|
||||
operating modes that change how ingress traffic is handled. Of relevance to
|
||||
FRR's implementation are the ``bridge`` and ``private`` modes. In ``private``
|
||||
mode, any ingress traffic on ``eth0`` (in our example) with a source MAC
|
||||
address equal to the MAC address on any of ``eth0``'s macvlan devices will be
|
||||
placed *only* on that macvlan device. This curious behavior is undesirable,
|
||||
since FRR's implementation of VRRP needs to be able to receive advertisements
|
||||
from neighbors while in Backup mode - i.e., while its macvlan devices are in
|
||||
``protodown on``. If the macvlan devices are instead set to ``bridge`` mode,
|
||||
all ingress traffic shows up on all interfaces - including ``eth0`` -
|
||||
regardless of source MAC or any other factor. Consequently, macvlans used by
|
||||
FRR for VRRP must be set to ``bridge`` mode or the protocol will not function
|
||||
correctly.
|
||||
|
||||
As for the MAC address assigned to this interface, the last byte of the address
|
||||
holds the :abbr:`VRID (Virtual Router Identifier)`, in this case ``0x05``. The
|
||||
second to last byte is ``0x01``, as specified by the RFC for IPv4 operation.
|
||||
The IPv6 MAC address is be identical except that the second to last byte is
|
||||
defined to be ``0x02``. Two things to note from this arrangement:
|
||||
|
||||
1. There can only be up to 255 unique Virtual Routers on an interface (only 1
|
||||
byte is available for the VRID)
|
||||
2. IPv4 and IPv6 addresses must be assigned to different macvlan devices,
|
||||
because they have different MAC addresses
|
||||
|
||||
Finally, take note of the generated IPv6 link local address on the interface.
|
||||
For interfaces on which VRRP will operate in IPv6 mode, this link local
|
||||
*cannot* be derived using the usual EUI-64 method. This is because VRRP
|
||||
advertisements are sent from the link local address of this interface, and VRRP
|
||||
uses the source address of received advertisements as part of its election
|
||||
algorithm. If the IPv6 link local of a router is equivalent to the IPv6 link
|
||||
local in a received advertisement, this can cause both routers to assume the
|
||||
Master role (very bad). ``ifupdown`` knows to set the ``addrgenmode`` of the
|
||||
interface properly, but when using ``iproute2`` to create the macvlan devices,
|
||||
you must be careful to manually specify ``addrgenmode random``.
|
||||
|
||||
A brief note on the Backup state
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
It is worth noting here that an alternate choice for the implementation of the
|
||||
Backup state, such as removing all the IP addresses assigned to the macvlan
|
||||
device or deleting their local routes instead of setting the device into
|
||||
``protodown on``, would allow the protocol to function regardless of whether
|
||||
the macvlan device(s) are set to ``private`` or ``bridge`` mode. Indeed, the
|
||||
strange behavior of the kernel macvlan driver in ``private`` mode, whereby it
|
||||
performs what may be thought of as a sort of interface-level layer 2 "NAT"
|
||||
based on source MAC, can be traced back to a patch clearly designed to
|
||||
accommodate a VRRP implementation from a different vendor. However, the
|
||||
``protodown`` based implementation allows for a configuration model in which
|
||||
FRR does not dynamically manage the addresses assigned on a system, but instead
|
||||
just manages interface state. Such a scenario was in mind when this protocol
|
||||
implementation was initially built, which is why the other choices are not
|
||||
currently present. Since support for placing macvlan devices into ``protodown``
|
||||
was not added to Linux until version 5.1, this also explains the relatively
|
||||
restrictive kernel versioning requirement.
|
||||
|
||||
In the future other methods of implementing Backup state may be added along
|
||||
with a configuration knob to choose between them.
|
||||
|
||||
.. _vrrp-interface-configuration:
|
||||
|
||||
Interface Configuration
|
||||
-----------------------
|
||||
|
||||
Continuing with the example from the previous section, we assume the macvlan
|
||||
interfaces have been properly configured with the proper MAC addresses and the
|
||||
IPvX addresses assigned.
|
||||
|
||||
In FRR, a possible VRRPv3 configuration for this interface is:
|
||||
|
||||
.. code-block:: frr
|
||||
|
||||
interface eth0
|
||||
vrrp 5 version 3
|
||||
vrrp 5 priority 200
|
||||
vrrp 5 advertisement-interval 1500
|
||||
vrrp 5 ip 10.0.2.16
|
||||
vrrp 5 ipv6 2001:0db8::0370:7334
|
||||
|
||||
VRRP will activate as soon as the first IPvX address configuration line is
|
||||
encountered. If you do not want this behavior, use the :clicmd:`vrrp (1-255)
|
||||
shutdown` command, and apply the ``no`` form when you are ready to activate
|
||||
VRRP.
|
||||
|
||||
At this point executing ``show vrrp`` will display the following:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
ubuntu-bionic# show vrrp
|
||||
|
||||
Virtual Router ID 5
|
||||
Protocol Version 3
|
||||
Autoconfigured Yes
|
||||
Shutdown No
|
||||
Interface eth0
|
||||
VRRP interface (v4) vrrp4-2-5
|
||||
VRRP interface (v6) vrrp6-2-5
|
||||
Primary IP (v4) 10.0.2.15
|
||||
Primary IP (v6) fe80::9b91:7155:bf6a:d386
|
||||
Virtual MAC (v4) 00:00:5e:00:01:05
|
||||
Virtual MAC (v6) 00:00:5e:00:02:05
|
||||
Status (v4) Master
|
||||
Status (v6) Master
|
||||
Priority 200
|
||||
Effective Priority (v4) 200
|
||||
Effective Priority (v6) 200
|
||||
Preempt Mode Yes
|
||||
Accept Mode Yes
|
||||
Advertisement Interval 1500 ms
|
||||
Master Advertisement Interval (v4) 1000 ms
|
||||
Master Advertisement Interval (v6) 1000 ms
|
||||
Advertisements Tx (v4) 14
|
||||
Advertisements Tx (v6) 14
|
||||
Advertisements Rx (v4) 0
|
||||
Advertisements Rx (v6) 0
|
||||
Gratuitous ARP Tx (v4) 1
|
||||
Neigh. Adverts Tx (v6) 1
|
||||
State transitions (v4) 2
|
||||
State transitions (v6) 2
|
||||
Skew Time (v4) 210 ms
|
||||
Skew Time (v6) 210 ms
|
||||
Master Down Interval (v4) 3210 ms
|
||||
Master Down Interval (v6) 3210 ms
|
||||
IPv4 Addresses 1
|
||||
.................................. 10.0.2.16
|
||||
IPv6 Addresses 1
|
||||
.................................. 2001:db8::370:7334
|
||||
|
||||
At this point, VRRP has sent gratuitous ARP requests for the IPv4 address,
|
||||
Unsolicited Neighbor Advertisements for the IPv6 address, and has asked Zebra
|
||||
to send Router Advertisements on its behalf. It is also transmitting VRRPv3
|
||||
advertisements on the macvlan interfaces.
|
||||
|
||||
The Primary IP fields are of some interest, as the behavior may be
|
||||
counterintuitive. These fields show the source address used for VRRP
|
||||
advertisements. Although VRRPv3 advertisements are always transmitted on the
|
||||
macvlan interfaces, in the IPv4 case the source address is set to the primary
|
||||
IPv4 address on the base interface, ``eth0`` in this case. This is a protocol
|
||||
requirement, and IPv4 VRRP will not function unless the base interface has an
|
||||
IPv4 address assigned. In the IPv6 case the link local of the macvlan interface
|
||||
is used.
|
||||
|
||||
If any misconfiguration errors are detected, VRRP for the misconfigured address
|
||||
family will not come up and the configuration issue will be logged to FRR's
|
||||
configured logging destination.
|
||||
|
||||
Per the RFC, IPv4 and IPv6 virtual routers are independent of each other. For
|
||||
instance, it is possible for the IPv4 router to be in Backup state while the
|
||||
IPv6 router is in Master state; or for either to be completely inoperative
|
||||
while the other is operative, etc. Instances sharing the same base interface
|
||||
and VRID are shown together in the show output for conceptual convenience.
|
||||
|
||||
To complete your VRRP deployment, configure other routers on the segment with
|
||||
the exact same system and FRR configuration as shown above. Provided each
|
||||
router receives the others' VRRP advertisements, the Master election protocol
|
||||
will run, one Master will be elected, and the other routers will place their
|
||||
macvlan interfaces into ``protodown on`` until Master fails or priority values
|
||||
are changed to favor another router.
|
||||
|
||||
Switching the protocol version to VRRPv2 is accomplished simply by changing
|
||||
``version 3`` to ``version 2`` in the VRID configuration line. Note that VRRPv2
|
||||
does not support IPv6, so any IPv6 configuration will be rejected by FRR when
|
||||
using VRRPv2.
|
||||
|
||||
.. note::
|
||||
|
||||
All VRRP routers initially start in Backup state, and wait for the
|
||||
calculated Master Down Interval to pass before they assume Master status.
|
||||
This prevents downstream neighbor table churn if another router is already
|
||||
Master with higher priority, meaning this box will ultimately assume Backup
|
||||
status once the first advertisement is received. However, if the calculated
|
||||
Master Down Interval is high and this router is configured such that it will
|
||||
ultimately assume Master status, then it will take a while for this to
|
||||
happen. This is a known issue.
|
||||
|
||||
|
||||
All interface configuration commands are documented below.
|
||||
|
||||
.. index:: [no] vrrp (1-255) [version (2-3)]
|
||||
.. clicmd:: [no] vrrp (1-255) [version (2-3)]
|
||||
|
||||
Create a VRRP router with the specified VRID on the interface. Optionally
|
||||
specify the protocol version. If the protocol version is not specified, the
|
||||
default is VRRPv3.
|
||||
|
||||
.. index:: [no] vrrp (1-255) advertisement-interval (10-40950)
|
||||
.. clicmd:: [no] vrrp (1-255) advertisement-interval (10-40950)
|
||||
|
||||
Set the advertisement interval. This is the interval at which VRRP
|
||||
advertisements will be sent. Values are given in milliseconds, but must be
|
||||
multiples of 10, as VRRP itself uses centiseconds.
|
||||
|
||||
.. index:: [no] vrrp (1-255) ip A.B.C.D
|
||||
.. clicmd:: [no] vrrp (1-255) ip A.B.C.D
|
||||
|
||||
Add an IPv4 address to the router. This address must already be configured
|
||||
on the appropriate macvlan device. Adding an IP address to the router will
|
||||
implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to
|
||||
override this behavior.
|
||||
|
||||
.. index:: [no] vrrp (1-255) ipv6 X:X::X:X
|
||||
.. clicmd:: [no] vrrp (1-255) ipv6 X:X::X:X
|
||||
|
||||
Add an IPv6 address to the router. This address must already be configured
|
||||
on the appropriate macvlan device. Adding an IP address to the router will
|
||||
implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to
|
||||
override this behavior.
|
||||
|
||||
This command will fail if the protocol version is set to VRRPv2, as VRRPv2
|
||||
does not support IPv6.
|
||||
|
||||
.. index:: [no] vrrp (1-255) preempt
|
||||
.. clicmd:: [no] vrrp (1-255) preempt
|
||||
|
||||
Toggle preempt mode. When enabled, preemption allows Backup routers with
|
||||
higher priority to take over Master status from the existing Master. Enabled
|
||||
by default.
|
||||
|
||||
.. index:: [no] vrrp (1-255) priority (1-254)
|
||||
.. clicmd:: [no] vrrp (1-255) priority (1-254)
|
||||
|
||||
Set the router priority. The router with the highest priority is elected as
|
||||
the Master. If all routers in the VRRP virtual router are configured with
|
||||
the same priority, the router with the highest primary IP address is elected
|
||||
as the Master. Priority value 255 is reserved for the acting Master router.
|
||||
|
||||
.. index:: [no] vrrp (1-255) shutdown
|
||||
.. clicmd:: [no] vrrp (1-255) shutdown
|
||||
|
||||
Place the router into administrative shutdown. VRRP will not activate for
|
||||
this router until this command is removed with the ``no`` form.
|
||||
|
||||
.. _vrrp-global-configuration:
|
||||
|
||||
Global Configuration
|
||||
--------------------
|
||||
|
||||
Show commands, global defaults and debugging configuration commands.
|
||||
|
||||
.. index:: show vrrp [interface INTERFACE] [(1-255)] [json]
|
||||
.. clicmd:: show vrrp [interface INTERFACE] [(1-255)] [json]
|
||||
|
||||
Shows VRRP status for some or all configured VRRP routers. Specifying an
|
||||
interface will only show routers configured on that interface. Specifying a
|
||||
VRID will only show routers with that VRID. Specifying ``json`` will dump
|
||||
each router state in a JSON array.
|
||||
|
||||
.. index:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}]
|
||||
.. clicmd:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}]
|
||||
|
||||
Toggle debugging logs for some or all components of VRRP.
|
||||
|
||||
protocol
|
||||
Logs state changes, election protocol decisions, and interface status
|
||||
changes.
|
||||
|
||||
autoconfigure
|
||||
Logs actions taken by the autoconfiguration procedures. See
|
||||
:ref:`vrrp-autoconfiguration`.
|
||||
|
||||
packets
|
||||
Logs details of ingress and egress packets. Includes packet decodes and
|
||||
hex dumps.
|
||||
|
||||
sockets
|
||||
Logs details of socket configuration and initialization.
|
||||
|
||||
ndisc
|
||||
Logs actions taken by the Neighbor Discovery component of VRRP.
|
||||
|
||||
arp
|
||||
Logs actions taken by the ARP component of VRRP.
|
||||
|
||||
zebra
|
||||
Logs communications with Zebra.
|
||||
|
||||
.. index:: [no] vrrp default <advertisement-interval (1-4096)|preempt|priority (1-254)|shutdown>
|
||||
.. clicmd:: [no] vrrp default <advertisement-interval (1-4096)|preempt|priority (1-254)|shutdown>
|
||||
|
||||
Configure defaults for new VRRP routers. These values will not affect
|
||||
already configured VRRP routers, but will be applied to newly configured
|
||||
ones.
|
||||
|
||||
.. _vrrp-autoconfiguration:
|
||||
|
||||
Autoconfiguration
|
||||
-----------------
|
||||
|
||||
In light of the complicated configuration required on the base system before
|
||||
VRRP can be enabled, FRR has the ability to automatically configure VRRP
|
||||
sessions by inspecting the interfaces present on the system. Since it is quite
|
||||
unlikely that macvlan devices with VRRP virtual MACs will exist on systems not
|
||||
using VRRP, this can be a convenient shortcut to automatically generate FRR
|
||||
configuration.
|
||||
|
||||
After configuring the interfaces as described in
|
||||
:ref:`vrrp-system-configuration`, and configuring any defaults you may want,
|
||||
execute the following command:
|
||||
|
||||
.. index:: [no] vrrp autoconfigure [version (2-3)]
|
||||
.. clicmd:: [no] vrrp autoconfigure [version (2-3)]
|
||||
|
||||
Generates VRRP configuration based on the interface configuration on the
|
||||
base system. Any existing interfaces that are configured properly for VRRP -
|
||||
i.e. have the correct MAC address, link local address (when required), IPv4
|
||||
and IPv6 addresses - are used to create a VRRP router on their parent
|
||||
interfaces, with VRRP IPvX addresses taken from the addresses assigned to
|
||||
the macvlan devices. The generated configuration appears in the output of
|
||||
``show run``, which can then be modified as needed and written to the config
|
||||
file. The ``version`` parameter controls the protocol version; if using
|
||||
VRRPv2, keep in mind that IPv6 is not supported and will not be configured.
|
||||
|
||||
The following configuration is then generated for you:
|
||||
|
||||
.. code-block:: frr
|
||||
|
||||
interface eth0
|
||||
vrrp 5
|
||||
vrrp 5 ip 10.0.2.16
|
||||
vrrp 5 ipv6 2001:db8::370:7334
|
||||
|
||||
VRRP is automatically activated. Global defaults, if set, are applied.
|
||||
|
||||
You can then edit this configuration with **vtysh** as needed, and commit it by
|
||||
writing to the configuration file.
|
@ -46,6 +46,24 @@ int /* return checksum in low-order 16 bits */
|
||||
return (answer);
|
||||
}
|
||||
|
||||
int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes)
|
||||
{
|
||||
uint8_t dat[sizeof(struct ipv4_ph) + nbytes];
|
||||
|
||||
memcpy(dat, ph, sizeof(struct ipv4_ph));
|
||||
memcpy(dat + sizeof(struct ipv4_ph), data, nbytes);
|
||||
return in_cksum(dat, sizeof(dat));
|
||||
}
|
||||
|
||||
int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes)
|
||||
{
|
||||
uint8_t dat[sizeof(struct ipv6_ph) + nbytes];
|
||||
|
||||
memcpy(dat, ph, sizeof(struct ipv6_ph));
|
||||
memcpy(dat + sizeof(struct ipv6_ph), data, nbytes);
|
||||
return in_cksum(dat, sizeof(dat));
|
||||
}
|
||||
|
||||
/* Fletcher Checksum -- Refer to RFC1008. */
|
||||
#define MODX 4102U /* 5802 should be fine */
|
||||
|
||||
|
@ -1,8 +1,33 @@
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int in_cksum(void *, int);
|
||||
|
||||
/* IPv4 pseudoheader */
|
||||
struct ipv4_ph {
|
||||
struct in_addr src;
|
||||
struct in_addr dst;
|
||||
uint8_t rsvd;
|
||||
uint8_t proto;
|
||||
uint16_t len;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* IPv6 pseudoheader */
|
||||
struct ipv6_ph {
|
||||
struct in6_addr src;
|
||||
struct in6_addr dst;
|
||||
uint32_t ulpl;
|
||||
uint8_t zero[3];
|
||||
uint8_t next_hdr;
|
||||
} __attribute__((packed));
|
||||
|
||||
extern int in_cksum(void *data, int nbytes);
|
||||
extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes);
|
||||
extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes);
|
||||
|
||||
#define FLETCHER_CHECKSUM_VALIDATE 0xffff
|
||||
extern uint16_t fletcher_checksum(uint8_t *, const size_t len,
|
||||
const uint16_t offset);
|
||||
|
@ -149,6 +149,7 @@ const char *node_names[] = {
|
||||
"bfd", /* BFD_NODE */
|
||||
"bfd peer", /* BFD_PEER_NODE */
|
||||
"openfabric", // OPENFABRIC_NODE
|
||||
"vrrp", /* VRRP_NODE */
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
|
@ -147,6 +147,7 @@ enum node_type {
|
||||
BFD_NODE, /* BFD protocol mode. */
|
||||
BFD_PEER_NODE, /* BFD peer configuration mode. */
|
||||
OPENFABRIC_NODE, /* OpenFabric router configuration node */
|
||||
VRRP_NODE, /* VRRP node */
|
||||
NODE_TYPE_MAX, /* maximum */
|
||||
};
|
||||
|
||||
|
41
lib/if.c
41
lib/if.c
@ -389,6 +389,34 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz,
|
||||
struct interface ***result, vrf_id_t vrf_id)
|
||||
{
|
||||
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
|
||||
|
||||
struct list *rs = list_new();
|
||||
struct interface *ifp;
|
||||
|
||||
FOR_ALL_INTERFACES (vrf, ifp) {
|
||||
if (ifp->hw_addr_len == (int)addrsz
|
||||
&& !memcmp(hw_addr, ifp->hw_addr, addrsz))
|
||||
listnode_add(rs, ifp);
|
||||
}
|
||||
|
||||
if (rs->count) {
|
||||
*result = XCALLOC(MTYPE_TMP,
|
||||
sizeof(struct interface *) * rs->count);
|
||||
list_to_array(rs, (void **)*result, rs->count);
|
||||
}
|
||||
|
||||
int count = rs->count;
|
||||
|
||||
list_delete(&rs);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/* Get interface by name if given name interface doesn't exist create
|
||||
one. */
|
||||
struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id)
|
||||
@ -876,6 +904,19 @@ struct connected *connected_add_by_prefix(struct interface *ifp,
|
||||
return ifc;
|
||||
}
|
||||
|
||||
struct connected *connected_get_linklocal(struct interface *ifp)
|
||||
{
|
||||
struct listnode *n;
|
||||
struct connected *c = NULL;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) {
|
||||
if (c->address->family == AF_INET6
|
||||
&& IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
#if 0 /* this route_table of struct connected's is unused \
|
||||
* however, it would be good to use a route_table rather than \
|
||||
* a list.. \
|
||||
|
7
lib/if.h
7
lib/if.h
@ -225,6 +225,10 @@ struct interface {
|
||||
not work as expected.
|
||||
*/
|
||||
ifindex_t ifindex;
|
||||
/*
|
||||
* ifindex of parent interface, if any
|
||||
*/
|
||||
ifindex_t link_ifindex;
|
||||
#define IFINDEX_INTERNAL 0
|
||||
|
||||
/* Zebra internal interface status */
|
||||
@ -482,6 +486,8 @@ extern struct connected *if_lookup_address(void *matchaddr, int family,
|
||||
vrf_id_t vrf_id);
|
||||
extern struct interface *if_lookup_prefix(struct prefix *prefix,
|
||||
vrf_id_t vrf_id);
|
||||
size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz,
|
||||
struct interface ***result, vrf_id_t vrf_id);
|
||||
|
||||
/* These 3 functions are to be used when the ifname argument is terminated
|
||||
by a '\0' character: */
|
||||
@ -540,6 +546,7 @@ extern struct connected *connected_lookup_prefix_exact(struct interface *,
|
||||
extern struct nbr_connected *nbr_connected_new(void);
|
||||
extern void nbr_connected_free(struct nbr_connected *);
|
||||
struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *);
|
||||
struct connected *connected_get_linklocal(struct interface *ifp);
|
||||
|
||||
/* link parameters */
|
||||
struct if_link_params *if_link_params_get(struct interface *);
|
||||
|
@ -56,6 +56,9 @@ struct ipaddr {
|
||||
#define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4
|
||||
#define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6
|
||||
|
||||
#define IPADDRSZ(p) \
|
||||
(IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr))
|
||||
|
||||
static inline int str2ipaddr(const char *str, struct ipaddr *ip)
|
||||
{
|
||||
int ret;
|
||||
|
@ -64,6 +64,11 @@ void json_object_boolean_true_add(struct json_object *obj, const char *key)
|
||||
json_object_object_add(obj, key, json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
void json_object_boolean_add(struct json_object *obj, const char *key, bool val)
|
||||
{
|
||||
json_object_object_add(obj, key, json_object_new_boolean(val));
|
||||
}
|
||||
|
||||
struct json_object *json_object_lock(struct json_object *obj)
|
||||
{
|
||||
return json_object_get(obj);
|
||||
|
@ -61,6 +61,8 @@ extern void json_object_string_add(struct json_object *obj, const char *key,
|
||||
const char *s);
|
||||
extern void json_object_int_add(struct json_object *obj, const char *key,
|
||||
int64_t i);
|
||||
void json_object_boolean_add(struct json_object *obj, const char *key,
|
||||
bool val);
|
||||
extern void json_object_boolean_false_add(struct json_object *obj,
|
||||
const char *key);
|
||||
extern void json_object_boolean_true_add(struct json_object *obj,
|
||||
|
@ -334,3 +334,18 @@ struct listnode *listnode_add_force(struct list **list, void *val)
|
||||
*list = list_new();
|
||||
return listnode_add(*list, val);
|
||||
}
|
||||
|
||||
void **list_to_array(struct list *list, void **arr, size_t arrlen)
|
||||
{
|
||||
struct listnode *ln;
|
||||
void *vp;
|
||||
size_t idx = 0;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(list, ln, vp)) {
|
||||
arr[idx++] = vp;
|
||||
if (idx == arrlen)
|
||||
break;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
@ -239,6 +239,26 @@ extern struct list *list_dup(struct list *l);
|
||||
extern void list_sort(struct list *list,
|
||||
int (*cmp)(const void **, const void **));
|
||||
|
||||
/*
|
||||
* Convert a list to an array of void pointers.
|
||||
*
|
||||
* Starts from the list head and ends either on the last node of the list or
|
||||
* when the provided array cannot store any more elements.
|
||||
*
|
||||
* list
|
||||
* list to convert
|
||||
*
|
||||
* arr
|
||||
* Pre-allocated array of void *
|
||||
*
|
||||
* arrlen
|
||||
* Number of elements in arr
|
||||
*
|
||||
* Returns:
|
||||
* arr
|
||||
*/
|
||||
void **list_to_array(struct list *list, void **arr, size_t arrlen);
|
||||
|
||||
/*
|
||||
* Delete a list and NULL its pointer.
|
||||
*
|
||||
|
@ -83,6 +83,7 @@ ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP"
|
||||
ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR"
|
||||
ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD"
|
||||
ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric"
|
||||
ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP"
|
||||
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
|
||||
|
||||
|
||||
@ -110,4 +111,5 @@ ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)"
|
||||
ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)"
|
||||
ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)"
|
||||
ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)"
|
||||
ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)"
|
||||
ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol"
|
||||
|
@ -163,7 +163,7 @@ int sockunion_accept(int sock, union sockunion *su)
|
||||
}
|
||||
|
||||
/* Return sizeof union sockunion. */
|
||||
static int sockunion_sizeof(const union sockunion *su)
|
||||
int sockunion_sizeof(const union sockunion *su)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -83,6 +83,7 @@ extern void sockunion_set(union sockunion *, int family, const uint8_t *addr,
|
||||
|
||||
extern union sockunion *sockunion_str2su(const char *str);
|
||||
extern int sockunion_accept(int sock, union sockunion *);
|
||||
extern int sockunion_sizeof(const union sockunion *su);
|
||||
extern int sockunion_stream_socket(union sockunion *);
|
||||
extern int sockopt_reuseaddr(int);
|
||||
extern int sockopt_reuseport(int);
|
||||
|
@ -555,6 +555,25 @@ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id,
|
||||
zclient_send_message(zclient);
|
||||
}
|
||||
|
||||
int zclient_send_interface_protodown(struct zclient *zclient, vrf_id_t vrf_id,
|
||||
struct interface *ifp, bool down)
|
||||
{
|
||||
struct stream *s;
|
||||
|
||||
if (zclient->sock < 0)
|
||||
return -1;
|
||||
|
||||
s = zclient->obuf;
|
||||
stream_reset(s);
|
||||
zclient_create_header(s, ZEBRA_INTERFACE_SET_PROTODOWN, vrf_id);
|
||||
stream_putl(s, ifp->ifindex);
|
||||
stream_putc(s, !!down);
|
||||
stream_putw_at(s, 0, stream_get_endp(s));
|
||||
zclient_send_message(zclient);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make connection to zebra daemon. */
|
||||
int zclient_start(struct zclient *zclient)
|
||||
{
|
||||
@ -1381,6 +1400,8 @@ stream_failure:
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | bandwidth |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | parent ifindex |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Link Layer Type |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Harware Address Length |
|
||||
@ -1561,6 +1582,7 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp)
|
||||
ifp->mtu = stream_getl(s);
|
||||
ifp->mtu6 = stream_getl(s);
|
||||
ifp->bandwidth = stream_getl(s);
|
||||
ifp->link_ifindex = stream_getl(s);
|
||||
ifp->ll_type = stream_getl(s);
|
||||
ifp->hw_addr_len = stream_getl(s);
|
||||
if (ifp->hw_addr_len)
|
||||
|
@ -76,6 +76,7 @@ typedef enum {
|
||||
ZEBRA_INTERFACE_UP,
|
||||
ZEBRA_INTERFACE_DOWN,
|
||||
ZEBRA_INTERFACE_SET_MASTER,
|
||||
ZEBRA_INTERFACE_SET_PROTODOWN,
|
||||
ZEBRA_ROUTE_ADD,
|
||||
ZEBRA_ROUTE_DELETE,
|
||||
ZEBRA_ROUTE_NOTIFY_OWNER,
|
||||
@ -466,6 +467,9 @@ extern void zclient_send_interface_radv_req(struct zclient *zclient,
|
||||
vrf_id_t vrf_id,
|
||||
struct interface *ifp, int enable,
|
||||
int ra_interval);
|
||||
extern int zclient_send_interface_protodown(struct zclient *zclient,
|
||||
vrf_id_t vrf_id,
|
||||
struct interface *ifp, bool down);
|
||||
|
||||
/* Send redistribute command to zebra daemon. Do not update zclient state. */
|
||||
extern int zebra_redistribute_send(int command, struct zclient *, afi_t,
|
||||
|
@ -24,6 +24,7 @@
|
||||
%{!?with_pam: %global with_pam 0 }
|
||||
%{!?with_pbrd: %global with_pbrd 1 }
|
||||
%{!?with_pimd: %global with_pimd 1 }
|
||||
%{!?with_vrrpd: %global with_vrrpd 1 }
|
||||
%{!?with_rpki: %global with_rpki 0 }
|
||||
%{!?with_rtadv: %global with_rtadv 1 }
|
||||
%{!?with_watchfrr: %global with_watchfrr 1 }
|
||||
@ -124,6 +125,12 @@
|
||||
%define daemon_babeld ""
|
||||
%endif
|
||||
|
||||
%if %{with_vrrpd}
|
||||
%define daemon_vrrpd vrrpd
|
||||
%else
|
||||
%define daemon_vrrpd ""
|
||||
%endif
|
||||
|
||||
%if %{with_watchfrr}
|
||||
%define daemon_watchfrr watchfrr
|
||||
%else
|
||||
@ -136,7 +143,7 @@
|
||||
%define daemon_bfdd ""
|
||||
%endif
|
||||
|
||||
%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd}
|
||||
%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd}
|
||||
|
||||
#release sub-revision (the two digits after the CONFDATE)
|
||||
%{!?release_rev: %global release_rev 01 }
|
||||
@ -306,6 +313,11 @@ developing OSPF-API and frr applications.
|
||||
%else
|
||||
--disable-babeld \
|
||||
%endif
|
||||
%if %{with_vrrpd}
|
||||
--enable-vrrpd \
|
||||
%else
|
||||
--disable-vrrpd \
|
||||
%endif
|
||||
%if %{with_pam}
|
||||
--with-libpam \
|
||||
%endif
|
||||
@ -461,6 +473,9 @@ zebra_spec_add_service isisd 2608/tcp "ISISd vty"
|
||||
zebra_spec_add_service bfdd 2617/tcp "BFDd vty"
|
||||
%endif
|
||||
zebra_spec_add_service fabricd 2618/tcp "Fabricd vty"
|
||||
%if %{with_vrrpd}
|
||||
zebra_spec_add_service vrrpd 2619/tcp "VRRPd vty"
|
||||
%endif
|
||||
|
||||
%if "%{initsystem}" == "systemd"
|
||||
for daemon in %all_daemons ; do
|
||||
@ -596,6 +611,9 @@ fi
|
||||
%if %{with_pbrd}
|
||||
%{_sbindir}/pbrd
|
||||
%endif
|
||||
%if %{with_vrrpd}
|
||||
%{_sbindir}/vrrpd
|
||||
%endif
|
||||
%{_sbindir}/isisd
|
||||
%{_sbindir}/fabricd
|
||||
%if %{with_ldpd}
|
||||
|
@ -29,6 +29,7 @@ sharpd=no
|
||||
pbrd=no
|
||||
bfdd=no
|
||||
fabricd=no
|
||||
vrrpd=no
|
||||
|
||||
#
|
||||
# If this option is set the /etc/init.d/frr script automatically loads
|
||||
@ -53,6 +54,7 @@ pbrd_options=" -A 127.0.0.1"
|
||||
staticd_options="-A 127.0.0.1"
|
||||
bfdd_options=" -A 127.0.0.1"
|
||||
fabricd_options="-A 127.0.0.1"
|
||||
vrrpd_options=" -A 127.0.0.1"
|
||||
|
||||
# The list of daemons to watch is automatically generated by the init script.
|
||||
#watchfrr_options=""
|
||||
|
@ -16,6 +16,7 @@ if $programname == 'babeld' or
|
||||
$programname == 'pimd' or
|
||||
$programname == 'ripd' or
|
||||
$programname == 'ripngd' or
|
||||
$programname == 'vrrpd' or
|
||||
$programname == 'watchfrr' or
|
||||
$programname == 'zebra'
|
||||
then :omfile:$frr_log
|
||||
@ -33,6 +34,7 @@ if $programname == 'babeld' or
|
||||
$programname == 'pimd' or
|
||||
$programname == 'ripd' or
|
||||
$programname == 'ripngd' or
|
||||
$programname == 'vrrpd' or
|
||||
$programname == 'watchfrr' or
|
||||
$programname == 'zebra'
|
||||
then stop
|
||||
|
@ -410,7 +410,8 @@ end
|
||||
"service ",
|
||||
"table ",
|
||||
"username ",
|
||||
"zebra ")
|
||||
"zebra ",
|
||||
"vrrp autoconfigure")
|
||||
|
||||
for line in self.lines:
|
||||
|
||||
|
@ -25,7 +25,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty
|
||||
# Local Daemon selection may be done by using /etc/frr/daemons.
|
||||
# See /usr/share/doc/frr/README.Debian.gz for further information.
|
||||
# Keep zebra first and do not list watchfrr!
|
||||
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd"
|
||||
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
|
||||
MAX_INSTANCES=5
|
||||
RELOAD_SCRIPT="$D_PATH/frr-reload.py"
|
||||
|
||||
|
@ -29,7 +29,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty
|
||||
# - keep zebra first
|
||||
# - watchfrr does NOT belong in this list
|
||||
|
||||
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd"
|
||||
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
|
||||
RELOAD_SCRIPT="$D_PATH/frr-reload.py"
|
||||
|
||||
#
|
||||
|
2
vrrpd/.gitignore
vendored
Normal file
2
vrrpd/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
libvrrp.a
|
||||
vrrpd
|
10
vrrpd/Makefile
Normal file
10
vrrpd/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
all: ALWAYS
|
||||
@$(MAKE) -s -C .. vrrp/vrrp
|
||||
%: ALWAYS
|
||||
@$(MAKE) -s -C .. vrrp/$@
|
||||
|
||||
Makefile:
|
||||
#nothing
|
||||
ALWAYS:
|
||||
.PHONY: ALWAYS makefiles
|
||||
.SUFFIXES:
|
39
vrrpd/subdir.am
Normal file
39
vrrpd/subdir.am
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# vrrpd
|
||||
#
|
||||
|
||||
if VRRPD
|
||||
noinst_LIBRARIES += vrrpd/libvrrp.a
|
||||
sbin_PROGRAMS += vrrpd/vrrpd
|
||||
# dist_examples_DATA += staticd/staticd.conf.sample
|
||||
vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c
|
||||
man8 += $(MANBUILD)/vrrpd.8
|
||||
endif
|
||||
|
||||
vrrpd_libvrrp_a_SOURCES = \
|
||||
vrrpd/vrrp.c \
|
||||
vrrpd/vrrp_arp.c \
|
||||
vrrpd/vrrp_debug.c \
|
||||
vrrpd/vrrp_memory.c \
|
||||
vrrpd/vrrp_ndisc.c \
|
||||
vrrpd/vrrp_packet.c \
|
||||
vrrpd/vrrp_vty.c \
|
||||
vrrpd/vrrp_zebra.c \
|
||||
# end
|
||||
|
||||
noinst_HEADERS += \
|
||||
vrrpd/vrrp.h \
|
||||
vrrpd/vrrp_arp.h \
|
||||
vrrpd/vrrp_debug.h \
|
||||
vrrpd/vrrp_memory.h \
|
||||
vrrpd/vrrp_ndisc.h \
|
||||
vrrpd/vrrp_packet.h \
|
||||
vrrpd/vrrp_vty.h \
|
||||
vrrpd/vrrp_zebra.h \
|
||||
# end
|
||||
|
||||
vrrpd/vrrp_vty_clippy.c: $(CLIPPY_DEPS)
|
||||
vrrpd/vrrp_vty.$(OBJEXT): vrrpd/vrrp_vty_clippy.c
|
||||
|
||||
vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c
|
||||
vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@
|
2379
vrrpd/vrrp.c
Normal file
2379
vrrpd/vrrp.c
Normal file
File diff suppressed because it is too large
Load Diff
570
vrrpd/vrrp.h
Normal file
570
vrrpd/vrrp.h
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
* VRRP global definitions and state machine.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_H__
|
||||
#define __VRRP_H__
|
||||
|
||||
#include <zebra.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include "lib/hash.h"
|
||||
#include "lib/hook.h"
|
||||
#include "lib/if.h"
|
||||
#include "lib/linklist.h"
|
||||
#include "lib/privs.h"
|
||||
#include "lib/stream.h"
|
||||
#include "lib/thread.h"
|
||||
#include "lib/vty.h"
|
||||
|
||||
/* Global definitions */
|
||||
#define VRRP_RADV_INT 16
|
||||
#define VRRP_PRIO_MASTER 255
|
||||
#define VRRP_MCASTV4_GROUP_STR "224.0.0.18"
|
||||
#define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12"
|
||||
#define VRRP_MCASTV4_GROUP 0xe0000012
|
||||
#define VRRP_MCASTV6_GROUP 0xff020000000000000000000000000012
|
||||
#define IPPROTO_VRRP 112
|
||||
|
||||
#define VRRP_LOGPFX_VRID "[VRID %u] "
|
||||
#define VRRP_LOGPFX_FAM "[%s] "
|
||||
|
||||
/* Default defaults */
|
||||
#define VRRP_DEFAULT_PRIORITY 100
|
||||
#define VRRP_DEFAULT_ADVINT 100
|
||||
#define VRRP_DEFAULT_PREEMPT true
|
||||
#define VRRP_DEFAULT_ACCEPT true
|
||||
#define VRRP_DEFAULT_SHUTDOWN false
|
||||
|
||||
/* User compatibility constant */
|
||||
#define CS2MS 10
|
||||
|
||||
/* Configured defaults */
|
||||
struct vrrp_defaults {
|
||||
uint8_t priority;
|
||||
uint16_t advertisement_interval;
|
||||
bool preempt_mode;
|
||||
bool accept_mode;
|
||||
bool shutdown;
|
||||
};
|
||||
|
||||
extern struct vrrp_defaults vd;
|
||||
|
||||
/* threadmaster */
|
||||
extern struct thread_master *master;
|
||||
|
||||
/* privileges */
|
||||
extern struct zebra_privs_t vrrp_privs;
|
||||
|
||||
/* Global hash of all Virtual Routers */
|
||||
extern struct hash *vrrp_vrouters_hash;
|
||||
|
||||
/*
|
||||
* VRRP Router.
|
||||
*
|
||||
* This struct contains all state for a particular VRRP Router operating
|
||||
* in a Virtual Router for either IPv4 or IPv6.
|
||||
*/
|
||||
struct vrrp_router {
|
||||
/*
|
||||
* Whether this VRRP Router is active.
|
||||
*/
|
||||
bool is_active;
|
||||
|
||||
/* Whether we are the address owner */
|
||||
bool is_owner;
|
||||
|
||||
/* Rx socket: Rx from parent of mvl_ifp */
|
||||
int sock_rx;
|
||||
/* Tx socket; Tx from mvl_ifp */
|
||||
int sock_tx;
|
||||
|
||||
/* macvlan interface */
|
||||
struct interface *mvl_ifp;
|
||||
|
||||
/* Source address for advertisements */
|
||||
struct ipaddr src;
|
||||
|
||||
/* Socket read buffer */
|
||||
uint8_t ibuf[IP_MAXPACKET];
|
||||
|
||||
/*
|
||||
* Address family of this Virtual Router.
|
||||
* Either AF_INET or AF_INET6.
|
||||
*/
|
||||
int family;
|
||||
|
||||
/*
|
||||
* Virtual Router this VRRP Router is participating in.
|
||||
*/
|
||||
struct vrrp_vrouter *vr;
|
||||
|
||||
/*
|
||||
* One or more IPvX addresses associated with this Virtual
|
||||
* Router. The first address must be the "primary" address this
|
||||
* Virtual Router is backing up in the case of IPv4. In the case of
|
||||
* IPv6 it must be the link-local address of vr->ifp.
|
||||
*
|
||||
* Type: struct ipaddr *
|
||||
*/
|
||||
struct list *addrs;
|
||||
|
||||
/*
|
||||
* This flag says whether we are waiting on an interface up
|
||||
* notification from Zebra before we send an ADVERTISEMENT.
|
||||
*/
|
||||
bool advert_pending;
|
||||
|
||||
/*
|
||||
* If this is an IPv4 VRRP router, this flag says whether we are
|
||||
* waiting on an interface up notification from Zebra before we send
|
||||
* gratuitous ARP packets for all our addresses. Should never be true
|
||||
* if family == AF_INET6.
|
||||
*/
|
||||
bool garp_pending;
|
||||
/*
|
||||
* If this is an IPv6 VRRP router, this flag says whether we are
|
||||
* waiting on an interface up notification from Zebra before we send
|
||||
* Unsolicited Neighbor Advertisement packets for all our addresses.
|
||||
* Should never be true if family == AF_INET.
|
||||
*/
|
||||
bool ndisc_pending;
|
||||
|
||||
/*
|
||||
* Effective priority
|
||||
* => vr->priority if we are Backup
|
||||
* => 255 if we are Master
|
||||
*/
|
||||
uint8_t priority;
|
||||
|
||||
/*
|
||||
* Advertisement interval contained in ADVERTISEMENTS received from the
|
||||
* Master (centiseconds)
|
||||
*/
|
||||
uint16_t master_adver_interval;
|
||||
|
||||
/*
|
||||
* Time to skew Master_Down_Interval in centiseconds. Calculated as:
|
||||
* (((256 - priority) * Master_Adver_Interval) / 256)
|
||||
*/
|
||||
uint16_t skew_time;
|
||||
|
||||
/*
|
||||
* Time interval for Backup to declare Master down (centiseconds).
|
||||
* Calculated as:
|
||||
* (3 * Master_Adver_Interval) + Skew_time
|
||||
*/
|
||||
uint16_t master_down_interval;
|
||||
|
||||
/*
|
||||
* The MAC address used for the source MAC address in VRRP
|
||||
* advertisements, advertised in ARP requests/responses, and advertised
|
||||
* in ND Neighbor Advertisements.
|
||||
*/
|
||||
struct ethaddr vmac;
|
||||
|
||||
struct {
|
||||
int state;
|
||||
} fsm;
|
||||
|
||||
struct {
|
||||
/* Total number of advertisements sent and received */
|
||||
uint32_t adver_tx_cnt;
|
||||
uint32_t adver_rx_cnt;
|
||||
/* Total number of gratuitous ARPs sent */
|
||||
uint32_t garp_tx_cnt;
|
||||
/* Total number of unsolicited Neighbor Advertisements sent */
|
||||
uint32_t una_tx_cnt;
|
||||
/* Total number of state transitions */
|
||||
uint32_t trans_cnt;
|
||||
} stats;
|
||||
|
||||
struct thread *t_master_down_timer;
|
||||
struct thread *t_adver_timer;
|
||||
struct thread *t_read;
|
||||
struct thread *t_write;
|
||||
};
|
||||
|
||||
/*
|
||||
* VRRP Virtual Router.
|
||||
*
|
||||
* This struct contains all state and configuration for a given Virtual Router
|
||||
* Identifier on a given interface, both v4 and v6.
|
||||
*
|
||||
* RFC5798 s. 1 states:
|
||||
* "Within a VRRP router, the virtual routers in each of the IPv4 and IPv6
|
||||
* address families are a domain unto themselves and do not overlap."
|
||||
*
|
||||
* This implementation has chosen the tuple (interface, VRID) as the key for a
|
||||
* particular VRRP Router, and the rest of the program is designed around this
|
||||
* assumption. Additionally, base protocol configuration parameters such as the
|
||||
* advertisement interval and (configured) priority are shared between v4 and
|
||||
* v6 instances. This corresponds to the choice made by other industrial
|
||||
* implementations.
|
||||
*/
|
||||
struct vrrp_vrouter {
|
||||
/* Whether this instance was automatically configured */
|
||||
bool autoconf;
|
||||
|
||||
/* Whether this VRRP router is in administrative shutdown */
|
||||
bool shutdown;
|
||||
|
||||
/* Interface */
|
||||
struct interface *ifp;
|
||||
|
||||
/* Version */
|
||||
uint8_t version;
|
||||
|
||||
/* Virtual Router Identifier */
|
||||
uint32_t vrid;
|
||||
|
||||
/* Configured priority */
|
||||
uint8_t priority;
|
||||
|
||||
/*
|
||||
* Time interval between ADVERTISEMENTS (centiseconds). Default is 100
|
||||
* centiseconds (1 second).
|
||||
*/
|
||||
uint16_t advertisement_interval;
|
||||
|
||||
/*
|
||||
* Controls whether a (starting or restarting) higher-priority Backup
|
||||
* router preempts a lower-priority Master router. Values are True to
|
||||
* allow preemption and False to prohibit preemption. Default is True.
|
||||
*/
|
||||
bool preempt_mode;
|
||||
|
||||
/*
|
||||
* Controls whether a virtual router in Master state will accept
|
||||
* packets addressed to the address owner's IPvX address as its own if
|
||||
* it is not the IPvX address owner. The default is False.
|
||||
*/
|
||||
bool accept_mode;
|
||||
|
||||
struct vrrp_router *v4;
|
||||
struct vrrp_router *v6;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize VRRP global datastructures.
|
||||
*/
|
||||
void vrrp_init(void);
|
||||
|
||||
/*
|
||||
* Destroy all VRRP instances and gracefully shutdown.
|
||||
*
|
||||
* For instances in Master state, VRRP advertisements with 0 priority will be
|
||||
* sent if possible to notify Backup routers that we are going away.
|
||||
*/
|
||||
void vrrp_fini(void);
|
||||
|
||||
|
||||
/* Creation and destruction ------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Create and register a new VRRP Virtual Router.
|
||||
*
|
||||
* ifp
|
||||
* Base interface to configure VRRP on
|
||||
*
|
||||
* vrid
|
||||
* Virtual Router Identifier
|
||||
*/
|
||||
struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid,
|
||||
uint8_t version);
|
||||
|
||||
/*
|
||||
* Destroy a VRRP Virtual Router, freeing all its resources.
|
||||
*
|
||||
* If there are any running VRRP instances, these are stopped and destroyed.
|
||||
*/
|
||||
void vrrp_vrouter_destroy(struct vrrp_vrouter *vr);
|
||||
|
||||
|
||||
/* Configuration controllers ----------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Check if a Virtual Router ought to be started, and if so, start it.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to checkstart
|
||||
*/
|
||||
void vrrp_check_start(struct vrrp_vrouter *vr);
|
||||
|
||||
/*
|
||||
* Change the configured priority of a VRRP Virtual Router.
|
||||
*
|
||||
* Note that this only changes the configured priority of the Virtual Router.
|
||||
* The currently effective priority will not be changed; to change the
|
||||
* effective priority, the Virtual Router must be restarted by issuing a
|
||||
* VRRP_EVENT_SHUTDOWN followed by a VRRP_EVENT_STARTUP.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to change priority of
|
||||
*
|
||||
* priority
|
||||
* New priority
|
||||
*/
|
||||
void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority);
|
||||
|
||||
/*
|
||||
* Set Advertisement Interval on this Virtual Router.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to change priority of
|
||||
*
|
||||
* advertisement_interval
|
||||
* New advertisement interval
|
||||
*/
|
||||
void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
|
||||
uint16_t advertisement_interval);
|
||||
|
||||
/*
|
||||
* Add an IPvX address to a VRRP Virtual Router.
|
||||
*
|
||||
* r
|
||||
* Virtual Router to add IPvx address to
|
||||
*
|
||||
* ip
|
||||
* Address to add
|
||||
*
|
||||
* activate
|
||||
* Whether to automatically start the VRRP router if this is the first IP
|
||||
* address added.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip);
|
||||
|
||||
/*
|
||||
* Add an IPv4 address to a VRRP Virtual Router.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to add IPv4 address to
|
||||
*
|
||||
* v4
|
||||
* Address to add
|
||||
*
|
||||
* activate
|
||||
* Whether to automatically start the VRRP router if this is the first IP
|
||||
* address added.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4);
|
||||
|
||||
/*
|
||||
* Add an IPv6 address to a VRRP Virtual Router.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to add IPv6 address to
|
||||
*
|
||||
* v6
|
||||
* Address to add
|
||||
*
|
||||
* activate
|
||||
* Whether to automatically start the VRRP router if this is the first IP
|
||||
* address added.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
|
||||
|
||||
/*
|
||||
* Remove an IP address from a VRRP Virtual Router.
|
||||
*
|
||||
* r
|
||||
* Virtual Router to remove IP address from
|
||||
*
|
||||
* ip
|
||||
* Address to remove
|
||||
*
|
||||
* deactivate
|
||||
* Whether to automatically stop the VRRP router if removing v4 would leave
|
||||
* us with an empty address list. If this is not true and ip is the only IP
|
||||
* address backed up by this virtual router, this function will not remove
|
||||
* the address and return failure.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip);
|
||||
|
||||
/*
|
||||
* Remove an IPv4 address from a VRRP Virtual Router.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to remove IPv4 address from
|
||||
*
|
||||
* v4
|
||||
* Address to remove
|
||||
*
|
||||
* deactivate
|
||||
* Whether to automatically stop the VRRP router if removing v4 would leave
|
||||
* us with an empty address list. If this is not true and v4 is the only
|
||||
* IPv4 address backed up by this virtual router, this function will not
|
||||
* remove the address and return failure.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4);
|
||||
|
||||
/*
|
||||
* Remove an IPv6 address from a VRRP Virtual Router.
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to remove IPv6 address from
|
||||
*
|
||||
* v6
|
||||
* Address to remove
|
||||
*
|
||||
* deactivate
|
||||
* Whether to automatically stop the VRRP router if removing v5 would leave
|
||||
* us with an empty address list. If this is not true and v4 is the only
|
||||
* IPv6 address backed up by this virtual router, this function will not
|
||||
* remove the address and return failure.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
|
||||
|
||||
/* State machine ----------------------------------------------------------- */
|
||||
|
||||
#define VRRP_STATE_INITIALIZE 0
|
||||
#define VRRP_STATE_MASTER 1
|
||||
#define VRRP_STATE_BACKUP 2
|
||||
#define VRRP_EVENT_STARTUP 0
|
||||
#define VRRP_EVENT_SHUTDOWN 1
|
||||
|
||||
extern const char *vrrp_state_names[3];
|
||||
extern const char *vrrp_event_names[2];
|
||||
|
||||
/*
|
||||
* This hook called whenever the state of a Virtual Router changes, after the
|
||||
* specific internal state handlers have run.
|
||||
*
|
||||
* Use this if you need to react to state changes to perform non-critical
|
||||
* tasks. Critical tasks should go in the internal state change handlers.
|
||||
*/
|
||||
DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to));
|
||||
|
||||
/*
|
||||
* Trigger a VRRP event on a given Virtual Router..
|
||||
*
|
||||
* vr
|
||||
* Virtual Router to operate on
|
||||
*
|
||||
* event
|
||||
* Event to kick off. All event related processing will have completed upon
|
||||
* return of this function.
|
||||
*
|
||||
* Returns:
|
||||
* < 0 if the event created an error
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_event(struct vrrp_router *r, int event);
|
||||
|
||||
/* Autoconfig -------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Search for and automatically configure VRRP instances on interfaces.
|
||||
*
|
||||
* ifp
|
||||
* Interface to autoconfig. If it is a macvlan interface and has a VRRP MAC,
|
||||
* a VRRP instance corresponding to VMAC assigned to macvlan will be created
|
||||
* on the parent interface and all addresses on the macvlan interface except
|
||||
* the v6 link local will be configured as VRRP addresses. If NULL, this
|
||||
* treatment will be applied to all existing interfaces matching the above
|
||||
* criterion.
|
||||
*
|
||||
* Returns:
|
||||
* -1 on failure
|
||||
* 0 otherwise
|
||||
*/
|
||||
int vrrp_autoconfig(void);
|
||||
|
||||
/*
|
||||
* Enable autoconfiguration.
|
||||
*
|
||||
* Calling this function will cause vrrpd to automatically configure VRRP
|
||||
* instances on existing compatible macvlan interfaces. These instances will
|
||||
* react to interface up/down and address add/delete events to keep themselves
|
||||
* in sync with the available interfaces.
|
||||
*
|
||||
* version
|
||||
* VRRP version to use for autoconfigured instances. Must be 2 or 3.
|
||||
*/
|
||||
void vrrp_autoconfig_on(int version);
|
||||
|
||||
/*
|
||||
* Disable autoconfiguration.
|
||||
*
|
||||
* Calling this function will delete all existing autoconfigured VRRP instances.
|
||||
*/
|
||||
void vrrp_autoconfig_off(void);
|
||||
|
||||
/* Interface Tracking ------------------------------------------------------ */
|
||||
|
||||
void vrrp_if_add(struct interface *ifp);
|
||||
void vrrp_if_del(struct interface *ifp);
|
||||
void vrrp_if_up(struct interface *ifp);
|
||||
void vrrp_if_down(struct interface *ifp);
|
||||
void vrrp_if_address_add(struct interface *ifp);
|
||||
void vrrp_if_address_del(struct interface *ifp);
|
||||
|
||||
/* Other ------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Write interface block-level configuration to vty.
|
||||
*
|
||||
* vty
|
||||
* vty to write config to
|
||||
*
|
||||
* Returns:
|
||||
* # of lines written
|
||||
*/
|
||||
int vrrp_config_write_interface(struct vty *vty);
|
||||
|
||||
/*
|
||||
* Write global level configuration to vty.
|
||||
*
|
||||
* vty
|
||||
* vty to write config to
|
||||
*
|
||||
* Returns:
|
||||
* # of lines written
|
||||
*/
|
||||
int vrrp_config_write_global(struct vty *vty);
|
||||
|
||||
/*
|
||||
* Find VRRP Virtual Router by Virtual Router ID
|
||||
*/
|
||||
struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid);
|
||||
|
||||
#endif /* __VRRP_H__ */
|
211
vrrpd/vrrp_arp.c
Normal file
211
vrrpd/vrrp_arp.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* VRRP ARP handling.
|
||||
* Copyright (C) 2001-2017 Alexandre Cassen
|
||||
* Portions:
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include <linux/if_packet.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include "lib/if.h"
|
||||
#include "lib/linklist.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/memory.h"
|
||||
#include "lib/prefix.h"
|
||||
|
||||
#include "vrrp.h"
|
||||
#include "vrrp_arp.h"
|
||||
#include "vrrp_debug.h"
|
||||
|
||||
#define VRRP_LOGPFX "[ARP] "
|
||||
|
||||
/*
|
||||
* The size of the garp packet buffer should be the large enough to hold the
|
||||
* largest arp packet to be sent + the size of the link layer header for the
|
||||
* corresponding protocol. In this case we hardcode for Ethernet.
|
||||
*/
|
||||
#define GARP_BUFFER_SIZE \
|
||||
sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN \
|
||||
+ 2 * sizeof(struct in_addr)
|
||||
|
||||
/* static vars */
|
||||
static int garp_fd = -1;
|
||||
|
||||
/* Send the gratuitous ARP message */
|
||||
static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf,
|
||||
ssize_t pack_len)
|
||||
{
|
||||
struct sockaddr_ll sll;
|
||||
ssize_t len;
|
||||
|
||||
/* Build the dst device */
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_protocol = ETH_P_ARP;
|
||||
sll.sll_ifindex = (int)ifp->ifindex;
|
||||
sll.sll_halen = ifp->hw_addr_len;
|
||||
memset(sll.sll_addr, 0xFF, ETH_ALEN);
|
||||
|
||||
/* Send packet */
|
||||
len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll,
|
||||
sizeof(sll));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Build a gratuitous ARP message over a specific interface */
|
||||
static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp,
|
||||
struct in_addr *v4)
|
||||
{
|
||||
uint8_t *arp_ptr;
|
||||
|
||||
if (ifp->hw_addr_len == 0)
|
||||
return -1;
|
||||
|
||||
/* Build Ethernet header */
|
||||
struct ether_header *eth = (struct ether_header *)buf;
|
||||
|
||||
memset(eth->ether_dhost, 0xFF, ETH_ALEN);
|
||||
memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN);
|
||||
eth->ether_type = htons(ETHERTYPE_ARP);
|
||||
|
||||
/* Build ARP payload */
|
||||
struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN);
|
||||
|
||||
arph->ar_hrd = htons(HWTYPE_ETHER);
|
||||
arph->ar_pro = htons(ETHERTYPE_IP);
|
||||
arph->ar_hln = ifp->hw_addr_len;
|
||||
arph->ar_pln = sizeof(struct in_addr);
|
||||
arph->ar_op = htons(ARPOP_REQUEST);
|
||||
arp_ptr = (uint8_t *)(arph + 1);
|
||||
/* Source MAC: us */
|
||||
memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len);
|
||||
arp_ptr += ifp->hw_addr_len;
|
||||
/* Source IP: us */
|
||||
memcpy(arp_ptr, v4, sizeof(struct in_addr));
|
||||
arp_ptr += sizeof(struct in_addr);
|
||||
/* Dest MAC: broadcast */
|
||||
memset(arp_ptr, 0xFF, ETH_ALEN);
|
||||
arp_ptr += ifp->hw_addr_len;
|
||||
/* Dest IP: us */
|
||||
memcpy(arp_ptr, v4, sizeof(struct in_addr));
|
||||
arp_ptr += sizeof(struct in_addr);
|
||||
|
||||
return arp_ptr - buf;
|
||||
}
|
||||
|
||||
void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4)
|
||||
{
|
||||
struct interface *ifp = r->mvl_ifp;
|
||||
uint8_t garpbuf[GARP_BUFFER_SIZE];
|
||||
ssize_t garpbuf_len;
|
||||
ssize_t sent_len;
|
||||
char astr[INET_ADDRSTRLEN];
|
||||
|
||||
/* If the interface doesn't support ARP, don't try sending */
|
||||
if (ifp->flags & IFF_NOARP) {
|
||||
zlog_warn(
|
||||
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
|
||||
"Unable to send gratuitous ARP on %s; has IFF_NOARP\n",
|
||||
r->vr->vrid, family2str(r->family), ifp->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build garp */
|
||||
garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4);
|
||||
|
||||
/* Send garp */
|
||||
inet_ntop(AF_INET, v4, astr, sizeof(astr));
|
||||
|
||||
DEBUGD(&vrrp_dbg_arp,
|
||||
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
|
||||
"Sending gratuitous ARP on %s for %s",
|
||||
r->vr->vrid, family2str(r->family), ifp->name, astr);
|
||||
if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL))
|
||||
zlog_hexdump(garpbuf, garpbuf_len);
|
||||
|
||||
sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len);
|
||||
|
||||
if (sent_len < 0)
|
||||
zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
|
||||
"Error sending gratuitous ARP on %s for %s",
|
||||
r->vr->vrid, family2str(r->family), ifp->name, astr);
|
||||
else
|
||||
++r->stats.garp_tx_cnt;
|
||||
}
|
||||
|
||||
void vrrp_garp_send_all(struct vrrp_router *r)
|
||||
{
|
||||
assert(r->family == AF_INET);
|
||||
|
||||
struct interface *ifp = r->mvl_ifp;
|
||||
|
||||
/* If the interface doesn't support ARP, don't try sending */
|
||||
if (ifp->flags & IFF_NOARP) {
|
||||
zlog_warn(
|
||||
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
|
||||
"Unable to send gratuitous ARP on %s; has IFF_NOARP\n",
|
||||
r->vr->vrid, family2str(r->family), ifp->name);
|
||||
return;
|
||||
}
|
||||
|
||||
struct listnode *ln;
|
||||
struct ipaddr *ip;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
|
||||
vrrp_garp_send(r, &ip->ipaddr_v4);
|
||||
}
|
||||
|
||||
|
||||
void vrrp_garp_init(void)
|
||||
{
|
||||
/* Create the socket descriptor */
|
||||
/* FIXME: why ETH_P_RARP? */
|
||||
errno = 0;
|
||||
frr_elevate_privs(&vrrp_privs) {
|
||||
garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC,
|
||||
htons(ETH_P_RARP));
|
||||
}
|
||||
|
||||
if (garp_fd > 0) {
|
||||
DEBUGD(&vrrp_dbg_sock,
|
||||
VRRP_LOGPFX "Initialized gratuitous ARP socket");
|
||||
DEBUGD(&vrrp_dbg_arp,
|
||||
VRRP_LOGPFX "Initialized gratuitous ARP subsystem");
|
||||
} else {
|
||||
zlog_err(VRRP_LOGPFX
|
||||
"Error initializing gratuitous ARP subsystem");
|
||||
}
|
||||
}
|
||||
|
||||
void vrrp_garp_fini(void)
|
||||
{
|
||||
close(garp_fd);
|
||||
garp_fd = -1;
|
||||
|
||||
DEBUGD(&vrrp_dbg_arp,
|
||||
VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem");
|
||||
}
|
||||
|
||||
bool vrrp_garp_is_init(void)
|
||||
{
|
||||
return garp_fd > 0;
|
||||
}
|
36
vrrpd/vrrp_arp.h
Normal file
36
vrrpd/vrrp_arp.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* VRRP ARP handling.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_ARP_H__
|
||||
#define __VRRP_ARP_H__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include "vrrp.h"
|
||||
|
||||
/* FIXME: Use the kernel define for this */
|
||||
#define HWTYPE_ETHER 1
|
||||
|
||||
extern void vrrp_garp_init(void);
|
||||
extern void vrrp_garp_fini(void);
|
||||
extern bool vrrp_garp_is_init(void);
|
||||
extern void vrrp_garp_send(struct vrrp_router *vr, struct in_addr *v4);
|
||||
extern void vrrp_garp_send_all(struct vrrp_router *vr);
|
||||
|
||||
#endif /* __VRRP_ARP_H__ */
|
131
vrrpd/vrrp_debug.c
Normal file
131
vrrpd/vrrp_debug.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* VRRP debugging.
|
||||
* Copyright (C) 2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/command.h"
|
||||
#include "lib/debug.h"
|
||||
#include "lib/vector.h"
|
||||
|
||||
#include "vrrp_debug.h"
|
||||
|
||||
/* clang-format off */
|
||||
struct debug vrrp_dbg_arp = {0, "VRRP ARP"};
|
||||
struct debug vrrp_dbg_auto = {0, "VRRP autoconfiguration events"};
|
||||
struct debug vrrp_dbg_ndisc = {0, "VRRP Neighbor Discovery"};
|
||||
struct debug vrrp_dbg_pkt = {0, "VRRP packets"};
|
||||
struct debug vrrp_dbg_proto = {0, "VRRP protocol events"};
|
||||
struct debug vrrp_dbg_sock = {0, "VRRP sockets"};
|
||||
struct debug vrrp_dbg_zebra = {0, "VRRP Zebra events"};
|
||||
|
||||
struct debug *vrrp_debugs[] = {
|
||||
&vrrp_dbg_arp,
|
||||
&vrrp_dbg_auto,
|
||||
&vrrp_dbg_ndisc,
|
||||
&vrrp_dbg_pkt,
|
||||
&vrrp_dbg_proto,
|
||||
&vrrp_dbg_sock,
|
||||
&vrrp_dbg_zebra
|
||||
};
|
||||
|
||||
const char *vrrp_debugs_conflines[] = {
|
||||
"debug vrrp arp",
|
||||
"debug vrrp autoconfigure",
|
||||
"debug vrrp ndisc",
|
||||
"debug vrrp packets",
|
||||
"debug vrrp protocol",
|
||||
"debug vrrp sockets",
|
||||
"debug vrrp zebra",
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Set or unset flags on all debugs for vrrpd.
|
||||
*
|
||||
* flags
|
||||
* The flags to set
|
||||
*
|
||||
* set
|
||||
* Whether to set or unset the specified flags
|
||||
*/
|
||||
static void vrrp_debug_set_all(uint32_t flags, bool set)
|
||||
{
|
||||
for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) {
|
||||
DEBUG_FLAGS_SET(vrrp_debugs[i], flags, set);
|
||||
|
||||
/* if all modes have been turned off, don't preserve options */
|
||||
if (!DEBUG_MODE_CHECK(vrrp_debugs[i], DEBUG_MODE_ALL))
|
||||
DEBUG_CLEAR(vrrp_debugs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int vrrp_debug_config_write_helper(struct vty *vty, bool config)
|
||||
{
|
||||
uint32_t mode = DEBUG_MODE_ALL;
|
||||
|
||||
if (config)
|
||||
mode = DEBUG_MODE_CONF;
|
||||
|
||||
for (unsigned int i = 0; i < array_size(vrrp_debugs); i++)
|
||||
if (DEBUG_MODE_CHECK(vrrp_debugs[i], mode))
|
||||
vty_out(vty, "%s\n", vrrp_debugs_conflines[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vrrp_config_write_debug(struct vty *vty)
|
||||
{
|
||||
return vrrp_debug_config_write_helper(vty, true);
|
||||
}
|
||||
|
||||
int vrrp_debug_status_write(struct vty *vty)
|
||||
{
|
||||
return vrrp_debug_config_write_helper(vty, false);
|
||||
}
|
||||
|
||||
void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode,
|
||||
bool onoff, bool proto, bool autoconf, bool pkt, bool sock,
|
||||
bool ndisc, bool arp, bool zebra)
|
||||
{
|
||||
uint32_t mode = DEBUG_NODE2MODE(vtynode);
|
||||
|
||||
if (proto)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_proto, mode, onoff);
|
||||
if (autoconf)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_auto, mode, onoff);
|
||||
if (pkt)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_pkt, mode, onoff);
|
||||
if (sock)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_sock, mode, onoff);
|
||||
if (ndisc)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_ndisc, mode, onoff);
|
||||
if (arp)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_arp, mode, onoff);
|
||||
if (zebra)
|
||||
DEBUG_MODE_SET(&vrrp_dbg_zebra, mode, onoff);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct debug_callbacks vrrp_dbg_cbs = {.debug_set_all = vrrp_debug_set_all};
|
||||
|
||||
void vrrp_debug_init(void)
|
||||
{
|
||||
debug_init(&vrrp_dbg_cbs);
|
||||
}
|
87
vrrpd/vrrp_debug.h
Normal file
87
vrrpd/vrrp_debug.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* VRRP debugging.
|
||||
* Copyright (C) 2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_DEBUG_H__
|
||||
#define __VRRP_DEBUG_H__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/debug.h"
|
||||
|
||||
/* VRRP debugging records */
|
||||
struct debug vrrp_dbg_arp;
|
||||
struct debug vrrp_dbg_auto;
|
||||
struct debug vrrp_dbg_ndisc;
|
||||
struct debug vrrp_dbg_pkt;
|
||||
struct debug vrrp_dbg_proto;
|
||||
struct debug vrrp_dbg_sock;
|
||||
struct debug vrrp_dbg_zebra;
|
||||
|
||||
/*
|
||||
* Initialize VRRP debugging.
|
||||
*
|
||||
* Installs VTY commands and registers callbacks.
|
||||
*/
|
||||
void vrrp_debug_init(void);
|
||||
|
||||
/*
|
||||
* Print VRRP debugging configuration.
|
||||
*
|
||||
* vty
|
||||
* VTY to print debugging configuration to.
|
||||
*/
|
||||
int vrrp_config_write_debug(struct vty *vty);
|
||||
|
||||
/*
|
||||
* Print VRRP debugging configuration, human readable form.
|
||||
*
|
||||
* vty
|
||||
* VTY to print debugging configuration to.
|
||||
*/
|
||||
int vrrp_debug_status_write(struct vty *vty);
|
||||
|
||||
/*
|
||||
* Set debugging status.
|
||||
*
|
||||
* ifp
|
||||
* Interface to set status on
|
||||
*
|
||||
* vrid
|
||||
* VRID of instance to set status on
|
||||
*
|
||||
* vtynode
|
||||
* vty->node
|
||||
*
|
||||
* onoff
|
||||
* Whether to turn the specified debugs on or off
|
||||
*
|
||||
* proto
|
||||
* Turn protocol debugging on or off
|
||||
*
|
||||
* autoconf
|
||||
* Turn autoconfiguration debugging on or off
|
||||
*
|
||||
* pkt
|
||||
* Turn packet debugging on or off
|
||||
*/
|
||||
void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode,
|
||||
bool onoff, bool proto, bool autoconf, bool pkt, bool sock,
|
||||
bool ndisc, bool arp, bool zebra);
|
||||
|
||||
#endif /* __VRRP_DEBUG_H__ */
|
159
vrrpd/vrrp_main.c
Normal file
159
vrrpd/vrrp_main.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* VRRP entry point.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include <lib/version.h>
|
||||
|
||||
#include "lib/command.h"
|
||||
#include "lib/filter.h"
|
||||
#include "lib/getopt.h"
|
||||
#include "lib/if.h"
|
||||
#include "lib/libfrr.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/memory.h"
|
||||
#include "lib/nexthop.h"
|
||||
#include "lib/privs.h"
|
||||
#include "lib/sigevent.h"
|
||||
#include "lib/thread.h"
|
||||
#include "lib/vrf.h"
|
||||
|
||||
#include "vrrp.h"
|
||||
#include "vrrp_debug.h"
|
||||
#include "vrrp_vty.h"
|
||||
#include "vrrp_zebra.h"
|
||||
|
||||
char backup_config_file[256];
|
||||
|
||||
zebra_capabilities_t _caps_p[] = {
|
||||
ZCAP_NET_RAW,
|
||||
};
|
||||
|
||||
struct zebra_privs_t vrrp_privs = {
|
||||
#if defined(FRR_USER) && defined(FRR_GROUP)
|
||||
.user = FRR_USER,
|
||||
.group = FRR_GROUP,
|
||||
#endif
|
||||
#if defined(VTY_GROUP)
|
||||
.vty_group = VTY_GROUP,
|
||||
#endif
|
||||
.caps_p = _caps_p,
|
||||
.cap_num_p = array_size(_caps_p),
|
||||
.cap_num_i = 0};
|
||||
|
||||
struct option longopts[] = { {0} };
|
||||
|
||||
/* Master of threads. */
|
||||
struct thread_master *master;
|
||||
|
||||
/* SIGHUP handler. */
|
||||
static void sighup(void)
|
||||
{
|
||||
zlog_info("SIGHUP received");
|
||||
}
|
||||
|
||||
/* SIGINT / SIGTERM handler. */
|
||||
static void __attribute__((noreturn)) sigint(void)
|
||||
{
|
||||
zlog_notice("Terminating on signal");
|
||||
|
||||
vrrp_fini();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* SIGUSR1 handler. */
|
||||
static void sigusr1(void)
|
||||
{
|
||||
zlog_rotate();
|
||||
}
|
||||
|
||||
struct quagga_signal_t vrrp_signals[] = {
|
||||
{
|
||||
.signal = SIGHUP,
|
||||
.handler = &sighup,
|
||||
},
|
||||
{
|
||||
.signal = SIGUSR1,
|
||||
.handler = &sigusr1,
|
||||
},
|
||||
{
|
||||
.signal = SIGINT,
|
||||
.handler = &sigint,
|
||||
},
|
||||
{
|
||||
.signal = SIGTERM,
|
||||
.handler = &sigint,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct frr_yang_module_info *vrrp_yang_modules[] = {
|
||||
&frr_interface_info,
|
||||
};
|
||||
|
||||
#define VRRP_VTY_PORT 2619
|
||||
|
||||
FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT,
|
||||
.proghelp = "Virtual Router Redundancy Protocol",
|
||||
.signals = vrrp_signals,
|
||||
.n_signals = array_size(vrrp_signals),
|
||||
.privs = &vrrp_privs,
|
||||
.yang_modules = vrrp_yang_modules,
|
||||
.n_yang_modules = array_size(vrrp_yang_modules),
|
||||
)
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
frr_preinit(&vrrpd_di, argc, argv);
|
||||
frr_opt_add("", longopts, "");
|
||||
|
||||
while (1) {
|
||||
int opt;
|
||||
|
||||
opt = frr_getopt(argc, argv, NULL);
|
||||
|
||||
if (opt == EOF)
|
||||
break;
|
||||
|
||||
switch (opt) {
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
frr_help_exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
master = frr_init();
|
||||
|
||||
vrrp_debug_init();
|
||||
vrrp_zebra_init();
|
||||
vrrp_vty_init();
|
||||
vrrp_init();
|
||||
|
||||
snprintf(backup_config_file, sizeof(backup_config_file),
|
||||
"%s/vrrpd.conf", frr_sysconfdir);
|
||||
vrrpd_di.backup_config_file = backup_config_file;
|
||||
|
||||
frr_config_fork();
|
||||
frr_run(master);
|
||||
|
||||
/* Not reached. */
|
||||
return 0;
|
||||
}
|
29
vrrpd/vrrp_memory.c
Normal file
29
vrrpd/vrrp_memory.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* VRRP memory types.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/memory.h"
|
||||
|
||||
#include "vrrp_memory.h"
|
||||
|
||||
DEFINE_MGROUP(VRRPD, "vrrpd");
|
||||
DEFINE_MTYPE(VRRPD, VRRP_IP, "VRRP IP address");
|
||||
DEFINE_MTYPE(VRRPD, VRRP_PKT, "VRRP packet");
|
||||
DEFINE_MTYPE(VRRPD, VRRP_RTR, "VRRP Router");
|
32
vrrpd/vrrp_memory.h
Normal file
32
vrrpd/vrrp_memory.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* VRRP memory types.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_MEMORY_H__
|
||||
#define __VRRP_MEMORY_H__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/memory.h"
|
||||
|
||||
DECLARE_MGROUP(VRRPD);
|
||||
DECLARE_MTYPE(VRRP_IP);
|
||||
DECLARE_MTYPE(VRRP_PKT);
|
||||
DECLARE_MTYPE(VRRP_RTR);
|
||||
|
||||
#endif /* __VRRP_MEMORY_H__ */
|
242
vrrpd/vrrp_ndisc.c
Normal file
242
vrrpd/vrrp_ndisc.c
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* VRRP Neighbor Discovery.
|
||||
* Copyright (C) 2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* Portions:
|
||||
* Copyright (C) 2001-2017 Alexandre Cassen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include <linux/if_packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "lib/checksum.h"
|
||||
#include "lib/if.h"
|
||||
#include "lib/ipaddr.h"
|
||||
#include "lib/log.h"
|
||||
|
||||
#include "vrrp_debug.h"
|
||||
#include "vrrp_ndisc.h"
|
||||
|
||||
#define VRRP_LOGPFX "[NDISC] "
|
||||
|
||||
#define VRRP_NDISC_HOPLIMIT 255
|
||||
#define VRRP_NDISC_SIZE \
|
||||
ETHER_HDR_LEN + sizeof(struct ip6_hdr) \
|
||||
+ sizeof(struct nd_neighbor_advert) \
|
||||
+ sizeof(struct nd_opt_hdr) + ETH_ALEN
|
||||
|
||||
/* static vars */
|
||||
static int ndisc_fd = -1;
|
||||
|
||||
/*
|
||||
* Build an unsolicited Neighbour Advertisement.
|
||||
*
|
||||
* ifp
|
||||
* Interface to send Neighbor Advertisement on
|
||||
*
|
||||
* ip
|
||||
* IP address to send Neighbor Advertisement for
|
||||
*
|
||||
* buf
|
||||
* Buffer to fill with IPv6 Neighbor Advertisement message. Includes
|
||||
* Ethernet header.
|
||||
*
|
||||
* bufsiz
|
||||
* Size of buf.
|
||||
*
|
||||
* Returns;
|
||||
* -1 if bufsiz is too small
|
||||
* 0 otherwise
|
||||
*/
|
||||
static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip,
|
||||
uint8_t *buf, size_t bufsiz)
|
||||
{
|
||||
if (bufsiz < VRRP_NDISC_SIZE)
|
||||
return -1;
|
||||
|
||||
memset(buf, 0x00, bufsiz);
|
||||
|
||||
struct ether_header *eth = (struct ether_header *)buf;
|
||||
struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN);
|
||||
struct nd_neighbor_advert *ndh =
|
||||
(struct nd_neighbor_advert *)((char *)ip6h
|
||||
+ sizeof(struct ip6_hdr));
|
||||
struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr;
|
||||
struct nd_opt_hdr *nd_opt_h =
|
||||
(struct nd_opt_hdr *)((char *)ndh
|
||||
+ sizeof(struct nd_neighbor_advert));
|
||||
char *nd_opt_lladdr =
|
||||
(char *)((char *)nd_opt_h + sizeof(struct nd_opt_hdr));
|
||||
char *lladdr = (char *)ifp->hw_addr;
|
||||
|
||||
/*
|
||||
* An IPv6 packet with a multicast destination address DST, consisting
|
||||
* of the sixteen octets DST[1] through DST[16], is transmitted to the
|
||||
* Ethernet multicast address whose first two octets are the value 3333
|
||||
* hexadecimal and whose last four octets are the last four octets of
|
||||
* DST.
|
||||
* - RFC2464.7
|
||||
*
|
||||
* In this case we are sending to the all nodes multicast address, so
|
||||
* the last four octets are 0x00 0x00 0x00 0x01.
|
||||
*/
|
||||
memset(eth->ether_dhost, 0, ETH_ALEN);
|
||||
eth->ether_dhost[0] = 0x33;
|
||||
eth->ether_dhost[1] = 0x33;
|
||||
eth->ether_dhost[5] = 1;
|
||||
|
||||
/* Set source Ethernet address to interface link layer address */
|
||||
memcpy(eth->ether_shost, lladdr, ETH_ALEN);
|
||||
eth->ether_type = htons(ETHERTYPE_IPV6);
|
||||
|
||||
/* IPv6 Header */
|
||||
ip6h->ip6_vfc = 6 << 4;
|
||||
ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert)
|
||||
+ sizeof(struct nd_opt_hdr) + ETH_ALEN);
|
||||
ip6h->ip6_nxt = IPPROTO_ICMPV6;
|
||||
ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT;
|
||||
memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr));
|
||||
/* All nodes multicast address */
|
||||
ip6h->ip6_dst.s6_addr[0] = 0xFF;
|
||||
ip6h->ip6_dst.s6_addr[1] = 0x02;
|
||||
ip6h->ip6_dst.s6_addr[15] = 0x01;
|
||||
|
||||
/* ICMPv6 Header */
|
||||
ndh->nd_na_type = ND_NEIGHBOR_ADVERT;
|
||||
ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
|
||||
ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE;
|
||||
memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr));
|
||||
|
||||
/* NDISC Option header */
|
||||
nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR;
|
||||
nd_opt_h->nd_opt_len = 1;
|
||||
memcpy(nd_opt_lladdr, lladdr, ETH_ALEN);
|
||||
|
||||
/* Compute checksum */
|
||||
uint32_t len = sizeof(struct nd_neighbor_advert)
|
||||
+ sizeof(struct nd_opt_hdr) + ETH_ALEN;
|
||||
struct ipv6_ph ph = {};
|
||||
|
||||
ph.src = ip6h->ip6_src;
|
||||
ph.dst = ip6h->ip6_dst;
|
||||
ph.ulpl = htonl(len);
|
||||
ph.next_hdr = IPPROTO_ICMPV6;
|
||||
icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, (void *)icmp6h, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip)
|
||||
{
|
||||
assert(r->family == AF_INET6);
|
||||
|
||||
int ret = 0;
|
||||
struct interface *ifp = r->mvl_ifp;
|
||||
uint8_t buf[VRRP_NDISC_SIZE];
|
||||
|
||||
ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf));
|
||||
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
|
||||
struct sockaddr_ll sll;
|
||||
ssize_t len;
|
||||
|
||||
/* Build the dst device */
|
||||
memset(&sll, 0, sizeof(sll));
|
||||
sll.sll_family = AF_PACKET;
|
||||
memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN);
|
||||
sll.sll_halen = ETH_ALEN;
|
||||
sll.sll_ifindex = (int)ifp->ifindex;
|
||||
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
ipaddr2str(ip, ipbuf, sizeof(ipbuf));
|
||||
|
||||
DEBUGD(&vrrp_dbg_ndisc,
|
||||
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
|
||||
"Sending unsolicited Neighbor Advertisement on %s for %s",
|
||||
r->vr->vrid, family2str(r->family), ifp->name, ipbuf);
|
||||
|
||||
if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL)
|
||||
&& DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL))
|
||||
zlog_hexdump(buf, VRRP_NDISC_SIZE);
|
||||
|
||||
len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll,
|
||||
sizeof(sll));
|
||||
|
||||
if (len < 0) {
|
||||
zlog_err(
|
||||
VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM
|
||||
"Error sending unsolicited Neighbor Advertisement on %s for %s",
|
||||
r->vr->vrid, family2str(r->family), ifp->name, ipbuf);
|
||||
ret = -1;
|
||||
} else {
|
||||
++r->stats.una_tx_cnt;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vrrp_ndisc_una_send_all(struct vrrp_router *r)
|
||||
{
|
||||
assert(r->family == AF_INET6);
|
||||
|
||||
struct listnode *ln;
|
||||
struct ipaddr *ip;
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
|
||||
vrrp_ndisc_una_send(r, ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vrrp_ndisc_init(void)
|
||||
{
|
||||
frr_elevate_privs(&vrrp_privs)
|
||||
{
|
||||
ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));
|
||||
}
|
||||
|
||||
if (ndisc_fd > 0) {
|
||||
DEBUGD(&vrrp_dbg_sock,
|
||||
VRRP_LOGPFX "Initialized Neighbor Discovery socket");
|
||||
DEBUGD(&vrrp_dbg_ndisc,
|
||||
VRRP_LOGPFX "Initialized Neighbor Discovery subsystem");
|
||||
} else {
|
||||
zlog_err(VRRP_LOGPFX
|
||||
"Error initializing Neighbor Discovery socket");
|
||||
}
|
||||
}
|
||||
|
||||
void vrrp_ndisc_fini(void)
|
||||
{
|
||||
close(ndisc_fd);
|
||||
ndisc_fd = -1;
|
||||
|
||||
DEBUGD(&vrrp_dbg_ndisc,
|
||||
VRRP_LOGPFX "Deinitialized Neighbor Discovery subsystem");
|
||||
}
|
||||
|
||||
bool vrrp_ndisc_is_init(void)
|
||||
{
|
||||
return ndisc_fd > 0;
|
||||
}
|
74
vrrpd/vrrp_ndisc.h
Normal file
74
vrrpd/vrrp_ndisc.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* VRRP Neighbor Discovery.
|
||||
* Copyright (C) 2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_NDISC_H__
|
||||
#define __VRRP_NDISC_H__
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
|
||||
#include "vrrp.h"
|
||||
|
||||
/*
|
||||
* Initialize VRRP neighbor discovery.
|
||||
*/
|
||||
extern void vrrp_ndisc_init(void);
|
||||
|
||||
/*
|
||||
* Check whether VRRP Neighbor Discovery is initialized.
|
||||
*
|
||||
* Returns:
|
||||
* True if initialized, false otherwise
|
||||
*/
|
||||
extern bool vrrp_ndisc_is_init(void);
|
||||
|
||||
/*
|
||||
* Finish VRRP Neighbor Discovery.
|
||||
*/
|
||||
extern void vrrp_ndisc_fini(void);
|
||||
|
||||
/*
|
||||
* Send VRRP Neighbor Advertisement.
|
||||
*
|
||||
* ifp
|
||||
* Interface to transmit on
|
||||
*
|
||||
* ip
|
||||
* IPv6 address to send Neighbor Advertisement for
|
||||
*
|
||||
* Returns:
|
||||
* -1 on failure
|
||||
* 0 otherwise
|
||||
*/
|
||||
extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip);
|
||||
|
||||
/*
|
||||
* Send VRRP Neighbor Advertisements for all virtual IPs.
|
||||
*
|
||||
* r
|
||||
* Virtual Router to send NA's for
|
||||
*
|
||||
* Returns:
|
||||
* -1 on failure
|
||||
* 0 otherwise
|
||||
*/
|
||||
extern int vrrp_ndisc_una_send_all(struct vrrp_router *r);
|
||||
|
||||
#endif /* __VRRP_NDISC_H__ */
|
321
vrrpd/vrrp_packet.c
Normal file
321
vrrpd/vrrp_packet.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* VRRP packet crafting.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
|
||||
#include "lib/checksum.h"
|
||||
#include "lib/ipaddr.h"
|
||||
#include "lib/memory.h"
|
||||
|
||||
#include "vrrp.h"
|
||||
#include "vrrp_debug.h"
|
||||
#include "vrrp_memory.h"
|
||||
#include "vrrp_packet.h"
|
||||
|
||||
/* clang-format off */
|
||||
const char *vrrp_packet_names[16] = {
|
||||
[0] = "Unknown",
|
||||
[VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT",
|
||||
[2] = "Unknown",
|
||||
[3] = "Unknown",
|
||||
[4] = "Unknown",
|
||||
[5] = "Unknown",
|
||||
[6] = "Unknown",
|
||||
[7] = "Unknown",
|
||||
[8] = "Unknown",
|
||||
[9] = "Unknown",
|
||||
[10] = "Unknown",
|
||||
[11] = "Unknown",
|
||||
[12] = "Unknown",
|
||||
[13] = "Unknown",
|
||||
[14] = "Unknown",
|
||||
[15] = "Unknown",
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Compute the VRRP checksum.
|
||||
*
|
||||
* Checksum is not set in the packet, just computed.
|
||||
*
|
||||
* pkt
|
||||
* VRRP packet, fully filled out except for checksum field.
|
||||
*
|
||||
* pktsize
|
||||
* sizeof(*pkt)
|
||||
*
|
||||
* src
|
||||
* IP address that pkt will be transmitted from.
|
||||
*
|
||||
* Returns:
|
||||
* VRRP checksum in network byte order.
|
||||
*/
|
||||
static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize,
|
||||
struct ipaddr *src)
|
||||
{
|
||||
uint16_t chksum;
|
||||
bool v6 = (src->ipa_type == IPADDR_V6);
|
||||
|
||||
uint16_t chksum_pre = pkt->hdr.chksum;
|
||||
|
||||
pkt->hdr.chksum = 0;
|
||||
|
||||
if (v6) {
|
||||
struct ipv6_ph ph = {};
|
||||
|
||||
ph.src = src->ipaddr_v6;
|
||||
inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst);
|
||||
ph.ulpl = htons(pktsize);
|
||||
ph.next_hdr = 112;
|
||||
chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
|
||||
} else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) {
|
||||
struct ipv4_ph ph = {};
|
||||
|
||||
ph.src = src->ipaddr_v4;
|
||||
inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst);
|
||||
ph.proto = 112;
|
||||
ph.len = htons(pktsize);
|
||||
chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
|
||||
} else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) {
|
||||
chksum = in_cksum(pkt, pktsize);
|
||||
} else {
|
||||
assert(!"Invalid VRRP protocol version");
|
||||
}
|
||||
|
||||
pkt->hdr.chksum = chksum_pre;
|
||||
|
||||
return chksum;
|
||||
}
|
||||
|
||||
ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
|
||||
uint8_t version, uint8_t vrid, uint8_t prio,
|
||||
uint16_t max_adver_int, uint8_t numip,
|
||||
struct ipaddr **ips)
|
||||
{
|
||||
bool v6 = false;
|
||||
size_t addrsz = 0;
|
||||
|
||||
assert(version >= 2 && version <= 3);
|
||||
|
||||
if (numip > 0) {
|
||||
v6 = IS_IPADDR_V6(ips[0]);
|
||||
addrsz = IPADDRSZ(ips[0]);
|
||||
}
|
||||
|
||||
assert(!(version == 2 && v6));
|
||||
|
||||
size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip);
|
||||
|
||||
*pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize);
|
||||
|
||||
(*pkt)->hdr.vertype |= version << 4;
|
||||
(*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
|
||||
(*pkt)->hdr.vrid = vrid;
|
||||
(*pkt)->hdr.priority = prio;
|
||||
(*pkt)->hdr.naddr = numip;
|
||||
if (version == 3)
|
||||
(*pkt)->hdr.v3.adver_int = htons(max_adver_int);
|
||||
else if (version == 2) {
|
||||
(*pkt)->hdr.v2.auth_type = 0;
|
||||
(*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1);
|
||||
}
|
||||
|
||||
uint8_t *aptr = (void *)(*pkt)->addrs;
|
||||
|
||||
for (int i = 0; i < numip; i++) {
|
||||
memcpy(aptr, &ips[i]->ip.addr, addrsz);
|
||||
aptr += addrsz;
|
||||
}
|
||||
|
||||
(*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
|
||||
|
||||
return pktsize;
|
||||
}
|
||||
|
||||
size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt)
|
||||
{
|
||||
if (buflen < 1)
|
||||
return 0;
|
||||
|
||||
char tmpbuf[BUFSIZ];
|
||||
size_t rs = 0;
|
||||
struct vrrp_hdr *hdr = &pkt->hdr;
|
||||
|
||||
buf[0] = 0x00;
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4));
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ",
|
||||
(hdr->vertype & 0x0F),
|
||||
vrrp_packet_names[(hdr->vertype & 0x0F)]);
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid);
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority);
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr);
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ",
|
||||
ntohs(hdr->v3.adver_int));
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum));
|
||||
rs += strlcat(buf, tmpbuf, buflen);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
|
||||
size_t read, struct ipaddr *src,
|
||||
struct vrrp_pkt **pkt, char *errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
/* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */
|
||||
size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr)
|
||||
: sizeof(struct in6_addr);
|
||||
|
||||
size_t pktsize;
|
||||
uint8_t *buf = m->msg_iov->iov_base;
|
||||
|
||||
#define VRRP_PKT_VCHECK(cond, _f, ...) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
if (errmsg) \
|
||||
snprintf(errmsg, errmsg_len, (_f), \
|
||||
##__VA_ARGS__); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* IPvX header check */
|
||||
|
||||
if (family == AF_INET) {
|
||||
VRRP_PKT_VCHECK(
|
||||
read >= sizeof(struct ip),
|
||||
"Datagram not large enough to contain IP header");
|
||||
|
||||
struct ip *ip = (struct ip *)buf;
|
||||
|
||||
/* IP total length check */
|
||||
VRRP_PKT_VCHECK(
|
||||
ntohs(ip->ip_len) == read,
|
||||
"IPv4 packet length field does not match # received bytes; %" PRIu16
|
||||
"!= %zu",
|
||||
ntohs(ip->ip_len), read);
|
||||
|
||||
/* TTL check */
|
||||
VRRP_PKT_VCHECK(ip->ip_ttl == 255,
|
||||
"IPv4 TTL is %" PRIu8 "; should be 255",
|
||||
ip->ip_ttl);
|
||||
|
||||
*pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2));
|
||||
pktsize = read - (ip->ip_hl << 2);
|
||||
|
||||
/* IP empty packet check */
|
||||
VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload");
|
||||
|
||||
/* Extract source address */
|
||||
struct sockaddr_in *sa = m->msg_name;
|
||||
|
||||
src->ipa_type = IPADDR_V4;
|
||||
src->ipaddr_v4 = sa->sin_addr;
|
||||
} else if (family == AF_INET6) {
|
||||
struct cmsghdr *c;
|
||||
|
||||
for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) {
|
||||
if (c->cmsg_level == IPPROTO_IPV6
|
||||
&& c->cmsg_type == IPV6_HOPLIMIT)
|
||||
break;
|
||||
}
|
||||
|
||||
VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received");
|
||||
|
||||
uint8_t *hoplimit = CMSG_DATA(c);
|
||||
|
||||
VRRP_PKT_VCHECK(*hoplimit == 255,
|
||||
"IPv6 Hop Limit is %" PRIu8 "; should be 255",
|
||||
*hoplimit);
|
||||
|
||||
*pkt = (struct vrrp_pkt *)buf;
|
||||
pktsize = read;
|
||||
|
||||
/* Extract source address */
|
||||
struct sockaddr_in6 *sa = m->msg_name;
|
||||
|
||||
src->ipa_type = IPADDR_V6;
|
||||
memcpy(&src->ipaddr_v6, &sa->sin6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
} else {
|
||||
assert(!"Unknown address family");
|
||||
}
|
||||
|
||||
/* Size check */
|
||||
size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4
|
||||
: VRRP_MIN_PKT_SIZE_V6;
|
||||
size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4
|
||||
: VRRP_MAX_PKT_SIZE_V6;
|
||||
VRRP_PKT_VCHECK(pktsize >= minsize,
|
||||
"VRRP packet is undersized (%zu < %zu)", pktsize,
|
||||
minsize);
|
||||
VRRP_PKT_VCHECK(pktsize <= maxsize,
|
||||
"VRRP packet is oversized (%zu > %zu)", pktsize,
|
||||
maxsize);
|
||||
|
||||
/* Version check */
|
||||
uint8_t pktver = (*pkt)->hdr.vertype >> 4;
|
||||
|
||||
VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver);
|
||||
|
||||
/* Checksum check */
|
||||
uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src);
|
||||
|
||||
VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum,
|
||||
"Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "",
|
||||
(*pkt)->hdr.chksum, chksum);
|
||||
|
||||
/* Type check */
|
||||
VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8,
|
||||
(*pkt)->hdr.vertype & 0x0f);
|
||||
|
||||
/* Exact size check */
|
||||
size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr);
|
||||
|
||||
VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s",
|
||||
pktver == 2 ? " or missing auth fields" : "");
|
||||
|
||||
/* auth type check */
|
||||
if (version == 2)
|
||||
VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0,
|
||||
"Bad authentication type %" PRIu8,
|
||||
(*pkt)->hdr.v2.auth_type);
|
||||
|
||||
/* Addresses check */
|
||||
char vbuf[INET6_ADDRSTRLEN];
|
||||
uint8_t *p = (uint8_t *)(*pkt)->addrs;
|
||||
|
||||
for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) {
|
||||
VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)),
|
||||
"Bad IP address, #%" PRIu8, i);
|
||||
p += addrsz;
|
||||
}
|
||||
|
||||
/* Everything checks out */
|
||||
return pktsize;
|
||||
}
|
202
vrrpd/vrrp_packet.h
Normal file
202
vrrpd/vrrp_packet.h
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* VRRP packet crafting.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_PACKET_H__
|
||||
#define __VRRP_PACKET_H__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/ipaddr.h"
|
||||
#include "lib/memory.h"
|
||||
#include "lib/prefix.h"
|
||||
|
||||
#define VRRP_TYPE_ADVERTISEMENT 1
|
||||
|
||||
extern const char *vrrp_packet_names[16];
|
||||
|
||||
/*
|
||||
* Shared header for VRRPv2/v3 packets.
|
||||
*/
|
||||
struct vrrp_hdr {
|
||||
/*
|
||||
* H L H L
|
||||
* 0000 0000
|
||||
* ver type
|
||||
*/
|
||||
uint8_t vertype;
|
||||
uint8_t vrid;
|
||||
uint8_t priority;
|
||||
uint8_t naddr;
|
||||
union {
|
||||
struct {
|
||||
uint8_t auth_type;
|
||||
/* advertisement interval (in sec) */
|
||||
uint8_t adver_int;
|
||||
} v2;
|
||||
struct {
|
||||
/*
|
||||
* advertisement interval (in centiseconds)
|
||||
* H L H L
|
||||
* 0000 000000000000
|
||||
* rsvd adver_int
|
||||
*/
|
||||
uint16_t adver_int;
|
||||
} v3;
|
||||
};
|
||||
uint16_t chksum;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define VRRP_HDR_SIZE sizeof(struct vrrp_hdr)
|
||||
|
||||
struct vrrp_pkt {
|
||||
struct vrrp_hdr hdr;
|
||||
/*
|
||||
* When used, this is actually an array of one or the other, not an
|
||||
* array of union. If N v4 addresses are stored then
|
||||
* sizeof(addrs) == N * sizeof(struct in_addr).
|
||||
*
|
||||
* Under v2, the last 2 entries in this array are the authentication
|
||||
* data fields. We don't support auth in v2 so these are always just 8
|
||||
* bytes of 0x00.
|
||||
*/
|
||||
union {
|
||||
struct in_addr v4;
|
||||
struct in6_addr v6;
|
||||
} addrs[];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define VRRP_PKT_SIZE(_f, _ver, _naddr) \
|
||||
({ \
|
||||
size_t _asz = ((_f) == AF_INET) ? sizeof(struct in_addr) \
|
||||
: sizeof(struct in6_addr); \
|
||||
size_t _auth = 2 * sizeof(uint32_t) * (3 - (_ver)); \
|
||||
sizeof(struct vrrp_hdr) + (_asz * (_naddr)) + _auth; \
|
||||
})
|
||||
|
||||
#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 3, 1)
|
||||
#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 2, 255)
|
||||
#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 1)
|
||||
#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 255)
|
||||
|
||||
#define VRRP_MIN_PKT_SIZE VRRP_MIN_PKT_SIZE_V4
|
||||
#define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6
|
||||
|
||||
/*
|
||||
* Builds a VRRP ADVERTISEMENT packet.
|
||||
*
|
||||
* pkt
|
||||
* Pointer to store pointer to result buffer in
|
||||
*
|
||||
* src
|
||||
* Source address packet will be transmitted from. This is needed to compute
|
||||
* the VRRP checksum. The returned packet must be sent in an IP datagram with
|
||||
* the source address equal to this field, or the checksum will be invalid.
|
||||
*
|
||||
* version
|
||||
* VRRP version; must be 2 or 3
|
||||
*
|
||||
* vrid
|
||||
* Virtual Router Identifier
|
||||
*
|
||||
* prio
|
||||
* Virtual Router Priority
|
||||
*
|
||||
* max_adver_int
|
||||
* time between ADVERTISEMENTs
|
||||
*
|
||||
* v6
|
||||
* whether 'ips' is an array of v4 or v6 addresses
|
||||
*
|
||||
* numip
|
||||
* number of IPvX addresses in 'ips'
|
||||
*
|
||||
* ips
|
||||
* array of pointer to either struct in_addr (v6 = false) or struct in6_addr
|
||||
* (v6 = true)
|
||||
*/
|
||||
ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src,
|
||||
uint8_t version, uint8_t vrid, uint8_t prio,
|
||||
uint16_t max_adver_int, uint8_t numip,
|
||||
struct ipaddr **ips);
|
||||
|
||||
/*
|
||||
* Dumps a VRRP ADVERTISEMENT packet to a string.
|
||||
*
|
||||
* Currently only dumps the header.
|
||||
*
|
||||
* buf
|
||||
* Buffer to store string representation
|
||||
*
|
||||
* buflen
|
||||
* Size of buf
|
||||
*
|
||||
* pkt
|
||||
* Packet to dump to a string
|
||||
*
|
||||
* Returns:
|
||||
* # bytes written to buf
|
||||
*/
|
||||
size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt);
|
||||
|
||||
|
||||
/*
|
||||
* Parses a VRRP packet, checking for illegal or invalid data.
|
||||
*
|
||||
* This function parses both VRRPv2 and VRRPv3 packets. Which version is
|
||||
* expected is determined by the version argument. For example, if version is 3
|
||||
* and the received packet has version field 2 it will fail to parse.
|
||||
*
|
||||
* Note that this function only checks whether the packet itself is a valid
|
||||
* VRRP packet. It is up to the caller to validate whether the VRID is correct,
|
||||
* priority and timer values are correct, etc.
|
||||
*
|
||||
* family
|
||||
* Address family of received packet
|
||||
*
|
||||
* version
|
||||
* VRRP version to use for validation
|
||||
*
|
||||
* m
|
||||
* msghdr containing results of recvmsg() on VRRP router socket
|
||||
*
|
||||
* read
|
||||
* Return value of recvmsg() on VRRP router socket; must be non-negative
|
||||
*
|
||||
* src
|
||||
* Pointer to struct ipaddr to store address of datagram sender
|
||||
*
|
||||
* pkt
|
||||
* Pointer to pointer to set to location of VRRP packet within buf
|
||||
*
|
||||
* errmsg
|
||||
* Buffer to store human-readable error message in case of error; may be
|
||||
* NULL, in which case no message will be stored
|
||||
*
|
||||
* errmsg_len
|
||||
* Size of errmsg
|
||||
*
|
||||
* Returns:
|
||||
* Size of VRRP packet, or -1 upon error
|
||||
*/
|
||||
ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
|
||||
size_t read, struct ipaddr *src,
|
||||
struct vrrp_pkt **pkt, char *errmsg,
|
||||
size_t errmsg_len);
|
||||
|
||||
#endif /* __VRRP_PACKET_H__ */
|
751
vrrpd/vrrp_vty.c
Normal file
751
vrrpd/vrrp_vty.c
Normal file
@ -0,0 +1,751 @@
|
||||
/*
|
||||
* VRRP CLI commands.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/command.h"
|
||||
#include "lib/if.h"
|
||||
#include "lib/ipaddr.h"
|
||||
#include "lib/json.h"
|
||||
#include "lib/prefix.h"
|
||||
#include "lib/termtable.h"
|
||||
#include "lib/vty.h"
|
||||
|
||||
#include "vrrp.h"
|
||||
#include "vrrp_debug.h"
|
||||
#include "vrrp_memory.h"
|
||||
#include "vrrp_vty.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "vrrpd/vrrp_vty_clippy.c"
|
||||
#endif
|
||||
|
||||
|
||||
#define VRRP_STR "Virtual Router Redundancy Protocol\n"
|
||||
#define VRRP_VRID_STR "Virtual Router ID\n"
|
||||
#define VRRP_PRIORITY_STR "Virtual Router Priority\n"
|
||||
#define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n"
|
||||
#define VRRP_IP_STR "Virtual Router IPv4 address\n"
|
||||
#define VRRP_VERSION_STR "VRRP protocol version\n"
|
||||
|
||||
#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \
|
||||
do { \
|
||||
_vr = vrrp_lookup(_ifp, _vrid); \
|
||||
if (!_vr) { \
|
||||
vty_out(_vty, \
|
||||
"%% Please configure VRRP instance %u\n", \
|
||||
(unsigned int)_vrid); \
|
||||
return CMD_WARNING_CONFIG_FAILED; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
DEFPY(vrrp_vrid,
|
||||
vrrp_vrid_cmd,
|
||||
"[no] vrrp (1-255)$vrid [version (2-3)]",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
VRRP_VRID_STR
|
||||
VRRP_VERSION_STR
|
||||
VRRP_VERSION_STR)
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
|
||||
|
||||
if (version == 0)
|
||||
version = 3;
|
||||
|
||||
if (no && vr)
|
||||
vrrp_vrouter_destroy(vr);
|
||||
else if (no && !vr)
|
||||
vty_out(vty, "%% VRRP instance %ld does not exist on %s\n",
|
||||
vrid, ifp->name);
|
||||
else if (!vr)
|
||||
vrrp_vrouter_create(ifp, vrid, version);
|
||||
else if (vr)
|
||||
vty_out(vty, "%% VRRP instance %ld already exists on %s\n",
|
||||
vrid, ifp->name);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_shutdown,
|
||||
vrrp_shutdown_cmd,
|
||||
"[no] vrrp (1-255)$vrid shutdown",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
VRRP_VRID_STR
|
||||
"Force VRRP router into administrative shutdown\n")
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr;
|
||||
|
||||
VROUTER_GET_VTY(vty, ifp, vrid, vr);
|
||||
|
||||
if (!no) {
|
||||
if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE)
|
||||
vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
|
||||
if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE)
|
||||
vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
|
||||
vr->shutdown = true;
|
||||
} else {
|
||||
vr->shutdown = false;
|
||||
vrrp_check_start(vr);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_priority,
|
||||
vrrp_priority_cmd,
|
||||
"[no] vrrp (1-255)$vrid priority (1-254)",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
VRRP_VRID_STR
|
||||
VRRP_PRIORITY_STR
|
||||
"Priority value")
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr;
|
||||
uint8_t newprio = no ? vd.priority : priority;
|
||||
|
||||
VROUTER_GET_VTY(vty, ifp, vrid, vr);
|
||||
|
||||
vrrp_set_priority(vr, newprio);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_advertisement_interval,
|
||||
vrrp_advertisement_interval_cmd,
|
||||
"[no] vrrp (1-255)$vrid advertisement-interval (10-40950)",
|
||||
NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
|
||||
"Advertisement interval in milliseconds; must be multiple of 10")
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr;
|
||||
uint16_t newadvint =
|
||||
no ? vd.advertisement_interval * 10 : advertisement_interval;
|
||||
|
||||
if (newadvint % 10 != 0) {
|
||||
vty_out(vty, "%% Value must be a multiple of 10\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
|
||||
/* all internal computations are in centiseconds */
|
||||
newadvint /= CS2MS;
|
||||
|
||||
VROUTER_GET_VTY(vty, ifp, vrid, vr);
|
||||
vrrp_set_advertisement_interval(vr, newadvint);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_ip,
|
||||
vrrp_ip_cmd,
|
||||
"[no] vrrp (1-255)$vrid ip A.B.C.D",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
VRRP_VRID_STR
|
||||
"Add IPv4 address\n"
|
||||
VRRP_IP_STR)
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr;
|
||||
bool deactivated = false;
|
||||
bool activated = false;
|
||||
bool failed = false;
|
||||
int ret = CMD_SUCCESS;
|
||||
int oldstate;
|
||||
|
||||
VROUTER_GET_VTY(vty, ifp, vrid, vr);
|
||||
|
||||
bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE);
|
||||
|
||||
if (no) {
|
||||
oldstate = vr->v4->fsm.state;
|
||||
failed = vrrp_del_ipv4(vr, ip);
|
||||
vrrp_check_start(vr);
|
||||
deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE
|
||||
&& oldstate != VRRP_STATE_INITIALIZE);
|
||||
} else {
|
||||
oldstate = vr->v4->fsm.state;
|
||||
failed = vrrp_add_ipv4(vr, ip);
|
||||
vrrp_check_start(vr);
|
||||
activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE
|
||||
&& oldstate == VRRP_STATE_INITIALIZE);
|
||||
}
|
||||
|
||||
if (activated)
|
||||
vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid);
|
||||
if (deactivated)
|
||||
vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid);
|
||||
if (failed) {
|
||||
vty_out(vty, "%% Failed to %s virtual IP\n",
|
||||
no ? "remove" : "add");
|
||||
ret = CMD_WARNING_CONFIG_FAILED;
|
||||
if (will_activate && !activated) {
|
||||
vty_out(vty,
|
||||
"%% Failed to activate IPv4 Virtual Router %ld\n",
|
||||
vrid);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_ip6,
|
||||
vrrp_ip6_cmd,
|
||||
"[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
VRRP_VRID_STR
|
||||
"Add IPv6 address\n"
|
||||
VRRP_IP_STR)
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr;
|
||||
bool deactivated = false;
|
||||
bool activated = false;
|
||||
bool failed = false;
|
||||
int ret = CMD_SUCCESS;
|
||||
int oldstate;
|
||||
|
||||
VROUTER_GET_VTY(vty, ifp, vrid, vr);
|
||||
|
||||
if (vr->version != 3) {
|
||||
vty_out(vty,
|
||||
"%% Cannot add IPv6 address to VRRPv2 virtual router\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
|
||||
bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE);
|
||||
|
||||
if (no) {
|
||||
oldstate = vr->v6->fsm.state;
|
||||
failed = vrrp_del_ipv6(vr, ipv6);
|
||||
vrrp_check_start(vr);
|
||||
deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE
|
||||
&& oldstate != VRRP_STATE_INITIALIZE);
|
||||
} else {
|
||||
oldstate = vr->v6->fsm.state;
|
||||
failed = vrrp_add_ipv6(vr, ipv6);
|
||||
vrrp_check_start(vr);
|
||||
activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE
|
||||
&& oldstate == VRRP_STATE_INITIALIZE);
|
||||
}
|
||||
|
||||
if (activated)
|
||||
vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid);
|
||||
if (deactivated)
|
||||
vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid);
|
||||
if (failed) {
|
||||
vty_out(vty, "%% Failed to %s virtual IP\n",
|
||||
no ? "remove" : "add");
|
||||
ret = CMD_WARNING_CONFIG_FAILED;
|
||||
if (will_activate && !activated) {
|
||||
vty_out(vty,
|
||||
"%% Failed to activate IPv6 Virtual Router %ld\n",
|
||||
vrid);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_preempt,
|
||||
vrrp_preempt_cmd,
|
||||
"[no] vrrp (1-255)$vrid preempt",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
VRRP_VRID_STR
|
||||
"Preempt mode\n")
|
||||
{
|
||||
VTY_DECLVAR_CONTEXT(interface, ifp);
|
||||
|
||||
struct vrrp_vrouter *vr;
|
||||
|
||||
VROUTER_GET_VTY(vty, ifp, vrid, vr);
|
||||
|
||||
vr->preempt_mode = !no;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_autoconfigure,
|
||||
vrrp_autoconfigure_cmd,
|
||||
"[no] vrrp autoconfigure [version (2-3)]",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
"Automatically set up VRRP instances on VRRP-compatible interfaces\n"
|
||||
"Version for automatically configured instances\n"
|
||||
VRRP_VERSION_STR)
|
||||
{
|
||||
version = version ? version : 3;
|
||||
|
||||
if (!no)
|
||||
vrrp_autoconfig_on(version);
|
||||
else
|
||||
vrrp_autoconfig_off();
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_default,
|
||||
vrrp_default_cmd,
|
||||
"[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
|
||||
NO_STR
|
||||
VRRP_STR
|
||||
"Configure defaults for new VRRP instances\n"
|
||||
VRRP_ADVINT_STR
|
||||
"Advertisement interval in milliseconds\n"
|
||||
"Preempt mode\n"
|
||||
VRRP_PRIORITY_STR
|
||||
"Priority value\n"
|
||||
"Force VRRP router into administrative shutdown\n")
|
||||
{
|
||||
if (adv) {
|
||||
if (advint % 10 != 0) {
|
||||
vty_out(vty, "%% Value must be a multiple of 10\n");
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
/* all internal computations are in centiseconds */
|
||||
advint /= CS2MS;
|
||||
vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint;
|
||||
}
|
||||
if (p)
|
||||
vd.preempt_mode = !no;
|
||||
if (prio)
|
||||
vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval;
|
||||
if (s)
|
||||
vd.shutdown = !no;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Build JSON representation of VRRP instance.
|
||||
*
|
||||
* vr
|
||||
* VRRP router to build json object from
|
||||
*
|
||||
* Returns:
|
||||
* JSON representation of VRRP instance. Must be freed by caller.
|
||||
*/
|
||||
static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr)
|
||||
{
|
||||
char ethstr4[ETHER_ADDR_STRLEN];
|
||||
char ethstr6[ETHER_ADDR_STRLEN];
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
const char *stastr4 = vrrp_state_names[vr->v4->fsm.state];
|
||||
const char *stastr6 = vrrp_state_names[vr->v6->fsm.state];
|
||||
char sipstr4[INET6_ADDRSTRLEN] = {};
|
||||
char sipstr6[INET6_ADDRSTRLEN] = {};
|
||||
struct listnode *ln;
|
||||
struct ipaddr *ip;
|
||||
struct json_object *j = json_object_new_object();
|
||||
struct json_object *v4 = json_object_new_object();
|
||||
struct json_object *v4_stats = json_object_new_object();
|
||||
struct json_object *v4_addrs = json_object_new_array();
|
||||
struct json_object *v6 = json_object_new_object();
|
||||
struct json_object *v6_stats = json_object_new_object();
|
||||
struct json_object *v6_addrs = json_object_new_array();
|
||||
|
||||
prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
|
||||
prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
|
||||
|
||||
json_object_int_add(j, "vrid", vr->vrid);
|
||||
json_object_int_add(j, "version", vr->version);
|
||||
json_object_boolean_add(j, "autoconfigured", vr->autoconf);
|
||||
json_object_boolean_add(j, "shutdown", vr->shutdown);
|
||||
json_object_boolean_add(j, "preemptMode", vr->preempt_mode);
|
||||
json_object_boolean_add(j, "acceptMode", vr->accept_mode);
|
||||
json_object_string_add(j, "interface", vr->ifp->name);
|
||||
json_object_int_add(j, "advertisementInterval",
|
||||
vr->advertisement_interval * CS2MS);
|
||||
/* v4 */
|
||||
json_object_string_add(v4, "interface",
|
||||
vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "");
|
||||
json_object_string_add(v4, "vmac", ethstr4);
|
||||
ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4));
|
||||
json_object_string_add(v4, "primaryAddress", sipstr4);
|
||||
json_object_string_add(v4, "status", stastr4);
|
||||
json_object_int_add(v4, "effectivePriority", vr->v4->priority);
|
||||
json_object_int_add(v4, "masterAdverInterval",
|
||||
vr->v4->master_adver_interval * CS2MS);
|
||||
json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS);
|
||||
json_object_int_add(v4, "masterDownInterval",
|
||||
vr->v4->master_down_interval * CS2MS);
|
||||
/* v4 stats */
|
||||
json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt);
|
||||
json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt);
|
||||
json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt);
|
||||
json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt);
|
||||
json_object_object_add(v4, "stats", v4_stats);
|
||||
/* v4 addrs */
|
||||
if (vr->v4->addrs->count) {
|
||||
for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) {
|
||||
inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr,
|
||||
sizeof(ipstr));
|
||||
json_object_array_add(v4_addrs,
|
||||
json_object_new_string(ipstr));
|
||||
}
|
||||
}
|
||||
json_object_object_add(v4, "addresses", v4_addrs);
|
||||
json_object_object_add(j, "v4", v4);
|
||||
|
||||
/* v6 */
|
||||
json_object_string_add(v6, "interface",
|
||||
vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "");
|
||||
json_object_string_add(v6, "vmac", ethstr6);
|
||||
ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6));
|
||||
if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00)
|
||||
strlcat(sipstr6, "::", sizeof(sipstr6));
|
||||
json_object_string_add(v6, "primaryAddress", sipstr6);
|
||||
json_object_string_add(v6, "status", stastr6);
|
||||
json_object_int_add(v6, "effectivePriority", vr->v6->priority);
|
||||
json_object_int_add(v6, "masterAdverInterval",
|
||||
vr->v6->master_adver_interval * CS2MS);
|
||||
json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS);
|
||||
json_object_int_add(v6, "masterDownInterval",
|
||||
vr->v6->master_down_interval * CS2MS);
|
||||
/* v6 stats */
|
||||
json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt);
|
||||
json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt);
|
||||
json_object_int_add(v6_stats, "neighborAdverTx",
|
||||
vr->v6->stats.una_tx_cnt);
|
||||
json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt);
|
||||
json_object_object_add(v6, "stats", v6_stats);
|
||||
/* v6 addrs */
|
||||
if (vr->v6->addrs->count) {
|
||||
for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) {
|
||||
inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr,
|
||||
sizeof(ipstr));
|
||||
json_object_array_add(v6_addrs,
|
||||
json_object_new_string(ipstr));
|
||||
}
|
||||
}
|
||||
json_object_object_add(v6, "addresses", v6_addrs);
|
||||
json_object_object_add(j, "v6", v6);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump VRRP instance status to VTY.
|
||||
*
|
||||
* vty
|
||||
* vty to dump to
|
||||
*
|
||||
* vr
|
||||
* VRRP router to dump
|
||||
*/
|
||||
static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
|
||||
{
|
||||
char ethstr4[ETHER_ADDR_STRLEN];
|
||||
char ethstr6[ETHER_ADDR_STRLEN];
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
const char *stastr4 = vrrp_state_names[vr->v4->fsm.state];
|
||||
const char *stastr6 = vrrp_state_names[vr->v6->fsm.state];
|
||||
char sipstr4[INET6_ADDRSTRLEN] = {};
|
||||
char sipstr6[INET6_ADDRSTRLEN] = {};
|
||||
struct listnode *ln;
|
||||
struct ipaddr *ip;
|
||||
|
||||
struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
||||
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid);
|
||||
ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version);
|
||||
ttable_add_row(tt, "%s|%s", "Autoconfigured",
|
||||
vr->autoconf ? "Yes" : "No");
|
||||
ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No");
|
||||
ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name);
|
||||
prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
|
||||
prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
|
||||
ttable_add_row(tt, "%s|%s", "VRRP interface (v4)",
|
||||
vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None");
|
||||
ttable_add_row(tt, "%s|%s", "VRRP interface (v6)",
|
||||
vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None");
|
||||
ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4));
|
||||
ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6));
|
||||
if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00)
|
||||
strlcat(sipstr6, "::", sizeof(sipstr6));
|
||||
ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4);
|
||||
ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6);
|
||||
ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4);
|
||||
ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6);
|
||||
ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4);
|
||||
ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6);
|
||||
ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->priority);
|
||||
ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v4)",
|
||||
vr->v4->priority);
|
||||
ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v6)",
|
||||
vr->v6->priority);
|
||||
ttable_add_row(tt, "%s|%s", "Preempt Mode",
|
||||
vr->preempt_mode ? "Yes" : "No");
|
||||
ttable_add_row(tt, "%s|%s", "Accept Mode",
|
||||
vr->accept_mode ? "Yes" : "No");
|
||||
ttable_add_row(tt, "%s|%d ms", "Advertisement Interval",
|
||||
vr->advertisement_interval * CS2MS);
|
||||
ttable_add_row(tt, "%s|%d ms",
|
||||
"Master Advertisement Interval (v4)",
|
||||
vr->v4->master_adver_interval * CS2MS);
|
||||
ttable_add_row(tt, "%s|%d ms",
|
||||
"Master Advertisement Interval (v6)",
|
||||
vr->v6->master_adver_interval * CS2MS);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)",
|
||||
vr->v4->stats.adver_tx_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)",
|
||||
vr->v6->stats.adver_tx_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)",
|
||||
vr->v4->stats.adver_rx_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)",
|
||||
vr->v6->stats.adver_rx_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)",
|
||||
vr->v4->stats.garp_tx_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)",
|
||||
vr->v6->stats.una_tx_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)",
|
||||
vr->v4->stats.trans_cnt);
|
||||
ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)",
|
||||
vr->v6->stats.trans_cnt);
|
||||
ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)",
|
||||
vr->v4->skew_time * CS2MS);
|
||||
ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)",
|
||||
vr->v6->skew_time * CS2MS);
|
||||
ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)",
|
||||
vr->v4->master_down_interval * CS2MS);
|
||||
ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)",
|
||||
vr->v6->master_down_interval * CS2MS);
|
||||
ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count);
|
||||
|
||||
char fill[35];
|
||||
|
||||
memset(fill, '.', sizeof(fill));
|
||||
fill[sizeof(fill) - 1] = 0x00;
|
||||
if (vr->v4->addrs->count) {
|
||||
for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) {
|
||||
inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr,
|
||||
sizeof(ipstr));
|
||||
ttable_add_row(tt, "%s|%s", fill, ipstr);
|
||||
}
|
||||
}
|
||||
|
||||
ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count);
|
||||
|
||||
if (vr->v6->addrs->count) {
|
||||
for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) {
|
||||
inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr,
|
||||
sizeof(ipstr));
|
||||
ttable_add_row(tt, "%s|%s", fill, ipstr);
|
||||
}
|
||||
}
|
||||
|
||||
char *table = ttable_dump(tt, "\n");
|
||||
|
||||
vty_out(vty, "\n%s\n", table);
|
||||
XFREE(MTYPE_TMP, table);
|
||||
ttable_del(tt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort comparator, used when sorting VRRP instances for display purposes.
|
||||
*
|
||||
* Sorts by interface name first, then by VRID ascending.
|
||||
*/
|
||||
static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2)
|
||||
{
|
||||
const struct vrrp_vrouter *vr1 = *d1;
|
||||
const struct vrrp_vrouter *vr2 = *d2;
|
||||
int result;
|
||||
|
||||
result = strcmp(vr1->ifp->name, vr2->ifp->name);
|
||||
result += !result * (vr1->vrid - vr2->vrid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
DEFPY(vrrp_vrid_show,
|
||||
vrrp_vrid_show_cmd,
|
||||
"show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]",
|
||||
SHOW_STR
|
||||
VRRP_STR
|
||||
INTERFACE_STR
|
||||
"Only show VRRP instances on this interface\n"
|
||||
VRRP_VRID_STR
|
||||
JSON_STR)
|
||||
{
|
||||
struct vrrp_vrouter *vr;
|
||||
struct listnode *ln;
|
||||
struct list *ll = hash_to_list(vrrp_vrouters_hash);
|
||||
struct json_object *j = json_object_new_array();
|
||||
|
||||
list_sort(ll, vrrp_instance_display_sort_cmp);
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) {
|
||||
if (ifn && !strmatch(ifn, vr->ifp->name))
|
||||
continue;
|
||||
if (vrid && ((uint8_t) vrid) != vr->vrid)
|
||||
continue;
|
||||
|
||||
if (!json)
|
||||
vrrp_show(vty, vr);
|
||||
else
|
||||
json_object_array_add(j, vrrp_build_json(vr));
|
||||
}
|
||||
|
||||
if (json)
|
||||
vty_out(vty, "%s\n",
|
||||
json_object_to_json_string_ext(
|
||||
j, JSON_C_TO_STRING_PRETTY));
|
||||
|
||||
json_object_free(j);
|
||||
|
||||
list_delete(&ll);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFPY(vrrp_vrid_show_summary,
|
||||
vrrp_vrid_show_summary_cmd,
|
||||
"show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary",
|
||||
SHOW_STR
|
||||
VRRP_STR
|
||||
INTERFACE_STR
|
||||
"Only show VRRP instances on this interface\n"
|
||||
VRRP_VRID_STR
|
||||
"Summarize all VRRP instances\n")
|
||||
{
|
||||
struct vrrp_vrouter *vr;
|
||||
struct listnode *ln;
|
||||
struct list *ll = hash_to_list(vrrp_vrouters_hash);
|
||||
|
||||
list_sort(ll, vrrp_instance_display_sort_cmp);
|
||||
|
||||
struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
|
||||
|
||||
ttable_add_row(
|
||||
tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)");
|
||||
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) {
|
||||
if (ifn && !strmatch(ifn, vr->ifp->name))
|
||||
continue;
|
||||
if (vrid && ((uint8_t)vrid) != vr->vrid)
|
||||
continue;
|
||||
|
||||
ttable_add_row(
|
||||
tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s",
|
||||
vr->ifp->name, vr->vrid, vr->priority,
|
||||
vr->v4->addrs->count, vr->v6->addrs->count,
|
||||
vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master"
|
||||
: "Backup",
|
||||
vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master"
|
||||
: "Backup");
|
||||
}
|
||||
|
||||
char *table = ttable_dump(tt, "\n");
|
||||
|
||||
vty_out(vty, "\n%s\n", table);
|
||||
XFREE(MTYPE_TMP, table);
|
||||
ttable_del(tt);
|
||||
|
||||
list_delete(&ll);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFPY(debug_vrrp,
|
||||
debug_vrrp_cmd,
|
||||
"[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]",
|
||||
NO_STR
|
||||
DEBUG_STR
|
||||
VRRP_STR
|
||||
"Debug protocol state\n"
|
||||
"Debug autoconfiguration\n"
|
||||
"Debug sent and received packets\n"
|
||||
"Debug socket creation and configuration\n"
|
||||
"Debug Neighbor Discovery\n"
|
||||
"Debug ARP\n"
|
||||
"Debug Zebra events\n")
|
||||
{
|
||||
/* If no specific are given on/off them all */
|
||||
if (strmatch(argv[argc - 1]->text, "vrrp"))
|
||||
vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true,
|
||||
true, true, true);
|
||||
else
|
||||
vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt,
|
||||
!!sock, !!ndisc, !!arp, !!zebra);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_NOSH (show_debugging_vrrp,
|
||||
show_debugging_vrrp_cmd,
|
||||
"show debugging [vrrp]",
|
||||
SHOW_STR
|
||||
DEBUG_STR
|
||||
"VRRP information\n")
|
||||
{
|
||||
vty_out(vty, "VRRP debugging status:\n");
|
||||
|
||||
vrrp_debug_status_write(vty);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1};
|
||||
static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
|
||||
static struct cmd_node vrrp_node = {VRRP_NODE, "", 1};
|
||||
|
||||
void vrrp_vty_init(void)
|
||||
{
|
||||
install_node(&debug_node, vrrp_config_write_debug);
|
||||
install_node(&interface_node, vrrp_config_write_interface);
|
||||
install_node(&vrrp_node, vrrp_config_write_global);
|
||||
if_cmd_init();
|
||||
|
||||
install_element(VIEW_NODE, &vrrp_vrid_show_cmd);
|
||||
install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd);
|
||||
install_element(VIEW_NODE, &show_debugging_vrrp_cmd);
|
||||
install_element(VIEW_NODE, &debug_vrrp_cmd);
|
||||
install_element(CONFIG_NODE, &debug_vrrp_cmd);
|
||||
install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd);
|
||||
install_element(CONFIG_NODE, &vrrp_default_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_vrid_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_shutdown_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_priority_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_ip_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
|
||||
install_element(INTERFACE_NODE, &vrrp_preempt_cmd);
|
||||
}
|
25
vrrpd/vrrp_vty.h
Normal file
25
vrrpd/vrrp_vty.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* VRRP CLI commands.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_VTY_H__
|
||||
#define __VRRP_VTY_H__
|
||||
|
||||
void vrrp_vty_init(void);
|
||||
|
||||
#endif /* __VRRP_VTY_H__ */
|
252
vrrpd/vrrp_zebra.c
Normal file
252
vrrpd/vrrp_zebra.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* VRRP Zebra interfacing.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/if.h"
|
||||
#include "lib/linklist.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/prefix.h"
|
||||
#include "lib/vty.h"
|
||||
#include "lib/zclient.h"
|
||||
|
||||
#include "vrrp.h"
|
||||
#include "vrrp_debug.h"
|
||||
#include "vrrp_zebra.h"
|
||||
|
||||
#define VRRP_LOGPFX "[ZEBRA] "
|
||||
|
||||
static struct zclient *zclient;
|
||||
|
||||
static void vrrp_zebra_debug_if_state(struct interface *ifp, vrf_id_t vrf_id,
|
||||
const char *func)
|
||||
{
|
||||
DEBUGD(&vrrp_dbg_zebra,
|
||||
"%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
|
||||
func, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags,
|
||||
ifp->metric, ifp->mtu, if_is_operative(ifp));
|
||||
}
|
||||
|
||||
static void vrrp_zebra_debug_if_dump_address(struct interface *ifp,
|
||||
const char *func)
|
||||
{
|
||||
struct connected *ifc;
|
||||
struct listnode *node;
|
||||
|
||||
DEBUGD(&vrrp_dbg_zebra, "%s: interface %s addresses:", func, ifp->name);
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
|
||||
struct prefix *p = ifc->address;
|
||||
|
||||
DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %s %s", func,
|
||||
ifp->name, inet_ntoa(p->u.prefix4),
|
||||
CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary"
|
||||
: "primary");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void vrrp_zebra_connected(struct zclient *zclient)
|
||||
{
|
||||
zclient_send_reg_requests(zclient, VRF_DEFAULT);
|
||||
}
|
||||
|
||||
/* Router-id update message from zebra. */
|
||||
static int vrrp_router_id_update_zebra(int command, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct prefix router_id;
|
||||
|
||||
zebra_router_id_update_read(zclient->ibuf, &router_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vrrp_zebra_if_add(int command, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
/*
|
||||
* zebra api adds/dels interfaces using the same call
|
||||
* interface_add_read below, see comments in lib/zclient.c
|
||||
*/
|
||||
ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
|
||||
|
||||
if (!ifp)
|
||||
return 0;
|
||||
|
||||
vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
|
||||
|
||||
vrrp_if_add(ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vrrp_zebra_if_del(int command, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
|
||||
|
||||
if (!ifp)
|
||||
return 0;
|
||||
|
||||
vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
|
||||
|
||||
vrrp_if_del(ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vrrp_zebra_if_state_up(int command, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
/*
|
||||
* zebra api notifies interface up/down events by using the same call
|
||||
* zebra_interface_state_read below, see comments in lib/zclient.c ifp =
|
||||
* zebra_interface_state_read(zclient->ibuf, vrf_id);
|
||||
*/
|
||||
ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
|
||||
|
||||
if (!ifp)
|
||||
return 0;
|
||||
|
||||
vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
|
||||
|
||||
vrrp_if_up(ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vrrp_zebra_if_state_down(int command, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct interface *ifp;
|
||||
|
||||
/*
|
||||
* zebra api notifies interface up/down events by using the same call
|
||||
* zebra_interface_state_read below, see comments in lib/zclient.c
|
||||
*/
|
||||
ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
|
||||
|
||||
if (!ifp)
|
||||
return 0;
|
||||
|
||||
vrrp_zebra_debug_if_state(ifp, vrf_id, __func__);
|
||||
|
||||
vrrp_if_down(ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vrrp_zebra_if_address_add(int command, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct connected *c;
|
||||
|
||||
/*
|
||||
* zebra api notifies address adds/dels events by using the same call
|
||||
* interface_add_read below, see comments in lib/zclient.c
|
||||
*
|
||||
* zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
|
||||
* will add address to interface list by calling
|
||||
* connected_add_by_prefix()
|
||||
*/
|
||||
c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__);
|
||||
vrrp_zebra_debug_if_dump_address(c->ifp, __func__);
|
||||
|
||||
vrrp_if_address_add(c->ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vrrp_zebra_if_address_del(int command, struct zclient *client,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
struct connected *c;
|
||||
|
||||
/*
|
||||
* zebra api notifies address adds/dels events by using the same call
|
||||
* interface_add_read below, see comments in lib/zclient.c
|
||||
*
|
||||
* zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
|
||||
* will remove address from interface list by calling
|
||||
* connected_delete_by_prefix()
|
||||
*/
|
||||
c = zebra_interface_address_read(command, client->ibuf, vrf_id);
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__);
|
||||
vrrp_zebra_debug_if_dump_address(c->ifp, __func__);
|
||||
|
||||
vrrp_if_address_del(c->ifp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable)
|
||||
{
|
||||
DEBUGD(&vrrp_dbg_zebra,
|
||||
VRRP_LOGPFX VRRP_LOGPFX_VRID
|
||||
"Requesting Zebra to turn router advertisements %s for %s",
|
||||
r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name);
|
||||
|
||||
zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp,
|
||||
enable, VRRP_RADV_INT);
|
||||
}
|
||||
|
||||
int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down)
|
||||
{
|
||||
DEBUGD(&vrrp_dbg_zebra,
|
||||
VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name,
|
||||
down ? "on" : "off");
|
||||
|
||||
return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp,
|
||||
down);
|
||||
}
|
||||
|
||||
void vrrp_zebra_init(void)
|
||||
{
|
||||
/* Socket for receiving updates from Zebra daemon */
|
||||
zclient = zclient_new(master, &zclient_options_default);
|
||||
|
||||
zclient->zebra_connected = vrrp_zebra_connected;
|
||||
zclient->router_id_update = vrrp_router_id_update_zebra;
|
||||
zclient->interface_add = vrrp_zebra_if_add;
|
||||
zclient->interface_delete = vrrp_zebra_if_del;
|
||||
zclient->interface_up = vrrp_zebra_if_state_up;
|
||||
zclient->interface_down = vrrp_zebra_if_state_down;
|
||||
zclient->interface_address_add = vrrp_zebra_if_address_add;
|
||||
zclient->interface_address_delete = vrrp_zebra_if_address_del;
|
||||
|
||||
zclient_init(zclient, ZEBRA_ROUTE_VRRP, 0, &vrrp_privs);
|
||||
|
||||
zlog_notice("%s: zclient socket initialized", __PRETTY_FUNCTION__);
|
||||
}
|
32
vrrpd/vrrp_zebra.h
Normal file
32
vrrpd/vrrp_zebra.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* VRRP Zebra interfacing.
|
||||
* Copyright (C) 2018-2019 Cumulus Networks, Inc.
|
||||
* Quentin Young
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; see the file COPYING; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef __VRRP_ZEBRA_H__
|
||||
#define __VRRP_ZEBRA_H__
|
||||
|
||||
#include <zebra.h>
|
||||
|
||||
#include "lib/if.h"
|
||||
|
||||
extern void vrrp_zebra_init(void);
|
||||
extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable);
|
||||
extern int vrrp_zclient_send_interface_protodown(struct interface *ifp,
|
||||
bool down);
|
||||
|
||||
#endif /* __VRRP_ZEBRA_H__ */
|
@ -137,6 +137,7 @@ struct vtysh_client vtysh_client[] = {
|
||||
{.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL},
|
||||
{.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL},
|
||||
{.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL},
|
||||
{.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL},
|
||||
};
|
||||
|
||||
enum vtysh_write_integrated vtysh_write_integrated =
|
||||
|
@ -42,6 +42,7 @@ DECLARE_MGROUP(MVTYSH)
|
||||
#define VTYSH_STATICD 0x08000
|
||||
#define VTYSH_BFDD 0x10000
|
||||
#define VTYSH_FABRICD 0x20000
|
||||
#define VTYSH_VRRPD 0x40000
|
||||
|
||||
#define VTYSH_WAS_ACTIVE (-2)
|
||||
|
||||
@ -50,9 +51,9 @@ DECLARE_MGROUP(MVTYSH)
|
||||
/* watchfrr is not in ALL since library CLI functions should not be
|
||||
* run on it (logging & co. should stay in a fixed/frozen config, and
|
||||
* things like prefix lists are not even initialised) */
|
||||
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD
|
||||
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD
|
||||
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD
|
||||
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD
|
||||
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD
|
||||
#define VTYSH_NS VTYSH_ZEBRA
|
||||
#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD
|
||||
#define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD
|
||||
|
@ -257,6 +257,10 @@ void vtysh_config_parse_line(void *arg, const char *line)
|
||||
strlen(" exit-vrf"))
|
||||
== 0) {
|
||||
config_add_line_uniq_end(config->line, line);
|
||||
} else if (!strncmp(line, " vrrp", strlen(" vrrp"))
|
||||
|| !strncmp(line, " no vrrp",
|
||||
strlen(" no vrrp"))) {
|
||||
config_add_line(config->line, line);
|
||||
} else if (config->index == RMAP_NODE
|
||||
|| config->index == INTERFACE_NODE
|
||||
|| config->index == LOGICALROUTER_NODE
|
||||
|
@ -1396,6 +1396,32 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netlink_protodown(struct interface *ifp, bool down)
|
||||
{
|
||||
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
|
||||
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifinfomsg ifa;
|
||||
char buf[NL_PKT_BUF_SIZE];
|
||||
} req;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
req.n.nlmsg_type = RTM_SETLINK;
|
||||
req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid;
|
||||
|
||||
req.ifa.ifi_index = ifp->ifindex;
|
||||
|
||||
addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, 4);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifp->ifindex, 4);
|
||||
|
||||
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
|
||||
0);
|
||||
}
|
||||
|
||||
/* Interface information read by netlink. */
|
||||
void interface_list(struct zebra_ns *zns)
|
||||
{
|
||||
|
@ -32,6 +32,20 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
|
||||
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
|
||||
extern int interface_lookup_netlink(struct zebra_ns *zns);
|
||||
|
||||
/*
|
||||
* Set protodown status of interface.
|
||||
*
|
||||
* ifp
|
||||
* Interface to set protodown on.
|
||||
*
|
||||
* down
|
||||
* If true, set protodown on. If false, set protodown off.
|
||||
*
|
||||
* Returns:
|
||||
* 0
|
||||
*/
|
||||
int netlink_protodown(struct interface *ifp, bool down);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "zebra/irdp.h"
|
||||
#include "zebra/zebra_ptm.h"
|
||||
#include "zebra/rt_netlink.h"
|
||||
#include "zebra/if_netlink.h"
|
||||
#include "zebra/interface.h"
|
||||
#include "zebra/zebra_vxlan.h"
|
||||
#include "zebra/zebra_errors.h"
|
||||
@ -1063,7 +1064,14 @@ void zebra_if_update_all_links(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void zebra_if_set_protodown(struct interface *ifp, bool down)
|
||||
{
|
||||
#ifdef HAVE_NETLINK
|
||||
netlink_protodown(ifp, down);
|
||||
#else
|
||||
zlog_warn("Protodown is not supported on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Output prefix string to vty. */
|
||||
static int prefix_vty_out(struct vty *vty, struct prefix *p)
|
||||
|
@ -422,6 +422,7 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id);
|
||||
extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex,
|
||||
ns_id_t ns_id);
|
||||
extern void zebra_if_update_all_links(void);
|
||||
extern void zebra_if_set_protodown(struct interface *ifp, bool down);
|
||||
|
||||
extern void vrf_add_update(struct vrf *vrfp);
|
||||
|
||||
|
@ -71,6 +71,8 @@
|
||||
static void zserv_encode_interface(struct stream *s, struct interface *ifp)
|
||||
{
|
||||
/* Interface information. */
|
||||
struct zebra_if *zif = ifp->info;
|
||||
|
||||
stream_put(s, ifp->name, INTERFACE_NAMSIZ);
|
||||
stream_putl(s, ifp->ifindex);
|
||||
stream_putc(s, ifp->status);
|
||||
@ -82,6 +84,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp)
|
||||
stream_putl(s, ifp->mtu);
|
||||
stream_putl(s, ifp->mtu6);
|
||||
stream_putl(s, ifp->bandwidth);
|
||||
stream_putl(s, zif->link_ifindex);
|
||||
stream_putl(s, ifp->ll_type);
|
||||
stream_putl(s, ifp->hw_addr_len);
|
||||
if (ifp->hw_addr_len)
|
||||
@ -1336,6 +1339,37 @@ static void zread_interface_delete(ZAPI_HANDLER_ARGS)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle message requesting interface be set up or down.
|
||||
*/
|
||||
static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS)
|
||||
{
|
||||
ifindex_t ifindex;
|
||||
struct interface *ifp;
|
||||
char down;
|
||||
|
||||
STREAM_GETL(msg, ifindex);
|
||||
STREAM_GETC(msg, down);
|
||||
|
||||
/* set ifdown */
|
||||
ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex);
|
||||
|
||||
if (ifp) {
|
||||
zlog_info("Setting interface %s (%u): protodown %s", ifp->name,
|
||||
ifindex, down ? "on" : "off");
|
||||
zebra_if_set_protodown(ifp, down);
|
||||
} else {
|
||||
zlog_warn(
|
||||
"Cannot set protodown %s for interface %u; does not exist",
|
||||
down ? "on" : "off", ifindex);
|
||||
}
|
||||
|
||||
|
||||
stream_failure:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void zserv_nexthop_num_warn(const char *caller, const struct prefix *p,
|
||||
const unsigned int nexthop_num)
|
||||
{
|
||||
@ -2412,6 +2446,7 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
|
||||
[ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,
|
||||
[ZEBRA_INTERFACE_ADD] = zread_interface_add,
|
||||
[ZEBRA_INTERFACE_DELETE] = zread_interface_delete,
|
||||
[ZEBRA_INTERFACE_SET_PROTODOWN] = zread_interface_set_protodown,
|
||||
[ZEBRA_ROUTE_ADD] = zread_route_add,
|
||||
[ZEBRA_ROUTE_DELETE] = zread_route_del,
|
||||
[ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add,
|
||||
|
@ -101,6 +101,7 @@ static const struct {
|
||||
[ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4},
|
||||
[ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4},
|
||||
[ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2},
|
||||
[ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 4}
|
||||
/* Any new route type added to zebra, should be mirrored here */
|
||||
|
||||
/* no entry/default: 150 */
|
||||
|
Loading…
Reference in New Issue
Block a user