Merge pull request #4168 from qlyoung/vrrp

VRRP
This commit is contained in:
Sri Mohana Singamsetty 2019-05-17 11:39:27 -07:00 committed by GitHub
commit 7cfaf4b339
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 6438 additions and 11 deletions

View File

@ -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

View File

@ -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])

View File

@ -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

View File

@ -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.

View File

@ -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 -------------------------------------------

View File

@ -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)

View File

@ -26,4 +26,5 @@
watchfrr
zebra
vtysh
vrrpd
frr

View File

@ -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
View 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

View File

@ -56,6 +56,7 @@ Protocols
sharp
static
vnc
vrrp
########
Appendix

View File

@ -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
View 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.

View 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 */

View File

@ -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);

View File

@ -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 */

View File

@ -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 */
};

View File

@ -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.. \

View File

@ -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 *);

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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.
*

View File

@ -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"

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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}

View File

@ -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=""

View File

@ -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

View File

@ -410,7 +410,8 @@ end
"service ",
"table ",
"username ",
"zebra ")
"zebra ",
"vrrp autoconfigure")
for line in self.lines:

View File

@ -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"

View File

@ -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
View File

@ -0,0 +1,2 @@
libvrrp.a
vrrpd

10
vrrpd/Makefile Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

570
vrrpd/vrrp.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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__ */

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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,

View File

@ -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 */