python/clidef.py \
python/clippy/__init__.py \
\
- redhat/frr.init \
- redhat/frr.service \
- redhat/daemons \
redhat/frr.logrotate \
redhat/frr.pam \
redhat/frr.spec \
- redhat/README.rpm_build.md \
\
snapcraft/snapcraft.yaml \
snapcraft/README.snap_build.md \
{
struct bfd_session_observer *bso;
- bso = XMALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
+ bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
+ bso->bso_isaddress = false;
bso->bso_bs = bs;
bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
if (bso->bso_isinterface)
if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
bpc->bpc_local.sa_sin.sin_family = AF_INET6;
- memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.peer,
+ memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.local,
sizeof(bpc->bpc_local.sa_sin.sin_addr));
}
break;
sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
- memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.peer,
+ memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.local,
sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
break;
}
if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
sd = bglobal.bg_echov6;
memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
if (bfd->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
sin6.sin6_scope_id = bfd->ifp->ifindex;
} else {
sd = bglobal.bg_echo;
memset(&sin6, 0, sizeof(sin6));
+ sin.sin_family = AF_INET;
memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
sin.sin_port = htons(BFD_DEF_ECHO_PORT);
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
if (!peer->nexthop.ifp) {
- zlog_warn("%s: interface not set appropriately to handle some attributes",
+ zlog_warn("%s: Received a V6/VPNV6 Global attribute but address is a V6 LL and we have no peer interface information, withdrawing",
peer->host);
return BGP_ATTR_PARSE_WITHDRAW;
}
stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
if (!peer->nexthop.ifp) {
- zlog_warn("%s: interface not set appropriately to handle some attributes",
+ zlog_warn("%s: Received V6/VPNV6 Global and LL attribute but global address is a V6 LL and we have no peer interface information, withdrawing",
peer->host);
return BGP_ATTR_PARSE_WITHDRAW;
}
attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
}
if (!peer->nexthop.ifp) {
- zlog_warn("%s: Interface not set appropriately to handle this some attributes",
+ zlog_warn("%s: Received a V6 LL nexthop and we have no peer interface information, withdrawing",
peer->host);
return BGP_ATTR_PARSE_WITHDRAW;
}
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_nexthop.h"
#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_mac.h"
/*
* Definitions and external declarations.
return 0;
}
+/* This API will scan evpn routes for checking attribute's rmac
+ * macthes with bgp instance router mac. It avoid installing
+ * route into bgp vrf table and remote rmac in bridge table.
+ */
+static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_path_info *pi)
+{
+ /* evpn route could have learnt prior to L3vni has come up,
+ * perform rmac check before installing route and
+ * remote router mac.
+ * The route will be removed from global bgp table once
+ * SVI comes up with MAC and stored in hash, triggers
+ * bgp_mac_rescan_all_evpn_tables.
+ */
+ if (pi->attr &&
+ memcmp(&bgp_vrf->rmac, &pi->attr->rmac, ETH_ALEN) == 0) {
+ if (bgp_debug_update(pi->peer, NULL, NULL, 1)) {
+ char buf1[PREFIX_STRLEN];
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(pi->attr, attr_str, BUFSIZ);
+
+ zlog_debug("%s: bgp %u prefix %s with attr %s - DENIED due to self mac",
+ __func__, bgp_vrf->vrf_id,
+ prefix2str(evp, buf1, sizeof(buf1)),
+ attr_str);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Install or uninstall mac-ip routes are appropriate for this
* particular VRF.
continue;
if (is_route_matching_for_vrf(bgp_vrf, pi)) {
+ if (bgp_evpn_route_rmac_self_check(
+ bgp_vrf, evp, pi))
+ continue;
+
if (install)
ret = install_evpn_route_entry_in_vrf(
bgp_vrf, evp, pi);
*/
void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp)
{
+ if (is_vrf_rd_configured(bgp))
+ return;
+
form_auto_rd(bgp->router_id, bgp->vrf_rd_id, &bgp->vrf_prd);
}
#define RT_ADDRSTRLEN 28
-/* EVPN prefix lengths. This reprsent the sizeof struct prefix_evpn */
-#define EVPN_ROUTE_PREFIXLEN 224
+/* EVPN prefix lengths. This represents the sizeof struct evpn_addr
+ * in bits */
+#define EVPN_ROUTE_PREFIXLEN (sizeof(struct evpn_addr) * 8)
/* EVPN route types. */
typedef enum {
vty_out(vty, " default-originate ipv6\n");
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD))
- vty_out(vty, " rd %s\n",
+ vty_out(vty, " rd %s\n",
prefix_rd2str(&bgp->vrf_prd, buf1, sizeof(buf1)));
/* import route-target */
static struct cache *find_cache(const uint8_t preference);
static int add_tcp_cache(const char *host, const char *port,
const uint8_t preference);
-static void print_record(const struct pfx_record *record, void *data);
+static void print_record(const struct pfx_record *record, struct vty *vty);
static int is_synchronized(void);
static int is_running(void);
static void route_match_free(void *rule);
return NULL;
}
-static void print_record(const struct pfx_record *record, void *data)
+static void print_record(const struct pfx_record *record, struct vty *vty)
{
char ip[INET6_ADDRSTRLEN];
+
+ lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip));
+ vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len,
+ record->max_len, record->asn);
+}
+
+static void print_record_cb(const struct pfx_record *record, void *data)
+{
struct rpki_for_each_record_arg *arg = data;
struct vty *vty = arg->vty;
(*arg->prefix_amount)++;
- lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip));
- vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len,
- record->max_len, record->asn);
+ print_record(record, vty);
}
static struct rtr_mgr_group *get_groups(void)
vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", "Origin-AS");
arg.prefix_amount = &number_of_ipv4_prefixes;
- pfx_table_for_each_ipv4_record(pfx_table, print_record, &arg);
+ pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg);
arg.prefix_amount = &number_of_ipv6_prefixes;
- pfx_table_for_each_ipv6_record(pfx_table, print_record, &arg);
+ pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg);
vty_out(vty, "Number of IPv4 Prefixes: %u\n", number_of_ipv4_prefixes);
vty_out(vty, "Number of IPv6 Prefixes: %u\n", number_of_ipv6_prefixes);
return CMD_SUCCESS;
}
+DEFPY (show_rpki_prefix,
+ show_rpki_prefix_cmd,
+ "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Lookup IP prefix and optionally ASN in prefix table\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "AS Number\n")
+{
+
+ if (!is_synchronized()) {
+ vty_out(vty, "No Conection to RPKI cache server.\n");
+ return CMD_WARNING;
+ }
+
+ struct lrtr_ip_addr addr;
+ char addr_str[INET6_ADDRSTRLEN];
+ size_t addr_len = strchr(prefix_str, '/') - prefix_str;
+
+ memset(addr_str, 0, sizeof(addr_str));
+ memcpy(addr_str, prefix_str, addr_len);
+
+ if (lrtr_ip_str_to_addr(addr_str, &addr) != 0) {
+ vty_out(vty, "Invalid IP prefix\n");
+ return CMD_WARNING;
+ }
+
+ struct pfx_record *matches = NULL;
+ unsigned int match_count = 0;
+ enum pfxv_state result;
+
+ if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count,
+ asn, &addr, prefix->prefixlen, &result)
+ != PFX_SUCCESS) {
+ vty_out(vty, "Prefix lookup failed");
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", "Origin-AS");
+ for (size_t i = 0; i < match_count; ++i) {
+ const struct pfx_record *record = &matches[i];
+
+ if (record->max_len >= prefix->prefixlen
+ && ((asn != 0 && asn == record->asn) || asn == 0)) {
+ print_record(&matches[i], vty);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_rpki_cache_server,
show_rpki_cache_server_cmd,
"show rpki cache-server",
install_element(ENABLE_NODE, &show_rpki_prefix_table_cmd);
install_element(ENABLE_NODE, &show_rpki_cache_connection_cmd);
install_element(ENABLE_NODE, &show_rpki_cache_server_cmd);
+ install_element(ENABLE_NODE, &show_rpki_prefix_cmd);
/* Install debug commands */
install_element(CONFIG_NODE, &debug_rpki_cmd);
+.. _building-centos6:
+
CentOS 6
========================================
-(As an alternative to this installation, you may prefer to create a FRR
-rpm package yourself and install that package instead. See instructions
-in redhat/README.rpm\_build.md on how to build a rpm package)
+This document describes installation from source. If you want to build an RPM,
+see :ref:`packaging-redhat`.
Instructions are tested with ``CentOS 6.8`` on ``x86_64`` platform
PIMd is needed
- MPLS is not supported on ``CentOS 6``. MPLS requires Linux Kernel 4.5
or higher (LDP can be built, but may have limited use without MPLS)
-- Zebra is unable to detect what bridge/vrf an interface is associcated
+- Zebra is unable to detect what bridge/vrf an interface is associated
with (IFLA\_INFO\_SLAVE\_KIND does not exist in the kernel headers,
you can use a newer kernel + headers to get this functionality)
- frr\_reload.py will not work, as this requires Python 2.7, and CentOS
# Controls source route verification
net.ipv4.conf.default.rp_filter = 0
-Load the modifed sysctl's on the system:
+Load the modified sysctl's on the system:
.. code-block:: shell
CentOS 7
========================================
-(As an alternative to this installation, you may prefer to create a FRR
-rpm package yourself and install that package instead. See instructions
-in redhat/README.rpm\_build.md on how to build a rpm package)
+This document describes installation from source. If you want to build an RPM,
+see :ref:`packaging-redhat`.
CentOS 7 restrictions:
----------------------
net.ipv4.conf.all.forwarding=1
net.ipv6.conf.all.forwarding=1
-Load the modifed sysctl's on the system:
+Load the modified sysctl's on the system:
::
Fedora 24
=========================================
-(As an alternative to this installation, you may prefer to create a FRR
-rpm package yourself and install that package instead. See instructions
-in redhat/README.rpm\_build.md on how to build a rpm package)
+This document describes installation from source. If you want to build an RPM,
+see :ref:`packaging-redhat`.
Install required packages
-------------------------
net.mpls.conf.eth2.input=1
net.mpls.platform_labels=100000
-Load the modifed sysctl's on the system:
+Load the modified sysctl's on the system:
::
Install required packages
-------------------------
-Add packages: (Allow the install of the package managment tool if this
+Add packages: (Allow the install of the package management tool if this
is first package install and asked)
::
Install required packages
-------------------------
-Add packages: (Allow the install of the package managment tool if this
+Add packages: (Allow the install of the package management tool if this
is first package install and asked)
.. code-block:: shell
Install required packages
-------------------------
-Add packages: (Allow the install of the package managment tool if this
+Add packages: (Allow the install of the package management tool if this
is first package install and asked)
::
-----
Edit ``/usr/sbin/frr.init`` and add/remove the daemons name in section
-``DAEMONS=`` or don't install unneded packages For example: zebra bgpd ldpd
+``DAEMONS=`` or don't install unneeded packages For example: zebra bgpd ldpd
isisd nhrpd ospfd ospf6d pimd ripd ripngd
-Enable the serivce
+Enable the service
^^^^^^^^^^^^^^^^^^
- ``service frr enable``
-The libyang library can be installed from third-party packages available `here
-<https://ci1.netdef.org/browse/LIBYANG-YANGRELEASE/latestSuccessful/artifact>`_.
+FRR depends on the relatively new ``libyang`` library to provide YANG/NETCONF
+support. Unfortunately, most distributions do not yet offer a ``libyang``
+package from their repositories. Therefore we offer two options to install this
+library.
-Note: the libyang dev/devel packages need to be installed in addition
-to the libyang core package in order to build FRR successfully.
+**Option 1: Binary Install**
+
+The FRR project builds binary ``libyang`` packages, which we offer for download
+`here <https://ci1.netdef.org/browse/LIBYANG-YANGRELEASE/latestSuccessful/artifact>`_.
.. warning::
- libyang ABI version 0.16.74 or newer will be required to build FRR in the
- near future since it significantly eases build and installation
- considerations. "0.16-r3" is equal to 0.16.105 and will work, "0.16-r2"
- is equal to 0.16.52 and will stop working. The CI artifacts will be
- updated shortly.
-For example, for CentOS 7.x:
+ ``libyang`` version 0.16.74 or newer is required to build FRR.
+
+.. note::
-.. code-block:: shell
+ The ``libyang`` development packages need to be installed in addition to the
+ libyang core package in order to build FRR successfully. Make sure to
+ download and install those from the link above alongside the binary
+ packages.
- wget https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/CentOS-7-x86_64-Packages/libyang-0.16.46-0.x86_64.rpm
- wget https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/CentOS-7-x86_64-Packages/libyang-devel-0.16.46-0.x86_64.rpm
- sudo rpm -i libyang-0.16.46-0.x86_64.rpm libyang-devel-0.16.46-0.x86_64.rpm
+ Depending on your platform, you may also need to install the PCRE
+ development package. Typically this is ``libpcre-dev`` or ``pcre-devel``.
-or Ubuntu 18.04:
+.. note::
-.. code-block:: shell
+ For Debian-based systems, the official ``libyang`` package requires recent
+ versions of ``swig`` (3.0.12) and ``debhelper`` (11) which are only
+ available in Debian buster (10). However, ``libyang`` packages built on
+ Debian buster can be installed on both Debian jessie (8) and Debian stretch
+ (9), as well as various Ubuntu systems. The ``python3-yang`` package will
+ not work, but the other packages (``libyang-dev`` is the one needed for FRR)
+ will.
- wget https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang-dev_0.16.46_amd64.deb
- wget https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang_0.16.46_amd64.deb
- sudo apt install libpcre3-dev
- sudo dpkg -i libyang-dev_0.16.46_amd64.deb libyang_0.16.46_amd64.deb
+**Option 2: Source Install**
.. note::
- For Debian-based systems, the official libyang package requires recent
- versions of swig (3.0.12) and debhelper (11) which are only available in
- Debian buster (10). However, libyang packages built on Debian buster can
- be installed on both Debian jessie (8) and Debian stretch (9), as well as
- various Ubuntu systems. The python3-yang package will not work, but the
- other packages (libyang-dev is the one needed for FRR) will.
-Alternatively, libyang can be built and installed manually by following
-the steps below:
+ Ensure that the `libyang build requirements
+ <https://github.com/CESNET/libyang/blob/master/README.md#build-requirements>`_
+ are met before continuing. Usually this entails installing ``cmake`` and
+ ``libpcre-dev`` or ``pcre-devel``.
-.. code-block:: shell
+.. code-block:: console
- git clone https://github.com/opensourcerouting/libyang
+ git clone https://github.com/CESNET/libyang.git
cd libyang
- git checkout -b tmp origin/tmp
mkdir build; cd build
- cmake -DENABLE_LYD_PRIV=ON ..
+ cmake -DENABLE_LYD_PRIV=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr ..
make
sudo make install
When building libyang on CentOS 6, it's also necessary to pass the
``-DENABLE_CACHE=OFF`` parameter to cmake.
-Note: please check the `libyang build requirements
-<https://github.com/CESNET/libyang/blob/master/README.md#build-requirements>`_
-first.
Libfrr provides type-safe subscribable hook points where other pieces of
code can add one or more callback functions. "type-safe" in this case
applies to the function pointers used for subscriptions. The
-implementations checks (at compile-time) wheter a callback to be added has
+implementations checks (at compile-time) whether a callback to be added has
the appropriate function signature (parameters) for the hook.
Example:
needs to get the user in the loop—and only these things—are warnings or
errors.
-Note that this doesn't neccessarily mean the user needs to fix something in
+Note that this doesn't necessarily mean the user needs to fix something in
the FRR instance. It also includes when we detect something else needs
fixing, for example another router, the system we're running on, or the
configuration. The common point is that the user should probably do
Memtypes
========
-FRR includes wrappers arround ``malloc()`` and ``free()`` that count the number
+FRR includes wrappers around ``malloc()`` and ``free()`` that count the number
of objects currently allocated, for each of a defined ``MTYPE``.
To this extent, there are *memory groups* and *memory types*. Each memory
The message type field can take one of the following values:
+-------------------------------+---------+
-| Messages to OSPF deamon | Value |
+| Messages to OSPF daemon | Value |
+===============================+=========+
| MSG\_REGISTER\_OPAQUETYPE | 1 |
+-------------------------------+---------+
+-------------------------------+---------+
+-----------------------------+---------+
-| Messages from OSPF deamon | Value |
+| Messages from OSPF daemon | Value |
+=============================+=========+
| MSG\_REPLY | 10 |
+-----------------------------+---------+
Concepts
^^^^^^^^
-Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various
+Segment Routing used 3 different OPAQUE LSA in OSPF to carry the various
information:
* **Router Information:** flood the Segment Routing capabilities of the node.
* **Extended Link:** flood the Adjaceny and Lan Adjacency Segment Identifier
* **Extended Prefix:** flood the Prefix Segment Identifier
-The implementation follow previous TE and Router Information codes. It used the
+The implementation follows previous TE and Router Information codes. It used the
OPAQUE LSA functions defined in ospf_opaque.[c,h] as well as the OSPF API. This
latter is mandatory for the implementation as it provides the Callback to
Segment Routing functions (see below) when an Extended Link / Prefix or Router
(4.0.0.0), Extended Prefix (7.0.0.X) and Link (8.0.0.X) by ospf_ri.c,
respectively ospf_ext.c.
* ospf_ri.c send back to ospf_sr.c received Router Information LSA and update
- Self Router Information LSA with paramters provided by ospf_sr.c i.e. SRGB
+ Self Router Information LSA with parameters provided by ospf_sr.c i.e. SRGB
and MSD. It use ospf_opaque.c functions to send/received these Opaque LSAs.
* ospf_ext.c send back to ospf_sr.c received Extended Prefix and Link Opaque
LSA and send self Extended Prefix and Link Opaque LSA through ospf_opaque.c
Note that incoming LSA which is already present in the LSDB will be inserted
after the old instance of this LSA remove from the LSDB. Thus, after the first
time, each incoming LSA will trigger a `delete` following by an `install`. This
-is not very helpfull to handle real LSA deletion. In fact, LSA deletion is done
-by Flushing LSA i.e. flood LSA after seting its age to MAX_AGE. Then, a garbage
+is not very helpful to handle real LSA deletion. In fact, LSA deletion is done
+by Flushing LSA i.e. flood LSA after setting its age to MAX_AGE. Then, a garbage
function has the role to remove all LSA with `age == MAX_AGE` in the LSDB. So,
to handle LSA Flush, the best is to look to the LSA age to determine if it is
an installation or a future deletion i.e. the flushed LSA is first store in the
`ospf_router_info_update_sr()` is called to indicate to Router Information
process that Segment Routing TLVs must be flood. Same function is called to
modify the Segment Routing Global Block (SRGB) and Maximum Stack Depth (MSD)
-TLV. Only Shortest Path First (SPF) Algorithm is supported, so no possiblity
+TLV. Only Shortest Path First (SPF) Algorithm is supported, so no possibility
to modify this TLV is offer by the code.
When Opaque LSA Tyep 4 i.e. Router Information are stored in LSDB, function
^^^^^^^^^^^^^^^^^^^^^^^^^
Like for Router Information, Segment Routing is activate at the Extended
-Link/Prefix level with new `segment-routing on` command. This trigger
-automtically the flooding of Extended Link LSA for all ospf interface where
+Link/Prefix level with new `segment-routing on` command. This triggers
+automatically the flooding of Extended Link LSA for all ospf interfaces where
adjacency is full. For Extended Prefix LSA, the new CLI command
`segment-routing prefix ...` will trigger the flooding of Prefix SID
TLV/SubTLVs.
third line the MSD and finally, set the Prefix SID index for a given prefix.
Note that only prefix of Loopback interface could be configured with a Prefix
SID. It is possible to add `no-php-flag` at the end of the prefix command to
-disbale Penultimate Hop Popping. This advertises peers that they MUST NOT pop
+disable Penultimate Hop Popping. This advertises to peers that they MUST NOT pop
the MPLS label prior to sending the packet.
Known limitations
+.. _packaging-debian:
+
Packaging Debian
================
--- /dev/null
+.. _packaging-redhat:
+
+Packaging Red Hat
+=================
+
+Tested on CentOS 6, CentOS 7 and Fedora 24.
+
+1. On CentOS 6, refer to :ref:`building-centos6` for details on installing
+ sufficiently up-to-date package versions to enable building FRR.
+
+ Newer automake/autoconf/bison is only needed to build the RPM and is **not**
+ needed to install the binary RPM package.
+
+2. Install the build dependencies for your platform. Refer to the
+ platform-specific build documentation on how to do this.
+
+3. Install the following additional packages::
+
+ yum install rpm-build net-snmp-devel pam-devel libcap-devel
+
+ If your platform uses systemd::
+
+ yum install systemd-devel
+
+ If ``yum`` is not present on your system, use ``dnf`` instead.
+
+3. Checkout FRR::
+
+ git clone https://github.com/frrouting/frr.git frr
+
+4. Run Bootstrap and make distribution tar.gz::
+
+ cd frr
+ ./bootstrap.sh
+ ./configure --with-pkg-extra-version=-MyRPMVersion SPHINXBUILD=sphinx-build2.7
+ make dist
+
+ .. note::
+
+ The only ``configure`` option respected when building RPMs is
+ ``--with-pkg-extra-version``.
+
+5. Create RPM directory structure and populate with sources::
+
+ mkdir rpmbuild
+ mkdir rpmbuild/SOURCES
+ mkdir rpmbuild/SPECS
+ cp redhat/*.spec rpmbuild/SPECS/
+ cp frr*.tar.gz rpmbuild/SOURCES/
+
+6. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed.
+
+ Look at the beginning of the file and adjust the following parameters to
+ enable or disable features as required::
+
+ ############### FRRouting (FRR) configure options #################
+ # with-feature options
+ %{!?with_pam: %global with_pam 0 }
+ %{!?with_ospfclient: %global with_ospfclient 1 }
+ %{!?with_ospfapi: %global with_ospfapi 1 }
+ %{!?with_irdp: %global with_irdp 1 }
+ %{!?with_rtadv: %global with_rtadv 1 }
+ %{!?with_ldpd: %global with_ldpd 1 }
+ %{!?with_nhrpd: %global with_nhrpd 1 }
+ %{!?with_eigrp: %global with_eigrpd 1 }
+ %{!?with_shared: %global with_shared 1 }
+ %{!?with_multipath: %global with_multipath 256 }
+ %{!?frr_user: %global frr_user frr }
+ %{!?vty_group: %global vty_group frrvty }
+ %{!?with_fpm: %global with_fpm 0 }
+ %{!?with_watchfrr: %global with_watchfrr 1 }
+ %{!?with_bgp_vnc: %global with_bgp_vnc 0 }
+ %{!?with_pimd: %global with_pimd 1 }
+ %{!?with_rpki: %global with_rpki 0 }
+
+7. Build the RPM::
+
+ rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/frr.spec
+
+ If building with RPKI, then download and install the additional RPKI
+ packages from
+ https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact
+
+If all works correctly, then you should end up with the RPMs under
+:file:`rpmbuild/RPMS` and the source RPM under :file:`rpmbuild/SRPMS`.
maintainer-release-build
packaging-debian
+ packaging-redhat
dev_RSTFILES = \
doc/developer/bgp-typecodes.rst \
doc/developer/bgpd.rst \
- doc/developer/building-frr-for-openwrt.rst \
doc/developer/building-frr-for-alpine.rst \
doc/developer/building-frr-for-centos6.rst \
doc/developer/building-frr-for-centos7.rst \
doc/developer/building-frr-for-netbsd7.rst \
doc/developer/building-frr-for-omnios.rst \
doc/developer/building-frr-for-openbsd6.rst \
+ doc/developer/building-frr-for-openwrt.rst \
doc/developer/building-frr-for-ubuntu1404.rst \
doc/developer/building-frr-for-ubuntu1604.rst \
doc/developer/building-frr-for-ubuntu1804.rst \
doc/developer/ospf-api.rst \
doc/developer/ospf-sr.rst \
doc/developer/ospf.rst \
- doc/developer/packaging.rst \
doc/developer/packaging-debian.rst \
+ doc/developer/packaging-redhat.rst
+ doc/developer/packaging.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
doc/developer/topotests.rst \
Interacting with equipment
^^^^^^^^^^^^^^^^^^^^^^^^^^
-You might want to interact with the topology equipments during the tests and
+You might want to interact with the topology equipment during the tests and
there are different ways to do so.
Notes:
-1. When using the Topogen API, all the equipments code derive from ``Topogear``
+1. When using the Topogen API, all the equipment code derives from ``Topogear``
(`lib/topogen.py <lib/topogen.py>`__). If you feel brave you can look by
- yourself how the abstractions that will be mentioned here works.
+ yourself how the abstractions that will be mentioned here work.
2. When not using the ``Topogen`` API there is only one way to interact with
- the equipments, which is by calling the ``mininet`` API functions directly
+ the equipment, which is by calling the ``mininet`` API functions directly
to spawn commands.
Interacting with the Linux sandbox
.. note::
- ``vtysh_(multi)cmd`` is only available for router type of equipments.
+ ``vtysh_(multi)cmd`` is only available for router types of equipment.
Invoking mininet CLI
^^^^^^^^^^^^^^^^^^^^
Comparing JSON output
^^^^^^^^^^^^^^^^^^^^^
-After obtaining JSON output formated with Python data structures, you may use
+After obtaining JSON output formatted with Python data structures, you may use
it to assert a minimalist schema:
.. code:: py
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FRR processes have the capabilities to report remaining memory allocations upon
-exit. To enable the reporting of the memory, define an enviroment variable
+exit. To enable the reporting of the memory, define an environment variable
``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
The first step to write a new test is to define the topology. This step can be
done in many ways, but the recommended is to use Graphviz to generate a drawing
of the topology. It allows us to see the topology graphically and to see the
-names of equipments, links and addresses.
+names of equipment, links and addresses.
Here is an example of Graphviz dot file that generates the template topology
:file:`tests/topotests/example-test/test_template.dot` (the inlined code might
Command Extraction
------------------
-When VTYSH is a built, a Perl script named :file:`extract.pl` searches the FRR
+When VTYSH is built, a Perl script named :file:`extract.pl` searches the FRR
codebase looking for ``DEFUN``'s. It extracts these ``DEFUN``'s, transforms
them into ``DEFSH``'s and appends them to ``vtysh_cmd.c``. Each ``DEFSH``
contains the name of the command plus ``_vtysh``, as well as a flag that
VTYSH communicates with FRR daemons by way of domain socket. Each daemon
creates its own socket, typically in :file:`/var/run/frr/<daemon>.vty`. The
protocol is very simple. In the VTYSH to daemon direction, messages are simply
-NULL-terminated strings, whose content are CLI commands. Here is a typical
+NUL-terminated strings, whose content are CLI commands. Here is a typical
message from VTYSH to a daemon:
::
00000010: 6c0a 00 l..
-The response format has some more data in it. First is a NULL-terminated string
+The response format has some more data in it. First is a NUL-terminated string
containing the plaintext response, which is just the output of the command that
was sent in the request. This is displayed to the user. The plaintext response
is followed by 3 null marker bytes, followed by a 1-byte status code that
``MAJOR.MINOR`` release branch is pulled. Depending on the severity of the bugs,
bugfix releases may occur sooner.
-Bugfixes are applied to the two most recent releases. Security fixes are
-backported to all releases less than or equal to at least one year old. Security
-fixes may also be backported to older releases depending on severity.
+Bugfixes are applied to the two most recent releases. However, backporting of bug
+fixes to older than the two most recent releases will not be prevented, if acked
+under the classical development workflow applying for a pull request.
+
+Security fixes are backported to all releases less than or equal to at least one
+year old. Security fixes may also be backported to older releases depending on
+severity.
+
Long term support branches ( LTS )
-----------------------------------------
must be at least one person that is in charge of the maintenance branch. The person
on people responsible for a maintenance branch must be a FRR maintainer. Note that
they may choose to abandon support for the maintenance branch at any time. If
-noone takes over the responsibility of the LTS branch, then the support will be
+no one takes over the responsibility of the LTS branch, then the support will be
discontinued.
The LTS branch duties are the following ones:
be merged by someone unaffiliated with that company.
Guidelines for code review
-""""""""""""""""""""""""""
+--------------------------
- As a rule of thumb, the depth of the review should be proportional to the
scope and / or impact of the patch.
.. |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), 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), mtracebis(8)
.. index:: find COMMAND...
.. clicmd:: find COMMAND...
- This commmand performs a simple substring search across all defined commands
+ This command performs a simple substring search across all defined commands
in all modes. As an example, suppose you're in enable mode and can't
remember where the command to turn OSPF segment routing on is:
in iBGP topologies. This can occur with full-mesh iBGP, but is particularly
problematic in non-full-mesh iBGP topologies that further reduce the routing
information known to each speaker. This has primarily been documented with iBGP
-route-reflection topologies. However, any route-hiding technologies potentially
-could also exacerbate oscillation with MED.
+:ref:`route-reflection <bgp-route-reflector>` topologies. However, any
+route-hiding technologies potentially could also exacerbate oscillation with MED.
This second issue occurs where speakers each have only a subset of routes, and
there are cycles in the preferences between different combinations of routes -
``graceful-shutdown`` represents well-known communities value
``GRACEFUL_SHUTDOWN`` ``0xFFFF0000`` ``65535:0``. :rfc:`8326` implements
the purpose Graceful BGP Session Shutdown to reduce the amount of
- lost traffic when taking BGP sessions down for maintainance. The use
+ lost traffic when taking BGP sessions down for maintenance. The use
of the community needs to be supported from your peers side to
actually have any effect.
``llgr-stale``
``llgr-stale`` represents well-known communities value ``LLGR_STALE``
``0xFFFF0006`` ``65535:6``.
- Assigned and intented only for use with routers supporting the
+ Assigned and intended only for use with routers supporting the
Long-lived Graceful Restart Capability as described in
[Draft-IETF-uttaro-idr-bgp-persistence]_.
- Routers recieving routes with this community may (depending on
+ Routers receiving routes with this community may (depending on
implementation) choose allow to reject or modify routes on the
presence or absence of this community.
``no-llgr``
``no-llgr`` represents well-known communities value ``NO_LLGR``
``0xFFFF0007`` ``65535:7``.
- Assigned and intented only for use with routers supporting the
+ Assigned and intended only for use with routers supporting the
Long-lived Graceful Restart Capability as described in
[Draft-IETF-uttaro-idr-bgp-persistence]_.
- Routers recieving routes with this community may (depending on
+ Routers receiving routes with this community may (depending on
implementation) choose allow to reject or modify routes on the
presence or absence of this community.
There are two types of community list:
standard
- This type accepts an explicit value for the atttribute.
+ This type accepts an explicit value for the attribute.
expanded
This type accepts a regular expression. Because the regex must be
Route Reflector
===============
-.. note:: This documentation is woefully incomplete.
+BGP routers connected inside the same AS through BGP belong to an internal
+BGP session, or IBGP. In order to prevent routing table loops, IBGP does not
+advertise IBGP-learned routes to other routers in the same session. As such,
+IBGP requires a full mesh of all peers. For large networks, this quickly becomes
+unscalable. Introducing route reflectors removes the need for the full-mesh.
-.. index:: bgp cluster-id A.B.C.D
-.. clicmd:: bgp cluster-id A.B.C.D
+When route reflectors are configured, these will reflect the routes announced
+by the peers configured as clients. A route reflector client is configured
+with:
.. index:: neighbor PEER route-reflector-client
.. clicmd:: neighbor PEER route-reflector-client
.. index:: no neighbor PEER route-reflector-client
.. clicmd:: no neighbor PEER route-reflector-client
+To avoid single points of failure, multiple route reflectors can be configured.
+
+A cluster is a collection of route reflectors and their clients, and is used
+by route reflectors to avoid looping.
+
+.. index:: bgp cluster-id A.B.C.D
+.. clicmd:: bgp cluster-id A.B.C.D
.. _routing-policy:
route-map rm-no-export permit 20
!
route-map rm-blackhole permit 10
- description blackhole, up-pref and ensure it cant escape this AS
+ description blackhole, up-pref and ensure it cannot escape this AS
set ip next-hop 127.0.0.1
set local-preference 10
set community additive no-export
depicts how Route Targets are configured and how VRFs and cross VRF
configuration is done. Note that the VRF are mapped on Linux Network
Namespaces. For data traffic to cross VRF boundaries, virtual ethernet
-interfaces are created with private IP adressing scheme.
+interfaces are created with private IP addressing scheme.
.. code-block:: frr
- The validation procedure depicted in :rfc:`5575` is not available.
This validation procedure has not been implemented, as this feature was not
- used in the existing setups you shared wih us.
+ used in the existing setups you shared with us.
- The filtering action shaper value, if positive, is not used to apply shaping.
.. option:: --enable-multipath=X
Compile FRR with up to X way ECMP supported. This number can be from 0-999.
- For backwards compatability with older configure options when setting X = 0,
+ For backwards compatibility with older configure options when setting X = 0,
we will build FRR with 64 way ECMP. This is needed because there are
hardcoded arrays that FRR builds towards, so we need to know how big to
make these arrays at build time. Additionally if this parameter is
SPF-triggering event occurs within the hold-time of the previous SPF
calculation.
- This command supercedes the *timers spf* command in previous FRR
+ This command supersedes the *timers spf* command in previous FRR
releases.
.. index:: max-metric router-lsa [on-startup|on-shutdown] (5-86400)
When a incoming packet matches the destination prefix specified, take the
packet and forward according to the nexthops specified. This command accepts
- both v4 and v6 prefixes. This command is used in conjuction of the
+ both v4 and v6 prefixes. This command is used in conjunction of the
:clicmd:`match src-ip PREFIX` command for matching.
.. clicmd:: set nexthop-group NAME
Set the IGMP version used on this interface. The default value is 3.
-.. index:: ip multicat boundary oil WORD
-.. clicmd:: ip multicat boundary oil WORD
+.. index:: ip multicast boundary oil WORD
+.. clicmd:: ip multicast boundary oil WORD
Set a pim multicast boundary, based upon the WORD prefix-list. If a pim join
or IGMP report is received on this interface and the Group is denied by the
Using SHARP
===========
-All sharp commands are under the enable node and preceeded by the ``sharp``
+All sharp commands are under the enable node and preceded by the ``sharp``
keyword. At present, no sharp commands will be preserved in the config.
.. index:: sharp install
statistics information matches the same file statistics information as
`/proc/self/ns/net` ( through stat() function). As statistics information
matches, then `vrf0` stands for the new default namespace name.
-Consequently, the VRF naming `Default` will be overriden by the new discovered
+Consequently, the VRF naming `Default` will be overridden by the new discovered
namespace name `vrf0`.
For those who don't use VRF backend with *Linux network namespace*, it is
.. index:: [no] ip route NETWORK MASK GATEWAY|INTERFACE label LABEL
.. clicmd:: [no] ip route NETWORK MASK GATEWAY|INTERFACE label LABEL
- NETWORK ans MASK stand for the IP prefix entry to be added as static
+ NETWORK and MASK stand for the IP prefix entry to be added as static
route entry.
GATEWAY is the gateway IP address to reach, in order to reach the prefix.
INTERFACE is the interface behind which the prefix is located.
struct interface *ifp;
switch (vrf_get_backend()) {
+ case VRF_BACKEND_UNKNOWN:
case VRF_BACKEND_NETNS:
ifp = if_lookup_by_name(name, vrf_id);
if (ifp)
vrf->name);
return NB_ERR_VALIDATION;
}
+
+ /* if VRF is netns or not yet known - init for instance
+ * then assumption is that passed config is exact
+ * then the user intent was not to use an other iface
+ */
if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) {
ifp = if_lookup_by_name_all_vrf(ifname);
if (ifp && ifp->vrf_id != vrf->vrf_id) {
DESC_ENTRY(ZEBRA_ROUTER_ID_DELETE),
DESC_ENTRY(ZEBRA_ROUTER_ID_UPDATE),
DESC_ENTRY(ZEBRA_HELLO),
+ DESC_ENTRY(ZEBRA_CAPABILITIES),
DESC_ENTRY(ZEBRA_NEXTHOP_REGISTER),
DESC_ENTRY(ZEBRA_NEXTHOP_UNREGISTER),
DESC_ENTRY(ZEBRA_NEXTHOP_UPDATE),
DESC_ENTRY(ZEBRA_VRF_LABEL),
DESC_ENTRY(ZEBRA_INTERFACE_VRF_UPDATE),
DESC_ENTRY(ZEBRA_BFD_CLIENT_REGISTER),
+ DESC_ENTRY(ZEBRA_BFD_CLIENT_DEREGISTER),
DESC_ENTRY(ZEBRA_INTERFACE_ENABLE_RADV),
DESC_ENTRY(ZEBRA_INTERFACE_DISABLE_RADV),
DESC_ENTRY(ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB),
DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT_ASYNC),
DESC_ENTRY(ZEBRA_GET_LABEL_CHUNK),
DESC_ENTRY(ZEBRA_RELEASE_LABEL_CHUNK),
+ DESC_ENTRY(ZEBRA_FEC_REGISTER),
+ DESC_ENTRY(ZEBRA_FEC_UNREGISTER),
+ DESC_ENTRY(ZEBRA_FEC_UPDATE),
DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI),
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
DESC_ENTRY(ZEBRA_ADVERTISE_SVI_MACIP),
DESC_ENTRY(ZEBRA_IPSET_DESTROY),
DESC_ENTRY(ZEBRA_IPSET_ENTRY_ADD),
DESC_ENTRY(ZEBRA_IPSET_ENTRY_DELETE),
+ DESC_ENTRY(ZEBRA_IPSET_NOTIFY_OWNER),
+ DESC_ENTRY(ZEBRA_IPSET_ENTRY_NOTIFY_OWNER),
+ DESC_ENTRY(ZEBRA_IPTABLE_ADD),
+ DESC_ENTRY(ZEBRA_IPTABLE_DELETE),
+ DESC_ENTRY(ZEBRA_IPTABLE_NOTIFY_OWNER),
DESC_ENTRY(ZEBRA_VXLAN_FLOOD_CONTROL),
};
#undef DESC_ENTRY
return strcmp(a, b);
}
+static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
+{
+ if (!a && !b)
+ return 0;
+
+ if (a && !b)
+ return -1;
+
+ if (!a && b)
+ return 1;
+
+ return sockunion_cmp(a, b);
+}
+
static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
{
int ret;
- ret = sockunion_cmp(&nh1->addr, &nh2->addr);
+ ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
if (ret)
return ret;
XFREE(MTYPE_TMP, nh->nhvrf_name);
+ if (nh->addr)
+ sockunion_free(nh->addr);
+
XFREE(MTYPE_TMP, nh);
}
nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
if (intf)
nh->intf = XSTRDUP(MTYPE_TMP, intf);
-
- nh->addr = *addr;
+ if (addr)
+ nh->addr = sockunion_dup(addr);
listnode_add_sort(nhgc->nhg_list, nh);
}
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
- sockunion_cmp(addr, &nh->addr) == 0 &&
+ nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
nhgc_cmp_helper(intf, nh->intf) == 0)
break;
}
return;
list_delete_node(nhgc->nhg_list, node);
-
- if (nh->nhvrf_name)
- XFREE(MTYPE_TMP, nh->nhvrf_name);
- if (nh->intf)
- XFREE(MTYPE_TMP, nh->intf);
-
- XFREE(MTYPE_TMP, nh);
+ nhgl_delete(nh);
}
static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
nhop->vrf_id = vrf->vrf_id;
- if (addr->sa.sa_family == AF_INET) {
- nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
- if (intf) {
- nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop->ifindex == IFINDEX_INTERNAL)
- return false;
- } else
- nhop->type = NEXTHOP_TYPE_IPV4;
- } else {
- memcpy(&nhop->gate.ipv6, &addr->sin6.sin6_addr, 16);
- if (intf) {
- nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop->ifindex == IFINDEX_INTERNAL)
- return false;
- } else
- nhop->type = NEXTHOP_TYPE_IPV6;
+ if (intf) {
+ nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
+ if (nhop->ifindex == IFINDEX_INTERNAL)
+ return false;
}
+ if (addr) {
+ if (addr->sa.sa_family == AF_INET) {
+ nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
+ if (intf)
+ nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else
+ nhop->type = NEXTHOP_TYPE_IPV4;
+ } else {
+ nhop->gate.ipv6 = addr->sin6.sin6_addr;
+ if (intf)
+ nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ else
+ nhop->type = NEXTHOP_TYPE_IPV6;
+ }
+ } else
+ nhop->type = NEXTHOP_TYPE_IFINDEX;
+
return true;
}
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
- "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
+ "[no] nexthop\
+ <\
+ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
+ |INTERFACE$intf\
+ >\
+ [nexthop-vrf NAME$name]",
NO_STR
"Specify one of the nexthops in this ECMP group\n"
"v4 Address\n"
"v6 Address\n"
"Interface to use\n"
+ "Interface to use\n"
"If the nexthop is in a different vrf tell us\n"
"The nexthop-vrf Name\n")
{
struct nexthop *nh;
bool legal;
- /*
- * This is impossible to happen as that the cli parser refuses
- * to let you get here without an addr, but the SA system
- * does not understand this intricacy
- */
- assert(addr);
-
legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name);
if (nhop.type == NEXTHOP_TYPE_IPV6
{
char buf[100];
- vty_out(vty, "nexthop ");
+ vty_out(vty, "nexthop");
- vty_out(vty, "%s", sockunion2str(&nh->addr, buf, sizeof(buf)));
+ if (nh->addr)
+ vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
if (nh->intf)
vty_out(vty, " %s", nh->intf);
vty_out(vty, "nexthop-group %s\n", nhgc->name);
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
- vty_out(vty, " ");
+ vty_out(vty, " ");
nexthop_group_write_nexthop_internal(vty, nh);
}
struct nexthop nhop;
struct nexthop *nh;
- if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
+ if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
nhh->intf,
nhh->nhvrf_name))
continue;
struct nexthop nhop;
struct nexthop *nh;
- if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
+ if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
nhh->intf,
nhh->nhvrf_name))
continue;
struct nexthop nhop;
if (!nexthop_group_parse_nexthop(
- &nhop, &nhh->addr, nhh->intf,
+ &nhop, nhh->addr, nhh->intf,
nhh->nhvrf_name))
continue;
struct nexthop_hold {
char *nhvrf_name;
- union sockunion addr;
+ union sockunion *addr;
char *intf;
};
/* If we're already elevated, just return */
pthread_mutex_lock(&(privs->mutex));
- if (++privs->refcount > 1) {
- pthread_mutex_unlock(&(privs->mutex));
- return privs;
+ {
+ if (++(privs->refcount) == 1) {
+ errno = 0;
+ if (privs->change(ZPRIVS_RAISE)) {
+ zlog_err("%s: Failed to raise privileges (%s)",
+ funcname, safe_strerror(errno));
+ }
+ errno = save_errno;
+ privs->raised_in_funcname = funcname;
+ }
}
pthread_mutex_unlock(&(privs->mutex));
- errno = 0;
- if (privs->change(ZPRIVS_RAISE)) {
- zlog_err("%s: Failed to raise privileges (%s)",
- funcname, safe_strerror(errno));
- }
- errno = save_errno;
- privs->raised_in_funcname = funcname;
return privs;
}
/* Don't lower privs if there's another caller */
pthread_mutex_lock(&(*privs)->mutex);
- if (--((*privs)->refcount) > 0) {
- pthread_mutex_unlock(&(*privs)->mutex);
- return;
+ {
+ if (--((*privs)->refcount) == 0) {
+ errno = 0;
+ if ((*privs)->change(ZPRIVS_LOWER)) {
+ zlog_err("%s: Failed to lower privileges (%s)",
+ (*privs)->raised_in_funcname,
+ safe_strerror(errno));
+ }
+ errno = save_errno;
+ (*privs)->raised_in_funcname = NULL;
+ }
}
pthread_mutex_unlock(&(*privs)->mutex);
- errno = 0;
- if ((*privs)->change(ZPRIVS_LOWER)) {
- zlog_err("%s: Failed to lower privileges (%s)",
- (*privs)->raised_in_funcname, safe_strerror(errno));
- }
- errno = save_errno;
- (*privs)->raised_in_funcname = NULL;
*privs = NULL;
}
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
static int vrf_backend;
+static int vrf_backend_configured;
static struct zebra_privs_t *vrf_daemon_privs;
static char vrf_default_name[VRF_NAMSIZ] = VRF_DEFAULT_NAME_INTERNAL;
int vrf_get_backend(void)
{
+ if (!vrf_backend_configured)
+ return VRF_BACKEND_UNKNOWN;
return vrf_backend;
}
void vrf_configure_backend(int vrf_backend_netns)
{
vrf_backend = vrf_backend_netns;
+ vrf_backend_configured = 1;
}
int vrf_handler_create(struct vty *vty, const char *vrfname,
DECLARE_QOBJ_TYPE(vrf)
/* Allow VRF with netns as backend */
-#define VRF_BACKEND_VRF_LITE 0
-#define VRF_BACKEND_NETNS 1
+#define VRF_BACKEND_VRF_LITE 0
+#define VRF_BACKEND_NETNS 1
+#define VRF_BACKEND_UNKNOWN 2
extern struct vrf_id_head vrfs_by_id;
extern struct vrf_name_head vrfs_by_name;
}
if (zclient_debug)
- zlog_debug("zclient_start is called");
+ zlog_debug("scheduling zclient connection");
zclient_event(ZCLIENT_SCHEDULE, zclient);
}
length -= ZEBRA_HEADER_SIZE;
if (zclient_debug)
- zlog_debug("zclient 0x%p command 0x%x VRF %u",
- (void *)zclient, command, vrf_id);
+ zlog_debug("zclient 0x%p command %s VRF %u",
+ (void *)zclient, zserv_command_string(command),
+ vrf_id);
switch (command) {
case ZEBRA_CAPABILITIES:
oi = nbr->oi;
- /* LS Request packet overflows interface MTU. */
+ /* LS Request packet overflows interface MTU
+ * delta is just number of bytes required for 1 LS Req
+ * ospf_packet_max will return the number of bytes can
+ * be accomodated without ospf header. So length+delta
+ * can be compared to ospf_packet_max
+ * to check if it can fit another lsreq in the same packet.
+ */
+
if (*length + delta > ospf_packet_max(oi))
return 0;
{
struct ospf_lsa *lsa;
uint16_t length = OSPF_LS_REQ_MIN_SIZE;
- unsigned long delta = stream_get_endp(s) + 12;
+ unsigned long delta = 12;
struct route_table *table;
struct route_node *rn;
int i;
assert(lsa->data);
- /* Will it fit? */
- if (length + delta + ntohs(lsa->data->length) > size_noauth)
+ /* Will it fit? Minimum it has to fit atleast one */
+ if ((length + delta + ntohs(lsa->data->length) > size_noauth) &&
+ (count > 0))
break;
/* Keep pointer to LS age. */
{
struct listnode *node, *nnode;
uint16_t length = OSPF_LS_ACK_MIN_SIZE;
- unsigned long delta = stream_get_endp(s) + 24;
+ unsigned long delta = OSPF_LSA_HEADER_SIZE;
struct ospf_lsa *lsa;
for (ALL_LIST_ELEMENTS(ack, node, nnode, lsa)) {
assert(lsa);
- if (length + delta > ospf_packet_max(oi))
+ /* LS Ack packet overflows interface MTU
+ * delta is just number of bytes required for
+ * 1 LS Ack(1 LS Hdr) ospf_packet_max will return
+ * the number of bytes can be accomodated without
+ * ospf header. So length+delta can be compared
+ * against ospf_packet_max to check if it can fit
+ * another ls header in the same packet.
+ */
+ if ((length + delta) > ospf_packet_max(oi))
break;
stream_put(s, lsa->data, OSPF_LSA_HEADER_SIZE);
static void
pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg,
- enum nexthop_types_t nh_afi);
+ enum nexthop_types_t nh_type);
/*
* Nexthop refcount.
switch (pbrnc1->nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
- return true;
+ return pbrnc1->nexthop->ifindex == pbrnc2->nexthop->ifindex;
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV4:
return pbrnc1->nexthop->gate.ipv4.s_addr
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
pbr_map_check_nh_group_change(nhgc->name);
+
+ if (nhop->type == NEXTHOP_TYPE_IFINDEX) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
+ if (ifp)
+ pbr_nht_nexthop_interface_update(ifp);
+ }
}
void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_cache pnhc_find = {};
struct pbr_nexthop_cache *pnhc;
- enum nexthop_types_t nh_afi = nhop->type;
+ enum nexthop_types_t nh_type = nhop->type;
/* find pnhgc by name */
strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
if (pnhgc->nhh->count)
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
else
- pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_afi);
+ pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_type);
pbr_map_check_nh_group_change(nhgc->name);
}
* - AFI_MAX on error
*/
static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
- enum nexthop_types_t nh_afi)
+ enum nexthop_types_t nh_type)
{
struct nexthop *nexthop;
afi_t install_afi = AFI_MAX;
bool v6, v4, bh;
+ if (nh_type) {
+ switch (nh_type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ return AFI_IP;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ return AFI_IP6;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return AFI_MAX;
+ }
+ }
+
v6 = v4 = bh = false;
- if (!nh_afi) {
- for (ALL_NEXTHOPS(nhg, nexthop)) {
- nh_afi = nexthop->type;
+ for (ALL_NEXTHOPS(nhg, nexthop)) {
+ nh_type = nexthop->type;
+
+ switch (nh_type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ v6 = true;
+ install_afi = AFI_IP;
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ v4 = true;
+ install_afi = AFI_IP6;
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ bh = true;
break;
}
}
- switch (nh_afi) {
- case NEXTHOP_TYPE_IFINDEX:
- break;
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- v6 = true;
- install_afi = AFI_IP;
- break;
- case NEXTHOP_TYPE_IPV6:
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- v4 = true;
- install_afi = AFI_IP6;
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- bh = true;
+ /* Interface and/or blackhole nexthops only. */
+ if (!v4 && !v6)
install_afi = AFI_MAX;
- break;
- }
if (!bh && v6 && v4)
DEBUGD(&pbr_dbg_nht,
struct nexthop_group nhg)
{
afi_t install_afi;
- enum nexthop_types_t nh_afi = 0;
+ enum nexthop_types_t nh_type = 0;
- install_afi = pbr_nht_which_afi(nhg, nh_afi);
+ install_afi = pbr_nht_which_afi(nhg, nh_type);
route_add(pnhgc, nhg, install_afi);
}
static void
pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg,
- enum nexthop_types_t nh_afi)
+ enum nexthop_types_t nh_type)
{
afi_t install_afi;
- install_afi = pbr_nht_which_afi(nhg, nh_afi);
+ install_afi = pbr_nht_which_afi(nhg, nh_type);
pnhgc->installed = false;
pnhgc->valid = false;
struct listnode *node;
struct pbr_map_interface *pmi;
struct nexthop *nh;
- enum nexthop_types_t nh_afi = 0;
+ enum nexthop_types_t nh_type = 0;
if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) {
for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi))
pnhgc = hash_lookup(pbr_nhg_hash, &find);
nh = pbrms->nhg->nexthop;
- nh_afi = nh->type;
+ nh_type = nh->type;
lup.nexthop = nh;
pnhc = hash_lookup(pnhgc->nhh, &lup);
pnhc->parent = NULL;
hash_release(pnhgc->nhh, pnhc);
pbr_nh_delete(&pnhc);
- pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_afi);
+ pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
hash_release(pbr_nhg_hash, pnhgc);
struct pbr_nht_individual {
struct zapi_route *nhr;
+ struct interface *ifp;
uint32_t valid;
};
hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
}
+static void
+pbr_nht_individual_nexthop_interface_update_lookup(struct hash_backet *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct pbr_nht_individual *pnhi = data;
+ bool old_valid;
+
+ old_valid = pnhc->valid;
+
+ if (pnhc->nexthop->type == NEXTHOP_TYPE_IFINDEX
+ && pnhc->nexthop->ifindex == pnhi->ifp->ifindex)
+ pnhc->valid = !!if_is_up(pnhi->ifp);
+
+ DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", pnhi->ifp->name,
+ old_valid, pnhc->valid);
+
+ if (pnhc->valid)
+ pnhi->valid += 1;
+}
+
+static void pbr_nht_nexthop_interface_update_lookup(struct hash_backet *b,
+ void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct pbr_nht_individual pnhi;
+ bool old_valid;
+
+ old_valid = pnhgc->valid;
+
+ pnhi.ifp = data;
+ pnhi.valid = 0;
+ hash_iterate(pnhgc->nhh,
+ pbr_nht_individual_nexthop_interface_update_lookup, &pnhi);
+
+ /*
+ * If any of the specified nexthops are valid we are valid
+ */
+ pnhgc->valid = !!pnhi.valid;
+
+ if (old_valid != pnhgc->valid)
+ pbr_map_check_nh_group_change(pnhgc->name);
+}
+
+void pbr_nht_nexthop_interface_update(struct interface *ifp)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_update_lookup,
+ ifp);
+}
+
static uint32_t pbr_nhg_hash_key(void *arg)
{
struct pbr_nexthop_group_cache *nhgc =
*/
extern void pbr_nht_nexthop_update(struct zapi_route *nhr);
+/*
+ * When we get a callback from zebra about an interface status update.
+ */
+extern void pbr_nht_nexthop_interface_update(struct interface *ifp);
+
extern void pbr_nht_init(void);
#endif
}
DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
- "[no] set nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
+ "[no] set nexthop\
+ <\
+ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
+ |INTERFACE$intf\
+ >\
+ [nexthop-vrf NAME$name]",
NO_STR
"Set for the PBR-MAP\n"
"Specify one of the nexthops in this map\n"
"v4 Address\n"
"v6 Address\n"
"Interface to use\n"
+ "Interface to use\n"
"If the nexthop is in a different vrf tell us\n"
"The nexthop-vrf Name\n")
{
memset(&nhop, 0, sizeof(nhop));
nhop.vrf_id = vrf->vrf_id;
- /*
- * Make SA happy. CLIPPY is not going to give us a NULL
- * addr.
- */
- assert(addr);
- if (addr->sa.sa_family == AF_INET) {
- nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
- if (intf) {
- nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
- nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop.ifindex == IFINDEX_INTERNAL) {
- vty_out(vty,
- "Specified Intf %s does not exist in vrf: %s\n",
- intf, vrf->name);
- return CMD_WARNING_CONFIG_FAILED;
- }
- } else
- nhop.type = NEXTHOP_TYPE_IPV4;
- } else {
- memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16);
- if (intf) {
- nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
- nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop.ifindex == IFINDEX_INTERNAL) {
- vty_out(vty,
- "Specified Intf %s does not exist in vrf: %s\n",
- intf, vrf->name);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (intf) {
+ nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
+ if (nhop.ifindex == IFINDEX_INTERNAL) {
+ vty_out(vty,
+ "Specified Intf %s does not exist in vrf: %s\n",
+ intf, vrf->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (addr) {
+ if (addr->sa.sa_family == AF_INET) {
+ nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
+ if (intf)
+ nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else
+ nhop.type = NEXTHOP_TYPE_IPV4;
} else {
- if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
- vty_out(vty,
- "Specified a v6 LL with no interface, rejecting\n");
- return CMD_WARNING_CONFIG_FAILED;
+ nhop.gate.ipv6 = addr->sin6.sin6_addr;
+ if (intf)
+ nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ else {
+ if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
+ vty_out(vty,
+ "Specified a v6 LL with no interface, rejecting\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ nhop.type = NEXTHOP_TYPE_IPV6;
}
- nhop.type = NEXTHOP_TYPE_IPV6;
}
- }
+ } else
+ nhop.type = NEXTHOP_TYPE_IFINDEX;
if (pbrms->nhg)
nh = nexthop_exists(pbrms->nhg, &nhop);
pbr_map_check(pbrms);
}
+ if (nhop.type == NEXTHOP_TYPE_IFINDEX) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(nhop.ifindex, nhop.vrf_id);
+ if (ifp)
+ pbr_nht_nexthop_interface_update(ifp);
+ }
+
return CMD_SUCCESS;
}
vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
if (pbrms->src)
- vty_out(vty, " match src-ip %s\n",
+ vty_out(vty, " match src-ip %s\n",
prefix2str(pbrms->src, buff, sizeof(buff)));
if (pbrms->dst)
- vty_out(vty, " match dst-ip %s\n",
+ vty_out(vty, " match dst-ip %s\n",
prefix2str(pbrms->dst, buff, sizeof(buff)));
if (pbrms->nhgrp_name)
- vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
+ vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
if (pbrms->nhg) {
- vty_out(vty, " set ");
+ vty_out(vty, " set ");
nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
}
if (!ifp->info)
pbr_if_new(ifp);
+ pbr_nht_nexthop_interface_update(ifp);
+
return 0;
}
DEBUGD(&pbr_dbg_zebra,
"%s: %s is up", __PRETTY_FUNCTION__, ifp->name);
+ pbr_nht_nexthop_interface_update(ifp);
+
return 0;
}
DEBUGD(&pbr_dbg_zebra,
"%s: %s is down", __PRETTY_FUNCTION__, ifp->name);
+ pbr_nht_nexthop_interface_update(ifp);
+
return 0;
}
up->t_join_timer);
/*
- * If we have a J/P timer for the neighbor display that
+ * If the upstream is not dummy and it has a J/P timer for the
+ * neighbor display that
*/
- if (!up->t_join_timer) {
+ if (!up->t_join_timer && up->rpf.source_nexthop.interface) {
struct pim_neighbor *nbr;
nbr = pim_neighbor_find(
json_row = json_object_new_object();
json_object_pim_upstream_add(json_row, up);
json_object_string_add(
- json_row, "inboundInterface",
- up->rpf.source_nexthop.interface->name);
+ json_row, "inboundInterface",
+ up->rpf.source_nexthop.interface
+ ? up->rpf.source_nexthop.interface->name
+ : "Unknown");
/*
* The RPF address we use is slightly different
} else {
vty_out(vty,
"%-10s%-15s %-15s %-11s %-8s %-9s %-9s %-9s %6d\n",
- up->rpf.source_nexthop.interface->name, src_str,
- grp_str, state_str, uptime, join_timer,
+ up->rpf.source_nexthop.interface
+ ? up->rpf.source_nexthop.interface->name
+ : "Unknown",
+ src_str, grp_str, state_str, uptime, join_timer,
rs_timer, ka_timer, up->ref_count);
}
}
if (!up)
return false;
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return false;
+ }
+
ifp_in = up->rpf.source_nexthop.interface;
nh_addr = up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4;
total = htonl(MTRACE_UNKNOWN_COUNT);
size_t packet_size = 0;
size_t group_size = 0;
- on_trace(__PRETTY_FUNCTION__, rpf->source_nexthop.interface,
- rpf->rpf_addr.u.prefix4);
-
if (rpf->source_nexthop.interface)
pim_ifp = rpf->source_nexthop.interface->info;
else {
return -1;
}
+ on_trace(__PRETTY_FUNCTION__, rpf->source_nexthop.interface,
+ rpf->rpf_addr.u.prefix4);
+
if (!pim_ifp) {
zlog_warn("%s: multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
{
#ifdef PIM_JP_AGG_DEBUG
struct interface *ifp;
- struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info;
- struct pim_instance *pim = pim_ifp->pim;
+ struct pim_interface *pim_ifp;
+ struct pim_instance *pim;
+
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return;
+ }
+
+ pim_ifp = up->rpf.source_nexthop.interface->info;
+ pim = pim_ifp->pim;
FOR_ALL_INTERFACES (pim->vrf, ifp) {
pim_ifp = ifp->info;
up->channel_oil->cc.pktcnt++;
PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
// resolve mfcc_parent prior to mroute_add in channel_add_oif
- if (up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
+ if (up->rpf.source_nexthop.interface &&
+ up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
int vif_index = 0;
vif_index = pim_if_find_vifindex_by_ifindex(
pim_ifp->pim,
return 0;
}
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return 0;
+ }
+
pim_ifp = up->rpf.source_nexthop.interface->info;
rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL;
#include "pim_jp_agg.h"
#include "pim_zebra.h"
#include "pim_zlookup.h"
+#include "pim_rp.h"
/**
* pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
struct pim_nexthop_cache *pnc = NULL;
struct pim_nexthop_cache lookup;
struct zclient *zclient = NULL;
+ struct listnode *upnode = NULL;
+ struct pim_upstream *upstream = NULL;
zclient = pim_zebra_zclient_get();
lookup.rpf.rpf_addr = *addr;
pnc = hash_lookup(pim->rpf_hash, &lookup);
if (pnc) {
- if (rp)
+ if (rp) {
+ /* Release the (*, G)upstream from pnc->upstream_hash,
+ * whose Group belongs to the RP getting deleted
+ */
+ for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
+ upstream)) {
+ struct prefix grp;
+ struct rp_info *trp_info;
+
+ if (upstream->sg.src.s_addr != INADDR_ANY)
+ continue;
+
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = upstream->sg.grp;
+
+ trp_info = pim_rp_find_match_group(pim, &grp);
+ if (trp_info == rp)
+ hash_release(pnc->upstream_hash,
+ upstream);
+ }
listnode_delete(pnc->rp_list, rp);
+ }
+
if (up)
hash_release(pnc->upstream_hash, up);
}
}
+void pim_rp_nexthop_del(struct rp_info *rp_info)
+{
+ rp_info->rp.source_nexthop.interface = NULL;
+ rp_info->rp.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
+ PIM_NET_INADDR_ANY;
+ rp_info->rp.source_nexthop.mrib_metric_preference =
+ router->infinite_assert_metric.metric_preference;
+ rp_info->rp.source_nexthop.mrib_route_metric =
+ router->infinite_assert_metric.route_metric;
+}
+
/* Update RP nexthop info based on Nexthop update received from Zebra.*/
static void pim_update_rp_nh(struct pim_instance *pim,
struct pim_nexthop_cache *pnc)
continue;
// Compute PIM RPF using cached nexthop
- pim_ecmp_nexthop_search(pim, pnc, &rp_info->rp.source_nexthop,
- &rp_info->rp.rpf_addr, &rp_info->group,
- 1);
+ if (!pim_ecmp_nexthop_search(pim, pnc,
+ &rp_info->rp.source_nexthop,
+ &rp_info->rp.rpf_addr, &rp_info->group,
+ 1))
+ pim_rp_nexthop_del(rp_info);
}
}
old.source_nexthop.interface = up->rpf.source_nexthop.interface;
rpf_result = pim_rpf_update(pim, up, &old, 0);
if (rpf_result == PIM_RPF_FAILURE) {
- pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_rpf_clear(pim, up);
return HASHWALK_CONTINUE;
}
/* update kernel multicast forwarding cache (MFC) */
- if (up->channel_oil) {
+ if (up->rpf.source_nexthop.interface) {
ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
if (PIM_DEBUG_PIM_NHT) {
zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s",
- __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
- old.source_nexthop.interface->name,
- up->rpf.source_nexthop.interface->name);
+ __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
+ old.source_nexthop.interface
+ ? old.source_nexthop.interface->name : "Unknwon",
+ up->rpf.source_nexthop.interface->name);
}
return HASHWALK_CONTINUE;
void pim_resolve_upstream_nh(struct pim_instance *pim, struct prefix *nht_p);
int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim,
struct prefix *src, struct prefix *grp);
+void pim_rp_nexthop_del(struct rp_info *rp_info);
#endif
return c_oil;
}
- ifp = pim_if_find_by_vif_index(pim, input_vif_index);
- if (!ifp) {
- /* warning only */
- zlog_warn(
- "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
- __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
- input_vif_index);
+ if (input_vif_index != MAXVIFS) {
+ ifp = pim_if_find_by_vif_index(pim, input_vif_index);
+ if (!ifp) {
+ /* warning only */
+ zlog_warn(
+ "%s: (S,G)=%s could not find input interface for input_vif_index=%d",
+ __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
+ input_vif_index);
+ }
}
c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
PIM_MROUTE_MIN_TTL;
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
- if (PIM_DEBUG_MROUTE) {
- char group_str[INET_ADDRSTRLEN];
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<group?>",
- channel_oil->oil.mfcc_mcastgrp,
- group_str, sizeof(group_str));
- pim_inet4_dump("<source?>",
- channel_oil->oil.mfcc_origin, source_str,
- sizeof(source_str));
- zlog_debug(
- "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__, oif->name,
- pim_ifp->mroute_vif_index, source_str,
- group_str);
- }
+ /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
+ * valid to get installed in kernel.
+ */
+ if (channel_oil->oil.mfcc_parent != MAXVIFS) {
+ if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (PIM_DEBUG_MROUTE) {
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>",
+ channel_oil->oil.mfcc_mcastgrp,
+ group_str, sizeof(group_str));
+ pim_inet4_dump("<source?>",
+ channel_oil->oil.mfcc_origin, source_str,
+ sizeof(source_str));
+ zlog_debug(
+ "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
+ __FILE__, __PRETTY_FUNCTION__, oif->name,
+ pim_ifp->mroute_vif_index, source_str,
+ group_str);
+ }
- channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
- return -5;
+ channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]
+ = old_ttl;
+ return -5;
+ }
}
channel_oil->oif_creation[pim_ifp->mroute_vif_index] =
Each channel_oil.oil is used to control an (S,G) entry in the Kernel
Multicast Forwarding Cache.
+
+ There is a case when we create a channel_oil but don't install in the kernel
+
+ Case where (S, G) entry not installed in the kernel:
+ FRR receives IGMP/PIM (*, G) join and RP is not configured or
+ not-reachable, then create a channel_oil for the group G with the incoming
+ interface(channel_oil.oil.mfcc_parent) as invalid i.e "MAXVIF" and populate
+ the outgoing interface where join is received. Keep this entry in the stack,
+ but don't install in the kernel(channel_oil.installed = 0).
+
+ Case where (S, G) entry installed in the kernel:
+ When RP is configured and is reachable for the group G, and receiving a
+ join if channel_oil is already present then populate the incoming interface
+ and install the entry in the kernel, if channel_oil not present, then create
+ a new_channel oil(channel_oil.installed = 1).
+
+ is_valid: indicate if this entry is valid to get installed in kernel.
+ installed: indicate if this entry is installed in the kernel.
+
*/
struct channel_oil {
#include "pim_iface.h"
#include "pim_msdp.h"
#include "pim_nht.h"
-
+#include "pim_mroute.h"
+#include "pim_oil.h"
+#include "pim_zebra.h"
/* Cleanup pim->rpf_hash each node data */
void pim_rp_list_hash_clean(void *data)
/*
* Given a group, return the rp_info for that group
*/
-static struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
+struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
const struct prefix *group)
{
struct listnode *node;
}
}
+void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up)
+{
+ struct pim_rpf old_rpf;
+ enum pim_rpf_result rpf_result;
+ struct in_addr old_upstream_addr;
+ struct in_addr new_upstream_addr;
+ struct prefix nht_p;
+
+ old_upstream_addr = up->upstream_addr;
+ pim_rp_set_upstream_addr(pim, &new_upstream_addr, up->sg.src,
+ up->sg.grp);
+
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: pim upstream update for old upstream %s",
+ __PRETTY_FUNCTION__,
+ inet_ntoa(old_upstream_addr));
+
+ if (old_upstream_addr.s_addr == new_upstream_addr.s_addr)
+ return;
+
+ /* Lets consider a case, where a PIM upstream has a better RP as a
+ * result of a new RP configuration with more precise group range.
+ * This upstream has to be added to the upstream hash of new RP's
+ * NHT(pnc) and has to be removed from old RP's NHT upstream hash
+ */
+ if (old_upstream_addr.s_addr != INADDR_ANY) {
+ /* Deregister addr with Zebra NHT */
+ nht_p.family = AF_INET;
+ nht_p.prefixlen = IPV4_MAX_BITLEN;
+ nht_p.u.prefix4 = old_upstream_addr;
+ if (PIM_DEBUG_TRACE) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&nht_p, buf, sizeof(buf));
+ zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
+ __PRETTY_FUNCTION__, up->sg_str, buf);
+ }
+ pim_delete_tracked_nexthop(pim, &nht_p, up, NULL);
+ }
+
+ /* Update the upstream address */
+ up->upstream_addr = new_upstream_addr;
+
+ old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface;
+
+ rpf_result = pim_rpf_update(pim, up, &old_rpf, 1);
+ if (rpf_result == PIM_RPF_FAILURE)
+ pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
+
+ /* update kernel multicast forwarding cache (MFC) */
+ if (up->rpf.source_nexthop.interface && up->channel_oil) {
+ ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
+ int vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
+ /* Pass Current selected NH vif index to mroute download */
+ if (vif_index)
+ pim_scan_individual_oil(up->channel_oil, vif_index);
+ else {
+ if (PIM_DEBUG_PIM_NHT)
+ zlog_debug(
+ "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
+ __PRETTY_FUNCTION__, up->sg_str,
+ up->rpf.source_nexthop.interface->name);
+ }
+ }
+
+ if (rpf_result == PIM_RPF_CHANGED)
+ pim_zebra_upstream_rpf_changed(pim, up, &old_rpf);
+
+ pim_zebra_update_all_interfaces(pim);
+}
+
int pim_rp_new(struct pim_instance *pim, const char *rp,
const char *group_range, const char *plist)
{
struct prefix temp;
struct pim_nexthop_cache pnc;
struct route_node *rn;
+ struct pim_upstream *up;
+ struct listnode *upnode;
rp_info = XCALLOC(MTYPE_PIM_RP, sizeof(*rp_info));
"%s: NHT Register rp_all addr %s grp %s ",
__PRETTY_FUNCTION__, buf, buf1);
}
+
+ for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode,
+ up)) {
+ /* Find (*, G) upstream whose RP is not
+ * configured yet
+ */
+ if ((up->upstream_addr.s_addr == INADDR_ANY)
+ && (up->sg.src.s_addr == INADDR_ANY)) {
+ struct prefix grp;
+ struct rp_info *trp_info;
+
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = up->sg.grp;
+ trp_info = pim_rp_find_match_group(pim,
+ &grp);
+ if (trp_info == rp_all)
+ pim_upstream_update(pim, up);
+ }
+ }
+
memset(&pnc, 0, sizeof(struct pim_nexthop_cache));
if (pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_all,
&pnc)) {
rn->lock);
}
+ for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+ if (up->sg.src.s_addr == INADDR_ANY) {
+ struct prefix grp;
+ struct rp_info *trp_info;
+
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = up->sg.grp;
+ trp_info = pim_rp_find_match_group(pim, &grp);
+
+ if (trp_info == rp_info)
+ pim_upstream_update(pim, up);
+ }
+ }
+
/* Register addr with Zebra NHT */
nht_p.family = AF_INET;
nht_p.prefixlen = IPV4_MAX_BITLEN;
struct prefix nht_p;
struct route_node *rn;
bool was_plist = false;
+ struct rp_info *trp_info;
+ struct pim_upstream *up;
+ struct listnode *upnode;
if (group_range == NULL)
result = str2prefix("224.0.0.0/4", &group);
rp_all = pim_rp_find_match_group(pim, &g_all);
if (rp_all == rp_info) {
+ for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+ /* Find the upstream (*, G) whose upstream address is
+ * same as the deleted RP
+ */
+ if ((up->upstream_addr.s_addr == rp_addr.s_addr) &&
+ (up->sg.src.s_addr == INADDR_ANY)) {
+ struct prefix grp;
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = up->sg.grp;
+ trp_info = pim_rp_find_match_group(pim, &grp);
+ if (trp_info == rp_all) {
+ pim_upstream_rpf_clear(pim, up);
+ up->upstream_addr.s_addr = INADDR_ANY;
+ }
+ }
+ }
rp_all->rp.rpf_addr.family = AF_INET;
rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE;
rp_all->i_am_rp = 0;
pim_rp_refresh_group_to_rp_mapping(pim);
+ for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+ /* Find the upstream (*, G) whose upstream address is same as
+ * the deleted RP
+ */
+ if ((up->upstream_addr.s_addr == rp_addr.s_addr) &&
+ (up->sg.src.s_addr == INADDR_ANY)) {
+ struct prefix grp;
+
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = up->sg.grp;
+
+ trp_info = pim_rp_find_match_group(pim, &grp);
+
+ /* RP not found for the group grp */
+ if (pim_rpf_addr_is_inaddr_none(&trp_info->rp)) {
+ pim_upstream_rpf_clear(pim, up);
+ pim_rp_set_upstream_addr(pim,
+ &up->upstream_addr,
+ up->sg.src, up->sg.grp);
+ }
+
+ /* RP found for the group grp */
+ else
+ pim_upstream_update(pim, up);
+ }
+ }
+
XFREE(MTYPE_PIM_RP, rp_info);
return PIM_SUCCESS;
}
else {
if (PIM_DEBUG_PIM_NHT_RP) {
char buf[PREFIX2STR_BUFFER];
+
prefix2str(&nht_p, buf, sizeof(buf));
zlog_debug(
"%s: NHT Local Nexthop not found for RP %s ",
* the rp configured and the source address
*
* If we have don't have a RP configured and the source address is *
- * then return failure.
+ * then set the upstream addr as INADDR_ANY and return failure.
*
*/
int pim_rp_set_upstream_addr(struct pim_instance *pim, struct in_addr *up,
if (PIM_DEBUG_PIM_NHT_RP)
zlog_debug("%s: Received a (*,G) with no RP configured",
__PRETTY_FUNCTION__);
+ up->s_addr = INADDR_ANY;
return 0;
}
bool uj);
void pim_resolve_rp_nh(struct pim_instance *pim);
int pim_rp_list_cmp(void *v1, void *v2);
+struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
+ const struct prefix *group);
+void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up);
#endif
struct prefix src, grp;
bool neigh_needed = true;
+ if (up->upstream_addr.s_addr == INADDR_ANY) {
+ zlog_debug("%s: RP is not configured yet for %s",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return PIM_RPF_OK;
+ }
+
saved.source_nexthop = rpf->source_nexthop;
saved.rpf_addr = rpf->rpf_addr;
return PIM_RPF_OK;
}
+/*
+ * In the case of RP deletion and RP unreachablity,
+ * uninstall the mroute in the kernel and clear the
+ * rpf information in the pim upstream and pim channel
+ * oil data structure.
+ */
+void pim_upstream_rpf_clear(struct pim_instance *pim,
+ struct pim_upstream *up)
+{
+ if (up->rpf.source_nexthop.interface) {
+ if (up->channel_oil) {
+ up->channel_oil->oil.mfcc_parent = MAXVIFS;
+ pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
+
+ }
+ pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED);
+ up->rpf.source_nexthop.interface = NULL;
+ up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
+ PIM_NET_INADDR_ANY;
+ up->rpf.source_nexthop.mrib_metric_preference =
+ router->infinite_assert_metric.metric_preference;
+ up->rpf.source_nexthop.mrib_route_metric =
+ router->infinite_assert_metric.route_metric;
+ up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
+ }
+}
+
/*
RFC 4601: 4.1.6. State Summarization Macros
enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
struct pim_upstream *up, struct pim_rpf *old,
uint8_t is_new);
-
+void pim_upstream_rpf_clear(struct pim_instance *pim,
+ struct pim_upstream *up);
int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf);
int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf);
pim_msdp_up_del(pim, &up->sg);
}
- /* Deregister addr with Zebra NHT */
- nht_p.family = AF_INET;
- nht_p.prefixlen = IPV4_MAX_BITLEN;
- nht_p.u.prefix4 = up->upstream_addr;
- if (PIM_DEBUG_TRACE) {
- char buf[PREFIX2STR_BUFFER];
- prefix2str(&nht_p, buf, sizeof(buf));
- zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
- __PRETTY_FUNCTION__, up->sg_str, buf);
+ /* When RP gets deleted, pim_rp_del() deregister addr with Zebra NHT
+ * and assign up->upstream_addr as INADDR_ANY.
+ * So before de-registering the upstream address, check if is not equal
+ * to INADDR_ANY. This is done in order to avoid de-registering for
+ * 255.255.255.255 which is maintained for some reason..
+ */
+ if (up->upstream_addr.s_addr != INADDR_ANY) {
+ /* Deregister addr with Zebra NHT */
+ nht_p.family = AF_INET;
+ nht_p.prefixlen = IPV4_MAX_BITLEN;
+ nht_p.u.prefix4 = up->upstream_addr;
+ if (PIM_DEBUG_TRACE) {
+ char buf[PREFIX2STR_BUFFER];
+ prefix2str(&nht_p, buf, sizeof(buf));
+ zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT",
+ __PRETTY_FUNCTION__, up->sg_str, buf);
+ }
+ pim_delete_tracked_nexthop(pim, &nht_p, up, NULL);
}
- pim_delete_tracked_nexthop(pim, &nht_p, up, NULL);
XFREE(MTYPE_PIM_UPSTREAM, up);
void pim_upstream_send_join(struct pim_upstream *up)
{
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return;
+ }
+
if (PIM_DEBUG_TRACE) {
char rpf_str[PREFIX_STRLEN];
pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str,
up = THREAD_ARG(t);
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return 0;
+ }
+
/*
* In the case of a HFR we will not ahve anyone to send this to.
*/
static void join_timer_stop(struct pim_upstream *up)
{
- struct pim_neighbor *nbr;
+ struct pim_neighbor *nbr = NULL;
THREAD_OFF(up->t_join_timer);
- nbr = pim_neighbor_find(up->rpf.source_nexthop.interface,
- up->rpf.rpf_addr.u.prefix4);
+ if (up->rpf.source_nexthop.interface)
+ nbr = pim_neighbor_find(up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr.u.prefix4);
if (nbr)
pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
long t_joinsuppress_msec;
long join_timer_remain_msec;
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return;
+ }
+
t_joinsuppress_msec =
MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
1000 * holdtime);
long join_timer_remain_msec;
int t_override_msec;
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return;
+ }
+
join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
t_override_msec =
pim_if_t_override_msec(up->rpf.source_nexthop.interface);
{
enum pim_upstream_state old_state = up->join_state;
+ if (up->upstream_addr.s_addr == INADDR_ANY) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug("%s: RPF not configured for %s",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return;
+ }
+
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug("%s: RP not reachable for %s",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return;
+ }
+
if (PIM_DEBUG_PIM_EVENTS) {
zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
__PRETTY_FUNCTION__, up->sg_str,
&& !I_am_RP(pim, up->sg.grp)) {
if (PIM_DEBUG_PIM_TRACE_DETAIL)
zlog_debug(
- "%s: *,G IIF %s S,G IIF %s ",
- __PRETTY_FUNCTION__,
- up->parent->rpf.source_nexthop
- .interface->name,
- up->rpf.source_nexthop.interface->name);
+ "%s: *,G IIF %s S,G IIF %s ",
+ __PRETTY_FUNCTION__,
+ up->parent->rpf.source_nexthop.interface ?
+ up->parent->rpf.source_nexthop.interface->name
+ : "Unknown",
+ up->rpf.source_nexthop.interface ?
+ up->rpf.source_nexthop.interface->name :
+ "Unknown");
pim_jp_agg_single_upstream_send(&up->parent->rpf,
up->parent,
1 /* (W,G) Join */);
ch->upstream = up;
up = hash_get(pim->upstream_hash, up, hash_alloc_intern);
+ /* Set up->upstream_addr as INADDR_ANY, if RP is not
+ * configured and retain the upstream data structure
+ */
if (!pim_rp_set_upstream_addr(pim, &up->upstream_addr, sg->src,
sg->grp)) {
if (PIM_DEBUG_TRACE)
zlog_debug("%s: Received a (*,G) with no RP configured",
__PRETTY_FUNCTION__);
-
- hash_release(pim->upstream_hash, up);
- XFREE(MTYPE_PIM_UPSTREAM, up);
- return NULL;
}
up->parent = pim_upstream_find_parent(pim, up);
if (up->sg.src.s_addr != INADDR_ANY)
wheel_add_item(pim->upstream_sg_wheel, up);
- rpf_result = pim_rpf_update(pim, up, NULL, 1);
- if (rpf_result == PIM_RPF_FAILURE) {
- struct prefix nht_p;
-
- if (PIM_DEBUG_TRACE)
- zlog_debug(
- "%s: Attempting to create upstream(%s), Unable to RPF for source",
- __PRETTY_FUNCTION__, up->sg_str);
-
- nht_p.family = AF_INET;
- nht_p.prefixlen = IPV4_MAX_BITLEN;
- nht_p.u.prefix4 = up->upstream_addr;
- pim_delete_tracked_nexthop(pim, &nht_p, up, NULL);
+ if (up->upstream_addr.s_addr == INADDR_ANY)
+ /* Create a dummmy channel oil with incoming ineterface MAXVIFS,
+ * since RP is not configured
+ */
+ up->channel_oil = pim_channel_oil_add(pim, &up->sg, MAXVIFS);
- if (up->parent) {
- listnode_delete(up->parent->sources, up);
- up->parent = NULL;
+ else {
+ rpf_result = pim_rpf_update(pim, up, NULL, 1);
+ if (rpf_result == PIM_RPF_FAILURE) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug(
+ "%s: Attempting to create upstream(%s), Unable to RPF for source",
+ __PRETTY_FUNCTION__, up->sg_str);
+ /* Create a dummmy channel oil with incoming ineterface
+ * MAXVIFS, since RP is not reachable
+ */
+ up->channel_oil = pim_channel_oil_add(
+ pim, &up->sg, MAXVIFS);
}
- if (up->sg.src.s_addr != INADDR_ANY)
- wheel_remove_item(pim->upstream_sg_wheel, up);
-
- pim_upstream_remove_children(pim, up);
- if (up->sources)
- list_delete(&up->sources);
-
- list_delete(&up->ifchannels);
-
- hash_release(pim->upstream_hash, up);
- XFREE(MTYPE_PIM_UPSTREAM, up);
- return NULL;
+ if (up->rpf.source_nexthop.interface) {
+ pim_ifp = up->rpf.source_nexthop.interface->info;
+ if (pim_ifp)
+ up->channel_oil = pim_channel_oil_add(pim,
+ &up->sg, pim_ifp->mroute_vif_index);
+ }
}
- if (up->rpf.source_nexthop.interface) {
- pim_ifp = up->rpf.source_nexthop.interface->info;
- if (pim_ifp)
- up->channel_oil = pim_channel_oil_add(
- pim, &up->sg, pim_ifp->mroute_vif_index);
- }
listnode_add_sort(pim->upstream_list, up);
if (PIM_DEBUG_TRACE) {
zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d",
__PRETTY_FUNCTION__, name,
up->sg_str, buf, up->rpf.source_nexthop.interface ?
- up->rpf.source_nexthop.interface->name : "NIL" ,
+ up->rpf.source_nexthop.interface->name : "Unknown" ,
found, up->ref_count);
} else
zlog_debug("%s(%s): (%s) failure to create",
PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
/* switched from false to true */
- if (is_join_desired && !was_join_desired) {
+ if (is_join_desired && (up->join_state == PIM_UPSTREAM_NOTJOINED)) {
pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED);
return;
}
(old_rpf_ifp == ch->interface) &&
/* RPF_interface(S) stopped being I */
(ch->upstream->rpf.source_nexthop
- .interface != ch->interface)) {
+ .interface) &&
+ (ch->upstream->rpf.source_nexthop
+ .interface != ch->interface)) {
assert_action_a5(ch);
}
} /* PIM_IFASSERT_I_AM_LOSER */
case PIM_REG_JOIN:
break;
case PIM_REG_PRUNE:
+ if (!up->rpf.source_nexthop.interface) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: up %s RPF is not present",
+ __PRETTY_FUNCTION__, up->sg_str);
+ return 0;
+ }
+
pim_ifp = up->rpf.source_nexthop.interface->info;
if (!pim_ifp) {
if (PIM_DEBUG_TRACE)
* Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
*/
for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) {
+ if (up->upstream_addr.s_addr == INADDR_ANY) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug(
+ "%s: RP not configured for Upstream %s",
+ __PRETTY_FUNCTION__, up->sg_str);
+ continue;
+ }
+
if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
if (PIM_DEBUG_TRACE)
zlog_debug(
- "Upstream %s without a path to send join, checking",
- up->sg_str);
+ "%s: Upstream %s without a path to send join, checking",
+ __PRETTY_FUNCTION__, up->sg_str);
pim_rpf_update(pim, up, NULL, 1);
}
}
/* "iif == RPF_interface(S)" check has to be done by the kernel or hw
* so we will skip that here */
- if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
+ if (up->rpf.source_nexthop.interface &&
+ pim_if_connected_to_source(up->rpf.source_nexthop.interface,
up->sg.src)) {
return true;
}
} else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags))
pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time);
- if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE) {
+ if ((up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE) &&
+ (up->rpf.source_nexthop.interface)) {
pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface);
}
return;
/*
Upstream (S,G) channel in Joined state
-
(S,G) in the "Not Joined" state is not represented
-
See RFC 4601: 4.5.7. Sending (S,G) Join/Prune Message
+
+ upstream_addr : Who we are talking to.
+ For (*, G), upstream_addr is RP address or INADDR_ANY(if RP not configured)
+ For (S, G), upstream_addr is source address
+
+ rpf: contains the nexthop information to whom we are talking to.
+
+ join_state: JOINED/NOTJOINED
+
+ In the case when FRR receives IGMP/PIM (*, G) join for group G and RP is not
+ configured, then create a pim_upstream with the below information.
+ pim_upstream->upstream address: INADDR_ANY
+ pim_upstream->rpf: Unknown
+ pim_upstream->state: NOTJOINED
+
+ When a new RP gets configured for G, find the corresponding pim upstream (*,G)
+ entries and update the upstream address as new RP address if it the better one
+ for the group G.
+
+ When RP becomes reachable, populate the nexthop information in
+ pim_upstream->rpf and update the state to JOINED.
+
*/
struct pim_upstream {
struct pim_upstream *parent;
struct pim_upstream *up,
struct pim_rpf *old)
{
- struct pim_neighbor *nbr;
+ if (old->source_nexthop.interface) {
+ struct pim_neighbor *nbr;
- nbr = pim_neighbor_find(old->source_nexthop.interface,
- old->rpf_addr.u.prefix4);
- if (nbr)
- pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
+ nbr = pim_neighbor_find(old->source_nexthop.interface,
+ old->rpf_addr.u.prefix4);
+ if (nbr)
+ pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
- /*
- * We have detected a case where we might need
- * to rescan the inherited o_list so do it.
- */
- if (up->channel_oil->oil_inherited_rescan) {
- pim_upstream_inherited_olist_decide(pim, up);
- up->channel_oil->oil_inherited_rescan = 0;
- }
-
- if (up->join_state == PIM_UPSTREAM_JOINED) {
/*
- * If we come up real fast we can be here
- * where the mroute has not been installed
- * so install it.
+ * We have detected a case where we might need
+ * to rescan the inherited o_list so do it.
*/
- if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ if (up->channel_oil->oil_inherited_rescan) {
+ pim_upstream_inherited_olist_decide(pim, up);
+ up->channel_oil->oil_inherited_rescan = 0;
+ }
+
+ if (up->join_state == PIM_UPSTREAM_JOINED) {
+ /*
+ * If we come up real fast we can be here
+ * where the mroute has not been installed
+ * so install it.
+ */
+ if (!up->channel_oil->installed)
+ pim_mroute_add(up->channel_oil,
+ __PRETTY_FUNCTION__);
+
+ /*
+ * RFC 4601: 4.5.7. Sending (S,G)
+ * Join/Prune Messages
+ *
+ * Transitions from Joined State
+ *
+ * RPF'(S,G) changes not due to an Assert
+ *
+ * The upstream (S,G) state machine remains
+ * in Joined state. Send Join(S,G) to the new
+ * upstream neighbor, which is the new value
+ * of RPF'(S,G). Send Prune(S,G) to the old
+ * upstream neighbor, which is the old value
+ * of RPF'(S,G). Set the Join Timer (JT) to
+ * expire after t_periodic seconds.
+ */
+ pim_jp_agg_switch_interface(old, &up->rpf, up);
+
+ pim_upstream_join_timer_restart(up, old);
+ } /* up->join_state == PIM_UPSTREAM_JOINED */
+ }
+ else {
/*
- * RFC 4601: 4.5.7. Sending (S,G)
- * Join/Prune Messages
- *
- * Transitions from Joined State
- *
- * RPF'(S,G) changes not due to an Assert
- *
- * The upstream (S,G) state machine remains
- * in Joined state. Send Join(S,G) to the new
- * upstream neighbor, which is the new value
- * of RPF'(S,G). Send Prune(S,G) to the old
- * upstream neighbor, which is the old value
- * of RPF'(S,G). Set the Join Timer (JT) to
- * expire after t_periodic seconds.
+ * We have detected a case where we might need
+ * to rescan the inherited o_list so do it.
*/
- pim_jp_agg_switch_interface(old, &up->rpf, up);
+ if (up->channel_oil->oil_inherited_rescan) {
+ pim_upstream_inherited_olist_decide(pim, up);
+ up->channel_oil->oil_inherited_rescan = 0;
+ }
- pim_upstream_join_timer_restart(up, old);
- } /* up->join_state == PIM_UPSTREAM_JOINED */
+ if (!up->channel_oil->installed)
+ pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ }
- /* FIXME can join_desired actually be changed by
- pim_rpf_update()
- returning PIM_RPF_CHANGED ? */
+ /* FIXME can join_desired actually be changed by pim_rpf_update()
+ * returning PIM_RPF_CHANGED ?
+ */
pim_upstream_update_join_desired(pim, up);
}
struct pim_rpf old;
struct prefix nht_p;
+ if (up->upstream_addr.s_addr == INADDR_ANY) {
+ if (PIM_DEBUG_TRACE)
+ zlog_debug(
+ "%s: RP not configured for Upstream %s",
+ __PRETTY_FUNCTION__, up->sg_str);
+ continue;
+ }
+
nht_p.family = AF_INET;
nht_p.prefixlen = IPV4_MAX_BITLEN;
nht_p.u.prefix4.s_addr = up->upstream_addr.s_addr;
int input_iface_vif_index;
int old_vif_index;
- if (!pim_rp_set_upstream_addr(c_oil->pim, &vif_source,
+ pim_rp_set_upstream_addr(c_oil->pim, &vif_source,
c_oil->oil.mfcc_origin,
- c_oil->oil.mfcc_mcastgrp))
- return;
+ c_oil->oil.mfcc_mcastgrp);
if (in_vif_index)
input_iface_vif_index = in_vif_index;
struct pim_upstream *up = NULL;
if (!pim_rp_set_upstream_addr(pim, &vif_source,
- source->source_addr, sg.grp))
- return;
-
- /* Register addr with Zebra NHT */
- nht_p.family = AF_INET;
- nht_p.prefixlen = IPV4_MAX_BITLEN;
- nht_p.u.prefix4 = vif_source;
- memset(&out_pnc, 0, sizeof(struct pim_nexthop_cache));
+ source->source_addr, sg.grp)) {
+ /*Create a dummy channel oil */
+ source->source_channel_oil =
+ pim_channel_oil_add(pim, &sg, MAXVIFS);
- src.family = AF_INET;
- src.prefixlen = IPV4_MAX_BITLEN;
- src.u.prefix4 = vif_source; // RP or Src address
- grp.family = AF_INET;
- grp.prefixlen = IPV4_MAX_BITLEN;
- grp.u.prefix4 = sg.grp;
+ if (!source->source_channel_oil) {
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug(
+ "%s %s: could not create OIL for channel (S,G)=%s",
+ __FILE__, __PRETTY_FUNCTION__,
+ pim_str_sg_dump(&sg));
+ }
+ return;
+ }
+ }
- if (pim_find_or_track_nexthop(pim, &nht_p, NULL, NULL,
+ else {
+ /* Register addr with Zebra NHT */
+ nht_p.family = AF_INET;
+ nht_p.prefixlen = IPV4_MAX_BITLEN;
+ nht_p.u.prefix4 = vif_source;
+ memset(&out_pnc, 0, sizeof(struct pim_nexthop_cache));
+
+ src.family = AF_INET;
+ src.prefixlen = IPV4_MAX_BITLEN;
+ src.u.prefix4 = vif_source; // RP or Src address
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = sg.grp;
+
+ if (pim_find_or_track_nexthop(pim, &nht_p, NULL, NULL,
&out_pnc)) {
- if (out_pnc.nexthop_num) {
- up = pim_upstream_find(pim, &sg);
- memset(&nexthop, 0, sizeof(nexthop));
- if (up)
- memcpy(&nexthop,
- &up->rpf.source_nexthop,
- sizeof(struct pim_nexthop));
- pim_ecmp_nexthop_search(pim, &out_pnc, &nexthop,
- &src, &grp, 0);
- if (nexthop.interface)
- input_iface_vif_index =
+ if (out_pnc.nexthop_num) {
+ up = pim_upstream_find(pim, &sg);
+ memset(&nexthop, 0, sizeof(nexthop));
+ if (up)
+ memcpy(&nexthop,
+ &up->rpf.source_nexthop,
+ sizeof(struct pim_nexthop));
+ pim_ecmp_nexthop_search(pim, &out_pnc,
+ &nexthop,
+ &src, &grp, 0);
+ if (nexthop.interface)
+ input_iface_vif_index =
pim_if_find_vifindex_by_ifindex(
- pim,
- nexthop.interface->ifindex);
- } else {
- if (PIM_DEBUG_ZEBRA) {
- char buf1[INET_ADDRSTRLEN];
- char buf2[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>",
+ pim,
+ nexthop.interface->ifindex);
+ } else {
+ if (PIM_DEBUG_ZEBRA) {
+ char buf1[INET_ADDRSTRLEN];
+ char buf2[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<source?>",
nht_p.u.prefix4, buf1,
sizeof(buf1));
- pim_inet4_dump("<source?>",
+ pim_inet4_dump("<source?>",
grp.u.prefix4, buf2,
sizeof(buf2));
- zlog_debug(
+ zlog_debug(
"%s: NHT Nexthop not found for addr %s grp %s",
__PRETTY_FUNCTION__, buf1,
buf2);
+ }
}
- }
- } else
- input_iface_vif_index =
- pim_ecmp_fib_lookup_if_vif_index(pim, &src,
+ } else
+ input_iface_vif_index =
+ pim_ecmp_fib_lookup_if_vif_index(pim, &src,
&grp);
- if (PIM_DEBUG_ZEBRA) {
- char buf2[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", vif_source, buf2,
- sizeof(buf2));
- zlog_debug("%s: NHT %s vif_source %s vif_index:%d ",
- __PRETTY_FUNCTION__, pim_str_sg_dump(&sg),
- buf2, input_iface_vif_index);
- }
-
- if (input_iface_vif_index < 1) {
- if (PIM_DEBUG_IGMP_TRACE) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", source->source_addr,
- source_str, sizeof(source_str));
- zlog_debug(
- "%s %s: could not find input interface for source %s",
- __FILE__, __PRETTY_FUNCTION__,
- source_str);
- }
- return;
- }
+ if (PIM_DEBUG_ZEBRA) {
+ char buf2[INET_ADDRSTRLEN];
- /*
- Protect IGMP against adding looped MFC entries created by both
- source and receiver attached to the same interface. See TODO
- T22.
- */
- if (input_iface_vif_index == pim_oif->mroute_vif_index) {
- /* ignore request for looped MFC entry */
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug(
- "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d",
+ pim_inet4_dump("<source?>", vif_source, buf2,
+ sizeof(buf2));
+ zlog_debug("%s: NHT %s vif_source %s vif_index:%d ",
__PRETTY_FUNCTION__,
pim_str_sg_dump(&sg),
- source->source_group->group_igmp_sock
- ->fd,
- source->source_group->group_igmp_sock
- ->interface->name,
- input_iface_vif_index);
+ buf2, input_iface_vif_index);
}
- return;
- }
- source->source_channel_oil =
- pim_channel_oil_add(pim, &sg, input_iface_vif_index);
- if (!source->source_channel_oil) {
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug(
- "%s %s: could not create OIL for channel (S,G)=%s",
- __FILE__, __PRETTY_FUNCTION__,
- pim_str_sg_dump(&sg));
+ if (input_iface_vif_index < 1) {
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char source_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<source?>",
+ source->source_addr,
+ source_str, sizeof(source_str));
+ zlog_debug(
+ "%s %s: could not find input interface for source %s",
+ __FILE__, __PRETTY_FUNCTION__,
+ source_str);
+ }
+ source->source_channel_oil =
+ pim_channel_oil_add(pim, &sg, MAXVIFS);
+ }
+
+ else {
+ /*
+ * Protect IGMP against adding looped MFC
+ * entries created by both source and receiver
+ * attached to the same interface. See TODO
+ * T22.
+ */
+ if (input_iface_vif_index ==
+ pim_oif->mroute_vif_index) {
+ /* ignore request for looped MFC entry
+ */
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug(
+ "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d",
+ __PRETTY_FUNCTION__,
+ pim_str_sg_dump(&sg),
+ source->source_group
+ ->group_igmp_sock->fd,
+ source->source_group
+ ->group_igmp_sock
+ ->interface->name,
+ input_iface_vif_index);
+ }
+ return;
+ }
+
+ source->source_channel_oil =
+ pim_channel_oil_add(pim, &sg,
+ input_iface_vif_index);
+ if (!source->source_channel_oil) {
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug(
+ "%s %s: could not create OIL for channel (S,G)=%s",
+ __FILE__,
+ __PRETTY_FUNCTION__,
+ pim_str_sg_dump(&sg));
+ }
+ return;
+ }
}
- return;
}
}
sizeof(upstream_str));
zlog_debug("%s: (S,G)=(%s,%s) oif=%s (%s)", __PRETTY_FUNCTION__,
source_str, group_str, ch->interface->name,
- upstream_str);
+ inet_ntoa(up->upstream_addr));
}
/* Resolve IIF for upstream as mroute_del sets mfcc_parent to MAXVIFS,
as part of mroute_del called by pim_forward_stop.
*/
- if (!up->channel_oil
- || (up->channel_oil
- && up->channel_oil->oil.mfcc_parent >= MAXVIFS)) {
+ if ((up->upstream_addr.s_addr != INADDR_ANY) && (!up->channel_oil)) {
struct prefix nht_p, src, grp;
struct pim_nexthop_cache out_pnc;
__FILE__, __PRETTY_FUNCTION__,
source_str);
}
- return;
+ up->channel_oil = pim_channel_oil_add(pim, &up->sg,
+ MAXVIFS);
}
+
+ else {
+ up->channel_oil = pim_channel_oil_add(pim, &up->sg,
+ input_iface_vif_index);
+ if (!up->channel_oil) {
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug(
+ "%s %s: could not create OIL for channel (S,G)=%s",
+ __FILE__, __PRETTY_FUNCTION__,
+ up->sg_str);
+ return;
+ }
+ }
+
if (PIM_DEBUG_TRACE) {
struct interface *in_intf = pim_if_find_by_vif_index(
pim, input_iface_vif_index);
zlog_debug(
"%s: Update channel_oil IIF %s VIFI %d entry %s ",
__PRETTY_FUNCTION__,
- in_intf ? in_intf->name : "NIL",
+ in_intf ? in_intf->name : "Unknown",
input_iface_vif_index, up->sg_str);
}
+
up->channel_oil = pim_channel_oil_add(pim, &up->sg,
input_iface_vif_index);
if (!up->channel_oil) {
+++ /dev/null
-Building your own FRRouting RPM
-======================================
-(Tested on CentOS 6, CentOS 7 and Fedora 24.)
-
-1. On CentOS 6 (which doesn't provide a bison/automake/autoconf of a recent enough version):
- - Check out ../doc/developer/building-frr-for-centos6.rst for details on installing
- a bison/automake/autoconf to support frr building.
-
- Newer automake/autoconf/bison is only needed to build the rpm and is
- **not** needed to install the binary rpm package
-
-2. Install the build packages as documented in doc/developer/building-frr-for-xxxxx.rst and the following additional packages:
-
- yum install rpm-build net-snmp-devel pam-devel libcap-devel
-
- Additionally, on systems with systemd (CentOS 7, Fedora)
-
- yum install systemd-devel
-
- (use `dnf install` on new Fedora instead of `yum install`)
-
-3. Checkout FRR under a **unpriviledged** user account
-
- git clone https://github.com/frrouting/frr.git frr
-
-4. Run Bootstrap and make distribution tar.gz
-
- cd frr
- ./bootstrap.sh
- ./configure --with-pkg-extra-version=-MyRPMVersion \
- SPHINXBUILD=sphinx-build2.7
- make dist
-
- Note: configure parameters are not important for the RPM building - except the `with-pkg-extra-version` if you want to give the RPM a specific name to
- mark your own unoffical build
-
-5. Create RPM directory structure and populate with sources
-
- mkdir rpmbuild
- mkdir rpmbuild/SOURCES
- mkdir rpmbuild/SPECS
- cp redhat/*.spec rpmbuild/SPECS/
- cp frr*.tar.gz rpmbuild/SOURCES/
-
-6. Edit rpm/SPECS/frr.spec with configuration as needed
- Look at the beginning of the file and adjust the following parameters to enable or disable features as required:
-
- ############### FRRouting (FRR) configure options #################
- # with-feature options
- %{!?with_pam: %global with_pam 0 }
- %{!?with_ospfclient: %global with_ospfclient 1 }
- %{!?with_ospfapi: %global with_ospfapi 1 }
- %{!?with_irdp: %global with_irdp 1 }
- %{!?with_rtadv: %global with_rtadv 1 }
- %{!?with_ldpd: %global with_ldpd 1 }
- %{!?with_nhrpd: %global with_nhrpd 1 }
- %{!?with_eigrp: %global with_eigrpd 1 }
- %{!?with_shared: %global with_shared 1 }
- %{!?with_multipath: %global with_multipath 256 }
- %{!?frr_user: %global frr_user frr }
- %{!?vty_group: %global vty_group frrvty }
- %{!?with_fpm: %global with_fpm 0 }
- %{!?with_watchfrr: %global with_watchfrr 1 }
- %{!?with_bgp_vnc: %global with_bgp_vnc 0 }
- %{!?with_pimd: %global with_pimd 1 }
- %{!?with_rpki: %global with_rpki 0 }
-
-7. Build the RPM
-
- rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/frr.spec
-
- If building with RPKI, then download and install the additional RPKI
- packages from
- https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact
-
-DONE.
-
-If all works correctly, then you should end up with the RPMs under
-`rpmbuild/RPMS` and the Source RPM under `rpmbuild/SRPMS`
-
-
-Enabling daemons after installation of the package:
----------------------------------------------------
-
-### init.d based systems (ie CentOS 6):
-
-1. Edit /etc/frr/daemons and enable required routing daemons (Zebra is probably needed for most deployments, so make sure to enable it.)
-
-2. Enable the daemons as needed to run after boot (Zebra is mandatory)
-
- chkconfig frr on
-
-3. Check your firewall / IPtables to make sure the routing protocols are
-allowed.
-
-5. Start the FRR daemons (or reboot)
-
- service frr start
-
-Configuration is stored in `/etc/frr/*.conf` files and daemon selection is stored in `/etc/frr/daemons`.
-
-
-### systemd based systems (ie CentOS 7, Fedora 24)
-
-1. Edit /etc/frr/daemons and enable required routing daemons (Zebra is probably needed for most deployments, so make sure to enable it.)
-
-2. Enable the frr daemons to run after boot.
-
- systemctl enable frr
-
-2. Check your firewall / IPtables to make sure the routing protocols are
-allowed.
-
-3. Start the daemons (or reboot)
-
- systemctl start frr
-
-Configuration is stored in `/etc/frr/*.conf` files and daemon selection is stored in `/etc/frr/daemons`.
-
+++ /dev/null
-# This file tells the frr package which daemons to start.
-#
-# Entries are in the format: <daemon>=(yes|no|priority)
-# 0, "no" = disabled
-# 1, "yes" = highest priority
-# 2 .. 10 = lower priorities
-#
-# For daemons which support multiple instances, a 2nd line listing
-# the instances can be added. Eg for ospfd:
-# ospfd=yes
-# ospfd_instances="1,2"
-#
-# Priorities were suggested by Dancer <dancer@zeor.simegen.com>.
-# They're used to start the FRR daemons in more than one step
-# (for example start one or two at network initialization and the
-# rest later). The number of FRR daemons being small, priorities
-# must be between 1 and 9, inclusive (or the initscript has to be
-# changed). /etc/init.d/frr then can be started as
-#
-# /etc/init.d/frr <start|stop|restart|<priority>>
-#
-# where priority 0 is the same as 'stop', priority 10 or 'start'
-# means 'start all'
-#
-# Sample configurations for these daemons can be found in
-# /usr/share/doc/frr/examples/.
-#
-# ATTENTION:
-#
-# When activation a daemon at the first time, a config file, even if it is
-# empty, has to be present *and* be owned by the user and group "frr", else
-# the daemon will not be started by /etc/init.d/frr. The permissions should
-# be u=rw,g=r,o=.
-# When using "vtysh" such a config file is also needed. It should be owned by
-# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
-#
-watchfrr_enable=yes
-watchfrr_options="-r '/usr/lib/frr/frr restart %s' -s '/usr/lib/frr/frr start %s' -k '/usr/lib/frr/frr stop %s'"
-#
-zebra=no
-bgpd=no
-ospfd=no
-ospf6d=no
-ripd=no
-ripngd=no
-isisd=no
-ldpd=no
-pimd=no
-nhrpd=no
-eigrpd=no
-babeld=no
-sharpd=no
-pbrd=no
-staticd=no
-bfdd=no
-fabricd=no
-
-#
-# Command line options for the daemons
-#
-zebra_options=("-A 127.0.0.1")
-bgpd_options=("-A 127.0.0.1")
-ospfd_options=("-A 127.0.0.1")
-ospf6d_options=("-A ::1")
-ripd_options=("-A 127.0.0.1")
-ripngd_options=("-A ::1")
-isisd_options=("-A 127.0.0.1")
-ldpd_options=("-A 127.0.0.1")
-pimd_options=("-A 127.0.0.1")
-nhrpd_options=("-A 127.0.0.1")
-eigrpd_options=("-A 127.0.0.1")
-babeld_options=("-A 127.0.0.1")
-sharpd_options=("-A 127.0.0.1")
-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")
-
-#
-# If the vtysh_enable is yes, then the unified config is read
-# and applied if it exists. If no unified frr.conf exists
-# then the per-daemon <daemon>.conf files are used)
-# If vtysh_enable is no or non-existant, the frr.conf is ignored.
-# it is highly suggested to have this set to yes
-vtysh_enable=yes
-
+++ /dev/null
-#!/bin/bash
-#
-# /etc/rc.d/init.d/frr
-#
-# Start/Stop the FRR Routing daemons
-# <any general comments about this init script>
-#
-# chkconfig: 2345 15 85
-#
-# description: FRRouting (FRR) is a routing suite for IP routing protocols
-# like BGP, OSPF, RIP and others. This script contols the main
-# daemon "frr" as well as the individual protocol daemons.
-#
-### BEGIN INIT INFO
-# Provides: frr
-# Required-Start: $local_fs $network $syslog
-# Required-Stop: $local_fs $syslog
-# Should-Start: $syslog
-# Should-Stop: $network $syslog
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: Start/Stop the FRR Routing daemons
-# Description: FRRouting (FRR) is a routing suite for IP routing protocols
-# like BGP, OSPF, RIP and others. This script contols the main
-# daemon "frr" as well as the individual protocol daemons.
-### END INIT INFO
-
-PATH=/bin:/usr/bin:/sbin:/usr/sbin
-D_PATH=/usr/lib/frr
-C_PATH=/etc/frr
-V_PATH=/var/run/frr
-
-# 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 pimd pbrd ldpd nhrpd eigrpd babeld staticd sharpd bfdd fabricd"
-MAX_INSTANCES=5
-RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py
-
-. /etc/init.d/functions
-
-# Print the name of the pidfile.
-pidfile()
-{
- echo "$V_PATH/$1.pid"
-}
-
-# Print the name of the vtysh.
-vtyfile()
-{
- echo "$V_PATH/$1.vty"
-}
-
-# Check if daemon is started by using the pidfile.
-started()
-{
- [ ! -e `pidfile $1` ] && return 3
- if [ -n "$2" ] && [ "$2" == "log" ]; then
- status -p `pidfile $1` $1 && return 0 || return $?
- else
- kill -0 `cat \`pidfile $1\`` 2> /dev/null || return 1
- return 0
- fi
-}
-
-# Loads the config via vtysh -b if configured to do so.
-vtysh_b ()
-{
- # Rember, that all variables have been incremented by 1 in convert_daemon_prios()
- if [ "$vtysh_enable" = 2 -a -f $C_PATH/frr.conf ]; then
- /usr/bin/vtysh -b -n
- fi
-}
-
-# Check if the daemon is activated and if its executable and config files
-# are in place.
-# params: daemon name
-# returns: 0=ok, 1=error
-check_daemon()
-{
- # If the integrated config file is used the others are not checked.
- if [ -r "$C_PATH/frr.conf" ]; then
- return 0
- fi
-
- # vtysh_enable has no config file nor binary so skip check.
- # (Not sure why vtysh_enable is in this list but does not hurt)
- if [ $1 != "watchfrr" -a $1 != "vtysh_enable" ]; then
- # check for daemon binary
- if [ ! -x "$D_PATH/$1" ]; then return 1; fi
-
- # check for config file
- if [ -n "$2" ]; then
- if [ ! -r "$C_PATH/$1-$2.conf" ]; then
- touch "$C_PATH/$1-$2.conf"
- chown frr:frr "$C_PATH/$1-$2.conf"
- fi
- elif [ ! -r "$C_PATH/$1.conf" ]; then
- touch "$C_PATH/$1.conf"
- chown frr:frr "$C_PATH/$1.conf"
- fi
- fi
- return 0
-}
-
-# Starts the server if it's not already running according to the pid file.
-# The Frr daemons creates the pidfile when starting.
-start()
-{
- local dmn inst
- dmn="$1"
- inst="$2"
-
- ulimit -n $MAX_FDS > /dev/null 2> /dev/null
- if [ "$dmn" = "watchfrr" ]; then
-
- # We may need to restart watchfrr if new daemons are added and/or
- # removed
- if started "$dmn" ; then
- stop watchfrr
- else
- # Echo only once. watchfrr is printed in the stop above
- echo -n " $dmn"
- fi
-
- if [ -e /var/run/frr/watchfrr.started ] ; then
- rm /var/run/frr/watchfrr.started
- fi
- # redhat /etc/init.d/functions daemon() re-expands args :(
- # eval "set - $watchfrr_options"
- daemon --pidfile=`pidfile $dmn` "$D_PATH/$dmn" -d "$watchfrr_options"
- RETVAL=$?
- [ $RETVAL -ne 0 ] && break
- for i in `seq 1 10`;
- do
- if [ -e /var/run/frr/watchfrr.started ] ; then
- RETVAL=0
- break
- else
- sleep 1
- fi
- done
- RETVAL=1
- elif [ -n "$inst" ]; then
- echo -n " $dmn-$inst"
- if ! check_daemon $dmn $inst ; then
- echo -n " (binary does not exist)"
- return;
- fi
- daemon --pidfile=`pidfile $dmn-$inst` "$D_PATH/$dmn" -d `eval echo "$""$dmn""_options"` -n "$inst"
- RETVAL=$?
- else
- echo -n " $dmn "
- if ! check_daemon $dmn; then
- echo " (binary does not exist)"
- return;
- fi
- daemon --pidfile=`pidfile $dmn` "$D_PATH/$dmn" -d `eval echo "$""$dmn""_options"`
- RETVAL=$?
- fi
- echo
- [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$dmn
- return $RETVAL
-}
-
-# Stop the daemon given in the parameter, printing its name to the terminal.
-stop()
-{
- local inst
-
- if [ -n "$2" ]; then
- inst="$1-$2"
- else
- inst="$1"
- fi
-
- if ! started "$inst" ; then
- # echo -n " ($inst)"
- return 0
- else
- echo -n " $inst"
- PIDFILE=`pidfile $inst`
- PID=`cat $PIDFILE 2>/dev/null`
- killproc -p "$PIDFILE" "$D_PATH/$1"
- RETVAL=$?
- [ $RETVAL -eq 0 ] && rm -f $lockfile
- rm -f `pidfile $inst`
- rm -f `vtyfile $inst`
- echo
- return $RETVAL
- fi
-}
-
-# Converts values from /etc/frr/daemons to all-numeric values.
-convert_daemon_prios()
-{
- for name in $DAEMONS zebra vtysh_enable watchfrr_enable; do
- # First, assign the value set by the user to $value
- eval value=\${${name}:0:3}
-
- # Daemon not activated or entry missing?
- if [ "$value" = "no" -o "$value" = "" ]; then value=0; fi
-
- # These strings parsed for backwards compatibility.
- if [ "$value" = "yes" -o "$value" = "true" ]; then
- value=1;
- fi
-
- # Zebra is threatened special. It must be between 0=off and the first
- # user assigned value "1" so we increase all other enabled daemons' values.
- if [ "$name" != "zebra" -a "$value" -gt 0 ]; then value=`expr "$value" + 1`; fi
-
- # If e.g. name is zebra then we set "zebra=yes".
- eval $name=$value
- done
-}
-
-# Starts watchfrr for all wanted daemons.
-start_watchfrr()
-{
- local daemon_name
- local daemon_prio
- local found_one
- local daemon_inst
-
- # Start the monitor daemon only if desired.
- if [ 0 -eq "$watchfrr_enable" ]; then
- return
- fi
-
- # Check variable type
- if declare -p watchfrr_options | grep -q '^declare \-a'; then
- # old array support
- watchfrr_options="${watchfrr_options[@]}"
- fi
-
- # Which daemons have been started?
- found_one=0
- for daemon_name in $DAEMONS; do
- eval daemon_prio=\$$daemon_name
- if [ "$daemon_prio" -gt 0 ]; then
- eval "daemon_inst=\${${daemon_name}_instances//,/ }"
- if [ -n "$daemon_inst" ]; then
- for inst in ${daemon_inst}; do
- eval "inst_disable=\${${daemon_name}_${inst}}"
- if [ -z ${inst_disable} ] || [ ${inst_disable} != 0 ]; then
- if check_daemon $daemon_name $inst; then
- watchfrr_options="$watchfrr_options ${daemon_name}-${inst}"
- fi
- fi
- done
- else
- if check_daemon $daemon_name; then
- watchfrr_options="$watchfrr_options $daemon_name"
- fi
- fi
- found_one=1
- fi
- done
-
- # Start if at least one daemon is activated.
- if [ $found_one -eq 1 ]; then
- echo "Starting FRRouting monitor daemon:"
- start watchfrr
- fi
-}
-
-# Stopps watchfrr.
-stop_watchfrr()
-{
- echo "Stopping FRRouting monitor daemon:"
- stop watchfrr
-}
-
-# Stops all daemons that have a lower level of priority than the given.
-# (technically if daemon_prio >= wanted_prio)
-stop_prio()
-{
- local wanted_prio
- local daemon_prio
- local daemon_list
- local daemon_inst
- local inst
-
- if [ -n "$2" ] && [[ "$2" =~ (.*)-(.*) ]]; then
- daemon=${BASH_REMATCH[1]}
- inst=${BASH_REMATCH[2]}
- else
- daemon="$2"
- fi
-
- wanted_prio=$1
- daemon_list=${daemon:-$DAEMONS}
-
- echo "Stopping FRRouting daemons (prio:$wanted_prio):"
-
- for prio_i in `seq 10 -1 $wanted_prio`; do
- for daemon_name in $daemon_list; do
- eval daemon_prio=\${${daemon_name}:0:3}
- daemon_inst=""
- if [ $daemon_prio -eq $prio_i ]; then
- eval "daemon_inst=\${${daemon_name}_instances//,/ }"
- if [ -n "$daemon_inst" ]; then
- for i in ${daemon_inst}; do
- if [ -n "$inst" ] && [ "$i" == "$inst" ]; then
- stop "$daemon_name" "$inst"
- elif [ x"$inst" == x ]; then
- stop "$daemon_name" "$i"
- fi
- done
- else
- stop "$daemon_name"
- fi
- fi
- done
- done
-
- if [ -z "$inst" ]; then
- # Now stop other daemons that're prowling, coz the daemons file changed
- echo "Stopping other FRRouting daemons"
- if [ -n "$daemon" ]; then
- eval "file_list_suffix="$V_PATH"/"$daemon*""
- else
- eval "file_list_suffix="$V_PATH/*""
- fi
- for pidfile in $file_list_suffix.pid; do
- if [ -f "$pidfile" ]; then
- filename=${pidfile##*/}
- daemon=${filename%.*}
- echo -n " $daemon"
- killproc -p "$pidfile" "$daemon"
- RETVAL=$?
- [ $RETVAL -eq 0 ] && rm -f $lockfile
- rm -f "$pidfile"
- echo
- fi
- done
- echo -n "Removing remaining .vty files"
- for vtyfile in $file_list_suffix.vty; do
- rm -rf "$vtyfile"
- done
- echo
- fi
-}
-
-# Starts all daemons that have a higher level of priority than the given.
-# (technically if daemon_prio <= wanted_prio)
-start_prio()
-{
- local wanted_prio
- local daemon_prio
- local daemon_list
- local daemon_name
- local daemon_inst
- local inst
-
- if [ -n "$2" ] && [[ "$2" =~ (.*)-(.*) ]]; then
- daemon=${BASH_REMATCH[1]}
- inst=${BASH_REMATCH[2]}
- else
- daemon="$2"
- fi
-
- wanted_prio=$1
- daemon_list=${daemon:-$DAEMONS}
-
- echo "Starting FRRouting daemons (prio:$wanted_prio):"
-
- for prio_i in `seq 1 $wanted_prio`; do
- for daemon_name in $daemon_list; do
- eval daemon_prio=\$${daemon_name}
- daemon_inst=""
- if [ $daemon_prio -eq $prio_i ]; then
- eval "daemon_inst=\${${daemon_name}_instances//,/ }"
- if [ -n "$daemon_inst" ]; then
- if [ `echo "$daemon_inst" | wc -w` -gt ${MAX_INSTANCES} ]; then
- echo "Max instances supported is ${MAX_INSTANCES}. Aborting"
- exit 1
- fi
- # Check if we're starting again by switching from single instance
- # to MI version
- if started "$daemon_name"; then
- PIDFILE=`pidfile $daemon_name`
- killproc -p "$PIDFILE" "$daemon_name"
- rm -f `pidfile $1`
- rm -f `vtyfile $1`
- fi
-
- for i in ${daemon_inst}; do
- if [ -n "$inst" ] && [ "$i" == "$inst" ]; then
- start "$daemon_name" "$inst"
- elif [ x"$inst" == x ]; then
- start "$daemon_name" "$i"
- fi
- done
- else
- # Check if we're starting again by switching from
- # single instance to MI version
- eval "file_list_suffix="$V_PATH"/"$daemon_name-*""
- for pidfile in $file_list_suffix.pid; do
- if [ -f "$pidfile" ]; then
- killproc -p "$pidfile" "$daemon_name"
- rm -rf "$pidfile"
- fi
- done
- for vtyfile in $file_list_suffix.vty; do
- rm -rf "$vtyfile"
- done
-
- start "$daemon_name"
- fi
- fi
- done
- done
-}
-
-check_status()
-{
- local daemon_name
- local daemon_prio
- local daemon_inst
- local failed_status=0
-
- if [ -n "$1" ] && [[ "$1" =~ (.*)-(.*) ]]; then
- daemon=${BASH_REMATCH[1]}
- inst=${BASH_REMATCH[2]}
- else
- daemon="$1"
- fi
-
- daemon_list=${daemon:-$DAEMONS}
-
- # Which daemons have been started?
- for daemon_name in $daemon_list; do
- eval daemon_prio=\$$daemon_name
- if [ "$daemon_prio" -gt 0 ]; then
- eval "daemon_inst=\${${daemon_name}_instances//,/ }"
- if [ -n "$daemon_inst" ]; then
- for i in ${daemon_inst}; do
- if [ -n "$inst" -a "$inst" = "$i" ]; then
- started "$1" "log" || failed_status=$?
- elif [ -z "$inst" ]; then
- started "$daemon_name-$i" "log" || failed_status=$?
- fi
- done
- else
- started "$daemon_name" "log" || failed_status=$?
- fi
- fi
- done
-
- # All daemons that need to have been started are up and running
- return $failed_status
-}
-
-#########################################################
-# Main program #
-#########################################################
-
-# Config broken but script must exit silently.
-[ ! -r "$C_PATH/daemons" ] && exit 0
-
-# Load configuration
-. "$C_PATH/daemons"
-
-# Read configuration variable file if it is present
-[ -r /etc/sysconfig/frr ] && . /etc/sysconfig/frr
-
-MAX_INSTANCES=${MAX_INSTANCES:=5}
-
-# Set priority of un-startable daemons to 'no' and substitute 'yes' to '0'
-convert_daemon_prios
-
-if [ ! -d $V_PATH ]; then
- echo "Creating $V_PATH"
- mkdir -p $V_PATH
- chown frr:frr $V_PATH
- chmod 755 /$V_PATH
-fi
-
-if [ -n "$3" ] && [ "$3" != "all" ]; then
- dmn="$2"-"$3"
-elif [ -n "$2" ] && [ "$2" != "all" ]; then
- dmn="$2"
-fi
-
-case "$1" in
- start)
- # Try to load this necessary (at least for 2.6) module.
- if [ -d /lib/modules/`uname -r` ] ; then
- echo "Loading capability module if not yet done."
- LC_ALL=C modprobe -a capability 2>&1 | egrep -v "(not found|Can't locate)"
- fi
-
- # Start all daemons
- cd $C_PATH/
- if [ "$2" != "watchfrr" ]; then
- start_prio 10 $dmn
- fi
- start_watchfrr
- vtysh_b
- ;;
-
- 1|2|3|4|5|6|7|8|9|10)
- # Stop/start daemons for the appropriate priority level
- stop_prio $1
- start_prio $1
- vtysh_b
- ;;
-
- stop|0)
- # Stop all daemons at level '0' or 'stop'
- stop_watchfrr
- if [ "$dmn" != "watchfrr" ]; then
- [ -n "${dmn}" ] && eval "${dmn/-/_}=0"
- stop_prio 0 $dmn
- fi
-
- if [ -z "$dmn" -o "$dmn" = "zebra" ]; then
- echo "Removing all routes made by zebra."
- ip route flush proto zebra
- # At least in CentOS/RHEL 6, iproute2 doesn't know
- # about the new protocol names, so we have to flush them
- # by number (it also doesn't support rt_protos.d
- ip route flush proto 186
- ip route flush proto 187
- ip route flush proto 188
- ip route flush proto 189
- ip route flush proto 190
- ip route flush proto 191
- ip route flush proto 192
- ip route flush proto 193
- ip route flush proto 194
- else
- [ -n "$dmn" ] && eval "${dmn/-/_}=0"
- start_watchfrr
- fi
- ;;
-
- reload)
- # Just apply the commands that have changed, no restart necessary
- if [ ! -x "$RELOAD_SCRIPT" ]; then
- echo "frr-reload - reload not supported. Use restart or install frr-pythontools package"
- exit 1
- fi
- NEW_CONFIG_FILE="${2:-$C_PATH/frr.conf}"
- if [ ! -r $NEW_CONFIG_FILE ]; then
- echo "Unable to read configuration file $NEW_CONFIG_FILE. Only supporting integrated config"
- exit 1
- fi
- echo "Applying only incremental changes to running configuration from frr.conf"
- "$RELOAD_SCRIPT" --reload /etc/frr/frr.conf
- exit $?
- ;;
-
- status)
- check_status $dmn
- exit $?
- ;;
-
- restart|force-reload)
- $0 stop $dmn
- sleep 1
- $0 start $dmn
- ;;
-
- *)
- echo "Usage: /etc/init.d/frr {start|stop|status|reload|restart|force-reload|<priority>} [daemon]"
- echo " E.g. '/etc/init.d/frr 5' would start all daemons with a prio 1-5."
- echo " reload applies only modifications from the running config to all daemons."
- echo " reload neither restarts starts any daemon nor starts any new ones."
- echo " Read /usr/share/doc/frr/README.Debian for details."
- exit 1
- ;;
-esac
-
-exit 0
+++ /dev/null
-[Unit]
-Description=FRRouting (FRR)
-Wants=network.target
-After=network-pre.target systemd-sysctl.service
-Before=network.target
-OnFailure=heartbeat-failed@%n.service
-
-[Service]
-Nice=-5
-Type=forking
-NotifyAccess=all
-StartLimitInterval=3m
-StartLimitBurst=3
-TimeoutSec=2m
-WatchdogSec=60s
-RestartSec=5
-Restart=on-abnormal
-LimitNOFILE=1024
-ExecStart=/usr/lib/frr/frr start
-ExecStop=/usr/lib/frr/frr stop
-ExecReload=/usr/lib/frr/frr reload
-
-[Install]
-WantedBy=multi-user.target
# install /etc sources
%if "%{initsystem}" == "systemd"
mkdir -p %{buildroot}%{_unitdir}
-install -m644 %{zeb_rh_src}/frr.service %{buildroot}%{_unitdir}/frr.service
-install %{zeb_rh_src}/frr.init %{buildroot}%{_sbindir}/frr
+install -m644 %{zeb_src}/tools/frr.service %{buildroot}%{_unitdir}/frr.service
%else
mkdir -p %{buildroot}%{_initddir}
-install %{zeb_rh_src}/frr.init %{buildroot}%{_sbindir}/frr
-ln -s %{_sbindir}/frr %{buildroot}%{_initddir}/frr
+ln -s %{_sbindir}/frrinit.sh %{buildroot}%{_initddir}/frr
%endif
-install %{zeb_rh_src}/daemons %{buildroot}%{_sysconfdir}/frr
+install %{zeb_src}/tools/etc/frr/daemons %{buildroot}%{_sysconfdir}/frr
# add rpki module to daemon
%if %{with_rpki}
sed -i -e 's/^\(bgpd_options=\)\(.*\)\(".*\)/\1\2 -M rpki\3/' %{buildroot}%{_sysconfdir}/frr/daemons
# Fix bad path in previous config files
# Config files won't get replaced by default, so we do this ugly hack to fix it
-%__sed -i 's|/etc/init.d/|%{_sbindir}/|g' %{configdir}/daemons 2> /dev/null || true
+%__sed -i 's|watchfrr_options=|#watchfrr_options=|g' %{configdir}/daemons 2> /dev/null || true
# With systemd, watchfrr is mandatory. Fix config to make sure it's enabled if
# we install or upgrade to a frr built with systemd
%else
%{_initddir}/frr
%endif
-%{_sbindir}/frr
%config(noreplace) %{_sysconfdir}/pam.d/frr
%config(noreplace) %{_sysconfdir}/logrotate.d/frr
%{_sbindir}/frr-reload
exec docker build --pull \
--compress \
- -t frrouting/frr:topotests-latest \
+ -t frrouting/topotests:latest \
.
fi
if [ "${TOPOTEST_PULL:-1}" = "1" ]; then
- docker pull frrouting/frr:topotests-latest
+ docker pull frrouting/topotests:latest
fi
set -- --rm -i \
-e "TOPOTEST_SANITIZER=$TOPOTEST_SANITIZER" \
--privileged \
$TOPOTEST_OPTIONS \
- frrouting/frr:topotests-latest "$@"
+ frrouting/topotests:latest "$@"
if [ -t 0 ]; then
set -- -t "$@"
--- /dev/null
+#!/usr/bin/env python
+#
+# mcast-rx.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND Cumulus Networks DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+"""
+Subscribe to a multicast group so that the kernel sends an IGMP JOIN
+for the multicast group we subscribed to.
+"""
+
+import argparse
+import logging
+import re
+import os
+import socket
+import subprocess
+import struct
+import sys
+import time
+
+
+def ifname_to_ifindex(ifname):
+ output = subprocess.check_output("ip link show %s" % ifname, shell=True)
+ first_line = output.split('\n')[0]
+ re_index = re.search('^(\d+):', first_line)
+
+ if re_index:
+ return int(re_index.group(1))
+
+ log.error("Could not parse the ifindex for %s out of\n%s" % (ifname, first_line))
+ return None
+
+
+# Thou shalt be root
+if os.geteuid() != 0:
+ sys.stderr.write('ERROR: You must have root privileges\n')
+ sys.exit(1)
+
+
+logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)5s: %(message)s')
+
+# Color the errors and warnings in red
+logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR))
+logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING))
+log = logging.getLogger(__name__)
+
+parser = argparse.ArgumentParser(description='Multicast RX utility',
+ version='1.0.0')
+parser.add_argument('group', help='Multicast IP')
+parser.add_argument('ifname', help='Interface name')
+parser.add_argument('--port', help='UDP port', default=1000)
+parser.add_argument('--sleep', help='Time to sleep before we stop waiting',
+ default = 5)
+args = parser.parse_args()
+
+# Create the datagram socket
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.bind((args.group, args.port))
+
+newpid = os.fork()
+
+if newpid == 0:
+ ifindex = ifname_to_ifindex(args.ifname)
+ mreq = struct.pack("=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+ time.sleep(float(args.sleep))
+ sock.close()
--- /dev/null
+#!/usr/bin/env python
+#
+# mcast-tx.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND Cumulus Networks DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+import argparse
+import logging
+import socket
+import struct
+import time
+
+
+logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)5s: %(message)s')
+
+# Color the errors and warnings in red
+logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR))
+logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING))
+log = logging.getLogger(__name__)
+
+parser = argparse.ArgumentParser(description='Multicast packet generator', version='1.0.0')
+parser.add_argument('group', help='Multicast IP')
+parser.add_argument('ifname', help='Interface name')
+parser.add_argument('--port', type=int, help='UDP port number', default=1000)
+parser.add_argument('--ttl', type=int, help='time-to-live', default=20)
+parser.add_argument('--count', type=int, help='Packets to send', default=1)
+parser.add_argument('--interval', type=int, help='ms between packets', default=100)
+args = parser.parse_args()
+
+# Create the datagram socket
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+# IN.SO_BINDTODEVICE is not defined in some releases of python but it is 25
+# https://github.com/sivel/bonding/issues/10
+#
+# Bind our socket to ifname
+sock.setsockopt(socket.SOL_SOCKET,
+ 25,
+ struct.pack("%ds" % len(args.ifname), args.ifname))
+
+# We need to make sure our sendto() finishes before we close the socket
+sock.setblocking(1)
+
+# Set the time-to-live
+sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack('b', args.ttl))
+
+ms = args.interval / 1000.0
+
+# Send data to the multicast group
+for x in xrange(args.count):
+ log.info('TX multicast UDP packet to %s:%d on %s' % (args.group, args.port, args.ifname))
+ sent = sock.sendto('foobar %d' % x, (args.group, args.port))
+
+ if args.count > 1 and ms:
+ time.sleep(ms)
+
+sock.close()
--- /dev/null
+hostname r1
+!
+interface r1-eth0
+ ip igmp
+ ip pim sm
+!
+interface lo
+ ip pim sm
+!
+ip pim rp 10.254.0.1
--- /dev/null
+hostname r1
+!
+interface r1-eth0
+ ip address 10.0.20.1/24
+!
+interface lo
+ ip address 10.254.0.1/32
+!
--- /dev/null
+hostname r2
--- /dev/null
+hostname r2
+!
+interface r2-eth0
+ ip address 10.0.20.2/24
+!
+interface lo
+ ip address 10.254.0.2/32
+!
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_pim.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+# Donald Sharp
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND Cumulus Networks DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_pim.py: Test pim
+"""
+
+import os
+import sys
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from mininet.topo import Topo
+
+class PIMTopo(Topo):
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ for routern in range(1, 3):
+ tgen.add_router('r{}'.format(routern))
+
+ # r1 <- sw1 -> r2
+ sw = tgen.add_switch('sw1')
+ sw.add_link(tgen.gears['r1'])
+ sw.add_link(tgen.gears['r2'])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(PIMTopo, mod.__name__)
+ tgen.start_topology()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM,
+ os.path.join(CWD, '{}/pimd.conf'.format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_pim_send_mcast_stream():
+ "Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate"
+ logger.info("Establish a Mcast stream from r2->r1 and then ensure S,G created")
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears['r2']
+ r1 = tgen.gears['r1']
+
+ # Let's establish a S,G stream from r2 -> r1
+ CWD = os.path.dirname(os.path.realpath(__file__))
+ r2.run("{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r2-eth0 > /tmp/bar".format(CWD))
+
+ # Let's see that it shows up and we have established some basic state
+ out = r1.vtysh_cmd("show ip pim upstream json", isjson=True)
+ expected = {
+ '229.1.1.1': {
+ '10.0.20.2': {
+ 'firstHopRouter': 1,
+ 'joinState': 'NotJoined',
+ 'regState': 'RegPrune',
+ 'inboundInterface': 'r1-eth0',
+ }
+ }
+ }
+
+ assert topotest.json_cmp(out, expected) is None, 'failed to converge pim'
+
+
+def test_pim_igmp_report():
+ "Send a igmp report from r2->r1 and ensure that the *,G state is created on r1"
+ logger.info("Send a igmp report from r2-r1 and ensure *,G created")
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears['r2']
+ r1 = tgen.gears['r1']
+
+ # Let's send a igmp report from r2->r1
+ CWD = os.path.dirname(os.path.realpath(__file__))
+ r2.run("{}/mcast-rx.py 229.1.1.2 r2-eth0 &".format(CWD))
+
+ out = r1.vtysh_cmd("show ip pim upstream json", isjson=True)
+ expected = {
+ '229.1.1.2': {
+ '*': {
+ 'sourceIgmp': 1,
+ 'joinState': 'Joined',
+ 'regState': 'RegNoInfo',
+ 'sptBit': 0,
+ }
+ }
+ }
+
+ assert topotest.json_cmp(out, expected) is None, 'failed to converge pim'
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip('Memory leak test/report is disabled')
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == '__main__':
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
#
# ATTENTION:
#
-# When activation a daemon at the first time, a config file, even if it is
+# When activating a daemon for the first time, a config file, even if it is
# empty, has to be present *and* be owned by the user and group "frr", else
# the daemon will not be started by /etc/init.d/frr. The permissions should
# be u=rw,g=r,o=.
--- /dev/null
+# FRR Support Bundle Command List
+# Do Not modify the lines that start with
+# PROC_NAME, CMD_LIST_START and CMD_LIST_END
+# Add the new command for each process between
+# CMD_LIST_START and CMD_LIST_END lines
+
+# BGP Support Bundle Command List
+PROC_NAME:bgp
+CMD_LIST_START
+show bgp summary
+show ip bgp
+show ip bgp neighbors
+show ip bgp summary
+show ip bgp statistics
+
+show ip bgp update-groups advertise-queue
+show ip bgp update-groups advertised-routes
+show ip bgp update-groups packet-queue
+show ip bgp update-groups statistics
+show ip bgp peer-group
+show ip bgp memory
+
+show bgp ipv6
+show bgp ipv6 neighbors
+show bgp ipv6 summary
+show bgp ipv6 update-groups advertise-queue
+show bgp ipv6 update-groups advertised-routes
+show bgp ipv6 update-groups packet-queue
+show bgp ipv6 update-groups statistics
+show ip bgp statistics
+
+show bgp evpn route
+CMD_LIST_END
+
+# Zebra Support Bundle Command List
+PROC_NAME:zebra
+CMD_LIST_START
+show zebra
+show zebra client summary
+show ip route
+
+show route-map
+show memory
+show interface
+show vrf
+show error all
+show work-queues
+show running-config
+show thread cpu
+show thread poll
+show daemons
+show version
+CMD_LIST_END
+
+# OSPF Support Bundle Command List
+# PROC_NAME:ospf
+# CMD_LIST_START
+# CMD_LIST_END
+
+# RIP Support Bundle Command List
+# PROC_NAME:rip
+# CMD_LIST_START
+# CMD_LIST_END
+
+# ISIS Support Bundle Command List
+# PROC_NAME:isis
+# CMD_LIST_START
+# CMD_LIST_END
+
+# BFD Support Bundle Command List
+# PROC_NAME:bfd
+# CMD_LIST_START
+# CMD_LIST_END
+
+# STATIC Support Bundle Command List
+# PROC_NAME:static
+# CMD_LIST_START
+# CMD_LIST_END
+
+# PIM Support Bundle Command List
+# PROC_NAME:pim
+# CMD_LIST_START
+# CMD_LIST_END
# Load configuration
. "$C_PATH/daemons"
-. "$C_PATH/daemons.conf"
+if [ -e "$C_PATH/daemons.conf" ]; then
+ . "$C_PATH/daemons.conf"
+fi
# Read configuration variable file if it is present
[ -r /etc/default/frr ] && . /etc/default/frr
load_old_config "/etc/default/frr"
load_old_config "/etc/sysconfig/frr"
-if declare -p watchfrr_options | grep -q '^declare \-a'; then
+if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then
log_warning_msg "watchfrr_options contains a bash array value." \
"The configured value is intentionally ignored since it is likely wrong." \
"Please remove or fix the setting."
--- /dev/null
+########################################################
+### Python Script to generate the FRR support bundle ###
+########################################################
+import os
+import subprocess
+import datetime
+
+TOOLS_DIR="tools/"
+ETC_DIR="/etc/frr/"
+LOG_DIR="/var/log/frr/"
+SUCCESS = 1
+FAIL = 0
+
+inputFile = ETC_DIR + "support_bundle_commands.conf"
+
+# Open support bundle configuration file
+def openConfFile(i_file):
+ try:
+ with open(i_file) as supportBundleConfFile:
+ lines = filter(None, (line.rstrip() for line in supportBundleConfFile))
+ return lines
+ except IOError:
+ return ([])
+
+# Create the output file name
+def createOutputFile(procName):
+ fileName = procName + "_support_bundle.log"
+ oldFile = LOG_DIR + fileName
+ cpFileCmd = "cp " + oldFile + " " + oldFile + ".prev"
+ rmFileCmd = "rm -rf " + oldFile
+ print "Making backup of " + oldFile
+ os.system(cpFileCmd)
+ print "Removing " + oldFile
+ os.system(rmFileCmd)
+ return fileName
+
+# Open the output file for this process
+def openOutputFile(fileName):
+ crt_file_cmd = LOG_DIR + fileName
+ print crt_file_cmd
+ try:
+ outputFile = open(crt_file_cmd, "w")
+ return outputFile
+ except IOError:
+ return ()
+
+# Close the output file for this process
+def closeOutputFile(file):
+ try:
+ file.close()
+ return SUCCESS
+ except IOError:
+ return FAIL
+
+# Execute the command over vtysh and store in the
+# output file
+def executeCommand(cmd, outputFile):
+ cmd_exec_str = "vtysh -c \"" + cmd + "\" "
+ try:
+ cmd_output = subprocess.check_output(cmd_exec_str, shell=True)
+ try:
+ dateTime = datetime.datetime.now()
+ outputFile.write(">>[" + str(dateTime) + "]" + cmd + "\n")
+ outputFile.write(cmd_output)
+ outputFile.write("########################################################\n")
+ outputFile.write('\n')
+ except:
+ print "Writing to ouptut file Failed"
+ except subprocess.CalledProcessError as e:
+ dateTime = datetime.datetime.now()
+ outputFile.write(">>[" + str(dateTime) + "]" + cmd + "\n")
+ outputFile.write(e.output)
+ outputFile.write("########################################################\n")
+ outputFile.write('\n')
+ print "Error:" + e.output
+
+
+# Process the support bundle configuration file
+# and call appropriate functions
+def processConfFile(lines):
+ for line in lines:
+ if line[0][0] == '#':
+ continue
+ cmd_line = line.split(':')
+ if cmd_line[0] == "PROC_NAME":
+ outputFileName = createOutputFile(cmd_line[1])
+ if outputFileName:
+ print outputFileName, "created for", cmd_line[1]
+ elif cmd_line[0] == "CMD_LIST_START":
+ outputFile = openOutputFile(outputFileName)
+ if outputFile:
+ print outputFileName, "opened"
+ else:
+ print outputFileName, "open failed"
+ return FAIL
+ elif cmd_line[0] == "CMD_LIST_END":
+ if closeOutputFile(outputFile):
+ print outputFileName, "closed"
+ else:
+ print outputFileName, "close failed"
+ else:
+ print "Execute:" , cmd_line[0]
+ executeCommand(cmd_line[0], outputFile)
+
+# Main Function
+lines = openConfFile(inputFile)
+if not lines:
+ print "File support_bundle_commands.conf not present in /etc/frr/ directory"
+else:
+ processConfFile(lines)
#include "lib/if.h"
#include "lib/prefix.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct connected *connected_check(struct interface *ifp,
union prefixconstptr p);
extern struct connected *connected_check_ptp(struct interface *ifp,
extern int connected_is_unnumbered(struct interface *);
+#ifdef __cplusplus
+}
+#endif
#endif /*_ZEBRA_CONNECTED_H */
#include "lib/vty.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Debug flags. */
#define ZEBRA_DEBUG_EVENT 0x01
DECLARE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty));
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_DEBUG_H */
#ifdef HAVE_NETLINK
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
int startup);
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* HAVE_NETLINK */
#endif /* _ZEBRA_IF_NETLINK_H */
#include "zebra/zebra_l2.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* For interface multicast configuration. */
#define IF_ZEBRA_MULTICAST_UNSPEC 0
#define IF_ZEBRA_MULTICAST_ON 1
extern int ifaddr_proc_ipv6(void);
#endif /* HAVE_PROC_NET_IF_INET6 */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_INTERFACE_H */
#ifndef _ZEBRA_IOCTL_H
#define _ZEBRA_IOCTL_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Prototypes. */
extern void ifreq_set_name(struct ifreq *, struct interface *);
extern int if_ioctl(unsigned long, caddr_t);
#endif /* SOLARIS_IPV6 */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_IOCTL_H */
#ifndef _ZEBRA_IF_IOCTL_SOLARIS_H
#define _ZEBRA_IF_IOCTL_SOLARIS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void lifreq_set_name(struct lifreq *, const char *);
int if_get_flags_direct(const char *, uint64_t *, unsigned int af);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_IF_IOCTL_SOLARIS_H */
#ifndef _ZEBRA_IPFORWARD_H
#define _ZEBRA_IPFORWARD_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern int ipforward(void);
extern int ipforward_on(void);
extern int ipforward_off(void);
extern int ipforward_ipv6_on(void);
extern int ipforward_ipv6_off(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_IPFORWARD_H */
#include "lib/vty.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define TRUE 1
#define FALSE 0
extern void send_packet(struct interface *ifp, struct stream *s, uint32_t dst,
struct prefix *p, uint32_t ttl);
+#ifdef __cplusplus
+}
+#endif
#endif /* _IRDP_H */
return lookup_msg(rttype_str, rttype, "");
}
-#define NL_OK(nla, len) \
+#define NLA_OK(nla, len) \
((len) >= (int)sizeof(struct nlattr) \
&& (nla)->nla_len >= sizeof(struct nlattr) \
&& (nla)->nla_len <= (len))
-#define NL_NEXT(nla, attrlen) \
- ((attrlen) -= RTA_ALIGN((nla)->nla_len), \
- (struct nlattr *)(((char *)(nla)) + RTA_ALIGN((nla)->nla_len)))
-#define NL_RTA(r) \
- ((struct nlattr *)(((char *)(r)) \
- + NLMSG_ALIGN(sizeof(struct nlmsgerr))))
+#define NLA_NEXT(nla, attrlen) \
+ ((attrlen) -= NLA_ALIGN((nla)->nla_len), \
+ (struct nlattr *)(((char *)(nla)) + NLA_ALIGN((nla)->nla_len)))
+#define NLA_LENGTH(len) (NLA_ALIGN(sizeof(struct nlattr)) + (len))
+#define NLA_DATA(nla) ((struct nlattr *)(((char *)(nla)) + NLA_LENGTH(0)))
+
+#define ERR_NLA(err, inner_len) \
+ ((struct nlattr *)(((char *)(err)) \
+ + NLMSG_ALIGN(sizeof(struct nlmsgerr)) \
+ + NLMSG_ALIGN((inner_len))))
static void netlink_parse_nlattr(struct nlattr **tb, int max,
struct nlattr *nla, int len)
{
- while (NL_OK(nla, len)) {
+ while (NLA_OK(nla, len)) {
if (nla->nla_type <= max)
tb[nla->nla_type] = nla;
- nla = NL_NEXT(nla, len);
+ nla = NLA_NEXT(nla, len);
}
}
static void netlink_parse_extended_ack(struct nlmsghdr *h)
{
- struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
- const struct nlmsgerr *err =
- (const struct nlmsgerr *)((uint8_t *)h
- + NLMSG_ALIGN(
- sizeof(struct nlmsghdr)));
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+ const struct nlmsgerr *err = (const struct nlmsgerr *)NLMSG_DATA(h);
const struct nlmsghdr *err_nlh = NULL;
- uint32_t hlen = sizeof(*err);
+ /* Length not including nlmsghdr */
+ uint32_t len = 0;
+ /* Inner error netlink message length */
+ uint32_t inner_len = 0;
const char *msg = NULL;
uint32_t off = 0;
if (!(h->nlmsg_flags & NLM_F_CAPPED))
- hlen += h->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));
+ inner_len = (uint32_t)NLMSG_PAYLOAD(&err->msg, 0);
+
+ len = (uint32_t)(NLMSG_PAYLOAD(h, sizeof(struct nlmsgerr)) - inner_len);
- memset(tb, 0, sizeof(tb));
- netlink_parse_nlattr(tb, NLMSGERR_ATTR_MAX, NL_RTA(h), hlen);
+ netlink_parse_nlattr(tb, NLMSGERR_ATTR_MAX, ERR_NLA(err, inner_len),
+ len);
if (tb[NLMSGERR_ATTR_MSG])
- msg = (const char *)RTA_DATA(tb[NLMSGERR_ATTR_MSG]);
+ msg = (const char *)NLA_DATA(tb[NLMSGERR_ATTR_MSG]);
if (tb[NLMSGERR_ATTR_OFFS]) {
- off = *(uint32_t *)RTA_DATA(tb[NLMSGERR_ATTR_OFFS]);
+ off = *(uint32_t *)NLA_DATA(tb[NLMSGERR_ATTR_OFFS]);
if (off > h->nlmsg_len) {
zlog_err("Invalid offset for NLMSGERR_ATTR_OFFS");
#ifndef _ZEBRA_KERNEL_NETLINK_H
#define _ZEBRA_KERNEL_NETLINK_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef HAVE_NETLINK
#define NL_RCV_PKT_BUF_SIZE 32768
#endif /* HAVE_NETLINK */
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_KERNEL_NETLINK_H */
#ifndef __ZEBRA_KERNEL_SOCKET_H
#define __ZEBRA_KERNEL_SOCKET_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Error codes of zebra. */
#define ZEBRA_ERR_NOERROR 0
#define ZEBRA_ERR_RTEXIST -1
enum blackhole_type, int);
extern const struct message rtm_type_str[];
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __ZEBRA_KERNEL_SOCKET_H */
#include "zebra/zserv.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define NO_PROTO 0
/*
int release_daemon_label_chunks(struct zserv *client);
void label_manager_close(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _LABEL_MANAGER_H */
#include "zebra/zserv.h"
#include "zebra/rib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* ZAPI command handlers */
extern void zebra_redistribute_add(ZAPI_HANDLER_ARGS);
extern void zebra_redistribute_delete(ZAPI_HANDLER_ARGS);
extern int zebra_import_table_config(struct vty *);
extern void zebra_import_table_rm_update(const char *rmap);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_REDISTRIBUTE_H */
#include "mpls.h"
#include "srcdest_table.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define DISTANCE_INFINITY 255
#define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */
extern pid_t pid;
extern bool v6_rr_semantics;
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /*_ZEBRA_RIB_H */
#include "zclient.h"
#include "if.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern void router_id_add_address(struct connected *);
extern void router_id_del_address(struct connected *);
extern void router_id_init(struct zebra_vrf *);
extern void router_id_write(struct vty *);
extern void router_id_get(struct prefix *, vrf_id_t);
+#ifdef __cplusplus
+}
+#endif
+
#endif
#include "zebra/zebra_mpls.h"
#include "zebra/zebra_dplane.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* Update or delete a route, LSP, or pseudowire from the kernel,
* using info from a dataplane context.
struct interface *vlan_if);
extern void route_read(struct zebra_ns *zns);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_RT_H */
#include "zebra/zebra_mpls.h"
#include "zebra/zebra_dplane.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define NL_DEFAULT_ROUTE_METRIC 20
/*
extern int netlink_neigh_read_specific_ip(struct ipaddr *ip,
struct interface *vlan_if);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* HAVE_NETLINK */
#endif /* _ZEBRA_RT_NETLINK_H */
#include "vty.h"
#include "zebra/interface.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* NB: RTADV is defined in zebra/interface.h above */
#if defined(HAVE_RTADV)
extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS);
extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS);
+#ifdef __cplusplus
+}
+#endif
#endif /* _ZEBRA_RTADV_H */
#ifdef HAVE_NETLINK
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* Handle netlink notification informing a rule add or delete.
*/
*/
extern int netlink_rules_read(struct zebra_ns *zns);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* HAVE_NETLINK */
#endif /* _ZEBRA_RULE_NETLINK_H */
#include "zebra/zserv.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* Table chunk struct
* Client daemon which the chunk belongs to can be identified by either
int release_daemon_table_chunks(struct zserv *client);
void table_manager_disable(ns_id_t ns_id);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _TABLE_MANAGER_H */
#include "zebra/zebra_pbr.h"
#include "zebra/zebra_errors.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* This is called to process inbound ZAPI messages.
*
const unsigned int nexthop_num);
extern void zsend_capabilities_all_clients(void);
+
+#ifdef __cplusplus
+}
+#endif
#include "zebra/zserv.h"
#include "zebra/zebra_mpls.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Key netlink info from zebra ns */
struct zebra_dplane_info {
ns_id_t ns_id;
void zebra_dplane_finish(void);
void zebra_dplane_shutdown(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_DPLANE_H */
#include "lib/ferr.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
enum zebra_log_refs {
EC_ZEBRA_LM_RESPONSE = ZEBRA_FERR_START,
EC_ZEBRA_LM_NO_SUCH_CLIENT,
void zebra_error_init(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __EC_ZEBRAORS_H__ */
#include "zebra/debug.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
#define zfpm_debug(...) \
uint8_t *in_buf, size_t in_buf_len);
extern struct route_entry *zfpm_route_for_update(rib_dest_t *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_FPM_PRIVATE_H */
#include "vlan.h"
#include "vxlan.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* zebra L2 interface information - bridge slave (linkage to bridge) */
struct zebra_l2info_brslave {
ifindex_t bridge_ifindex; /* Bridge Master */
extern void zebra_l2if_update_bond_slave(struct interface *ifp,
ifindex_t bond_ifindex);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_L2_H */
#include "memory.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
DECLARE_MGROUP(ZEBRA)
DECLARE_MTYPE(RTADV_PREFIX)
DECLARE_MTYPE(ZEBRA_NS)
DECLARE_MTYPE(DP_CTX)
DECLARE_MTYPE(DP_PROV)
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _QUAGGA_ZEBRA_MEMORY_H */
#include "mlag.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void zebra_mlag_init(void);
void zebra_mlag_terminate(void);
enum mlag_role zebra_mlag_get_role(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
#include "zebra/zserv.h"
#include "zebra/zebra_vrf.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
/* Definitions and macros. */
/* Global variables. */
extern int mpls_enabled;
+#ifdef __cplusplus
+}
+#endif
+
#endif /*_ZEBRA_MPLS_H */
#include "zebra/zserv.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct mcast_route_data {
struct prefix_sg sg;
unsigned int ifindex;
void zebra_ipmr_route_stats(ZAPI_HANDLER_ARGS);
+#ifdef __cplusplus
+}
+#endif
+
#endif
#include "zebra.h"
#include "ns.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern ns_id_t zebra_ns_id_get(const char *netnspath);
extern ns_id_t zebra_ns_id_get_default(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __ZEBRA_NS_ID_H__ */
#ifndef _NETNS_NOTIFY_H
#define _NETNS_NOTIFY_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern void zebra_ns_notify_init(void);
extern void zebra_ns_notify_parse(void);
extern void zebra_ns_notify_close(void);
extern struct zebra_privs_t zserv_privs;
+#ifdef __cplusplus
+}
+#endif
+
#endif /* NETNS_NOTIFY_H */
#include "zebra/rib.h"
#include "zebra/zebra_vrf.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifdef HAVE_NETLINK
/* Socket interface to kernel */
struct nlsock {
int zebra_ns_config_write(struct vty *vty, struct ns *ns);
+#ifdef __cplusplus
+}
+#endif
+
#endif
#include "rt.h"
#include "pbr.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct zebra_pbr_rule {
int sock;
DECLARE_HOOK(zebra_pbr_ipset_update,
(int cmd, struct zebra_pbr_ipset *ipset), (cmd, ipset));
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_PBR_H */
#include "zebra/zserv.h"
#include "zebra/interface.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Zebra ptm context block */
struct zebra_ptm_cb {
int ptm_sock; /* ptm file descriptor. */
void zebra_ptm_if_set_ptm_state(struct interface *ifp,
struct zebra_if *zebra_ifp);
void zebra_ptm_if_write(struct vty *vty, struct zebra_if *zebra_ifp);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
#ifndef _ZEBRA_PTM_REDISTRIBUTE_H
#define _ZEBRA_PTM_REDISTRIBUTE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern void zebra_interface_bfd_update(struct interface *, struct prefix *,
struct prefix *, int, vrf_id_t);
extern void zebra_bfd_peer_replay_req(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_PTM_REDISTRIBUTE_H */
#include "zebra/zebra_vrf.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define PW_INSTALL_RETRY_INTERVAL 30
struct zebra_pw {
void zebra_pw_exit(struct zebra_vrf *);
void zebra_pw_vty_init(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* ZEBRA_PW_H_ */
#include "prefix.h"
#include "vty.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Nexthop structure. */
struct rnh {
uint8_t flags;
extern void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty,
rnh_type_t);
extern char *rnh_str(struct rnh *rnh, char *buf, int size);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /*_ZEBRA_RNH_H */
#include "lib/routemap.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern void zebra_route_map_init(void);
extern void zebra_routemap_config_write_protocol(struct vty *vty,
struct zebra_vrf *vrf);
struct zebra_vrf *zvrf, struct route_entry *,
struct nexthop *nexthop);
+#ifdef __cplusplus
+}
+#endif
+
#endif
#include "zebra/zebra_ns.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* This header file contains the idea of a router and as such
* owns data that is associated with a router from zebra's
extern void zebra_router_show_table_summary(struct vty *vty);
extern uint32_t zebra_router_get_next_sequence(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
/* Remove all routes. */
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ route_table_finish(zvrf->rnh_table[afi]);
+ zvrf->rnh_table[afi] = NULL;
+ route_table_finish(zvrf->import_check_table[afi]);
+ zvrf->import_check_table[afi] = NULL;
+
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
rib_close_table(zvrf->table[afi][safi]);
}
safi);
zvrf->table[afi][safi] = NULL;
}
-
- route_table_finish(zvrf->rnh_table[afi]);
- zvrf->rnh_table[afi] = NULL;
- route_table_finish(zvrf->import_check_table[afi]);
- zvrf->import_check_table[afi] = NULL;
}
return 0;
}
}
- route_table_finish(zvrf->rnh_table[afi]);
- route_table_finish(zvrf->import_check_table[afi]);
+ if (zvrf->rnh_table[afi])
+ route_table_finish(zvrf->rnh_table[afi]);
+ if (zvrf->import_check_table[afi])
+ route_table_finish(zvrf->import_check_table[afi]);
}
/* Cleanup EVPN states for vrf */
#include <zebra/zebra_pw.h>
#include <lib/vxlan.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* MPLS (Segment Routing) global block */
typedef struct mpls_srgb_t_ {
uint32_t start_label;
extern void zebra_rtable_node_cleanup(struct route_table *table,
struct route_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* ZEBRA_VRF_H */
if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED))
json_object_boolean_true_add(json_route, "queued");
- if (re->type != ZEBRA_ROUTE_CONNECT) {
- json_object_int_add(json_route, "distance",
- re->distance);
- json_object_int_add(json_route, "metric", re->metric);
- }
-
if (re->tag)
json_object_int_add(json_route, "tag", re->tag);
#include "zebra/zebra_vrf.h"
#include "zebra/zserv.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Is EVPN enabled? */
#define EVPN_ENABLED(zvrf) (zvrf)->advertise_all_vni
static inline int is_evpn_enabled(void)
struct zebra_vrf *zvrf,
vni_t vni);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_VXLAN_H */
#include <zebra.h>
-#include <zebra.h>
-
#include "if.h"
#include "linklist.h"
#include "zebra_vxlan.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define ERR_STR_SZ 256
/* definitions */
struct json_object *json;
};
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_VXLAN_PRIVATE_H */
#include "zebra/zebra_vrf.h" /* for zebra_vrf */
/* clang-format on */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Default port information. */
#define ZEBRA_VTY_PORT 2601
/* TODO */
int zebra_finalize(struct thread *event);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_ZEBRA_H */