summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--alpine/APKBUILD.in32
-rwxr-xr-xalpine/docker-start10
-rwxr-xr-xalpine/frr.post-deinstall6
-rwxr-xr-xalpine/frr.pre-deinstall4
-rwxr-xr-xalpine/frr.pre-install10
-rw-r--r--babeld/route.c1
-rw-r--r--bgpd/Makefile.am5
-rw-r--r--bgpd/bgp_clist.c28
-rw-r--r--bgpd/bgp_clist.h4
-rw-r--r--bgpd/bgp_debug.c54
-rw-r--r--bgpd/bgp_debug.h3
-rw-r--r--bgpd/bgp_ecommunity.c12
-rw-r--r--bgpd/bgp_ecommunity.h1
-rw-r--r--bgpd/bgp_flowspec_vty.c3
-rw-r--r--bgpd/bgp_labelpool.c592
-rw-r--r--bgpd/bgp_labelpool.h51
-rw-r--r--bgpd/bgp_main.c6
-rw-r--r--bgpd/bgp_mpath.c2
-rw-r--r--bgpd/bgp_mplsvpn.c176
-rw-r--r--bgpd/bgp_mplsvpn.h12
-rw-r--r--bgpd/bgp_packet.c7
-rw-r--r--bgpd/bgp_route.c70
-rw-r--r--bgpd/bgp_vty.c75
-rw-r--r--bgpd/bgp_zebra.c40
-rw-r--r--bgpd/bgpd.c9
-rw-r--r--bgpd/bgpd.h40
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.c11
-rw-r--r--bgpd/rfapi/rfapi_import.c36
-rwxr-xr-xconfigure.ac1
-rw-r--r--debianpkg/backports/ubuntu17.10/debian/control2
-rw-r--r--debianpkg/backports/ubuntu18.04/debian/control2
-rw-r--r--debianpkg/control2
-rw-r--r--doc/Makefile.am9
-rw-r--r--doc/developer/_static/overrides.css4
-rw-r--r--doc/developer/building-frr-on-alpine.rst29
-rw-r--r--doc/developer/building-frr-on-centos6.rst18
-rw-r--r--doc/developer/building-frr-on-centos7.rst20
-rw-r--r--doc/developer/building-frr-on-debian8.rst10
-rw-r--r--doc/developer/building-frr-on-debian9.rst10
-rw-r--r--doc/developer/building-frr-on-fedora24.rst18
-rw-r--r--doc/developer/building-frr-on-freebsd10.rst6
-rw-r--r--doc/developer/building-frr-on-freebsd11.rst6
-rw-r--r--doc/developer/building-frr-on-freebsd9.rst6
-rw-r--r--doc/developer/building-frr-on-lede-openwrt.rst4
-rw-r--r--doc/developer/building-frr-on-netbsd6.rst12
-rw-r--r--doc/developer/building-frr-on-netbsd7.rst12
-rw-r--r--doc/developer/building-frr-on-omnios.rst6
-rw-r--r--doc/developer/building-frr-on-openbsd6.rst14
-rw-r--r--doc/developer/building-frr-on-ubuntu1204.rst14
-rw-r--r--doc/developer/building-frr-on-ubuntu1404.rst17
-rw-r--r--doc/developer/building-frr-on-ubuntu1604.rst20
-rw-r--r--doc/developer/building-frr-on-ubuntu1804.rst20
-rw-r--r--doc/developer/building.rst3
-rw-r--r--doc/developer/conf.py56
-rw-r--r--doc/developer/hooks.rst26
-rw-r--r--doc/developer/library.rst5
-rw-r--r--doc/developer/memtypes.rst44
-rw-r--r--doc/developer/next-hop-tracking.rst12
-rw-r--r--doc/developer/ospf-sr.rst10
-rw-r--r--doc/developer/workflow.rst128
-rw-r--r--doc/extra/frrlexer.py38
-rw-r--r--doc/manpages/common-options.rst1
-rw-r--r--doc/manpages/conf.py41
-rw-r--r--doc/manpages/defines.rst2
-rw-r--r--doc/manpages/index.rst1
-rw-r--r--doc/manpages/pbrd.rst38
-rw-r--r--doc/user/_static/overrides.css4
-rw-r--r--doc/user/basic.rst136
-rw-r--r--doc/user/bgp.rst124
-rw-r--r--doc/user/conf.py56
-rw-r--r--doc/user/eigrpd.rst5
-rw-r--r--doc/user/filter.rst2
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/isisd.rst12
-rw-r--r--doc/user/nhrpd.rst29
-rw-r--r--doc/user/ospf6d.rst6
-rw-r--r--doc/user/ospf_fundamentals.rst182
-rw-r--r--doc/user/ospfd.rst182
-rw-r--r--doc/user/overview.rst22
-rw-r--r--doc/user/pbr.rst123
-rw-r--r--doc/user/pim.rst4
-rw-r--r--doc/user/ripd.rst35
-rw-r--r--doc/user/routemap.rst8
-rw-r--r--doc/user/routeserver.rst204
-rw-r--r--doc/user/rpki.rst4
-rw-r--r--doc/user/snmp.rst84
-rw-r--r--doc/user/snmptrap.rst2
-rw-r--r--doc/user/vnc.rst120
-rw-r--r--doc/user/zebra.rst60
-rw-r--r--docker/alpine/Dockerfile4
-rw-r--r--eigrpd/eigrp_snmp.c2
-rw-r--r--lib/command.c3
-rw-r--r--lib/command.h11
-rw-r--r--lib/command_lex.l5
-rw-r--r--lib/command_match.c3
-rw-r--r--lib/defun_lex.l2
-rw-r--r--lib/graph.c33
-rw-r--r--lib/graph.h20
-rw-r--r--lib/imsg.c2
-rw-r--r--lib/libfrr.c4
-rw-r--r--lib/log.c4
-rw-r--r--lib/netns_linux.c56
-rw-r--r--lib/netns_other.c5
-rw-r--r--lib/nexthop.c15
-rw-r--r--lib/nexthop.h19
-rw-r--r--lib/nexthop_group.c6
-rw-r--r--lib/ns.h10
-rw-r--r--lib/pbr.h86
-rwxr-xr-xlib/route_types.pl14
-rw-r--r--lib/route_types.txt60
-rw-r--r--lib/subdir.am1
-rw-r--r--lib/vrf.c20
-rw-r--r--lib/vrf.h3
-rw-r--r--lib/vty.c2
-rw-r--r--lib/zclient.c134
-rw-r--r--lib/zclient.h69
-rw-r--r--ospf6d/ospf6_abr.c3
-rw-r--r--ospf6d/ospf6_interface.c22
-rw-r--r--ospf6d/ospf6_interface.h1
-rw-r--r--ospf6d/ospf6_intra.c442
-rw-r--r--ospf6d/ospf6_intra.h3
-rw-r--r--ospf6d/ospf6_neighbor.c5
-rw-r--r--ospf6d/ospf6_route.c22
-rw-r--r--ospf6d/ospf6_route.h2
-rw-r--r--ospf6d/ospf6_zebra.c7
-rw-r--r--ospfd/ospf_lsa.c1
-rw-r--r--ospfd/ospf_snmp.c2
-rw-r--r--ospfd/ospf_te.c21
-rw-r--r--ospfd/ospf_zebra.c3
-rw-r--r--pbrd/.gitignore15
-rw-r--r--pbrd/Makefile10
-rw-r--r--pbrd/pbr_debug.c173
-rw-r--r--pbrd/pbr_debug.h53
-rw-r--r--pbrd/pbr_main.c170
-rw-r--r--pbrd/pbr_map.c569
-rw-r--r--pbrd/pbr_map.h162
-rw-r--r--pbrd/pbr_memory.c27
-rw-r--r--pbrd/pbr_memory.h24
-rw-r--r--pbrd/pbr_nht.c842
-rw-r--r--pbrd/pbr_nht.h115
-rw-r--r--pbrd/pbr_vty.c585
-rw-r--r--pbrd/pbr_vty.h24
-rw-r--r--pbrd/pbr_zebra.c527
-rw-r--r--pbrd/pbr_zebra.h42
-rw-r--r--pbrd/pbrd.conf.sample3
-rw-r--r--pbrd/subdir.am37
-rw-r--r--python/clidef.py2
-rw-r--r--redhat/daemons2
-rwxr-xr-xredhat/frr.init2
-rw-r--r--redhat/frr.spec.in24
-rw-r--r--ripd/rip_snmp.c2
-rw-r--r--tests/ospf6d/test_lsdb.c8
-rw-r--r--tools/etc/frr/daemons1
-rw-r--r--tools/etc/frr/daemons.conf1
-rwxr-xr-xtools/frr6
-rwxr-xr-xtools/indent.py2
-rw-r--r--tools/lsan-suppressions.txt4
-rw-r--r--vtysh/Makefile.am6
-rwxr-xr-xvtysh/extract.pl.in3
-rw-r--r--vtysh/vtysh.c176
-rw-r--r--vtysh/vtysh.h33
-rw-r--r--vtysh/vtysh_config.c5
-rw-r--r--vtysh/vtysh_main.c10
-rw-r--r--zebra/client_main.c5
-rw-r--r--zebra/connected.c30
-rw-r--r--zebra/connected.h7
-rw-r--r--zebra/if_ioctl.c2
-rw-r--r--zebra/if_ioctl_solaris.c2
-rw-r--r--zebra/if_netlink.c2
-rw-r--r--zebra/interface.c11
-rw-r--r--zebra/interface.h7
-rw-r--r--zebra/kernel_socket.c3
-rw-r--r--zebra/redistribute.c5
-rw-r--r--zebra/rt_netlink.c94
-rw-r--r--zebra/rule_netlink.c63
-rw-r--r--zebra/zebra_netns_notify.c10
-rw-r--r--zebra/zebra_ns.c30
-rw-r--r--zebra/zebra_ns.h6
-rw-r--r--zebra/zebra_pbr.c384
-rw-r--r--zebra/zebra_pbr.h155
-rw-r--r--zebra/zebra_rib.c7
-rw-r--r--zebra/zebra_rnh.c56
-rw-r--r--zebra/zebra_static.c249
-rw-r--r--zebra/zebra_static.h3
-rw-r--r--zebra/zebra_vrf.c44
-rw-r--r--zebra/zebra_vty.c67
-rw-r--r--zebra/zebra_vxlan.c11
-rw-r--r--zebra/zserv.c271
-rw-r--r--zebra/zserv.h13
190 files changed, 8263 insertions, 1439 deletions
diff --git a/Makefile.am b/Makefile.am
index 30150b594b..6cac1a7ba3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,6 +56,7 @@ include babeld/subdir.am
include eigrpd/subdir.am
include sharpd/subdir.am
include pimd/subdir.am
+include pbrd/subdir.am
SUBDIRS = . @LIBRFP@ @RFPTEST@ \
@BGPD@ \
diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in
index 2b211ccafd..c3e7e939e4 100644
--- a/alpine/APKBUILD.in
+++ b/alpine/APKBUILD.in
@@ -4,9 +4,9 @@ pkgver=@VERSION@
pkgrel=0
pkgdesc="Free Range Routing is a fork of quagga"
url="https://frrouting.org/"
-arch="all"
+arch="x86_64"
license="GPL-2.0"
-depends="iproute2 json-c c-ares ipsec-tools iproute2"
+depends="json-c c-ares ipsec-tools iproute2 python py-ipaddr bash"
makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
acct autoconf automake bash
binutils binutils-libs bison bsd-compat-headers build-base
@@ -20,18 +20,42 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
patch pax-utils pcre perl pkgconf python2 python2-dev readline
readline-dev sqlite-libs squashfs-tools sudo tar texinfo xorriso xz-libs
py-sphinx"
+install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
-source="$pkgname-$pkgver.tar.gz"
+source="$pkgname-$pkgver.tar.gz docker-start daemons daemons.conf"
builddir="$srcdir"/$pkgname-$pkgver
+_sbindir=/usr/lib/frr
+_sysconfdir=/etc/frr
+_libdir=/usr/lib
+_localstatedir=/var/run/frr
+_user=frr
+
build() {
cd "$builddir"
- ./configure --prefix=/usr || return 1
+ ./configure \
+ --prefix=/usr \
+ --sbindir=$_sbindir \
+ --sysconfdir=$_sysconfdir \
+ --libdir=$_libdir \
+ --localstatedir=$_localstatedir \
+ --enable-systemd=no \
+ --enable-vtysh \
+ --enable-multipath=64 \
+ --enable-vty-group=frrvty \
+ --enable-user=$_user \
+ --enable-group=$_user || return 1
make || return 1
}
package() {
cd "$builddir"
make DESTDIR="$pkgdir" install || return 1
+
+ install -Dm755 "$srcdir"/docker-start "$pkgdir"$_sbindir
+ install -Dm644 "$srcdir"/daemons "$pkgdir"$_sysconfdir
+ install -Dm644 "$srcdir"/daemons.conf "$pkgdir"$_sysconfdir
+ install -d "$pkgdir"/etc/init.d
+ ln -s ${_sbindir}/frr "$pkgdir"/etc/init.d/frr
}
diff --git a/alpine/docker-start b/alpine/docker-start
new file mode 100755
index 0000000000..43854ab142
--- /dev/null
+++ b/alpine/docker-start
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -e
+
+##
+# For volume mounts...
+##
+chown -R frr:frr /etc/frr
+/etc/init.d/frr start
+exec sleep 10000d
diff --git a/alpine/frr.post-deinstall b/alpine/frr.post-deinstall
new file mode 100755
index 0000000000..8f5d3dc40c
--- /dev/null
+++ b/alpine/frr.post-deinstall
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+getent passwd frr > /dev/null && deluser frr
+getent group frrvty > /dev/null && delgroup frrvty
+getent group frr > /dev/null && delgroup frr
+exit 0
diff --git a/alpine/frr.pre-deinstall b/alpine/frr.pre-deinstall
new file mode 100755
index 0000000000..72cf73bc1c
--- /dev/null
+++ b/alpine/frr.pre-deinstall
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+/etc/init.d/frr stop
+exit 0
diff --git a/alpine/frr.pre-install b/alpine/frr.pre-install
new file mode 100755
index 0000000000..da608cdcbd
--- /dev/null
+++ b/alpine/frr.pre-install
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+for g in frr frrvty; do
+ ! getent group $g > /dev/null && addgroup -S $g
+done
+
+! getent passwd frr > /dev/null && \
+ adduser -S -D -h /var/run/frr -s /sbin/nologin -G frr -g frr frr
+
+adduser frr frrvty
diff --git a/babeld/route.c b/babeld/route.c
index 501dd1f4df..bc7590fb39 100644
--- a/babeld/route.c
+++ b/babeld/route.c
@@ -176,6 +176,7 @@ insert_route(struct babel_route *route)
resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
if(route_slots >= max_route_slots)
return NULL;
+ assert(routes);
route->next = NULL;
if(n < route_slots)
memmove(routes + n + 1, routes + n,
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index 61d46dfcb9..a2880b7b94 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -87,7 +87,7 @@ libbgp_a_SOURCES = \
bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \
bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \
bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \
- bgp_flowspec_vty.c
+ bgp_flowspec_vty.c bgp_labelpool.c
noinst_HEADERS = \
bgp_memory.h \
@@ -100,7 +100,8 @@ noinst_HEADERS = \
bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \
$(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \
bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \
- bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h
+ bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \
+ bgp_labelpool.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 8f38f5765f..d1bc7f6e53 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -852,7 +852,7 @@ int community_list_set(struct community_list_handler *ch, const char *name,
/* Unset community-list */
int community_list_unset(struct community_list_handler *ch, const char *name,
- const char *str, int direct, int style, int delete_all)
+ const char *str, int direct, int style)
{
struct community_entry *entry = NULL;
struct community_list *list;
@@ -864,16 +864,14 @@ int community_list_unset(struct community_list_handler *ch, const char *name,
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
/* Delete all of entry belongs to this community-list. */
- if (delete_all) {
+ if (!str) {
community_list_delete(list);
route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
return 0;
}
- if (style == COMMUNITY_LIST_STANDARD) {
- if (str)
- com = community_str2com(str);
- }
+ if (style == COMMUNITY_LIST_STANDARD)
+ com = community_str2com(str);
if (com) {
entry = community_list_entry_lookup(list, com, direct);
@@ -1117,11 +1115,13 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name,
return 0;
}
-/* Unset extcommunity-list. When str is NULL, delete all of
- extcommunity-list entry belongs to the specified name. */
+/* Unset extcommunity-list.
+ *
+ * When str is NULL, delete all extcommunity-list entries belonging to the
+ * specified name.
+ */
int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
- const char *str, int direct, int style,
- int delete_all)
+ const char *str, int direct, int style)
{
struct community_entry *entry = NULL;
struct community_list *list;
@@ -1133,16 +1133,14 @@ int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
/* Delete all of entry belongs to this extcommunity-list. */
- if (delete_all) {
+ if (!str) {
community_list_delete(list);
route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
return 0;
}
- if (style == EXTCOMMUNITY_LIST_STANDARD) {
- if (str)
- ecom = ecommunity_str2com(str, 0, 1);
- }
+ if (style == EXTCOMMUNITY_LIST_STANDARD)
+ ecom = ecommunity_str2com(str, 0, 1);
if (ecom) {
entry = community_list_entry_lookup(list, ecom, direct);
diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h
index 0dbde2a453..9efb34d7b9 100644
--- a/bgpd/bgp_clist.h
+++ b/bgpd/bgp_clist.h
@@ -133,13 +133,13 @@ extern int community_list_set(struct community_list_handler *ch,
int style);
extern int community_list_unset(struct community_list_handler *ch,
const char *name, const char *str, int direct,
- int style, int delete_all);
+ int style);
extern int extcommunity_list_set(struct community_list_handler *ch,
const char *name, const char *str, int direct,
int style);
extern int extcommunity_list_unset(struct community_list_handler *ch,
const char *name, const char *str,
- int direct, int style, int delete_all);
+ int direct, int style);
extern int lcommunity_list_set(struct community_list_handler *ch,
const char *name, const char *str, int direct,
int style);
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index ae4ff5d67e..29ac5f520d 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -58,6 +58,7 @@ unsigned long conf_bgp_debug_nht;
unsigned long conf_bgp_debug_update_groups;
unsigned long conf_bgp_debug_vpn;
unsigned long conf_bgp_debug_flowspec;
+unsigned long conf_bgp_debug_labelpool;
unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
@@ -73,6 +74,7 @@ unsigned long term_bgp_debug_nht;
unsigned long term_bgp_debug_update_groups;
unsigned long term_bgp_debug_vpn;
unsigned long term_bgp_debug_flowspec;
+unsigned long term_bgp_debug_labelpool;
struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
@@ -1655,6 +1657,44 @@ DEFUN (no_debug_bgp_vpn,
return CMD_SUCCESS;
}
+DEFUN (debug_bgp_labelpool,
+ debug_bgp_labelpool_cmd,
+ "debug bgp labelpool",
+ DEBUG_STR
+ BGP_STR
+ "label pool\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(labelpool, LABELPOOL);
+ else
+ TERM_DEBUG_ON(labelpool, LABELPOOL);
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "enabled debug bgp labelpool\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_labelpool,
+ no_debug_bgp_labelpool_cmd,
+ "no debug bgp labelpool",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "label pool\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(labelpool, LABELPOOL);
+ else
+ TERM_DEBUG_OFF(labelpool, LABELPOOL);
+
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "disabled debug bgp labelpool\n");
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_bgp,
no_debug_bgp_cmd,
"no debug bgp",
@@ -1692,6 +1732,7 @@ DEFUN (no_debug_bgp,
TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
TERM_DEBUG_OFF(flowspec, FLOWSPEC);
+ TERM_DEBUG_OFF(labelpool, LABELPOOL);
vty_out(vty, "All possible debugging has been turned off\n");
return CMD_SUCCESS;
@@ -1764,6 +1805,8 @@ DEFUN_NOSH (show_debugging_bgp,
vty_out(vty, " BGP vpn label event debugging is on\n");
if (BGP_DEBUG(flowspec, FLOWSPEC))
vty_out(vty, " BGP flowspec debugging is on\n");
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ vty_out(vty, " BGP labelpool debugging is on\n");
vty_out(vty, "\n");
return CMD_SUCCESS;
@@ -1819,6 +1862,8 @@ int bgp_debug_count(void)
ret++;
if (BGP_DEBUG(flowspec, FLOWSPEC))
ret++;
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ ret++;
return ret;
}
@@ -1916,6 +1961,10 @@ static int bgp_config_write_debug(struct vty *vty)
vty_out(vty, "debug bgp flowspec\n");
write++;
}
+ if (CONF_BGP_DEBUG(labelpool, LABELPOOL)) {
+ vty_out(vty, "debug bgp labelpool\n");
+ write++;
+ }
return write;
}
@@ -2015,6 +2064,11 @@ void bgp_debug_init(void)
install_element(CONFIG_NODE, &debug_bgp_vpn_cmd);
install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd);
install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_labelpool_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd);
}
/* Return true if this prefix is on the per_prefix_list of prefixes to debug
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index a0b179e213..ad476ee918 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -74,6 +74,7 @@ extern unsigned long conf_bgp_debug_nht;
extern unsigned long conf_bgp_debug_update_groups;
extern unsigned long conf_bgp_debug_vpn;
extern unsigned long conf_bgp_debug_flowspec;
+extern unsigned long conf_bgp_debug_labelpool;
extern unsigned long term_bgp_debug_as4;
extern unsigned long term_bgp_debug_neighbor_events;
@@ -87,6 +88,7 @@ extern unsigned long term_bgp_debug_nht;
extern unsigned long term_bgp_debug_update_groups;
extern unsigned long term_bgp_debug_vpn;
extern unsigned long term_bgp_debug_flowspec;
+extern unsigned long term_bgp_debug_labelpool;
extern struct list *bgp_debug_neighbor_events_peers;
extern struct list *bgp_debug_keepalive_peers;
@@ -120,6 +122,7 @@ struct bgp_debug_filter {
#define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04
#define BGP_DEBUG_VPN_LEAK_LABEL 0x08
#define BGP_DEBUG_FLOWSPEC 0x01
+#define BGP_DEBUG_LABELPOOL 0x01
#define BGP_DEBUG_PACKET_SEND 0x01
#define BGP_DEBUG_PACKET_SEND_DETAIL 0x02
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 54ec7d392b..80166dd32b 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -736,6 +736,14 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
"MM:%u", seqnum);
} else
unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
+ sub_type = *pnt++;
+ if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
+ len = sprintf(
+ str_buf + str_pnt,
+ "FS:redirect IP 0x%x", *(pnt+5));
+ } else
+ unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_TRANS_EXP) {
sub_type = *pnt++;
@@ -785,10 +793,6 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
len = sprintf(
str_buf + str_pnt,
"FS:marking %u", *(pnt+5));
- } else if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
- len = sprintf(
- str_buf + str_pnt,
- "FS:redirect IP 0x%x", *(pnt+5));
} else
unk_ecom = 1;
} else
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 31ff1481ba..0c22c5a149 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -28,6 +28,7 @@
#define ECOMMUNITY_ENCODE_OPAQUE 0x03
#define ECOMMUNITY_ENCODE_EVPN 0x06
#define ECOMMUNITY_ENCODE_TRANS_EXP 0x80 /* Flow Spec */
+#define ECOMMUNITY_ENCODE_REDIRECT_IP_NH 0x08 /* Flow Spec */
/* RFC7674 */
#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 0x81
#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82
diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c
index 247da5d183..7bf11f12aa 100644
--- a/bgpd/bgp_flowspec_vty.c
+++ b/bgpd/bgp_flowspec_vty.c
@@ -312,6 +312,9 @@ void route_vty_out_flowspec(struct vty *vty, struct prefix *p,
json_object_array_add(json_paths,
json_ecom_path);
}
+ if (attr->nexthop.s_addr != 0 &&
+ display == NLRI_STRING_FORMAT_LARGE)
+ vty_out(vty, "\tNH %-16s\n", inet_ntoa(attr->nexthop));
XFREE(MTYPE_ECOMMUNITY_STR, s);
}
peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c
new file mode 100644
index 0000000000..2c98cd9ef9
--- /dev/null
+++ b/bgpd/bgp_labelpool.c
@@ -0,0 +1,592 @@
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "mpls.h"
+#include "vty.h"
+#include "fifo.h"
+#include "linklist.h"
+#include "skiplist.h"
+#include "workqueue.h"
+#include "zclient.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_debug.h"
+
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
+/*
+ * Remember where pool data are kept
+ */
+static struct labelpool *lp;
+
+/* request this many labels at a time from zebra */
+#define LP_CHUNK_SIZE 50
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment")
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback")
+
+#define LABEL_FIFO_ADD(F, N) \
+ do { \
+ FIFO_ADD((F), (N)); \
+ (F)->count++; \
+ } while (0)
+
+#define LABEL_FIFO_DEL(F, N) \
+ do { \
+ FIFO_DEL((N)); \
+ (F)->count--; \
+ } while (0)
+
+#define LABEL_FIFO_INIT(F) \
+ do { \
+ FIFO_INIT((F)); \
+ (F)->count = 0; \
+ } while (0)
+
+#define LABEL_FIFO_COUNT(F) ((F)->count)
+
+#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F)
+
+#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next)
+
+struct lp_chunk {
+ uint32_t first;
+ uint32_t last;
+};
+
+/*
+ * label control block
+ */
+struct lp_lcb {
+ mpls_label_t label; /* MPLS_LABEL_NONE = not allocated */
+ int type;
+ void *labelid; /* unique ID */
+ /*
+ * callback for label allocation and loss
+ *
+ * allocated: false = lost
+ */
+ int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+};
+
+/* XXX same first elements as "struct fifo" */
+struct lp_fifo {
+ struct lp_fifo *next;
+ struct lp_fifo *prev;
+
+ uint32_t count;
+ struct lp_lcb lcb;
+};
+
+struct lp_cbq_item {
+ int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+ int type;
+ mpls_label_t label;
+ void *labelid;
+ bool allocated; /* false = lost */
+};
+
+static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data)
+{
+ struct lp_cbq_item *lcbq = data;
+ int rc;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ if (debug)
+ zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d",
+ __func__, lcbq->labelid, lcbq->label, lcbq->allocated);
+
+ if (lcbq->label == MPLS_LABEL_NONE) {
+ /* shouldn't happen */
+ zlog_err("%s: error: label==MPLS_LABEL_NONE", __func__);
+ return WQ_SUCCESS;
+ }
+
+ rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated);
+
+ if (lcbq->allocated && rc) {
+ /*
+ * Callback rejected allocation. This situation could arise
+ * if there was a label request followed by the requestor
+ * deciding it didn't need the assignment (e.g., config
+ * change) while the reply to the original request (with
+ * label) was in the work queue.
+ */
+ if (debug)
+ zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u",
+ __func__, lcbq->labelid, lcbq->label);
+
+ uintptr_t lbl = lcbq->label;
+ void *labelid;
+ struct lp_lcb *lcb;
+
+ /*
+ * If the rejected label was marked inuse by this labelid,
+ * release the label back to the pool.
+ *
+ * Further, if the rejected label was still assigned to
+ * this labelid in the LCB, delete the LCB.
+ */
+ if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) {
+ if (labelid == lcbq->labelid) {
+ if (!skiplist_search(lp->ledger, labelid,
+ (void **)&lcb)) {
+ if (lcbq->label == lcb->label)
+ skiplist_delete(lp->ledger,
+ labelid, NULL);
+ }
+ skiplist_delete(lp->inuse, (void *)lbl, NULL);
+ }
+ }
+ }
+
+ return WQ_SUCCESS;
+}
+
+static void lp_cbq_item_free(struct work_queue *wq, void *data)
+{
+ XFREE(MTYPE_BGP_LABEL_CBQ, data);
+}
+
+static void lp_lcb_free(void *goner)
+{
+ if (goner)
+ XFREE(MTYPE_BGP_LABEL_CB, goner);
+}
+
+static void lp_chunk_free(void *goner)
+{
+ if (goner)
+ XFREE(MTYPE_BGP_LABEL_CHUNK, goner);
+}
+
+void bgp_lp_init(struct thread_master *master, struct labelpool *pool)
+{
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: entry", __func__);
+
+ lp = pool; /* Set module pointer to pool data */
+
+ lp->ledger = skiplist_new(0, NULL, lp_lcb_free);
+ lp->inuse = skiplist_new(0, NULL, NULL);
+ lp->chunks = list_new();
+ lp->chunks->del = lp_chunk_free;
+ lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo));
+ LABEL_FIFO_INIT(lp->requests);
+ lp->callback_q = work_queue_new(master, "label callbacks");
+ if (!lp->callback_q) {
+ zlog_err("%s: Failed to allocate work queue", __func__);
+ exit(1);
+ }
+
+ lp->callback_q->spec.workfunc = lp_cbq_docallback;
+ lp->callback_q->spec.del_item_data = lp_cbq_item_free;
+ lp->callback_q->spec.max_retries = 0;
+}
+
+void bgp_lp_finish(void)
+{
+ struct lp_fifo *lf;
+
+ if (!lp)
+ return;
+
+ skiplist_free(lp->ledger);
+ lp->ledger = NULL;
+
+ skiplist_free(lp->inuse);
+ lp->inuse = NULL;
+
+ list_delete_and_null(&lp->chunks);
+
+ while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+
+ LABEL_FIFO_DEL(lp->requests, lf);
+ XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+ }
+ XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests);
+ lp->requests = NULL;
+
+ work_queue_free_and_null(&lp->callback_q);
+
+ lp = NULL;
+}
+
+static mpls_label_t get_label_from_pool(void *labelid)
+{
+ struct listnode *node;
+ struct lp_chunk *chunk;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ /*
+ * Find a free label
+ * Linear search is not efficient but should be executed infrequently.
+ */
+ for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+ uintptr_t lbl;
+
+ if (debug)
+ zlog_debug("%s: chunk first=%u last=%u",
+ __func__, chunk->first, chunk->last);
+
+ for (lbl = chunk->first; lbl <= chunk->last; ++lbl) {
+ /* labelid is key to all-request "ledger" list */
+ if (!skiplist_insert(lp->inuse, (void *)lbl, labelid)) {
+ /*
+ * Success
+ */
+ return lbl;
+ }
+ }
+ }
+ return MPLS_LABEL_NONE;
+}
+
+/*
+ * Success indicated by value of "label" field in returned LCB
+ */
+static struct lp_lcb *lcb_alloc(
+ int type,
+ void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+ /*
+ * Set up label control block
+ */
+ struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB,
+ sizeof(struct lp_lcb));
+
+ new->label = get_label_from_pool(labelid);
+ new->type = type;
+ new->labelid = labelid;
+ new->cbfunc = cbfunc;
+
+ return new;
+}
+
+/*
+ * Callers who need labels must supply a type, labelid, and callback.
+ * The type is a value defined in bgp_labelpool.h (add types as needed).
+ * The callback is for asynchronous notification of label allocation.
+ * The labelid is passed as an argument to the callback. It should be unique
+ * to the requested label instance.
+ *
+ * If zebra is not connected, callbacks with labels will be delayed
+ * until connection is established. If zebra connection is lost after
+ * labels have been assigned, existing assignments via this labelpool
+ * module will continue until reconnection.
+ *
+ * When connection to zebra is reestablished, previous label assignments
+ * will be invalidated (via callbacks having the "allocated" parameter unset)
+ * and new labels will be automatically reassigned by this labelpool module
+ * (that is, a requestor does not need to call lp_get() again if it is
+ * notified via callback that its label has been lost: it will eventually
+ * get another callback with a new label assignment).
+ *
+ * Prior requests for a given labelid are detected so that requests and
+ * assignments are not duplicated.
+ */
+void bgp_lp_get(
+ int type,
+ void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+ struct lp_lcb *lcb;
+ int requested = 0;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ if (debug)
+ zlog_debug("%s: labelid=%p", __func__, labelid);
+
+ /*
+ * Have we seen this request before?
+ */
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ requested = 1;
+ } else {
+ lcb = lcb_alloc(type, labelid, cbfunc);
+ if (debug)
+ zlog_debug("%s: inserting lcb=%p label=%u",
+ __func__, lcb, lcb->label);
+ int rc = skiplist_insert(lp->ledger, labelid, lcb);
+
+ if (rc) {
+ /* shouldn't happen */
+ zlog_err("%s: can't insert new LCB into ledger list",
+ __func__);
+ XFREE(MTYPE_BGP_LABEL_CB, lcb);
+ return;
+ }
+ }
+
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /*
+ * Fast path: we filled the request from local pool (or
+ * this is a duplicate request that we filled already).
+ * Enqueue response work item with new label.
+ */
+ struct lp_cbq_item *q;
+
+ q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item));
+
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = true;
+
+ work_queue_add(lp->callback_q, q);
+
+ return;
+ }
+
+ if (requested)
+ return;
+
+ if (debug)
+ zlog_debug("%s: slow path. lcb=%p label=%u",
+ __func__, lcb, lcb->label);
+
+ /*
+ * Slow path: we are out of labels in the local pool,
+ * so remember the request and also get another chunk from
+ * the label manager.
+ *
+ * We track number of outstanding label requests: don't
+ * need to get a chunk for each one.
+ */
+
+ struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+ sizeof(struct lp_fifo));
+
+ lf->lcb = *lcb;
+ LABEL_FIFO_ADD(lp->requests, lf);
+
+ if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) {
+ if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) {
+ lp->pending_count += LP_CHUNK_SIZE;
+ return;
+ }
+ }
+}
+
+void bgp_lp_release(
+ int type,
+ void *labelid,
+ mpls_label_t label)
+{
+ struct lp_lcb *lcb;
+
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ if (label == lcb->label && type == lcb->type) {
+ uintptr_t lbl = label;
+
+ /* no longer in use */
+ skiplist_delete(lp->inuse, (void *)lbl, NULL);
+
+ /* no longer requested */
+ skiplist_delete(lp->ledger, labelid, NULL);
+ }
+ }
+}
+
+/*
+ * zebra response giving us a chunk of labels
+ */
+void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
+{
+ struct lp_chunk *chunk;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+ struct lp_fifo *lf;
+
+ if (last < first) {
+ zlog_err("%s: zebra label chunk invalid: first=%u, last=%u",
+ __func__, first, last);
+ return;
+ }
+
+ chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk));
+
+ chunk->first = first;
+ chunk->last = last;
+
+ listnode_add(lp->chunks, chunk);
+
+ lp->pending_count -= (last - first + 1);
+
+ if (debug) {
+ zlog_debug("%s: %u pending requests", __func__,
+ LABEL_FIFO_COUNT(lp->requests));
+ }
+
+ while ((lf = LABEL_FIFO_HEAD(lp->requests))) {
+
+ struct lp_lcb *lcb;
+ void *labelid = lf->lcb.labelid;
+
+ if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ /* request no longer in effect */
+
+ if (debug) {
+ zlog_debug("%s: labelid %p: request no longer in effect",
+ __func__, labelid);
+ }
+ goto finishedrequest;
+ }
+
+ /* have LCB */
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /* request already has a label */
+ if (debug) {
+ zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p",
+ __func__, labelid,
+ lcb->label, lcb->label, lcb);
+ }
+ goto finishedrequest;
+ }
+
+ lcb->label = get_label_from_pool(lcb->labelid);
+
+ if (lcb->label == MPLS_LABEL_NONE) {
+ /*
+ * Out of labels in local pool, await next chunk
+ */
+ if (debug) {
+ zlog_debug("%s: out of labels, await more",
+ __func__);
+ }
+ break;
+ }
+
+ /*
+ * we filled the request from local pool.
+ * Enqueue response work item with new label.
+ */
+ struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+ sizeof(struct lp_cbq_item));
+
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = true;
+
+ if (debug)
+ zlog_debug("%s: assigning label %u to labelid %p",
+ __func__, q->label, q->labelid);
+
+ work_queue_add(lp->callback_q, q);
+
+finishedrequest:
+ LABEL_FIFO_DEL(lp->requests, lf);
+ XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+ }
+}
+
+/*
+ * continue using allocated labels until zebra returns
+ */
+void bgp_lp_event_zebra_down(void)
+{
+ /* rats. */
+}
+
+/*
+ * Inform owners of previously-allocated labels that their labels
+ * are not valid. Request chunk from zebra large enough to satisfy
+ * previously-allocated labels plus any outstanding requests.
+ */
+void bgp_lp_event_zebra_up(void)
+{
+ int labels_needed;
+ int chunks_needed;
+ void *labelid;
+ struct lp_lcb *lcb;
+
+ /*
+ * Get label chunk allocation request dispatched to zebra
+ */
+ labels_needed = LABEL_FIFO_COUNT(lp->requests) +
+ skiplist_count(lp->inuse);
+
+ /* round up */
+ chunks_needed = (labels_needed / LP_CHUNK_SIZE) + 1;
+ labels_needed = chunks_needed * LP_CHUNK_SIZE;
+
+ zclient_send_get_label_chunk(zclient, 0, labels_needed);
+ lp->pending_count = labels_needed;
+
+ /*
+ * Invalidate current list of chunks
+ */
+ list_delete_all_node(lp->chunks);
+
+ /*
+ * Invalidate any existing labels and requeue them as requests
+ */
+ while (!skiplist_first(lp->inuse, NULL, &labelid)) {
+
+ /*
+ * Get LCB
+ */
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /*
+ * invalidate
+ */
+ struct lp_cbq_item *q;
+
+ q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+ sizeof(struct lp_cbq_item));
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = false;
+ work_queue_add(lp->callback_q, q);
+
+ lcb->label = MPLS_LABEL_NONE;
+ }
+
+ /*
+ * request queue
+ */
+ struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+ sizeof(struct lp_fifo));
+
+ lf->lcb = *lcb;
+ LABEL_FIFO_ADD(lp->requests, lf);
+ }
+
+ skiplist_delete_first(lp->inuse);
+ }
+}
diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h
new file mode 100644
index 0000000000..fa35cde0e1
--- /dev/null
+++ b/bgpd/bgp_labelpool.h
@@ -0,0 +1,51 @@
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_LABELPOOL_H
+#define _FRR_BGP_LABELPOOL_H
+
+#include <zebra.h>
+
+#include "mpls.h"
+
+/*
+ * Types used in bgp_lp_get for debug tracking; add more as needed
+ */
+#define LP_TYPE_VRF 0x00000001
+
+struct labelpool {
+ struct skiplist *ledger; /* all requests */
+ struct skiplist *inuse; /* individual labels */
+ struct list *chunks; /* granted by zebra */
+ struct lp_fifo *requests; /* blocked on zebra */
+ struct work_queue *callback_q;
+ uint32_t pending_count; /* requested from zebra */
+};
+
+extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool);
+extern void bgp_lp_finish(void);
+extern void bgp_lp_get(int type, void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated));
+extern void bgp_lp_release(int type, void *labelid, mpls_label_t label);
+extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last);
+extern void bgp_lp_event_zebra_down(void);
+extern void bgp_lp_event_zebra_up(void);
+
+#endif /* _FRR_BGP_LABELPOOL_H */
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index bf60f9c118..5158717b5d 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -337,7 +337,7 @@ int main(int argc, char **argv)
frr_preinit(&bgpd_di, argc, argv);
frr_opt_add(
"p:l:rSne:", longopts,
- " -p, --bgp_port Set bgp protocol's port number\n"
+ " -p, --bgp_port Set BGP listen port number (0 means do not listen).\n"
" -l, --listenon Listen on specified address (implies -n)\n"
" -r, --retain When program terminates, retain added route by bgpd.\n"
" -n, --no_kernel Do not install route to kernel.\n"
@@ -356,7 +356,7 @@ int main(int argc, char **argv)
break;
case 'p':
tmp_port = atoi(optarg);
- if (tmp_port <= 0 || tmp_port > 0xffff)
+ if (tmp_port < 0 || tmp_port > 0xffff)
bgp_port = BGP_PORT_DEFAULT;
else
bgp_port = tmp_port;
@@ -395,6 +395,8 @@ int main(int argc, char **argv)
/* BGP master init. */
bgp_master_init(frr_init());
bm->port = bgp_port;
+ if (bgp_port == 0)
+ bgp_option_set(BGP_OPT_NO_LISTEN);
bm->address = bgp_address;
if (no_fib_flag)
bgp_option_set(BGP_OPT_NO_FIB);
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index e33f3d3477..915387ca2d 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -592,6 +592,8 @@ void bgp_info_mpath_update(struct bgp_node *rn, struct bgp_info *new_best,
*/
new_mpath = listgetdata(mp_node);
list_delete_node(mp_list, mp_node);
+ assert(new_mpath);
+ assert(prev_mpath);
if ((mpath_count < maxpaths) && (new_mpath != new_best)
&& bgp_info_nexthop_cmp(prev_mpath, new_mpath)) {
if (new_mpath == next_mpath)
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 06d82f654d..08aaed6577 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -310,6 +310,65 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi)
bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
}
+int vpn_leak_label_callback(
+ mpls_label_t label,
+ void *labelid,
+ bool allocated)
+{
+ struct vpn_policy *vp = (struct vpn_policy *)labelid;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+ if (debug)
+ zlog_debug("%s: label=%u, allocated=%d",
+ __func__, label, allocated);
+
+ if (!allocated) {
+ /*
+ * previously-allocated label is now invalid
+ */
+ if (CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
+ (vp->tovpn_label != MPLS_LABEL_NONE)) {
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ vp->tovpn_label = MPLS_LABEL_NONE;
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ }
+ return 0;
+ }
+
+ /*
+ * New label allocation
+ */
+ if (!CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ /*
+ * not currently configured for auto label, reject allocation
+ */
+ return -1;
+ }
+
+ if (vp->tovpn_label != MPLS_LABEL_NONE) {
+ if (label == vp->tovpn_label) {
+ /* already have same label, accept but do nothing */
+ return 0;
+ }
+ /* Shouldn't happen: different label allocation */
+ zlog_err("%s: %s had label %u but got new assignment %u",
+ __func__, vp->bgp->name_pretty, vp->tovpn_label, label);
+ /* use new one */
+ }
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ vp->tovpn_label = label;
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+
+ return 0;
+}
+
static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
{
int i;
@@ -331,6 +390,57 @@ static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
return 0;
}
+static bool labels_same(struct bgp_info *bi, mpls_label_t *label, uint32_t n)
+{
+ uint32_t i;
+
+ if (!bi->extra) {
+ if (!n)
+ return true;
+ else
+ return false;
+ }
+
+ if (n != bi->extra->num_labels)
+ return false;
+
+ for (i = 0; i < n; ++i) {
+ if (label[i] != bi->extra->label[i])
+ return false;
+ }
+ return true;
+}
+
+/*
+ * make encoded route labels match specified encoded label set
+ */
+static void setlabels(
+ struct bgp_info *bi,
+ mpls_label_t *label, /* array of labels */
+ uint32_t num_labels)
+{
+ if (num_labels)
+ assert(label);
+ assert(num_labels <= BGP_MAX_LABELS);
+
+ if (!num_labels) {
+ if (bi->extra)
+ bi->extra->num_labels = 0;
+ return;
+ }
+
+ struct bgp_info_extra *extra = bgp_info_extra_get(bi);
+ uint32_t i;
+
+ for (i = 0; i < num_labels; ++i) {
+ extra->label[i] = label[i];
+ if (!bgp_is_valid_label(&label[i])) {
+ bgp_set_valid_label(&extra->label[i]);
+ }
+ }
+ extra->num_labels = num_labels;
+}
+
/*
* returns pointer to new bgp_info upon success
*/
@@ -343,7 +453,7 @@ leak_update(
safi_t safi,
struct bgp_info *source_bi,
mpls_label_t *label,
- int num_labels,
+ uint32_t num_labels,
void *parent,
struct bgp *bgp_orig,
struct prefix *nexthop_orig,
@@ -371,7 +481,10 @@ leak_update(
}
if (bi) {
+ bool labelssame = labels_same(bi, label, num_labels);
+
if (attrhash_cmp(bi->attr, new_attr)
+ && labelssame
&& !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) {
bgp_attr_unintern(&new_attr);
@@ -395,6 +508,12 @@ leak_update(
bi->attr = new_attr;
bi->uptime = bgp_clock();
+ /*
+ * rewrite labels
+ */
+ if (!labelssame)
+ setlabels(bi, label, num_labels);
+
if (nexthop_self_flag)
bgp_info_set_flag(bn, bi, BGP_INFO_ANNC_NH_SELF);
@@ -442,23 +561,10 @@ leak_update(
if (nexthop_self_flag)
bgp_info_set_flag(bn, new, BGP_INFO_ANNC_NH_SELF);
+ if (num_labels)
+ setlabels(new, label, num_labels);
+
bgp_info_extra_get(new);
- if (label) {
- int i;
-
- for (i = 0; i < num_labels; ++i) {
- new->extra->label[i] = label[i];
- if (!bgp_is_valid_label(&label[i])) {
- if (debug) {
- zlog_debug(
- "%s: %s: marking label %d valid",
- __func__, buf_prefix, i);
- }
- bgp_set_valid_label(&new->extra->label[i]);
- }
- }
- new->extra->num_labels = num_labels;
- }
new->extra->parent = parent;
if (bgp_orig)
@@ -665,8 +771,7 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */
label_val = bgp_vrf->vpn_policy[afi].tovpn_label;
if (label_val == MPLS_LABEL_NONE) {
- /* TBD get from label manager */
- label = MPLS_LABEL_IMPLICIT_NULL;
+ encode_label(MPLS_LABEL_IMPLICIT_NULL, &label);
} else {
encode_label(label_val, &label);
}
@@ -890,8 +995,10 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */
const char *debugmsg;
struct prefix nexthop_orig;
mpls_label_t *pLabels = NULL;
- int num_labels = 0;
+ uint32_t num_labels = 0;
int nexthop_self_flag = 1;
+ struct bgp_info *bi_ultimate = NULL;
+ int origin_local = 0;
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
@@ -983,13 +1090,40 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */
/*
* ensure labels are copied
+ *
+ * However, there is a special case: if the route originated in
+ * another local VRF (as opposed to arriving via VPN), then the
+ * nexthop is reached by hairpinning through this router (me)
+ * using IP forwarding only (no LSP). Therefore, the route
+ * imported to the VRF should not have labels attached. Note
+ * that nexthop tracking is also involved: eliminating the
+ * labels for these routes enables the non-labeled nexthops
+ * from the originating VRF to be considered valid for this route.
*/
- if (info_vpn->extra && info_vpn->extra->num_labels) {
+
+ /* work back to original route */
+ for (bi_ultimate = info_vpn;
+ bi_ultimate->extra && bi_ultimate->extra->parent;
+ bi_ultimate = bi_ultimate->extra->parent)
+ ;
+
+ /* if original route was unicast, then it did not arrive over vpn */
+ if (bi_ultimate->net) {
+ struct bgp_table *table;
+
+ table = bgp_node_table(bi_ultimate->net);
+ if (table && (table->safi == SAFI_UNICAST))
+ origin_local = 1;
+ }
+
+ /* copy labels */
+ if (!origin_local && info_vpn->extra && info_vpn->extra->num_labels) {
num_labels = info_vpn->extra->num_labels;
if (num_labels > BGP_MAX_LABELS)
num_labels = BGP_MAX_LABELS;
pLabels = info_vpn->extra->label;
}
+
if (debug) {
char buf_prefix[PREFIX_STRLEN];
prefix2str(p, buf_prefix, sizeof(buf_prefix));
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 64303325ee..7398559498 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -77,6 +77,7 @@ extern void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn,
extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi);
extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi);
+extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc);
static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
const char **pmsg)
@@ -111,6 +112,17 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
*pmsg = "rd not defined";
return 0;
}
+
+ /* Is there an "auto" export label that isn't allocated yet? */
+ if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
+ (bgp_vrf->vpn_policy[afi].tovpn_label == MPLS_LABEL_NONE)) {
+
+ if (pmsg)
+ *pmsg = "auto label not allocated";
+ return 0;
+ }
+
return 1;
}
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index f0b30f0186..9ea2f22cec 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -649,7 +649,6 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
uint8_t sub_code, uint8_t *data, size_t datalen)
{
struct stream *s;
- int length;
/* Lock I/O mutex to prevent other threads from pushing packets */
pthread_mutex_lock(&peer->io_mtx);
@@ -670,7 +669,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
stream_write(s, data, datalen);
/* Set BGP packet length. */
- length = bgp_packet_set_size(s);
+ bgp_packet_set_size(s);
/* wipe output buffer */
stream_fifo_clean(peer->obuf);
@@ -697,13 +696,13 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
bgp_notify.code = code;
bgp_notify.subcode = sub_code;
bgp_notify.data = NULL;
- bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE;
+ bgp_notify.length = datalen;
bgp_notify.raw_data = data;
peer->notify.code = bgp_notify.code;
peer->notify.subcode = bgp_notify.subcode;
- if (bgp_notify.length) {
+ if (bgp_notify.length && data) {
bgp_notify.data =
XMALLOC(MTYPE_TMP, bgp_notify.length * 3);
for (i = 0; i < bgp_notify.length; i++)
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 944ae5b5dc..a71f5ac956 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1874,7 +1874,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
if (debug) {
bgp_info_path_with_addpath_rx_str(new_select,
path_buf);
- zlog_debug("%s: %s is the bestpath from AS %d",
+ zlog_debug("%s: %s is the bestpath from AS %u",
pfx_buf, path_buf,
aspath_get_first_as(
new_select->attr->aspath));
@@ -6545,8 +6545,20 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
} else
vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
} else if (safi == SAFI_FLOWSPEC) {
- /* already done */
- /* IPv4 Next Hop */
+ if (attr->nexthop.s_addr != 0) {
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+ json_object_string_add(
+ json_nexthop_global, "ip",
+ inet_ntoa(attr->nexthop));
+ json_object_string_add(json_nexthop_global,
+ "afi", "ipv4");
+ json_object_boolean_true_add(json_nexthop_global,
+ "used");
+ } else {
+ vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
+ }
+ }
} else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
if (json_paths) {
json_nexthop_global = json_object_new_object();
@@ -7035,32 +7047,36 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p,
default:
vty_out(vty, "?");
}
+
+ char *str = esi2str(&(attr->evpn_overlay.eth_s_id));
+
+ vty_out(vty, "%s", str);
+ XFREE(MTYPE_TMP, str);
+
+ if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) {
+ vty_out(vty, "/%s",
+ inet_ntoa(attr->evpn_overlay.gw_ip.ipv4));
+ } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) {
+ vty_out(vty, "/%s",
+ inet_ntop(AF_INET6,
+ &(attr->evpn_overlay.gw_ip.ipv6), buf,
+ BUFSIZ));
+ }
+ if (attr->ecommunity) {
+ char *mac = NULL;
+ struct ecommunity_val *routermac = ecommunity_lookup(
+ attr->ecommunity, ECOMMUNITY_ENCODE_EVPN,
+ ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
+ if (routermac)
+ mac = ecom_mac2str((char *)routermac->val);
+ if (mac) {
+ vty_out(vty, "/%s", (char *)mac);
+ XFREE(MTYPE_TMP, mac);
+ }
+ }
+ vty_out(vty, "\n");
}
- struct eth_segment_id *id = &(attr->evpn_overlay.eth_s_id);
- char *str = esi2str(id);
- vty_out(vty, "%s", str);
- XFREE(MTYPE_TMP, str);
- if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) {
- vty_out(vty, "/%s", inet_ntoa(attr->evpn_overlay.gw_ip.ipv4));
- } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) {
- vty_out(vty, "/%s",
- inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6),
- buf, BUFSIZ));
- }
- if (attr->ecommunity) {
- char *mac = NULL;
- struct ecommunity_val *routermac = ecommunity_lookup(
- attr->ecommunity, ECOMMUNITY_ENCODE_EVPN,
- ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
- if (routermac)
- mac = ecom_mac2str((char *)routermac->val);
- if (mac) {
- vty_out(vty, "/%s", (char *)mac);
- XFREE(MTYPE_TMP, mac);
- }
- }
- vty_out(vty, "\n");
}
/* dampening route */
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 3e577317ad..6bc50fb77e 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -6245,12 +6245,13 @@ ALIAS (af_rd_vpn_export,
DEFPY (af_label_vpn_export,
af_label_vpn_export_cmd,
- "[no] label vpn export (0-1048575)$label_val",
+ "[no] label vpn export <(0-1048575)$label_val|auto$label_auto>",
NO_STR
"label value for VRF\n"
"Between current address-family and vpn\n"
"For routes leaked from current address-family to vpn\n"
- "Label Value <0-1048575>\n")
+ "Label Value <0-1048575>\n"
+ "Automatically assign a label\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
mpls_label_t label = MPLS_LABEL_NONE;
@@ -6263,8 +6264,10 @@ DEFPY (af_label_vpn_export,
if (argv_find(argv, argc, "no", &idx))
yes = 0;
- if (yes)
- label = label_val; /* rely on parser to force unsigned */
+ if (yes) {
+ if (!label_auto)
+ label = label_val; /* parser should force unsigned */
+ }
ret = vpn_policy_getafi(vty, doafi);
if (ret != CMD_SUCCESS)
@@ -6274,13 +6277,48 @@ DEFPY (af_label_vpn_export,
if (!doafi[afi])
continue;
+ if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO))
+
+ continue; /* no change */
+
/*
* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
*/
vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
bgp_get_default(), bgp);
+ if (!label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ if (bgp->vpn_policy[afi].tovpn_label !=
+ MPLS_LABEL_NONE) {
+
+ /*
+ * label has previously been automatically
+ * assigned by labelpool: release it
+ *
+ * NB if tovpn_label == MPLS_LABEL_NONE it
+ * means the automatic assignment is in flight
+ * and therefore the labelpool callback must
+ * detect that the auto label is not needed.
+ */
+
+ bgp_lp_release(LP_TYPE_VRF,
+ &bgp->vpn_policy[afi],
+ bgp->vpn_policy[afi].tovpn_label);
+ }
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
+ }
+
bgp->vpn_policy[afi].tovpn_label = label;
+ if (label_auto) {
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
+ bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi],
+ vpn_leak_label_callback);
+ }
/* post-change: re-export vpn routes */
vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
@@ -11706,9 +11744,16 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
{
int indent = 2;
- if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
- vty_out(vty, "%*slabel vpn export %u\n", indent, "",
- bgp->vpn_policy[afi].tovpn_label);
+ if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ vty_out(vty, "%*slabel vpn export %s\n", indent, "", "auto");
+
+ } else {
+ if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
+ vty_out(vty, "%*slabel vpn export %u\n", indent, "",
+ bgp->vpn_policy[afi].tovpn_label);
+ }
}
if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_RD_SET)) {
@@ -13161,8 +13206,6 @@ DEFUN (no_ip_community_list_standard_all,
"Specify community to accept\n"
COMMUNITY_VAL_STR)
{
- int delete_all = 0;
-
char *cl_name_or_number = NULL;
int direct = 0;
int style = COMMUNITY_LIST_STANDARD;
@@ -13177,7 +13220,7 @@ DEFUN (no_ip_community_list_standard_all,
char *str = argv_concat(argv, argc, idx);
int ret = community_list_unset(bgp_clist, cl_name_or_number, str,
- direct, style, delete_all);
+ direct, style);
XFREE(MTYPE_TMP, str);
@@ -13242,8 +13285,6 @@ DEFUN (no_ip_community_list_expanded_all,
"Specify community to accept\n"
COMMUNITY_VAL_STR)
{
- int delete_all = 0;
-
char *cl_name_or_number = NULL;
int direct = 0;
int style = COMMUNITY_LIST_EXPANDED;
@@ -13258,7 +13299,7 @@ DEFUN (no_ip_community_list_expanded_all,
char *str = argv_concat(argv, argc, idx);
int ret = community_list_unset(bgp_clist, cl_name_or_number, str,
- direct, style, delete_all);
+ direct, style);
XFREE(MTYPE_TMP, str);
@@ -13795,8 +13836,6 @@ DEFUN (no_ip_extcommunity_list_standard_all,
"Specify community to accept\n"
EXTCOMMUNITY_VAL_STR)
{
- int deleteall = 0;
-
int style = EXTCOMMUNITY_LIST_STANDARD;
int direct = 0;
char *cl_number_or_name = NULL;
@@ -13811,7 +13850,7 @@ DEFUN (no_ip_extcommunity_list_standard_all,
char *str = argv_concat(argv, argc, idx);
int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str,
- direct, style, deleteall);
+ direct, style);
XFREE(MTYPE_TMP, str);
@@ -13836,8 +13875,6 @@ DEFUN (no_ip_extcommunity_list_expanded_all,
"Specify community to accept\n"
"An ordered list as a regular-expression\n")
{
- int deleteall = 0;
-
int style = EXTCOMMUNITY_LIST_EXPANDED;
int direct = 0;
char *cl_number_or_name = NULL;
@@ -13852,7 +13889,7 @@ DEFUN (no_ip_extcommunity_list_expanded_all,
char *str = argv_concat(argv, argc, idx);
int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str,
- direct, style, deleteall);
+ direct, style);
XFREE(MTYPE_TMP, str);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 023a866315..b564fccf43 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -55,6 +55,7 @@
#endif
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_labelpool.h"
/* All information about zebra. */
struct zclient *zclient = NULL;
@@ -1876,6 +1877,9 @@ static void bgp_zebra_connected(struct zclient *zclient)
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER);
+ /* tell label pool that zebra is connected */
+ bgp_lp_event_zebra_up();
+
/* TODO - What if we have peers and networks configured, do we have to
* kick-start them?
*/
@@ -2042,6 +2046,41 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient,
}
}
+static void bgp_zebra_process_label_chunk(
+ int cmd,
+ struct zclient *zclient,
+ zebra_size_t length,
+ vrf_id_t vrf_id)
+{
+ struct stream *s = NULL;
+ uint8_t response_keep;
+ uint32_t first;
+ uint32_t last;
+
+ s = zclient->ibuf;
+ STREAM_GETC(s, response_keep);
+ STREAM_GETL(s, first);
+ STREAM_GETL(s, last);
+
+ if (first > last ||
+ first < MPLS_LABEL_UNRESERVED_MIN ||
+ last > MPLS_LABEL_UNRESERVED_MAX) {
+
+ zlog_err("%s: Invalid Label chunk: %u - %u",
+ __func__, first, last);
+ return;
+ }
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ zlog_debug("Label Chunk assign: %u - %u (%u) ",
+ first, last, response_keep);
+ }
+
+ bgp_lp_event_chunk(response_keep, first, last);
+
+stream_failure: /* for STREAM_GETX */
+ return;
+}
+
extern struct zebra_privs_t bgpd_privs;
void bgp_zebra_init(struct thread_master *master)
@@ -2076,6 +2115,7 @@ void bgp_zebra_init(struct thread_master *master)
zclient->local_l3vni_del = bgp_zebra_process_local_l3vni;
zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix;
zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix;
+ zclient->label_chunk = bgp_zebra_process_label_chunk;
}
void bgp_zebra_destroy(void)
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index c46111e1fb..df0f1bd19c 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -81,7 +81,7 @@
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_flowspec.h"
-
+#include "bgpd/bgp_labelpool.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
@@ -2951,6 +2951,8 @@ static struct bgp *bgp_create(as_t *as, const char *name,
#endif /* ENABLE_BGP_VNC */
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ bgp->vpn_policy[afi].bgp = bgp;
+ bgp->vpn_policy[afi].afi = afi;
bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent =
MPLS_LABEL_NONE;
@@ -7536,6 +7538,9 @@ void bgp_master_init(struct thread_master *master)
/* Enable multiple instances by default. */
bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE);
+ /* mpls label dynamic allocation pool */
+ bgp_lp_init(bm->master, &bm->labelpool);
+
QOBJ_REG(bm, bgp_master);
}
@@ -7714,4 +7719,6 @@ void bgp_terminate(void)
if (bm->t_rmap_update)
BGP_TIMER_OFF(bm->t_rmap_update);
+
+ bgp_lp_finish();
}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 0c5f72662c..680bac0214 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -37,6 +37,7 @@
#include "bgp_memory.h"
#include "bitfield.h"
#include "vxlan.h"
+#include "bgp_labelpool.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
@@ -140,6 +141,9 @@ struct bgp_master {
/* Id space for automatic RD derivation for an EVI/VRF */
bitfield_t rd_idspace;
+ /* dynamic mpls label allocation pool */
+ struct labelpool labelpool;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp_master)
@@ -167,6 +171,25 @@ typedef enum {
BGP_VPN_POLICY_DIR_MAX = 2
} vpn_policy_direction_t;
+struct vpn_policy {
+ struct bgp *bgp; /* parent */
+ afi_t afi;
+ struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX];
+ struct ecommunity *import_redirect_rtlist;
+ char *rmap_name[BGP_VPN_POLICY_DIR_MAX];
+ struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX];
+
+ /* should be mpls_label_t? */
+ uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */
+ uint32_t tovpn_zebra_vrf_label_last_sent;
+ struct prefix_rd tovpn_rd;
+ struct prefix tovpn_nexthop; /* unset => set to 0 */
+ uint32_t flags;
+#define BGP_VPN_POLICY_TOVPN_LABEL_AUTO (1 << 0)
+#define BGP_VPN_POLICY_TOVPN_RD_SET (1 << 1)
+#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2)
+};
+
/*
* Type of 'struct bgp'.
* - Default: The default instance
@@ -465,22 +488,7 @@ struct bgp {
/* route map for advertise ipv4/ipv6 unicast (type-5 routes) */
struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX];
- /* vpn-policy */
- struct {
- struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX];
- struct ecommunity *import_redirect_rtlist;
- char *rmap_name[BGP_VPN_POLICY_DIR_MAX];
- struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX];
-
- /* should be mpls_label_t? */
- uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */
- uint32_t tovpn_zebra_vrf_label_last_sent;
- struct prefix_rd tovpn_rd;
- struct prefix tovpn_nexthop; /* unset => set to 0 */
- uint32_t flags;
-#define BGP_VPN_POLICY_TOVPN_RD_SET 0x00000004
-#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET 0x00000008
- } vpn_policy[AFI_MAX];
+ struct vpn_policy vpn_policy[AFI_MAX];
QOBJ_FIELDS
};
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c
index 8c4d5ab043..3e9722d920 100644
--- a/bgpd/rfapi/bgp_rfapi_cfg.c
+++ b/bgpd/rfapi/bgp_rfapi_cfg.c
@@ -548,13 +548,12 @@ rfapi_group_new(struct bgp *bgp, rfapi_group_cfg_type_t type, const char *name)
rfg = XCALLOC(MTYPE_RFAPI_GROUP_CFG,
sizeof(struct rfapi_nve_group_cfg));
- if (rfg) {
- rfg->type = type;
- rfg->name = strdup(name);
- /* add to tail of list */
- listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg);
- }
+ rfg->type = type;
+ rfg->name = strdup(name);
+ /* add to tail of list */
+ listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg);
rfg->label = MPLS_LABEL_NONE;
+
QOBJ_REG(rfg, rfapi_nve_group_cfg);
return rfg;
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index d5208f6966..c1af269d3f 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -1543,10 +1543,15 @@ static int rfapiNhlAddNodeRoutes(
int count = 0;
int is_l2 = (rn->p.family == AF_ETHERNET);
- if (rfapiRibFTDFilterRecentPrefix(
- (struct rfapi_descriptor *)(rfd_rib_node->table->info), rn,
- pfx_target_original)) {
- return 0;
+ if (rfd_rib_node && rfd_rib_node->table && rfd_rib_node->table->info) {
+ struct rfapi_descriptor *rfd;
+
+ rfd = (struct rfapi_descriptor *)(rfd_rib_node->table->info);
+
+ if (rfapiRibFTDFilterRecentPrefix(
+ rfd, rn, pfx_target_original))
+
+ return 0;
}
seen_nexthops =
@@ -2797,19 +2802,16 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table,
uint32_t lifetime;
struct rfapi_withdraw *wcb;
- if
- CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)
- {
- /*
- * Already on the path to being withdrawn,
- * should already have a timer set up to
- * delete it.
- */
- vnc_zlog_debug_verbose(
- "%s: already being withdrawn, do nothing",
- __func__);
- return;
- }
+ if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) {
+ /*
+ * Already on the path to being withdrawn,
+ * should already have a timer set up to
+ * delete it.
+ */
+ vnc_zlog_debug_verbose(
+ "%s: already being withdrawn, do nothing", __func__);
+ return;
+ }
rfapiGetVncLifetime(bi->attr, &lifetime);
vnc_zlog_debug_verbose("%s: VNC lifetime is %u", __func__, lifetime);
diff --git a/configure.ac b/configure.ac
index 0b94115dba..53a80e790f 100755
--- a/configure.ac
+++ b/configure.ac
@@ -1377,6 +1377,7 @@ AM_CONDITIONAL(BABELD, test "${enable_babeld}" != "no")
AM_CONDITIONAL(OSPF6D, test "${enable_ospf6d}" != "no")
AM_CONDITIONAL(ISISD, test "${enable_isisd}" != "no")
AM_CONDITIONAL(PIMD, test "${enable_pimd}" != "no")
+AM_CONDITIONAL(PBRD, test "${enable_pbrd}" != "no")
if test "${enable_bgp_announce}" = "no";then
AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra)
diff --git a/debianpkg/backports/ubuntu17.10/debian/control b/debianpkg/backports/ubuntu17.10/debian/control
index 692501db18..1bc3c4e4b5 100644
--- a/debianpkg/backports/ubuntu17.10/debian/control
+++ b/debianpkg/backports/ubuntu17.10/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Nobody <nobody@frrouting.org>
Uploaders: Nobody <nobody@frrouting.org>
XSBC-Original-Maintainer: <maintainers@frrouting.org>
-Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), imagemagick, ghostscript, groff, autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr
+Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), imagemagick, ghostscript, groff, autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr, libpython-dev
Standards-Version: 3.9.6
Homepage: http://www.frrouting.org/
diff --git a/debianpkg/backports/ubuntu18.04/debian/control b/debianpkg/backports/ubuntu18.04/debian/control
index d28f9332ce..1e31ad6d51 100644
--- a/debianpkg/backports/ubuntu18.04/debian/control
+++ b/debianpkg/backports/ubuntu18.04/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Nobody <nobody@frrouting.org>
Uploaders: Nobody <nobody@frrouting.org>
XSBC-Original-Maintainer: <maintainers@frrouting.org>
-Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), imagemagick, ghostscript, groff, autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr, python-sphinx
+Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), imagemagick, ghostscript, groff, autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr, python-sphinx, libpython-dev
Standards-Version: 3.9.6
Homepage: http://www.frrouting.org/
diff --git a/debianpkg/control b/debianpkg/control
index bbd4c43b42..468581045b 100644
--- a/debianpkg/control
+++ b/debianpkg/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Nobody <nobody@frrouting.org>
Uploaders: Nobody <nobody@frrouting.org>
XSBC-Original-Maintainer: <maintainers@frrouting.org>
-Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr, python-sphinx
+Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr, python-sphinx, libpython-dev
Standards-Version: 3.9.6
Homepage: http://www.frrouting.org/
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 6228683f94..dec6b53e0f 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -26,6 +26,10 @@ man_MANS += $(MANPAGE_BUILDDIR)/pimd.8
man_MANS += $(MANPAGE_BUILDDIR)/mtracebis.8
endif
+if PBRD
+man_MANS += $(MANPAGE_BUILDDIR)/pbrd.8
+endif
+
if BGPD
man_MANS += $(MANPAGE_BUILDDIR)/bgpd.8
endif
@@ -140,6 +144,7 @@ EXTRA_DIST = frr-sphinx.mk \
manpages/ospfd.rst \
manpages/pimd.rst \
manpages/ripd.rst \
+ manpages/pbrd.rst \
manpages/ripngd.rst \
manpages/vtysh.rst \
manpages/watchfrr.rst \
@@ -202,6 +207,7 @@ EXTRA_DIST = frr-sphinx.mk \
user/overview.rst \
user/pim.rst \
user/ripd.rst \
+ user/pbr.rst \
user/ripngd.rst \
user/routemap.rst \
user/routeserver.rst \
@@ -258,4 +264,5 @@ EXTRA_DIST = frr-sphinx.mk \
figures/ospf_api_architecture.png \
figures/ospf_api_msghdr.png \
figures/ospf_api_msgs1.png \
- figures/ospf_api_msgs2.png
+ figures/ospf_api_msgs2.png \
+ extra/frrlexer.py
diff --git a/doc/developer/_static/overrides.css b/doc/developer/_static/overrides.css
index 1e0de66c55..0d871c961a 100644
--- a/doc/developer/_static/overrides.css
+++ b/doc/developer/_static/overrides.css
@@ -2,3 +2,7 @@
div.body {
max-width: none;
}
+
+pre {
+ background-color: #e2e2e2;
+}
diff --git a/doc/developer/building-frr-on-alpine.rst b/doc/developer/building-frr-on-alpine.rst
index f0f2aee138..089032b03c 100644
--- a/doc/developer/building-frr-on-alpine.rst
+++ b/doc/developer/building-frr-on-alpine.rst
@@ -52,13 +52,32 @@ And to run the image:
::
- docker run -it --rm frr:latest /bin/sh
+ docker run -it --rm --name frr frr:latest
-Currently, we only package the raw daemons and example files, so, you'll
-need to run the daemons by hand (or, better, orchestrate in the Dockerfile).
+In the default configuration, none of the frr daemons will be running.
+To configure the daemons, exec into the container and edit the configuration
+files or mount a volume with configuration files into the container on
+startup. To configure by hand:
-We can also build directly from docker-compose, with a docker-compose.yml file
-like this one:
+::
+
+ docker exec -it frr /bin/sh
+ vi /etc/frr/daemons
+ vi /etc/frr/daemons.conf
+ cp /etc/frr/zebra.conf.sample /etc/frr/zebra.conf
+ vi /etc/frr/zebra.conf
+ /etc/init.d/frr start
+
+Or, to configure the daemons using /etc/frr from a host volume, put the
+config files in, say, ./docker/etc and bind mount that into the
+container:
+
+::
+
+ docker run -it --rm -v `pwd`/docker/etc:/etc/frr frr:latest
+
+We can also build the base image directly from docker-compose, with a
+docker-compose.yml file like this one:
::
diff --git a/doc/developer/building-frr-on-centos6.rst b/doc/developer/building-frr-on-centos6.rst
index 1c53ea6ba3..d50376548f 100644
--- a/doc/developer/building-frr-on-centos6.rst
+++ b/doc/developer/building-frr-on-centos6.rst
@@ -125,7 +125,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -135,7 +135,7 @@ Add frr groups and user
-c "FRR FRRouting suite" -d /var/run/frr frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -176,7 +176,7 @@ an example.)
sudo make SPHINXBUILD=sphinx-build2.7 install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -198,7 +198,7 @@ Create empty FRR configuration files
sudo chmod 640 /etc/frr/*.conf
Install daemon config file
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -206,13 +206,13 @@ Install daemon config file
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
Enable the daemons as required by changing the value to ``yes``
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and set the following values (ignore the other
settings)
@@ -233,7 +233,7 @@ Load the modifed sysctl's on the system:
sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
Add init.d startup files
-~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -241,14 +241,14 @@ Add init.d startup files
sudo chkconfig --add frr
Enable frr daemon at startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo chkconfig frr on
Start FRR manually (or reboot)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
diff --git a/doc/developer/building-frr-on-centos7.rst b/doc/developer/building-frr-on-centos7.rst
index 9807543e17..31cd4dcc49 100644
--- a/doc/developer/building-frr-on-centos7.rst
+++ b/doc/developer/building-frr-on-centos7.rst
@@ -31,7 +31,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -41,7 +41,7 @@ Add frr groups and user
-c "FRR FRRouting suite" -d /var/run/frr frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -83,7 +83,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -106,7 +106,7 @@ Create empty FRR configuration files
sudo chmod 640 /etc/frr/*.conf
Install daemon config file
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -114,13 +114,13 @@ Install daemon config file
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
Enable the daemons as required by changing the value to ``yes``
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the
following content:
@@ -140,7 +140,7 @@ Load the modifed sysctl's on the system:
sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
Install frr Service and redhat init files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -148,21 +148,21 @@ Install frr Service and redhat init files
sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr
Register the systemd files
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo systemctl preset frr.service
Enable required frr at startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo systemctl enable frr
Reboot or start FRR manually
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
diff --git a/doc/developer/building-frr-on-debian8.rst b/doc/developer/building-frr-on-debian8.rst
index 36ed0c4e66..d1e65a472d 100644
--- a/doc/developer/building-frr-on-debian8.rst
+++ b/doc/developer/building-frr-on-debian8.rst
@@ -32,7 +32,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -43,7 +43,7 @@ Add frr groups and user
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -80,7 +80,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -99,7 +99,7 @@ Create empty FRR configuration files
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
@@ -118,7 +118,7 @@ other settings)
system
Troubleshooting
-~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^
**Local state directory**
diff --git a/doc/developer/building-frr-on-debian9.rst b/doc/developer/building-frr-on-debian9.rst
index a1aa63236c..7dad9a7bd4 100644
--- a/doc/developer/building-frr-on-debian9.rst
+++ b/doc/developer/building-frr-on-debian9.rst
@@ -19,7 +19,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -30,7 +30,7 @@ Add frr groups and user
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -68,7 +68,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -88,7 +88,7 @@ Create empty FRR configuration files
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
@@ -110,7 +110,7 @@ Troubleshooting
---------------
Shared library error
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
If you try and start any of the frrouting daemons you may see the below
error due to the frrouting shared library directory not being found:
diff --git a/doc/developer/building-frr-on-fedora24.rst b/doc/developer/building-frr-on-fedora24.rst
index ed81d3f59c..208c580b63 100644
--- a/doc/developer/building-frr-on-fedora24.rst
+++ b/doc/developer/building-frr-on-fedora24.rst
@@ -24,7 +24,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -34,7 +34,7 @@ Add frr groups and user
-c "FRR FRRouting suite" -d /var/run/frr frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -75,7 +75,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -99,7 +99,7 @@ Create empty FRR configuration files
sudo chmod 640 /etc/frr/*.conf
Install daemon config file
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -107,13 +107,13 @@ Install daemon config file
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc.
Enable the daemons as required by changing the value to ``yes``
Enable IP & IPv6 forwarding (and MPLS)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the
following content: (Please make sure to list all interfaces with
@@ -155,7 +155,7 @@ And load the kernel modules on the running system:
sudo modprobe mpls-router mpls-iptunnel
Install frr Service and redhat init files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -163,14 +163,14 @@ Install frr Service and redhat init files
sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr
Enable required frr at startup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
sudo systemctl enable frr
Reboot or start FRR manually
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
diff --git a/doc/developer/building-frr-on-freebsd10.rst b/doc/developer/building-frr-on-freebsd10.rst
index ccbe8c55c3..5d14db5fa7 100644
--- a/doc/developer/building-frr-on-freebsd10.rst
+++ b/doc/developer/building-frr-on-freebsd10.rst
@@ -34,7 +34,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
@@ -76,7 +76,7 @@ an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -95,7 +95,7 @@ Create empty FRR configuration files
sudo chmod 640 /usr/local/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
diff --git a/doc/developer/building-frr-on-freebsd11.rst b/doc/developer/building-frr-on-freebsd11.rst
index 214fdbf9c8..87fb30226d 100644
--- a/doc/developer/building-frr-on-freebsd11.rst
+++ b/doc/developer/building-frr-on-freebsd11.rst
@@ -34,7 +34,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
@@ -76,7 +76,7 @@ an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -95,7 +95,7 @@ Create empty FRR configuration files
sudo chmod 640 /usr/local/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
diff --git a/doc/developer/building-frr-on-freebsd9.rst b/doc/developer/building-frr-on-freebsd9.rst
index 909b3a8d64..02279debea 100644
--- a/doc/developer/building-frr-on-freebsd9.rst
+++ b/doc/developer/building-frr-on-freebsd9.rst
@@ -47,7 +47,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
@@ -89,7 +89,7 @@ an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -108,7 +108,7 @@ Create empty FRR configuration files
sudo chmod 640 /usr/local/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
diff --git a/doc/developer/building-frr-on-lede-openwrt.rst b/doc/developer/building-frr-on-lede-openwrt.rst
index b9ee9c51e1..d14754b37f 100644
--- a/doc/developer/building-frr-on-lede-openwrt.rst
+++ b/doc/developer/building-frr-on-lede-openwrt.rst
@@ -98,11 +98,11 @@ DAEMONS= or don't install unneded packages For example: zebra bgpd ldpd
isisd nhrpd ospfd ospf6d pimd ripd ripngd
Enable the serivce
-~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^
- service frr enable
Start the service
-~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^
- service frr start
diff --git a/doc/developer/building-frr-on-netbsd6.rst b/doc/developer/building-frr-on-netbsd6.rst
index ce5c58045f..ca0845d0d0 100644
--- a/doc/developer/building-frr-on-netbsd6.rst
+++ b/doc/developer/building-frr-on-netbsd6.rst
@@ -44,7 +44,7 @@ Get FRR, compile it and install it (from Git)
---------------------------------------------
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -54,7 +54,7 @@ Add frr groups and user
-d /nonexistent -s /sbin/nologin frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example)
@@ -89,7 +89,7 @@ an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -109,7 +109,7 @@ Create empty FRR configuration files
sudo chmod 640 /usr/pkg/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
@@ -123,7 +123,7 @@ Add the following lines to the end of ``/etc/sysctl.conf``:
system
Install rc.d init files
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -131,7 +131,7 @@ Install rc.d init files
chmod 555 /etc/rc.d/*.sh
Enable FRR processes
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
(Enable the required processes only)
diff --git a/doc/developer/building-frr-on-netbsd7.rst b/doc/developer/building-frr-on-netbsd7.rst
index eaaf87fd19..86242ef965 100644
--- a/doc/developer/building-frr-on-netbsd7.rst
+++ b/doc/developer/building-frr-on-netbsd7.rst
@@ -35,7 +35,7 @@ Get FRR, compile it and install it (from Git)
---------------------------------------------
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -45,7 +45,7 @@ Add frr groups and user
-d /nonexistent -s /sbin/nologin frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example)
@@ -80,7 +80,7 @@ an example)
sudo gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -99,7 +99,7 @@ Create empty FRR configuration files
sudo chmod 640 /usr/pkg/etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/sysctl.conf``:
@@ -113,7 +113,7 @@ Add the following lines to the end of ``/etc/sysctl.conf``:
system
Install rc.d init files
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -121,7 +121,7 @@ Install rc.d init files
chmod 555 /etc/rc.d/*.sh
Enable FRR processes
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
(Enable the required processes only)
diff --git a/doc/developer/building-frr-on-omnios.rst b/doc/developer/building-frr-on-omnios.rst
index 0eb2b9fec8..03f3845de8 100644
--- a/doc/developer/building-frr-on-omnios.rst
+++ b/doc/developer/building-frr-on-omnios.rst
@@ -9,7 +9,7 @@ OmniOS restrictions:
use without MPLS
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -93,7 +93,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
@@ -138,7 +138,7 @@ an example)
sudo gmake install
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
diff --git a/doc/developer/building-frr-on-openbsd6.rst b/doc/developer/building-frr-on-openbsd6.rst
index 895048886e..1f3aec8d92 100644
--- a/doc/developer/building-frr-on-openbsd6.rst
+++ b/doc/developer/building-frr-on-openbsd6.rst
@@ -30,7 +30,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr group and user
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
@@ -40,7 +40,7 @@ Add frr group and user
-d /nonexistent -s /sbin/nologin _frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example)
@@ -75,7 +75,7 @@ an example)
doas gmake install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -100,7 +100,7 @@ Create empty FRR configuration files
doas chmod 640 /etc/frr/*.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to the end of ``/etc/rc.conf``:
@@ -113,7 +113,7 @@ Add the following lines to the end of ``/etc/rc.conf``:
**Reboot** to apply the config to the system
Enable MPLS Forwarding
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
To enable MPLS forwarding on a given interface, use the following
command:
@@ -132,7 +132,7 @@ Example:
inet 10.0.1.1 255.255.255.0 mpls
Install rc.d init files
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
(create them in /etc/rc.d - no example are included at this time with
FRR source)
@@ -152,7 +152,7 @@ Example (for zebra - store as ``/etc/rc.d/frr_zebra.sh``)
rc_cmd $1
Enable FRR processes
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
(Enable the required processes only)
diff --git a/doc/developer/building-frr-on-ubuntu1204.rst b/doc/developer/building-frr-on-ubuntu1204.rst
index 68e476fec2..bba49c1ce7 100644
--- a/doc/developer/building-frr-on-ubuntu1204.rst
+++ b/doc/developer/building-frr-on-ubuntu1204.rst
@@ -70,7 +70,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -81,7 +81,7 @@ Add frr groups and user
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -116,7 +116,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -135,7 +135,7 @@ Create empty FRR configuration files
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
@@ -154,7 +154,7 @@ other settings)
system
Install the init.d service
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -164,7 +164,7 @@ Install the init.d service
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
@@ -181,7 +181,7 @@ Enable daemons
isisd=yes
Start the init.d service
-~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^
- /etc/init.d/frr start
- use ``/etc/init.d/frr status`` to check its status.
diff --git a/doc/developer/building-frr-on-ubuntu1404.rst b/doc/developer/building-frr-on-ubuntu1404.rst
index 10944cb8e1..c86f1124a7 100644
--- a/doc/developer/building-frr-on-ubuntu1404.rst
+++ b/doc/developer/building-frr-on-ubuntu1404.rst
@@ -24,7 +24,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -35,7 +35,7 @@ Add frr groups and user
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -71,7 +71,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -90,7 +90,7 @@ Create empty FRR configuration files
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IP & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
@@ -106,7 +106,10 @@ other settings)
net.ipv6.conf.all.forwarding=1
**Reboot** or use ``sysctl -p`` to apply the same config to the running
-system ### Install the init.d service
+system
+
+Install the init.d service
+^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -116,7 +119,7 @@ system ### Install the init.d service
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
@@ -133,7 +136,7 @@ Enable daemons
isisd=yes
Start the init.d service
-~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^
- /etc/init.d/frr start
- use ``/etc/init.d/frr status`` to check its status.
diff --git a/doc/developer/building-frr-on-ubuntu1604.rst b/doc/developer/building-frr-on-ubuntu1604.rst
index 9c296f8edf..1b371893e2 100644
--- a/doc/developer/building-frr-on-ubuntu1604.rst
+++ b/doc/developer/building-frr-on-ubuntu1604.rst
@@ -25,7 +25,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -36,7 +36,7 @@ Add frr groups and user
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -72,7 +72,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -91,7 +91,7 @@ Create empty FRR configuration files
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IPv4 & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
@@ -107,7 +107,7 @@ other settings)
net.ipv6.conf.all.forwarding=1
Enable MPLS Forwarding (with Linux Kernel >= 4.5)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a
line equal to ``net.mpls.conf.eth0.input`` or each interface used with
@@ -122,7 +122,7 @@ MPLS
net.mpls.platform_labels=100000
Add MPLS kernel modules
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to ``/etc/modules-load.d/modules.conf``:
@@ -136,7 +136,7 @@ Add the following lines to ``/etc/modules-load.d/modules.conf``:
system
Install the systemd service (if rebooted from last step, change directory back to frr directory)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -148,7 +148,7 @@ Install the systemd service (if rebooted from last step, change directory back t
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
@@ -165,12 +165,12 @@ Enable daemons
isisd=yes
Enable the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl enable frr
Start the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl start frr
- use ``systemctl status frr`` to check its status.
diff --git a/doc/developer/building-frr-on-ubuntu1804.rst b/doc/developer/building-frr-on-ubuntu1804.rst
index 6c79ed87d6..17edb7ef4f 100644
--- a/doc/developer/building-frr-on-ubuntu1804.rst
+++ b/doc/developer/building-frr-on-ubuntu1804.rst
@@ -20,7 +20,7 @@ Get FRR, compile it and install it (from Git)
using any packages**
Add frr groups and user
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -31,7 +31,7 @@ Add frr groups and user
sudo usermod -a -G frrvty frr
Download Source, configure and compile it
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(You may prefer different options on configure statement. These are just
an example.)
@@ -67,7 +67,7 @@ an example.)
sudo make install
Create empty FRR configuration files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -86,7 +86,7 @@ Create empty FRR configuration files
sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf
Enable IPv4 & IPv6 forwarding
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the
other settings)
@@ -102,7 +102,7 @@ other settings)
net.ipv6.conf.all.forwarding=1
Enable MPLS Forwarding (with Linux Kernel >= 4.5)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a
line equal to ``net.mpls.conf.eth0.input`` or each interface used with
@@ -117,7 +117,7 @@ MPLS
net.mpls.platform_labels=100000
Add MPLS kernel modules
-~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^
Add the following lines to ``/etc/modules-load.d/modules.conf``:
@@ -131,7 +131,7 @@ Add the following lines to ``/etc/modules-load.d/modules.conf``:
system
Install the systemd service (if rebooted from last step, change directory back to frr directory)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@@ -143,7 +143,7 @@ Install the systemd service (if rebooted from last step, change directory back t
sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
Enable daemons
-~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^
| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for
those daemons you want to start by systemd.
@@ -160,12 +160,12 @@ Enable daemons
isisd=yes
Enable the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl enable frr
Start the systemd service
-~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^
- systemctl start frr
- use ``systemctl status frr`` to check its status.
diff --git a/doc/developer/building.rst b/doc/developer/building.rst
index c7a53734d9..92fd1bb63a 100644
--- a/doc/developer/building.rst
+++ b/doc/developer/building.rst
@@ -1,5 +1,6 @@
+************
Building FRR
-=========================
+************
.. toctree::
:maxdepth: 2
diff --git a/doc/developer/conf.py b/doc/developer/conf.py
index 9ae1991817..a3968b60ff 100644
--- a/doc/developer/conf.py
+++ b/doc/developer/conf.py
@@ -15,6 +15,8 @@
import sys
import os
import re
+import pygments
+from sphinx.highlighting import lexers
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -51,7 +53,7 @@ master_doc = 'index'
# General information about the project.
project = u'FRR'
copyright = u'2017, FRR'
-author = u'FRR'
+author = u'FRR authors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -67,34 +69,40 @@ release = u'?.?-?'
# Extract values from codebase for substitution into docs.
# -----------------------------------------------------------------------------
-# Various installation prefixes. Reasonable defaults are set where possible.
-# Values are overridden by logic below.
+# Various installation prefixes. Values are extracted from config.status.
+# Reasonable defaults are set in case that file does not exist.
replace_vars = {
- 'AUTHORS': 'Kunihiro Ishiguro, et al.',
+ 'AUTHORS': author,
'COPYRIGHT_YEAR': '1999-2005',
- 'COPYRIGHT_STR': None,
+ 'COPYRIGHT_STR': 'Copyright (c) 1999-2005',
'PACKAGE_NAME': project.lower(),
'PACKAGE_TARNAME': project.lower(),
- 'PACKAGE_STRING': None,
+ 'PACKAGE_STRING': project.lower() + ' latest',
'PACKAGE_URL': 'https://frrouting.org/',
- 'PACKAGE_VERSION': None,
- 'INSTALL_PREFIX_ETC': None,
- 'INSTALL_PREFIX_SBIN': None,
- 'INSTALL_PREFIX_STATE': None,
- 'INSTALL_PREFIX_MODULES': None,
- 'INSTALL_USER': None,
- 'INSTALL_GROUP': None,
- 'INSTALL_VTY_GROUP': None,
+ 'PACKAGE_VERSION': 'latest',
+ 'INSTALL_PREFIX_ETC': '/etc/frr',
+ 'INSTALL_PREFIX_SBIN': '/usr/lib/frr',
+ 'INSTALL_PREFIX_STATE': '/var/run/frr',
+ 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules',
+ 'INSTALL_USER': 'frr',
+ 'INSTALL_GROUP': 'frr',
+ 'INSTALL_VTY_GROUP': 'frrvty',
+ 'GROUP': 'frr',
+ 'USER': 'frr',
}
# extract version information, installation location, other stuff we need to
# use when building final documents
val = re.compile('^S\["([^"]+)"\]="(.*)"$')
-with open('../../config.status', 'r') as cfgstatus:
- for ln in cfgstatus.readlines():
- m = val.match(ln)
- if not m or m.group(1) not in replace_vars.keys(): continue
- replace_vars[m.group(1)] = m.group(2)
+try:
+ with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if not m or m.group(1) not in replace_vars.keys(): continue
+ replace_vars[m.group(1)] = m.group(2)
+except IOError:
+ # if config.status doesn't exist, just ignore it
+ pass
# manually fill out some of these we can't get from config.status
replace_vars['COPYRIGHT_STR'] = "Copyright (c)"
@@ -341,3 +349,13 @@ def setup(app):
app.add_object_type('clicmd', 'clicmd')
# css overrides for HTML theme
app.add_stylesheet('overrides.css')
+ # load Pygments lexer for FRR config syntax
+ #
+ # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we
+ # do it manually since not all of our supported build platforms have 2.2
+ # yet.
+ #
+ # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer")
+ custom_namespace = {}
+ exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace)
+ lexers['frr'] = custom_namespace['FRRLexer']()
diff --git a/doc/developer/hooks.rst b/doc/developer/hooks.rst
index 0afa297aa7..4140a0d171 100644
--- a/doc/developer/hooks.rst
+++ b/doc/developer/hooks.rst
@@ -12,26 +12,26 @@ the appropriate function signature (parameters) for the hook.
Example:
.. code-block:: c
- :caption: mydaemon.h
+ :caption: mydaemon.h
- #include "hook.h"
- DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info))
+ #include "hook.h"
+ DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info))
.. code-block:: c
- :caption: mydaemon.c
+ :caption: mydaemon.c
- #include "mydaemon.h"
- DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info))
- ...
- hook_call(some_update_event, info);
+ #include "mydaemon.h"
+ DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info))
+ ...
+ hook_call(some_update_event, info);
.. code-block:: c
- :caption: mymodule.c
+ :caption: mymodule.c
- #include "mydaemon.h"
- static int event_handler(struct eventinfo *info);
- ...
- hook_register(some_update_event, event_handler);
+ #include "mydaemon.h"
+ static int event_handler(struct eventinfo *info);
+ ...
+ hook_register(some_update_event, event_handler);
Do not use parameter names starting with "hook", these can collide with
names used by the hook code itself.
diff --git a/doc/developer/library.rst b/doc/developer/library.rst
index 791aedb624..c5ce1f5982 100644
--- a/doc/developer/library.rst
+++ b/doc/developer/library.rst
@@ -1,5 +1,6 @@
-libfrr library facilities
-=========================
+***************************
+Library Facilities (libfrr)
+***************************
.. toctree::
:maxdepth: 2
diff --git a/doc/developer/memtypes.rst b/doc/developer/memtypes.rst
index d40ebe31cd..d43bc2555e 100644
--- a/doc/developer/memtypes.rst
+++ b/doc/developer/memtypes.rst
@@ -6,37 +6,37 @@ Memtypes
FRR includes wrappers arround ``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
+To this extent, there are *memory groups* and *memory types*. Each memory
type must belong to a memory group, this is used just to provide some basic
structure.
Example:
.. code-block:: c
- :caption: mydaemon.h
+ :caption: mydaemon.h
- DECLARE_MGROUP(MYDAEMON)
- DECLARE_MTYPE(MYNEIGHBOR)
+ DECLARE_MGROUP(MYDAEMON)
+ DECLARE_MTYPE(MYNEIGHBOR)
.. code-block:: c
- :caption: mydaemon.c
-
- DEFINE_MGROUP( MYDAEMON, "My daemon's memory")
- DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry")
- DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name")
-
- struct neigh *neighbor_new(const char *name)
- {
- struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n));
- n->name = XSTRDUP(MYNEIGHBORNAME, name);
- return n;
- }
-
- void neighbor_free(struct neigh *n)
- {
- XFREE(MYNEIGHBORNAME, n->name);
- XFREE(MYNEIGHBOR, n);
- }
+ :caption: mydaemon.c
+
+ DEFINE_MGROUP( MYDAEMON, "My daemon's memory")
+ DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry")
+ DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name")
+
+ struct neigh *neighbor_new(const char *name)
+ {
+ struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n));
+ n->name = XSTRDUP(MYNEIGHBORNAME, name);
+ return n;
+ }
+
+ void neighbor_free(struct neigh *n)
+ {
+ XFREE(MYNEIGHBORNAME, n->name);
+ XFREE(MYNEIGHBOR, n);
+ }
Definition
diff --git a/doc/developer/next-hop-tracking.rst b/doc/developer/next-hop-tracking.rst
index 25e2d1fe1a..8a6a4ccb33 100644
--- a/doc/developer/next-hop-tracking.rst
+++ b/doc/developer/next-hop-tracking.rst
@@ -100,7 +100,7 @@ Design
------
Modules
-~~~~~~~
+^^^^^^^
The core design introduces an "nht" (next hop tracking) module in BGP
and "rnh" (recursive nexthop) module in Zebra. The "nht" module
@@ -178,7 +178,7 @@ The next hop notification control flow is the following:
zclient message format
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER messages are
encoded in the following way:
@@ -233,7 +233,7 @@ encoded in the following way:
BGP data structure
-~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^
Legend:
::
@@ -260,7 +260,7 @@ Legend:
Zebra data structure
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
RNH table::
@@ -279,7 +279,7 @@ RNH table::
};
User interface changes
-~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^
::
@@ -338,7 +338,7 @@ User interface changes
+ no shut all links to r4
Future work
-~~~~~~~~~~~
+^^^^^^^^^^^
- route-policy for next hop validation (e.g. ignore default route)
- damping for rapid next hop changes
diff --git a/doc/developer/ospf-sr.rst b/doc/developer/ospf-sr.rst
index 3f8b549df3..4a673b155b 100644
--- a/doc/developer/ospf-sr.rst
+++ b/doc/developer/ospf-sr.rst
@@ -29,7 +29,7 @@ Implementation details
----------------------
Concepts
-~~~~~~~~
+^^^^^^^^
Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various
information:
@@ -47,7 +47,7 @@ Segment Routing functions (see below) when an Extended Link / Prefix or Router
Information LSA s are received.
Overview
-~~~~~~~~
+^^^^^^^^
Following files where modified or added:
@@ -113,7 +113,7 @@ The figure below shows the relation between the various files:
Figure 1: Overview of Segment Routing interaction
Module interactions
-~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^
To process incoming LSA, the code is based on the capability to call `hook()`
functions when LSA are inserted or delete to / from the LSDB and the
@@ -186,7 +186,7 @@ Configuration
-------------
Linux Kernel
-~~~~~~~~~~~~
+^^^^^^^^^^^^
In order to use OSPF Segment Routing, you must setup MPLS data plane. Up to
know, only Linux Kernel version >= 4.5 is supported.
@@ -231,7 +231,7 @@ especially the `lo` one. For that purpose, disable RP filtering with:
sysctl -w net.ipv4.conf.lo.rp_filter=0
OSPFd
-~~~~~
+^^^^^
Here it is a simple example of configuration to enable Segment Routing. Note
that `opaque capability` and `router information` must be set to activate
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index 291a277c6b..f025c47bb4 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -479,6 +479,16 @@ checkpatch.sh
When convenient it is preferred that these be cleaned up inline, but this is
not required.
+ In general, a developer should heed the information reported by checkpatch.
+ However, some flexibility is needed for cases where human judgement yields
+ better clarity than the script. Accordingly, it may be appropriate to
+ ignore some checkpatch.sh warnings per discussion among the submitter(s)
+ and reviewer(s) of a change. Misreporting of errors by the script is
+ possible. When this occurs, the exception should be handled either by
+ patching checkpatch to correct the false error report, or by documenting the
+ exception in this document under :ref:`style-exceptions`. If the incorrect
+ report is likely to appear again, a checkpatch update is preferred.
+
If the script finds one or more WARNINGs it will exit with 1. If it finds
one or more ERRORs it will exit with 2.
@@ -488,6 +498,12 @@ responsibility for properly formatting your code ultimately lies on the
shoulders of the submitter. As such, it is recommended to double-check the
results of these tools to avoid delays in merging your submission.
+In some cases, these tools modify or flag the format in ways that go beyond or
+even conflict [#tool_style_conflicts]_ with the canonical documented Linux
+kernel style. In these cases, the Linux kernel style takes priority;
+non-canonical issues flagged by the tools are not compulsory but rather are
+opportunities for discussion among the submitter(s) and reviewer(s) of a change.
+
**Whitespace changes in untouched parts of the code are not acceptable
in patches that change actual code.** To change/fix formatting issues,
please create a separate patch that only does formatting changes and
@@ -533,6 +549,8 @@ necessary replacements.
| u_long | unsigned long |
+-----------+--------------------------+
+.. _style-exceptions:
+
Exceptions
^^^^^^^^^^
@@ -571,6 +589,23 @@ BSD coding style applies to:
- ``ldpd/``
+
+Specific Exceptions
+^^^^^^^^^^^^^^^^^^^
+
+Most of the time checkpatch errors should be corrected. Occasionally as a group
+maintainers will decide to ignore certain stylistic issues. Usually this is
+because correcting the issue is not possible without large unrelated code
+changes. When an exception is made, if it is unlikely to show up again and
+doesn't warrant an update to checkpatch, it is documented here.
+
++------------------------------------------+---------------------------------------------------------------+
+| Issue | Ignore Reason |
++==========================================+===============================================================+
+| DEFPY_HIDDEN, DEFPY_ATTR: complex macros | DEF* macros cannot be wrapped in parentheses without updating |
+| should be wrapped in parentheses | all usages of the macro, which would be highly disruptive. |
++------------------------------------------+---------------------------------------------------------------+
+
Compile-time conditional code
-----------------------------
@@ -633,7 +668,7 @@ AddressSanitizer
instrumentation and run through a series of tests to look for any results.
Testing your own code with this tool before submission is encouraged. You
can enable it by passing::
-
+
--enable-address-sanitizer
to ``configure``.
@@ -643,7 +678,7 @@ ThreadSanitizer
detecting data races. If you are working on or around multithreaded code,
extensive testing with this instrumtation enabled is *highly* recommended.
You can enable it by passing::
-
+
--enable-thread-sanitizer
to ``configure``.
@@ -652,7 +687,7 @@ MemorySanitizer
Similar to AddressSanitizer, this tool provides runtime instrumentation for
detecting use of uninitialized heap memory. Testing your own code with this
tool before submission is encouraged. You can enable it by passing::
-
+
--enable-memory-sanitizer
to ``configure``.
@@ -740,27 +775,33 @@ FRR uses Sphinx+RST as its documentation system. The document you are currently
reading was generated by Sphinx from RST source in
:file:`doc/developer/workflow.rst`. The documentation is structured as follows:
-+-----------------------+--------------------------------------------------------------+
-| Directory | Contents |
-+=======================+==============================================================+
-| :file:`doc/user` | User documentation; configuration guides; protocol overviews |
-+-----------------------+--------------------------------------------------------------+
-| :file:`doc/developer` | Developer's documentation; API specs; datastructures; |
-| | architecture overviews; project management procedure |
-+-----------------------+--------------------------------------------------------------+
-| :file:`doc/manpages` | Source for manpages |
-+-----------------------+--------------------------------------------------------------+
-| :file:`doc/figures` | Images and diagrams |
-+-----------------------+--------------------------------------------------------------+
-
-Each of these directories, with the exception of :file:`doc/figures`, contains
-a Sphinx-generated Makefile and configuration script :file:`conf.py` used to
-set various document parameters. The makefile can be used for a variety of
-targets; invoke `make help` in any of these directories for a listing of
-available output formats. For convenience, there is a top-level
-:file:`Makefile.am` that has targets for PDF and HTML documentation for both
-developer and user documentation, respectively. That makefile is also
-responsible for building manual pages packed with distribution builds.
++-----------------------+-------------------------------------------+
+| Directory | Contents |
++=======================+===========================================+
+| :file:`doc/user` | User documentation; configuration guides; |
+| | protocol overviews |
++-----------------------+-------------------------------------------+
+| :file:`doc/developer` | Developer's documentation; API specs; |
+| | datastructures; architecture overviews; |
+| | project management procedure |
++-----------------------+-------------------------------------------+
+| :file:`doc/manpages` | Source for manpages |
++-----------------------+-------------------------------------------+
+| :file:`doc/figures` | Images and diagrams |
++-----------------------+-------------------------------------------+
+| :file:`doc/extra` | Miscellaneous Sphinx extensions, scripts, |
+| | customizations, etc. |
++-----------------------+-------------------------------------------+
+
+Each of these directories, with the exception of :file:`doc/figures` and
+:file:`doc/extra`, contains a Sphinx-generated Makefile and configuration
+script :file:`conf.py` used to set various document parameters. The makefile
+can be used for a variety of targets; invoke `make help` in any of these
+directories for a listing of available output formats. For convenience, there
+is a top-level :file:`Makefile.am` that has targets for PDF and HTML
+documentation for both developer and user documentation, respectively. That
+makefile is also responsible for building manual pages packed with distribution
+builds.
Indent and styling should follow existing conventions:
@@ -855,6 +896,15 @@ your implementation of a new BGP draft should go in the BGP chapter instead of
being its own chapter. If you are adding a new protocol daemon, please create a
new chapter.
+FRR Specific Markup
+-------------------
+
+FRR has some customizations applied to the Sphinx markup that go a long way
+towards making documentation easier to use, write and maintain.
+
+CLI Commands
+^^^^^^^^^^^^
+
When documenting CLI please use a combination of the ``.. index::`` and
``.. clicmd::`` directives. For example, the command :clicmd:`show pony` would
be documented as follows:
@@ -888,5 +938,35 @@ When documented this way, CLI commands can be cross referenced with the
This is very helpful for users who want to quickly remind themselves what a
particular command does.
+Configuration Snippets
+^^^^^^^^^^^^^^^^^^^^^^
+
+When putting blocks of example configuration please use the
+``.. code-block::`` directive and specify ``frr`` as the highlighting language,
+as in the following example. This will tell Sphinx to use a custom Pygments
+lexer to highlight FRR configuration syntax.
+
+.. code-block:: rest
+
+ .. code-block:: frr
+
+ !
+ ! Example configuration file.
+ !
+ log file /tmp/log.log
+ service integrated-vtysh-config
+ !
+ ip route 1.2.3.0/24 reject
+ ipv6 route de:ea:db:ee:ff::/64 reject
+ !
+
+
.. _GitHub: https://github.com/frrouting/frr
.. _GitHub issues: https://github.com/frrouting/frr/issues
+
+.. rubric:: Footnotes
+
+.. [#tool_style_conflicts] For example, lines over 80 characters are allowed
+ for text strings to make it possible to search the code for them: please
+ see `Linux kernel style (breaking long lines and strings) <https://www.kernel.org/doc/html/v4.10/process/coding-style.html#breaking-long-lines-and-strings>`_
+ and `Issue #1794 <https://github.com/FRRouting/frr/issues/1794>`_.
diff --git a/doc/extra/frrlexer.py b/doc/extra/frrlexer.py
new file mode 100644
index 0000000000..528bec985b
--- /dev/null
+++ b/doc/extra/frrlexer.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017 Vincent Bernat <bernat@luffy.cx>
+#
+# 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+
+from pygments.lexer import RegexLexer, bygroups
+from pygments.token import Text, Comment, Keyword
+from pygments.token import String, Number, Name
+
+
+class FRRLexer(RegexLexer):
+ name = "frr"
+ aliases = ["frr"]
+ tokens = {
+ 'root': [
+ (r'^[ \t]*!.*?\n', Comment.Singleline),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r'[a-f0-9]*:[a-f0-9]*:[a-f0-9:]*(:\d+\.\d+\.\d+\.\d+)?(/\d+)?',
+ Number), # IPv6
+ (r'\d+\.\d+\.\d+\.\d+(/\d+)?', Number), # IPv4
+ (r'^([ \t]*)(no[ \t]+)?([-\w]+)',
+ bygroups(Text, Keyword, Name.Function)),
+ (r'[ \t]+', Text),
+ (r'\n', Text),
+ (r'\d+', Number),
+ (r'\S+', Text),
+ ],
+ }
diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst
index 5b136f5e1a..e433c720af 100644
--- a/doc/manpages/common-options.rst
+++ b/doc/manpages/common-options.rst
@@ -122,6 +122,7 @@ These following options control the daemon's VTY (interactive command line) inte
pimd 2611
ldpd 2612
eigrpd 2613
+ pbrd 2615
Port 2607 is used for ospfd's Opaque LSA API, while port 2600 is used for the (insecure) TCP-ZEBRA interface.
diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py
index a78e1a2c38..41683ed678 100644
--- a/doc/manpages/conf.py
+++ b/doc/manpages/conf.py
@@ -51,7 +51,7 @@ master_doc = 'index'
# General information about the project.
project = u'FRR'
copyright = u'2017, FRR'
-author = u'Kunihiro Ishiguro, et al.'
+author = u'FRR authors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -67,24 +67,24 @@ release = u'?.?-?'
# Extract values from codebase for substitution into docs.
# -----------------------------------------------------------------------------
-# Various installation prefixes. Reasonable defaults are set where possible.
-# Values are overridden by logic below.
+# Various installation prefixes. Values are extracted from config.status.
+# Reasonable defaults are set in case that file does not exist.
replace_vars = {
'AUTHORS': author,
'COPYRIGHT_YEAR': '1999-2005',
- 'COPYRIGHT_STR': None,
+ 'COPYRIGHT_STR': 'Copyright (c) 1999-2005',
'PACKAGE_NAME': project.lower(),
'PACKAGE_TARNAME': project.lower(),
- 'PACKAGE_STRING': None,
+ 'PACKAGE_STRING': project.lower() + ' latest',
'PACKAGE_URL': 'https://frrouting.org/',
- 'PACKAGE_VERSION': None,
- 'INSTALL_PREFIX_ETC': None,
- 'INSTALL_PREFIX_SBIN': None,
- 'INSTALL_PREFIX_STATE': None,
- 'INSTALL_PREFIX_MODULES': None,
- 'INSTALL_USER': None,
- 'INSTALL_GROUP': None,
- 'INSTALL_VTY_GROUP': None,
+ 'PACKAGE_VERSION': 'latest',
+ 'INSTALL_PREFIX_ETC': '/etc/frr',
+ 'INSTALL_PREFIX_SBIN': '/usr/lib/frr',
+ 'INSTALL_PREFIX_STATE': '/var/run/frr',
+ 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules',
+ 'INSTALL_USER': 'frr',
+ 'INSTALL_GROUP': 'frr',
+ 'INSTALL_VTY_GROUP': 'frrvty',
'GROUP': 'frr',
'USER': 'frr',
}
@@ -92,11 +92,15 @@ replace_vars = {
# extract version information, installation location, other stuff we need to
# use when building final documents
val = re.compile('^S\["([^"]+)"\]="(.*)"$')
-with open('../../config.status', 'r') as cfgstatus:
- for ln in cfgstatus.readlines():
- m = val.match(ln)
- if not m or m.group(1) not in replace_vars.keys(): continue
- replace_vars[m.group(1)] = m.group(2)
+try:
+ with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if not m or m.group(1) not in replace_vars.keys(): continue
+ replace_vars[m.group(1)] = m.group(2)
+except IOError:
+ # if config.status doesn't exist, just ignore it
+ pass
# manually fill out some of these we can't get from config.status
replace_vars['COPYRIGHT_STR'] = "Copyright (c)"
@@ -318,6 +322,7 @@ man_pages = [
('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
+ ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8),
('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),
diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst
index 841abe300e..21c3790afa 100644
--- a/doc/manpages/defines.rst
+++ b/doc/manpages/defines.rst
@@ -1,3 +1,3 @@
.. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
.. |synopsis-options-hv| replace:: [-h] [-v]
-.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), ldpd(8), eigrpd(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), mtracebis(8)
diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst
index ae5c4ca3bd..964cc07d71 100644
--- a/doc/manpages/index.rst
+++ b/doc/manpages/index.rst
@@ -18,6 +18,7 @@ Welcome to FRR's documentation!
ospfclient
ospfd
pimd
+ pbrd
mtracebis
ripd
ripngd
diff --git a/doc/manpages/pbrd.rst b/doc/manpages/pbrd.rst
new file mode 100644
index 0000000000..d9a80b1f84
--- /dev/null
+++ b/doc/manpages/pbrd.rst
@@ -0,0 +1,38 @@
+****
+PBRD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: pbrd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
diff --git a/doc/user/_static/overrides.css b/doc/user/_static/overrides.css
index 1e0de66c55..0d871c961a 100644
--- a/doc/user/_static/overrides.css
+++ b/doc/user/_static/overrides.css
@@ -2,3 +2,7 @@
div.body {
max-width: none;
}
+
+pre {
+ background-color: #e2e2e2;
+}
diff --git a/doc/user/basic.rst b/doc/user/basic.rst
index 29d05abba7..4a5056e233 100644
--- a/doc/user/basic.rst
+++ b/doc/user/basic.rst
@@ -42,7 +42,7 @@ Config files are generally found in |INSTALL_PREFIX_ETC|.
Each of the daemons has its own config file. The daemon name plus ``.conf`` is
the default config file name. For example, zebra's default config file name is
:file:`zebra.conf`. You can specify a config file using the :option:`-f` or
-:option:`--config-file` options when starting the daemon.
+:option:`--config_file` options when starting the daemon.
.. _basic-config-commands:
@@ -261,27 +261,27 @@ Sample Config File
Below is a sample configuration file for the zebra daemon.
-::
+.. code-block:: frr
- !
- ! Zebra configuration file
- !
- hostname Router
- password zebra
- enable password zebra
- !
- log stdout
- !
- !
+ !
+ ! Zebra configuration file
+ !
+ hostname Router
+ password zebra
+ enable password zebra
+ !
+ log stdout
+ !
+ !
'!' and '#' are comment characters. If the first character of the word
is one of the comment characters then from the rest of the line forward
will be ignored as a comment.
-::
+.. code-block:: frr
- password zebra!password
+ password zebra!password
If a comment character is not the first character of the word, it's a
normal character. So in the above example '!' will not be regarded as a
@@ -358,13 +358,11 @@ Common Invocation Options
These options apply to all |PACKAGE_NAME| daemons.
-.. option:: -d
-.. option:: --daemon
+.. option:: -d, --daemon
Run in daemon mode.
-.. option:: -f <file>
-.. option:: --config-file <file>
+.. option:: -f, --config_file <file>
Set configuration file name.
@@ -372,8 +370,7 @@ These options apply to all |PACKAGE_NAME| daemons.
Display this help and exit.
-.. option:: -i <file>
-.. option:: --pid-file <file>
+.. option:: -i, --pid_file <file>
Upon startup the process identifier of the daemon is written to a file,
typically in :file:`/var/run`. This file can be used by the init system
@@ -386,25 +383,21 @@ These options apply to all |PACKAGE_NAME| daemons.
machine can be used to collect differing routing views from differing
points in the network.
-.. option:: -A <address>
-.. option:: --vty-addr <address>
+.. option:: -A, --vty_addr <address>
Set the VTY local address to bind to. If set, the VTY socket will only
be bound to this address.
-.. option:: -P <port>
-.. option:: --vty-port <port>
+.. option:: -P, --vty_port <port>
Set the VTY TCP port number. If set to 0 then the TCP VTY sockets will not
be opened.
.. option:: -u <user>
-.. option:: --vty_addr <user>
Set the user and group to run as.
-.. option:: -v
-.. option:: --version
+.. option:: -v, --version
Print program version.
@@ -418,8 +411,7 @@ unloading modules at runtime is not supported (yet). To load a module, use
the following command line option at daemon startup:
-.. option:: -M <module:options>
-.. option:: --module <module:options>
+.. option:: -M, --module <module:options>
Load the specified module, optionally passing options to it. If the module
name contains a slash (/), it is assumed to be a full pathname to a file to
@@ -474,32 +466,32 @@ is no VTY password, one cannot connect to the VTY interface at all.
::
- % telnet localhost 2601
- Trying 127.0.0.1...
- Connected to localhost.
- Escape character is '^]'.
+ % telnet localhost 2601
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
- Hello, this is |PACKAGE_NAME| (version |PACKAGE_VERSION|)
- |COPYRIGHT_STR|
+ Hello, this is |PACKAGE_NAME| (version |PACKAGE_VERSION|)
+ |COPYRIGHT_STR|
- User Access Verification
+ User Access Verification
- Password: XXXXX
- Router> ?
- enable . . . Turn on privileged commands
- exit . . . Exit current mode and down to previous mode
- help . . . Description of the interactive help system
- list . . . Print command list
- show . . . Show system inform
+ Password: XXXXX
+ Router> ?
+ enable . . . Turn on privileged commands
+ exit . . . Exit current mode and down to previous mode
+ help . . . Description of the interactive help system
+ list . . . Print command list
+ show . . . Show system inform
- wh. . . Display who is on a vty
- Router> enable
- Password: XXXXX
- Router# configure terminal
- Router(config)# interface eth0
- Router(config-if)# ip address 10.0.0.1/8
- Router(config-if)# ^Z
- Router#
+ wh. . . Display who is on a vty
+ Router> enable
+ Password: XXXXX
+ Router# configure terminal
+ Router(config)# interface eth0
+ Router(config-if)# ip address 10.0.0.1/8
+ Router(config-if)# ^Z
+ Router#
:kbd:`?` and the ``find`` command are very useful for looking up commands.
@@ -553,22 +545,22 @@ These commands are used for moving the CLI cursor. The :kbd:`C` character
means press the Control Key.
:kbd:`C-f` / :kbd:`LEFT`
- Move forward one character.
+ Move forward one character.
:kbd:`C-b` / :kbd:`RIGHT`
- Move backward one character.
+ Move backward one character.
:kbd:`M-f`
- Move forward one word.
+ Move forward one word.
:kbd:`M-b`
- Move backward one word.
+ Move backward one word.
:kbd:`C-a`
- Move to the beginning of the line.
+ Move to the beginning of the line.
:kbd:`C-e`
- Move to the end of the line.
+ Move to the end of the line.
.. _cli-editing-commands:
@@ -581,31 +573,31 @@ character means press the Control Key.
:kbd:`C-h` / :kbd:`DEL`
- Delete the character before point.
+ Delete the character before point.
:kbd:`C-d`
- Delete the character after point.
+ Delete the character after point.
:kbd:`M-d`
- Forward kill word.
+ Forward kill word.
:kbd:`C-w`
- Backward kill word.
+ Backward kill word.
:kbd:`C-k`
- Kill to the end of the line.
+ Kill to the end of the line.
:kbd:`C-u`
- Kill line from the beginning, erasing input.
+ Kill line from the beginning, erasing input.
:kbd:`C-t`
- Transpose character.
+ Transpose character.
CLI Advanced Commands
@@ -616,27 +608,27 @@ insta-help, and VTY session management.
:kbd:`C-c`
- Interrupt current input and moves to the next line.
+ Interrupt current input and moves to the next line.
:kbd:`C-z`
- End current configuration session and move to top node.
+ End current configuration session and move to top node.
:kbd:`C-n` / :kbd:`DOWN`
- Move down to next line in the history buffer.
+ Move down to next line in the history buffer.
:kbd:`C-p` / :kbd:`UP`
- Move up to previous line in the history buffer.
+ Move up to previous line in the history buffer.
:kbd:`TAB`
- Use command line completion by typing :kbd:`TAB`.
+ Use command line completion by typing :kbd:`TAB`.
:kbd:`?`
- You can use command line help by typing `help` at the beginning of
- the line. Typing :kbd:`?` at any point in the line will show possible
- completions.
+ You can use command line help by typing ``help`` at the beginning of the
+ line. Typing :kbd:`?` at any point in the line will show possible
+ completions.
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 6504e7d206..899977e830 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -26,18 +26,16 @@ be specified (:ref:`common-invocation-options`).
.. program:: bgpd
-.. option:: -p <port>
-.. option:: --bgp_port <port>
+.. option:: -p, --bgp_port <port>
- Set the bgp protocol's port number.
+ Set the bgp protocol's port number. When port number is 0, that means do not
+ listen bgp port.
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When program terminates, retain BGP routes added by zebra.
-.. option:: -l
-.. option:: --listenon
+.. option:: -l, --listenon
Specify a specific IP address for bgpd to listen on, rather than its
default of INADDR_ANY / IN6ADDR_ANY. This can be useful to constrain bgpd
@@ -472,12 +470,14 @@ BGP route
.. index:: network A.B.C.D/M
.. clicmd:: network A.B.C.D/M
- This command adds the announcement network.::
+ This command adds the announcement network.
- router bgp 1
- address-family ipv4 unicast
- network 10.0.0.0/8
- exit-address-family
+ .. code-block:: frr
+
+ router bgp 1
+ address-family ipv4 unicast
+ network 10.0.0.0/8
+ exit-address-family
This configuration example says that network 10.0.0.0/8 will be
announced to all neighbors. Some vendors' routers don't advertise
@@ -605,15 +605,17 @@ Defining Peer
.. clicmd:: neighbor PEER remote-as ASN
Creates a new neighbor whose remote-as is ASN. PEER can be an IPv4 address
- or an IPv6 address or an interface to use for the connection.::
+ or an IPv6 address or an interface to use for the connection.
- router bgp 1
- neighbor 10.0.0.1 remote-as 2
+ .. code-block:: frr
+
+ router bgp 1
+ neighbor 10.0.0.1 remote-as 2
In this case my router, in AS-1, is trying to peer with AS-2 at 10.0.0.1.
This command must be the first command used when configuring a neighbor. If
- the remote-as is not specified, *bgpd* will complain like this:::
+ the remote-as is not specified, *bgpd* will complain like this: ::
can't find neighbor 10.0.0.1
@@ -713,7 +715,9 @@ required.
Specify the IPv4 source address to use for the :abbr:`BGP` session to this
neighbour, may be specified as either an IPv4 address directly or as an
interface name (in which case the *zebra* daemon MUST be running in order
- for *bgpd* to be able to retrieve interface state).::
+ for *bgpd* to be able to retrieve interface state).
+
+ .. code-block:: frr
router bgp 64555
neighbor foo update-source 192.168.0.1
@@ -1189,7 +1193,10 @@ Following configuration is the most typical usage of BGP communities
attribute. AS 7675 provides upstream Internet connection to AS 100.
When following configuration exists in AS 7675, AS 100 networks
operator can set local preference in AS 7675 network by setting BGP
-communities attribute to the updates.::
+communities attribute to the updates.
+
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
@@ -1220,7 +1227,9 @@ communities attribute to the updates.::
Following configuration announce 10.0.0.0/8 from AS 100 to AS 7675.
The route has communities value 7675:80 so when above configuration
exists in AS 7675, announced route's local preference will be set to
-value 80.::
+value 80.
+
+.. code-block:: frr
router bgp 100
network 10.0.0.0/8
@@ -1240,7 +1249,9 @@ Following configuration is an example of BGP route filtering using
communities attribute. This configuration only permit BGP routes
which has BGP communities value 0:80 or 0:90. Network operator can
put special internal communities value at BGP border router, then
-limit the BGP routes announcement into the internal network.::
+limit the BGP routes announcement into the internal network.
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
@@ -1256,7 +1267,9 @@ limit the BGP routes announcement into the internal network.::
Following exmaple filter BGP routes which has communities value 1:1.
When there is no match community-list returns deny. To avoid
-filtering all of routes, we need to define permit any at last.::
+filtering all of routes, we need to define permit any at last.
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
@@ -1275,7 +1288,9 @@ Communities value keyword `internet` has special meanings in
standard community lists. In below example `internet` act as
match any. It matches all of BGP routes even if the route does not
have communities attribute at all. So community list ``INTERNET``
-is same as above example's ``FILTER``.::
+is same as above example's ``FILTER``.
+
+.. code-block:: frr
ip community-list standard INTERNET deny 1:1
ip community-list standard INTERNET permit internet
@@ -1284,7 +1299,9 @@ is same as above example's ``FILTER``.::
Following configuration is an example of communities value deletion.
With this configuration communities value 100:1 and 100:2 is removed
from BGP updates. For communities value deletion, only `permit`
-community-list is used. `deny` community-list is ignored.::
+community-list is used. `deny` community-list is ignored.
+
+.. code-block:: frr
router bgp 7675
neighbor 192.168.0.1 remote-as 100
@@ -1381,11 +1398,9 @@ Lists.
.. clicmd:: show ip extcommunity-list NAME
This command displays current extcommunity-list information. When `name` is
- specified the community list's information is shown.
-
-::
+ specified the community list's information is shown.::
- # show ip extcommunity-list
+ # show ip extcommunity-list
.. _bgp-extended-communities-in-route-map:
@@ -1606,14 +1621,17 @@ address-family:
Deletes any previously-configured import or export route-target list.
-.. index:: label vpn export (0..1048575)
-.. clicmd:: label vpn export (0..1048575)
+.. index:: label vpn export (0..1048575)|auto
+.. clicmd:: label vpn export (0..1048575)|auto
Specifies an optional MPLS label to be attached to a route exported from the
- current unicast VRF to VPN.
+ current unicast VRF to VPN. If label is specified as ``auto``, the label
+ value is automatically assigned from a pool maintained by the zebra
+ daemon. If zebra is not running, automatic label assignment will not
+ complete, which will block corresponding route export.
-.. index:: no label vpn export [(0..1048575)]
-.. clicmd:: no label vpn export [(0..1048575)]
+.. index:: no label vpn export [(0..1048575)|auto]
+.. clicmd:: no label vpn export [(0..1048575)|auto]
Deletes any previously-configured export label.
@@ -1932,7 +1950,9 @@ neighbor. If a user manually disables the feature, the community attribute is
not sent to the neighbor. When ``bgp config-type cisco`` is specified, the
community attribute is not sent to the neighbor by default. To send the
community attribute user has to specify *neighbor A.B.C.D send-community*
-command.::
+command.
+
+.. code-block:: frr
!
router bgp 1
@@ -1968,17 +1988,17 @@ multiple instance feature is enabled.
Make a new BGP instance. You can use an arbitrary word for the `name`.
- ::
+ .. code-block:: frr
- bgp multiple-instance
- !
- router bgp 1
- neighbor 10.0.0.1 remote-as 2
- neighbor 10.0.0.2 remote-as 3
- !
- router bgp 2
- neighbor 10.0.0.3 remote-as 4
- neighbor 10.0.0.4 remote-as 5
+ bgp multiple-instance
+ !
+ router bgp 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.2 remote-as 3
+ !
+ router bgp 2
+ neighbor 10.0.0.3 remote-as 4
+ neighbor 10.0.0.4 remote-as 5
BGP view is almost same as normal BGP process. The result of route selection
@@ -1993,7 +2013,7 @@ routing information.
With this command, you can setup Route Server like below.
- ::
+ .. code-block:: frr
bgp multiple-instance
!
@@ -2012,7 +2032,9 @@ Routing policy
--------------
You can set different routing policy for a peer. For example, you can set
-different filter for a peer.::
+different filter for a peer.
+
+.. code-block:: frr
bgp multiple-instance
!
@@ -2086,10 +2108,10 @@ _
How to set up a 6-Bone connection
=================================
-::
+.. code-block:: frr
- bgpd configuration
- ==================
+ ! bgpd configuration
+ ! ==================
!
! MP-BGP configuration
!
@@ -2173,7 +2195,9 @@ Dump BGP packets and table
BGP Configuration Examples
==========================
-Example of a session to an upstream, advertising only one prefix to it.::
+Example of a session to an upstream, advertising only one prefix to it.
+
+.. code-block:: frr
router bgp 64512
bgp router-id 10.236.87.1
@@ -2198,7 +2222,7 @@ feature to support selective advertising of prefixes. This example is intended
as guidance only, it has NOT been tested and almost certainly containts silly
mistakes, if not serious flaws.
-::
+.. code-block:: frr
router bgp 64512
bgp router-id 10.236.87.1
diff --git a/doc/user/conf.py b/doc/user/conf.py
index 7a77e492ce..886403b69d 100644
--- a/doc/user/conf.py
+++ b/doc/user/conf.py
@@ -15,6 +15,8 @@
import sys
import os
import re
+import pygments
+from sphinx.highlighting import lexers
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -51,7 +53,7 @@ master_doc = 'index'
# General information about the project.
project = u'FRR'
copyright = u'2017, FRR'
-author = u'FRR'
+author = u'FRR authors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -67,34 +69,40 @@ release = u'?.?-?'
# Extract values from codebase for substitution into docs.
# -----------------------------------------------------------------------------
-# Various installation prefixes. Reasonable defaults are set where possible.
-# Values are overridden by logic below.
+# Various installation prefixes. Values are extracted from config.status.
+# Reasonable defaults are set in case that file does not exist.
replace_vars = {
- 'AUTHORS': 'Kunihiro Ishiguro, et al.',
+ 'AUTHORS': author,
'COPYRIGHT_YEAR': '1999-2005',
- 'COPYRIGHT_STR': None,
+ 'COPYRIGHT_STR': 'Copyright (c) 1999-2005',
'PACKAGE_NAME': project.lower(),
'PACKAGE_TARNAME': project.lower(),
- 'PACKAGE_STRING': None,
+ 'PACKAGE_STRING': project.lower() + ' latest',
'PACKAGE_URL': 'https://frrouting.org/',
- 'PACKAGE_VERSION': None,
- 'INSTALL_PREFIX_ETC': None,
- 'INSTALL_PREFIX_SBIN': None,
- 'INSTALL_PREFIX_STATE': None,
- 'INSTALL_PREFIX_MODULES': None,
- 'INSTALL_USER': None,
- 'INSTALL_GROUP': None,
- 'INSTALL_VTY_GROUP': None,
+ 'PACKAGE_VERSION': 'latest',
+ 'INSTALL_PREFIX_ETC': '/etc/frr',
+ 'INSTALL_PREFIX_SBIN': '/usr/lib/frr',
+ 'INSTALL_PREFIX_STATE': '/var/run/frr',
+ 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules',
+ 'INSTALL_USER': 'frr',
+ 'INSTALL_GROUP': 'frr',
+ 'INSTALL_VTY_GROUP': 'frrvty',
+ 'GROUP': 'frr',
+ 'USER': 'frr',
}
# extract version information, installation location, other stuff we need to
# use when building final documents
val = re.compile('^S\["([^"]+)"\]="(.*)"$')
-with open('../../config.status', 'r') as cfgstatus:
- for ln in cfgstatus.readlines():
- m = val.match(ln)
- if not m or m.group(1) not in replace_vars.keys(): continue
- replace_vars[m.group(1)] = m.group(2)
+try:
+ with open('../../config.status', 'r') as cfgstatus:
+ for ln in cfgstatus.readlines():
+ m = val.match(ln)
+ if not m or m.group(1) not in replace_vars.keys(): continue
+ replace_vars[m.group(1)] = m.group(2)
+except IOError:
+ # if config.status doesn't exist, just ignore it
+ pass
# manually fill out some of these we can't get from config.status
replace_vars['COPYRIGHT_STR'] = "Copyright (c)"
@@ -341,3 +349,13 @@ def setup(app):
app.add_object_type('clicmd', 'clicmd')
# css overrides for HTML theme
app.add_stylesheet('overrides.css')
+ # load Pygments lexer for FRR config syntax
+ #
+ # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we
+ # do it manually since not all of our supported build platforms have 2.2
+ # yet.
+ #
+ # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer")
+ custom_namespace = {}
+ exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace)
+ lexers['frr'] = custom_namespace['FRRLexer']()
diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst
index 309ee27cc4..c626faf4e2 100644
--- a/doc/user/eigrpd.rst
+++ b/doc/user/eigrpd.rst
@@ -60,8 +60,7 @@ Certain signals have special meanings to *eigrpd*.
.. program:: eigrpd
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When the program terminates, retain routes added by *eigrpd*.
@@ -100,7 +99,7 @@ EIGRP Configuration
Below is very simple EIGRP configuration. Interface `eth0` and
interface which address match to `10.0.0.0/8` are EIGRP enabled.
- ::
+ .. code-block:: frr
!
router eigrp 1
diff --git a/doc/user/filter.rst b/doc/user/filter.rst
index 57f9fbe426..9d7361443d 100644
--- a/doc/user/filter.rst
+++ b/doc/user/filter.rst
@@ -18,7 +18,7 @@ IP Access List
Basic filtering is done by `access-list` as shown in the
following example.
- ::
+ .. code-block:: frr
access-list filter deny 10.0.0.0/9
access-list filter permit 10.0.0.0/8
diff --git a/doc/user/index.rst b/doc/user/index.rst
index e505042994..c264b4c83e 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -22,6 +22,7 @@ Welcome to FRR's documentation!
ospfd
ospf6d
pim
+ pbr
ripd
ripngd
vnc
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index b6989809a7..54f82f6832 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -559,7 +559,9 @@ Debugging ISIS
ISIS Configuration Examples
===========================
-A simple example, with MD5 authentication enabled:::
+A simple example, with MD5 authentication enabled:
+
+.. code-block:: frr
!
interface eth0
@@ -575,7 +577,9 @@ A simple example, with MD5 authentication enabled:::
A Traffic Engineering configuration, with Inter-ASv2 support.
-First, the 'zebra.conf' part:::
+First, the :file:`zebra.conf` part:
+
+.. code-block:: frr
hostname HOSTNAME
password PASSWORD
@@ -614,7 +618,9 @@ First, the 'zebra.conf' part:::
neighbor 10.1.1.2 as 65000
-Then the 'isisd.conf' itself:::
+Then the :file:`isisd.conf` itself:
+
+.. code-block:: frr
hostname HOSTNAME
password PASSWORD
diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst
index 28e78f66fb..33cdbd9591 100644
--- a/doc/user/nhrpd.rst
+++ b/doc/user/nhrpd.rst
@@ -52,7 +52,9 @@ hub nodes, these routes should be internally redistributed using some
routing protocol (e.g. iBGP) to allow hubs to be able to relay all traffic.
This can be achieved in hubs with the following bgp configuration (network
-command defines the GRE subnet):::
+command defines the GRE subnet):
+
+.. code-block:: frr
router bgp 65555
address-family ipv4 unicast
@@ -82,12 +84,12 @@ using NFLOG. Typically you want to send Traffic Indications for network
traffic that is routed from gre1 back to gre1 in rate limited manner.
This can be achieved with the following iptables rule.
-::
+.. code-block:: shell
- iptables -A FORWARD -i gre1 -o gre1 \\
- -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\
- --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\
- --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
+ iptables -A FORWARD -i gre1 -o gre1 \\
+ -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\
+ --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\
+ --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
You can fine tune the src/dstmask according to the prefix lengths you
@@ -95,15 +97,20 @@ announce internal, add additional IP range matches, or rate limitation
if needed. However, the above should be good in most cases.
This kernel NFLOG target's nflog-group is configured in global nhrp config
-with:::
+with:
+
+.. code-block:: frr
- nhrp nflog-group 1
+ nhrp nflog-group 1
To start sending these traffic notices out from hubs, use the nhrp
-per-interface directive:::
+per-interface directive:
+
+.. code-block:: frr
+
+ interface gre1
+ ip nhrp redirect
- interface gre1
- ip nhrp redirect
.. _integration-with-ike:
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index b0823f21e7..3c84135405 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -49,7 +49,9 @@ OSPF6 router
will cause the holdtime to be increased by `initial-holdtime`, bounded
by the `maximum-holdtime` configured with this command. If the adaptive
hold-time elapses without any SPF-triggering event occuring then
- the current holdtime is reset to the `initial-holdtime`.::
+ the current holdtime is reset to the `initial-holdtime`.
+
+ .. code-block:: frr
router ospf6
timers throttle spf 200 400 10000
@@ -187,7 +189,7 @@ OSPF6 Configuration Examples
Example of ospf6d configured on one interface and area:
-::
+.. code-block:: frr
interface eth0
ipv6 ospf6 instance-id 0
diff --git a/doc/user/ospf_fundamentals.rst b/doc/user/ospf_fundamentals.rst
index 5a4f7095ed..c35df85ddf 100644
--- a/doc/user/ospf_fundamentals.rst
+++ b/doc/user/ospf_fundamentals.rst
@@ -336,61 +336,61 @@ are fully adjacent with 192.168.0.49.
::
- # show ip ospf database router 192.168.0.49
-
- OSPF Router with ID (192.168.0.53)
-
- Router Link States (Area 0.0.0.0)
-
- LS age: 38
- Options: 0x2 : *|-|-|-|-|-|E|*
- LS Flags: 0x6
- Flags: 0x2 : ASBR
- LS Type: router-LSA
- Link State ID: 192.168.0.49
- Advertising Router: 192.168.0.49
- LS Seq Number: 80000f90
- Checksum: 0x518b
- Length: 60
- Number of Links: 3
-
- Link connected to: a Transit Network
- (Link ID) Designated Router address: 192.168.1.3
- (Link Data) Router Interface address: 192.168.1.3
- Number of TOS metrics: 0
- TOS 0 Metric: 10
-
- Link connected to: a Transit Network
- (Link ID) Designated Router address: 192.168.0.49
- (Link Data) Router Interface address: 192.168.0.49
- Number of TOS metrics: 0
- TOS 0 Metric: 10
-
- Link connected to: Stub Network
- (Link ID) Net: 192.168.3.190
- (Link Data) Network Mask: 255.255.255.255
- Number of TOS metrics: 0
- TOS 0 Metric: 39063
- # show ip ospf database network 192.168.0.49
-
- OSPF Router with ID (192.168.0.53)
-
- Net Link States (Area 0.0.0.0)
-
- LS age: 285
- Options: 0x2 : *|-|-|-|-|-|E|*
- LS Flags: 0x6
- LS Type: network-LSA
- Link State ID: 192.168.0.49 (address of Designated Router)
- Advertising Router: 192.168.0.49
- LS Seq Number: 80000074
- Checksum: 0x0103
- Length: 40
- Network Mask: /29
- Attached Router: 192.168.0.49
- Attached Router: 192.168.0.52
- Attached Router: 192.168.0.53
- Attached Router: 192.168.0.54
+ # show ip ospf database router 192.168.0.49
+
+ OSPF Router with ID (192.168.0.53)
+
+ Router Link States (Area 0.0.0.0)
+
+ LS age: 38
+ Options: 0x2 : *|-|-|-|-|-|E|*
+ LS Flags: 0x6
+ Flags: 0x2 : ASBR
+ LS Type: router-LSA
+ Link State ID: 192.168.0.49
+ Advertising Router: 192.168.0.49
+ LS Seq Number: 80000f90
+ Checksum: 0x518b
+ Length: 60
+ Number of Links: 3
+
+ Link connected to: a Transit Network
+ (Link ID) Designated Router address: 192.168.1.3
+ (Link Data) Router Interface address: 192.168.1.3
+ Number of TOS metrics: 0
+ TOS 0 Metric: 10
+
+ Link connected to: a Transit Network
+ (Link ID) Designated Router address: 192.168.0.49
+ (Link Data) Router Interface address: 192.168.0.49
+ Number of TOS metrics: 0
+ TOS 0 Metric: 10
+
+ Link connected to: Stub Network
+ (Link ID) Net: 192.168.3.190
+ (Link Data) Network Mask: 255.255.255.255
+ Number of TOS metrics: 0
+ TOS 0 Metric: 39063
+ # show ip ospf database network 192.168.0.49
+
+ OSPF Router with ID (192.168.0.53)
+
+ Net Link States (Area 0.0.0.0)
+
+ LS age: 285
+ Options: 0x2 : *|-|-|-|-|-|E|*
+ LS Flags: 0x6
+ LS Type: network-LSA
+ Link State ID: 192.168.0.49 (address of Designated Router)
+ Advertising Router: 192.168.0.49
+ LS Seq Number: 80000074
+ Checksum: 0x0103
+ Length: 40
+ Network Mask: /29
+ Attached Router: 192.168.0.49
+ Attached Router: 192.168.0.52
+ Attached Router: 192.168.0.53
+ Attached Router: 192.168.0.54
Note that from one LSA, you can find the other. E.g. Given the
@@ -412,26 +412,26 @@ following partial topology:
::
- ------------------------ Network: ......
- | Designated Router IP: 192.168.1.3
- |
- IP: 192.168.1.3
- (transit link)
- (cost: 10)
- Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32
- (cost: 10) (cost: 39063)
- (transit link)
- IP: 192.168.0.49
- |
- |
- ------------------------------ Network: 192.168.0.48/29
- | | | Designated Router IP: 192.168.0.49
- | | |
- | | Router ID: 192.168.0.54
- | |
- | Router ID: 192.168.0.53
- |
- Router ID: 192.168.0.52
+ ------------------------ Network: ......
+ | Designated Router IP: 192.168.1.3
+ |
+ IP: 192.168.1.3
+ (transit link)
+ (cost: 10)
+ Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32
+ (cost: 10) (cost: 39063)
+ (transit link)
+ IP: 192.168.0.49
+ |
+ |
+ ------------------------------ Network: 192.168.0.48/29
+ | | | Designated Router IP: 192.168.0.49
+ | | |
+ | | Router ID: 192.168.0.54
+ | |
+ | Router ID: 192.168.0.53
+ |
+ Router ID: 192.168.0.52
Note the Router IDs, though they look like IP addresses and often are
@@ -495,22 +495,22 @@ should forward to the originating ASBR if selected.
::
- # show ip ospf database external 192.168.165.0
- LS age: 995
- Options: 0x2 : *|-|-|-|-|-|E|*
- LS Flags: 0x9
- LS Type: AS-external-LSA
- Link State ID: 192.168.165.0 (External Network Number)
- Advertising Router: 192.168.0.49
- LS Seq Number: 800001d8
- Checksum: 0xea27
- Length: 36
- Network Mask: /24
- Metric Type: 2 (Larger than any link state path)
- TOS: 0
- Metric: 20
- Forward Address: 0.0.0.0
- External Route Tag: 0
+ # show ip ospf database external 192.168.165.0
+ LS age: 995
+ Options: 0x2 : *|-|-|-|-|-|E|*
+ LS Flags: 0x9
+ LS Type: AS-external-LSA
+ Link State ID: 192.168.165.0 (External Network Number)
+ Advertising Router: 192.168.0.49
+ LS Seq Number: 800001d8
+ Checksum: 0xea27
+ Length: 36
+ Network Mask: /24
+ Metric Type: 2 (Larger than any link state path)
+ TOS: 0
+ Metric: 20
+ Forward Address: 0.0.0.0
+ External Route Tag: 0
We can add this to our partial topology from above, which now looks
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 59917f5262..f1b77ffe09 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -163,7 +163,7 @@ writing, *ospfd* does not support multiple OSPF processes.
holdtime can be viewed with :clicmd:`show ip ospf`, where it is expressed as
a multiplier of the `initial-holdtime`.
- ::
+ .. code-block:: frr
router ospf
timers throttle spf 200 400 10000
@@ -249,11 +249,10 @@ writing, *ospfd* does not support multiple OSPF processes.
on this interface so router can provide network information to the other
ospf routers via this interface.
-::
-
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
+ .. code-block:: frr
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
Prefix length in interface must be equal or bigger (ie. smaller network) than
prefix length in network statement. For example statement above doesn't enable
@@ -288,23 +287,23 @@ OSPF area
.. index:: no area (0-4294967295) range A.B.C.D/M
.. clicmd:: no area (0-4294967295) range A.B.C.D/M
- Summarize intra area paths from specified area into one Type-3 summary-LSA
- announced to other areas. This command can be used only in ABR and ONLY
- router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can
- be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
- Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR.
+ Summarize intra area paths from specified area into one Type-3 summary-LSA
+ announced to other areas. This command can be used only in ABR and ONLY
+ router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can
+ be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
+ Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR.
-::
+ .. code-block:: frr
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
- network 10.0.0.0/8 area 0.0.0.10
- area 0.0.0.10 range 10.0.0.0/8
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8
- With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is
- announced into backbone area if area 0.0.0.10 contains at least one intra-area
- network (ie. described with router or network LSA) from this range.
+ With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is
+ announced into backbone area if area 0.0.0.10 contains at least one intra-area
+ network (ie. described with router or network LSA) from this range.
.. index:: area A.B.C.D range IPV4_PREFIX not-advertise
.. clicmd:: area A.B.C.D range IPV4_PREFIX not-advertise
@@ -324,12 +323,12 @@ OSPF area
Substitute summarized prefix with another prefix.
-::
+ .. code-block:: frr
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
- network 10.0.0.0/8 area 0.0.0.10
- area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8
One Type-3 summary-LSA with routing info 11.0.0.0/8 is announced into backbone area if
@@ -421,16 +420,15 @@ OSPF area
Filter Type-3 summary-LSAs announced to other areas originated from intra-
area paths from specified area.
-::
-
- router ospf
- network 192.168.1.0/24 area 0.0.0.0
- network 10.0.0.0/8 area 0.0.0.10
- area 0.0.0.10 export-list foo
- !
- access-list foo permit 10.10.0.0/16
- access-list foo deny any
+ .. code-block:: frr
+ router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 export-list foo
+ !
+ access-list foo permit 10.10.0.0/16
+ access-list foo deny any
With example above any intra-area paths from area 0.0.0.10 and from range
10.10.0.0/16 (for example 10.10.1.0/24 and 10.10.2.128/30) are announced into
@@ -452,8 +450,8 @@ OSPF area
.. index:: no area (0-4294967295) import-list NAME
.. clicmd:: no area (0-4294967295) import-list NAME
- Same as export-list, but it applies to paths announced into specified area as
- Type-3 summary-LSAs.
+ Same as export-list, but it applies to paths announced into specified area
+ as Type-3 summary-LSAs.
.. index:: area A.B.C.D filter-list prefix NAME in
.. clicmd:: area A.B.C.D filter-list prefix NAME in
@@ -479,8 +477,8 @@ OSPF area
.. index:: no area (0-4294967295) filter-list prefix NAME out
.. clicmd:: no area (0-4294967295) filter-list prefix NAME out
- Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
- makes sense in ABR only.
+ Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
+ makes sense in ABR only.
.. index:: area A.B.C.D authentication
.. clicmd:: area A.B.C.D authentication
@@ -494,8 +492,8 @@ OSPF area
.. index:: no area (0-4294967295) authentication
.. clicmd:: no area (0-4294967295) authentication
- Specify that simple password authentication should be used for the given
- area.
+ Specify that simple password authentication should be used for the given
+ area.
.. index:: area A.B.C.D authentication message-digest
.. clicmd:: area A.B.C.D authentication message-digest
@@ -568,12 +566,11 @@ OSPF interface
Set OSPF authentication key to a cryptographic password. The cryptographic
algorithm is MD5.
- KEYID identifies secret key used to create the message digest. This ID
- is part of the protocol and must be consistent across routers on a
- link.
+ KEYID identifies secret key used to create the message digest. This ID is
+ part of the protocol and must be consistent across routers on a link.
- KEY is the actual message digest key, of up to 16 chars (larger strings
- will be truncated), and is associated with the given KEYID.
+ KEY is the actual message digest key, of up to 16 chars (larger strings will
+ be truncated), and is associated with the given KEYID.
.. index:: ip ospf cost (1-65535)
.. clicmd:: ip ospf cost (1-65535)
@@ -581,8 +578,8 @@ OSPF interface
.. index:: no ip ospf cost
.. clicmd:: no ip ospf cost
- Set link cost for the specified interface. The cost value is set to router-LSA's
- metric field and used for SPF calculation.
+ Set link cost for the specified interface. The cost value is set to
+ router-LSA's metric field and used for SPF calculation.
.. index:: ip ospf dead-interval (1-65535)
.. clicmd:: ip ospf dead-interval (1-65535)
@@ -635,10 +632,9 @@ OSPF interface
.. index:: no ip ospf priority
.. clicmd:: no ip ospf priority
- Set RouterPriority integer value. The router with the highest priority
- will be more eligible to become Designated Router. Setting the value
- to 0, makes the router ineligible to become Designated Router. The
- default value is 1.
+ Set RouterPriority integer value. The router with the highest priority will
+ be more eligible to become Designated Router. Setting the value to 0, makes
+ the router ineligible to become Designated Router. The default value is 1.
.. index:: ip ospf retransmit-interval (1-65535)
.. clicmd:: ip ospf retransmit-interval (1-65535)
@@ -646,9 +642,9 @@ OSPF interface
.. index:: no ip ospf retransmit interval
.. clicmd:: no ip ospf retransmit interval
- Set number of seconds for RxmtInterval timer value. This value is used
- when retransmitting Database Description and Link State Request packets.
- The default value is 5 seconds.
+ Set number of seconds for RxmtInterval timer value. This value is used when
+ retransmitting Database Description and Link State Request packets. The
+ default value is 5 seconds.
.. index:: ip ospf transmit-delay
.. clicmd:: ip ospf transmit-delay
@@ -657,8 +653,7 @@ OSPF interface
.. clicmd:: no ip ospf transmit-delay
Set number of seconds for InfTransDelay value. LSAs' age should be
- incremented by this value when transmitting.
- The default value is 1 seconds.
+ incremented by this value when transmitting. The default value is 1 second.
.. index:: ip ospf area (A.B.C.D|(0-4294967295))
.. clicmd:: ip ospf area (A.B.C.D|(0-4294967295))
@@ -666,7 +661,7 @@ OSPF interface
.. index:: no ip ospf area
.. clicmd:: no ip ospf area
- Enable ospf on an interface and set associated area.
+ Enable ospf on an interface and set associated area.
.. _redistribute-routes-to-ospf:
@@ -702,16 +697,16 @@ Redistribute routes to OSPF
.. _ospf-redistribute:
- Redistribute routes of the specified protocol
- or kind into OSPF, with the metric type and metric set if specified,
- filtering the routes using the given route-map if specified.
- Redistributed routes may also be filtered with distribute-lists, see
+ Redistribute routes of the specified protocol or kind into OSPF, with the
+ metric type and metric set if specified, filtering the routes using the
+ given route-map if specified. Redistributed routes may also be filtered
+ with distribute-lists, see
:ref:`ospf distribute-list configuration <ospf-distribute-list>`.
- Redistributed routes are distributed as into OSPF as Type-5 External
- LSAs into links to areas that accept external routes, Type-7 External LSAs
- for NSSA areas and are not redistributed at all into Stub areas, where
- external routes are not permitted.
+ Redistributed routes are distributed as into OSPF as Type-5 External LSAs
+ into links to areas that accept external routes, Type-7 External LSAs for
+ NSSA areas and are not redistributed at all into Stub areas, where external
+ routes are not permitted.
Note that for connected routes, one may instead use the `passive-interface`
configuration.
@@ -747,10 +742,10 @@ Redistribute routes to OSPF
.. index:: no default-information originate
.. clicmd:: no default-information originate
- Originate an AS-External (type-5) LSA describing a default route into
- all external-routing capable areas, of the specified metric and metric
- type. If the 'always' keyword is given then the default is always
- advertised, even when there is no default present in the routing table.
+ Originate an AS-External (type-5) LSA describing a default route into all
+ external-routing capable areas, of the specified metric and metric type. If
+ the 'always' keyword is given then the default is always advertised, even
+ when there is no default present in the routing table.
.. index:: distribute-list NAME out (kernel|connected|static|rip|ospf
.. clicmd:: distribute-list NAME out (kernel|connected|static|rip|ospf
@@ -760,9 +755,9 @@ Redistribute routes to OSPF
.. _ospf-distribute-list:
- Apply the access-list filter, NAME, to
- redistributed routes of the given type before allowing the routes to
- redistributed into OSPF (:ref:`ospf redistribution <ospf-redistribute>`).
+ Apply the access-list filter, NAME, to redistributed routes of the given
+ type before allowing the routes to redistributed into OSPF
+ (:ref:`ospf redistribution <ospf-redistribute>`).
.. index:: default-metric (0-16777214)
.. clicmd:: default-metric (0-16777214)
@@ -850,7 +845,8 @@ Showing OSPF information
.. index:: show ip ospf route
.. clicmd:: show ip ospf route
- Show the OSPF routing table, as determined by the most recent SPF calculation.
+ Show the OSPF routing table, as determined by the most recent SPF
+ calculation.
.. _opaque-lsa:
@@ -869,9 +865,9 @@ Opaque LSA
.. index:: no capability opaque
.. clicmd:: no capability opaque
- *ospfd* support Opaque LSA (RFC2370) as fondment for MPLS Traffic Engineering
- LSA. Prior to used MPLS TE, opaque-lsa must be enable in the configuration
- file. Alternate command could be "mpls-te on"
+ *ospfd* support Opaque LSA (:rfc:`2370`) as fondment for MPLS Traffic
+ Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the
+ configuration file. Alternate command could be "mpls-te on"
(:ref:`ospf-traffic-engineering`).
.. index:: show ip ospf database (opaque-link|opaque-area|opaque-external)
@@ -981,18 +977,19 @@ Router Information
.. index:: no pce scope
.. clicmd:: no pce scope
- The commands are conform to :rfc:`5088` and allow OSPF router announce Path
- Compuatation Elemenent (PCE) capabilities through the Router Information (RI)
- LSA. Router Information must be enable prior to this. The command set/unset
- respectively the PCE IP adress, Autonomous System (AS) numbers of controlled
- domains, neighbor ASs, flag and scope. For flag and scope, please refer to
- :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor' command
- could be specified in order to specify all PCE neighbours.
+ The commands are conform to :rfc:`5088` and allow OSPF router announce Path
+ Compuatation Elemenent (PCE) capabilities through the Router Information
+ (RI) LSA. Router Information must be enable prior to this. The command
+ set/unset respectively the PCE IP adress, Autonomous System (AS) numbers of
+ controlled domains, neighbor ASs, flag and scope. For flag and scope, please
+ refer to :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor'
+ command could be specified in order to specify all PCE neighbours.
.. index:: show ip ospf router-info
.. clicmd:: show ip ospf router-info
Show Router Capabilities flag.
+
.. index:: show ip ospf router-info pce
.. clicmd:: show ip ospf router-info pce
@@ -1028,10 +1025,10 @@ This is an EXPERIMENTAL support of Segment Routing as per draft
.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]
.. clicmd:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]
- Set the Segment Rounting index for the specifyed prefix. Note
- that, only prefix with /32 corresponding to a loopback interface are
- currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that
- allows SR node to request to its neighbor to not pop the label.
+ Set the Segment Rounting index for the specifyed prefix. Note that, only
+ prefix with /32 corresponding to a loopback interface are currently
+ supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR
+ node to request to its neighbor to not pop the label.
.. index:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json]
.. clicmd:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json]
@@ -1140,7 +1137,7 @@ OSPF Configuration Examples
A simple example, with MD5 authentication enabled:
-::
+.. code-block:: frr
!
interface bge0
@@ -1155,7 +1152,7 @@ A simple example, with MD5 authentication enabled:
An :abbr:`ABR` router, with MD5 authentication and performing summarisation
of networks between the areas:
-::
+.. code-block:: frr
!
password ABCDEF
@@ -1189,7 +1186,9 @@ of networks between the areas:
A Traffic Engineering configuration, with Inter-ASv2 support.
-First, the 'zebra.conf' part:::
+First, the :file:`zebra.conf` part:
+
+.. code-block:: frr
interface eth0
ip address 198.168.1.1/24
@@ -1262,7 +1261,9 @@ First, the 'zebra.conf' part:::
unrsv-bw 7 1.25e+06
neighbor 192.168.2.2 as 65000
-Then the 'ospfd.conf' itself:::
+Then the :file:`ospfd.conf` itself:
+
+.. code-block:: frr
hostname HOSTNAME
password PASSWORD
@@ -1288,8 +1289,9 @@ Then the 'ospfd.conf' itself:::
!
line vty
+A router information example with PCE advsertisement:
-A router information example with PCE advsertisement:::
+.. code-block:: frr
!
router ospf
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index 064dc436ea..38d55d68ad 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -95,17 +95,17 @@ architecture creates new possibilities for the routing system.
::
- +----+ +----+ +-----+ +-----+
- |bgpd| |ripd| |ospfd| |zebra|
- +----+ +----+ +-----+ +-----+
- |
- +---------------------------|--+
- | v |
- | UNIX Kernel routing table |
- | |
- +------------------------------+
-
- FRR System Architecture
+ +----+ +----+ +-----+ +-----+
+ |bgpd| |ripd| |ospfd| |zebra|
+ +----+ +----+ +-----+ +-----+
+ |
+ +---------------------------|--+
+ | v |
+ | UNIX Kernel routing table |
+ | |
+ +------------------------------+
+
+ FRR System Architecture
Multi-process architecture brings extensibility, modularity and
diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst
new file mode 100644
index 0000000000..eec575cf3f
--- /dev/null
+++ b/doc/user/pbr.rst
@@ -0,0 +1,123 @@
+.. _pbr:
+
+***
+PBR
+***
+
+:abbr:`PBR` is Policy Based Routing. This implementation supports a very simple
+interface to allow admins to influence routing on their router. At this time
+you can only match on destination and source prefixes for an incoming interface.
+At this point in time, this implementation will only work on Linux.
+
+.. _starting-pbr:
+
+Starting PBR
+============
+
+Default configuration file for *pbrd* is :file:`pbrd.conf`. The typical
+location of :file:`pbrd.conf` is |INSTALL_PREFIX_ETC|/pbrd.conf.
+
+If the user is using integrated config, then :file:`pbrd.conf` need not be
+present and the :file:`frr.conf` is read instead.
+
+.. program:: pbrd
+
+:abbr:`PBR` supports all the common FRR daemon start options which are
+documented elsewhere.
+
+.. _nexthop-groups:
+
+Nexthop Groups
+==============
+
+Nexthop groups are a way to encapsulate ECMP information together. It's a
+listing of ECMP nexthops used to forward packets for when a pbr-map is matched.
+
+.. index:: nexthop-group
+.. clicmd:: nexthop-group NAME
+
+ Create a nexthop-group with an associated NAME. This will put you into a
+ sub-mode where you can specify individual nexthops. To exit this mode type
+ exit or end as per normal conventions for leaving a sub-mode.
+
+.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME]
+
+ Create a v4 or v6 nexthop. All normal rules for creating nexthops that you
+ are used to are allowed here. The syntax was intentionally kept the same as
+ creating nexthops as you would for static routes.
+
+.. _pbr-maps:
+
+PBR Maps
+========
+
+PBR maps are a way to group policies that we would like to apply
+to individual interfaces. These policies when applied are matched
+against incoming packets. If matched the nexthop-group or nexthop
+is used to forward the packets to the end destination
+
+.. index:: pbr-map
+.. clicmd:: pbr-map NAME seq (1-1000)
+
+ Create a pbr-map with NAME and sequence number specified. This command puts
+ you into a new submode for pbr-map specification. To exit this mode type
+ exit or end as per normal conventions for leaving a sub-mode.
+
+.. index:: match
+.. clicmd:: match src-ip PREFIX
+
+ When a incoming packet matches the source 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 conjunction of the
+ :clicmd:`match dst-ip PREFIX` command for matching.
+
+.. clicmd:: match dst-ip PREFIX
+
+ 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
+ :clicmd:`match src-ip PREFIX` command for matching.
+
+.. clicmd:: set nexthop-group NAME
+
+ Use the nexthop-group NAME as the place to forward packets when the match
+ commands have matched a packet.
+
+.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME]
+
+ Use this individual nexthop as the place to forward packets when the match
+ commands have matched a packet.
+
+.. _pbr-policy:
+
+PBR Policy
+==========
+
+After you have specified a PBR map, in order for it to be turned on, you must
+apply the PBR map to an interface. This policy application to an interface
+causes the policy to be installed into the kernel.
+
+.. index:: pbr-policy
+.. clicmd:: pbr-policy NAME
+
+ This command is available under interface sub-mode. This turns
+ on the PBR map NAME and allows it to work properly.
+
+.. _pbr-details:
+
+PBR Details
+===========
+
+Under the covers a PBR map is translated into two separate constructs in the
+Linux kernel.
+
+.. index:: PBR Rules
+
+The PBR map specified creates a `ip rule ...` that is inserted into the Linux
+kernel that points to a table to use for forwarding once the rule matches.
+
+.. index:: PBR Tables
+
+The creation of a nexthop or nexthop-group is translated to a default route in a
+table with the nexthops specified as the nexthops for the default route.
+
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index b9945680cf..2dda88a6d1 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -214,8 +214,8 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
.. _pim-multicast-rib-insertion:
-PIM Multicast RIB insertion::
-=============================
+PIM Multicast RIB insertion:
+============================
In order to influence Multicast RPF lookup, it is possible to insert
into zebra routes for the Multicast RIB. These routes are only
diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst
index b8cbd161c6..d2686d2eae 100644
--- a/doc/user/ripd.rst
+++ b/doc/user/ripd.rst
@@ -60,8 +60,7 @@ Certain signals have special meaningss to *ripd*.
*ripd* invocation options. Common options that can be specified
(:ref:`common-invocation-options`).
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When the program terminates, retain routes added by *ripd*.
@@ -147,7 +146,7 @@ RIP Configuration
Below is very simple RIP configuration. Interface `eth0` and interface which
address match to `10.0.0.0/8` are RIP enabled.
- ::
+ .. code-block:: frr
!
router rip
@@ -355,7 +354,7 @@ RIP routes can be filtered by a distribute-list.
the distribute-list command. For example, in the following configuration
``eth0`` will permit only the paths that match the route 10.0.0.0/8
- ::
+ .. code-block:: frr
!
router rip
@@ -447,11 +446,11 @@ Usage of *ripd*'s route-map support.
Optional argument route-map MAP_NAME can be added to each `redistribute`
statement.
-::
+.. code-block:: frr
- redistribute static [route-map MAP_NAME]
- redistribute connected [route-map MAP_NAME]
- .....
+ redistribute static [route-map MAP_NAME]
+ redistribute connected [route-map MAP_NAME]
+ .....
Cisco applies route-map _before_ routes will exported to rip route table. In
@@ -573,17 +572,17 @@ To prevent such unauthenticated querying of routes disable RIPv1,
Specifiy Keyed MD5 chain.
-::
+ .. code-block:: frr
- !
- key chain test
- key 1
- key-string test
- !
- interface eth1
- ip rip authentication mode md5
- ip rip authentication key-chain test
- !
+ !
+ key chain test
+ key 1
+ key-string test
+ !
+ interface eth1
+ ip rip authentication mode md5
+ ip rip authentication key-chain test
+ !
.. _rip-timers:
diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst
index 97094c2d6b..a0f28b5fc8 100644
--- a/doc/user/routemap.rst
+++ b/doc/user/routemap.rst
@@ -302,11 +302,11 @@ Route Map Examples
A simple example of a route-map:
-::
+.. code-block:: frr
- route-map test permit 10
- match ip address 10
- set local-preference 200
+ route-map test permit 10
+ match ip address 10
+ set local-preference 200
This means that if a route matches ip access-list number 10 it's
diff --git a/doc/user/routeserver.rst b/doc/user/routeserver.rst
index 890897bb62..f2c2c6d33e 100644
--- a/doc/user/routeserver.rst
+++ b/doc/user/routeserver.rst
@@ -246,7 +246,7 @@ against the other two routers. These peerings have In and Out route-maps
configured, named like 'PEER-X-IN' or 'PEER-X-OUT'. For example the
configuration file for router RA could be the following:
-::
+.. code-block:: frr
#Configuration for router 'RA'
!
@@ -319,29 +319,29 @@ modify the configuration of routers RA, RB and RC. Now they must not peer
between them, but only with the route server. For example, RA's
configuration would turn into:
-::
+.. code-block:: frr
+
+ # Configuration for router 'RA'
+ !
+ hostname RA
+ password ****
+ !
+ router bgp 65001
+ no bgp default ipv4-unicast
+ neighbor 2001:0DB8::FFFF remote-as 65000
+ !
+ address-family ipv6
+ network 2001:0DB8:AAAA:1::/64
+ network 2001:0DB8:AAAA:2::/64
+ network 2001:0DB8:0000:1::/64
+ network 2001:0DB8:0000:2::/64
- # Configuration for router 'RA'
- !
- hostname RA
- password ****
- !
- router bgp 65001
- no bgp default ipv4-unicast
- neighbor 2001:0DB8::FFFF remote-as 65000
- !
- address-family ipv6
- network 2001:0DB8:AAAA:1::/64
- network 2001:0DB8:AAAA:2::/64
- network 2001:0DB8:0000:1::/64
- network 2001:0DB8:0000:2::/64
-
- neighbor 2001:0DB8::FFFF activate
- neighbor 2001:0DB8::FFFF soft-reconfiguration inbound
- exit-address-family
- !
- line vty
- !
+ neighbor 2001:0DB8::FFFF activate
+ neighbor 2001:0DB8::FFFF soft-reconfiguration inbound
+ exit-address-family
+ !
+ line vty
+ !
Which is logically much simpler than its initial configuration, as it now
@@ -362,84 +362,84 @@ server.
This is a fragment of the route server configuration (we only show
the policies for client RA):
-::
+.. code-block:: frr
- # Configuration for Route Server ('RS')
- !
- hostname RS
- password ix
- !
- bgp multiple-instance
- !
- router bgp 65000 view RS
- no bgp default ipv4-unicast
- neighbor 2001:0DB8::A remote-as 65001
- neighbor 2001:0DB8::B remote-as 65002
- neighbor 2001:0DB8::C remote-as 65003
- !
- address-family ipv6
- neighbor 2001:0DB8::A activate
- neighbor 2001:0DB8::A route-server-client
- neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
- neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export
- neighbor 2001:0DB8::A soft-reconfiguration inbound
-
- neighbor 2001:0DB8::B activate
- neighbor 2001:0DB8::B route-server-client
- neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import
- neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export
- neighbor 2001:0DB8::B soft-reconfiguration inbound
-
- neighbor 2001:0DB8::C activate
- neighbor 2001:0DB8::C route-server-client
- neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import
- neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export
- neighbor 2001:0DB8::C soft-reconfiguration inbound
- exit-address-family
- !
- ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64
- ipv6 prefix-list COMMON-PREFIXES seq 10 deny any
- !
- ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64
- ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any
- !
- ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64
- ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any
- !
- ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64
- ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any
- !
- route-map RSCLIENT-A-IMPORT permit 10
- match peer 2001:0DB8::B
- call A-IMPORT-FROM-B
- route-map RSCLIENT-A-IMPORT permit 20
- match peer 2001:0DB8::C
- call A-IMPORT-FROM-C
- !
- route-map A-IMPORT-FROM-B permit 10
- match ipv6 address prefix-list COMMON-PREFIXES
- set metric 100
- route-map A-IMPORT-FROM-B permit 20
- match ipv6 address prefix-list PEER-B-PREFIXES
- set community 65001:11111
- !
- route-map A-IMPORT-FROM-C permit 10
- match ipv6 address prefix-list COMMON-PREFIXES
- set metric 200
- route-map A-IMPORT-FROM-C permit 20
- match ipv6 address prefix-list PEER-C-PREFIXES
- set community 65001:22222
- !
- route-map RSCLIENT-A-EXPORT permit 10
- match peer 2001:0DB8::B
- match ipv6 address prefix-list PEER-A-PREFIXES
- route-map RSCLIENT-A-EXPORT permit 20
- match peer 2001:0DB8::C
- match ipv6 address prefix-list PEER-A-PREFIXES
- !
- ...
- ...
- ...
+ # Configuration for Route Server ('RS')
+ !
+ hostname RS
+ password ix
+ !
+ bgp multiple-instance
+ !
+ router bgp 65000 view RS
+ no bgp default ipv4-unicast
+ neighbor 2001:0DB8::A remote-as 65001
+ neighbor 2001:0DB8::B remote-as 65002
+ neighbor 2001:0DB8::C remote-as 65003
+ !
+ address-family ipv6
+ neighbor 2001:0DB8::A activate
+ neighbor 2001:0DB8::A route-server-client
+ neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
+ neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export
+ neighbor 2001:0DB8::A soft-reconfiguration inbound
+
+ neighbor 2001:0DB8::B activate
+ neighbor 2001:0DB8::B route-server-client
+ neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import
+ neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export
+ neighbor 2001:0DB8::B soft-reconfiguration inbound
+
+ neighbor 2001:0DB8::C activate
+ neighbor 2001:0DB8::C route-server-client
+ neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import
+ neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export
+ neighbor 2001:0DB8::C soft-reconfiguration inbound
+ exit-address-family
+ !
+ ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64
+ ipv6 prefix-list COMMON-PREFIXES seq 10 deny any
+ !
+ ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64
+ ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any
+ !
+ ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64
+ ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any
+ !
+ ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64
+ ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any
+ !
+ route-map RSCLIENT-A-IMPORT permit 10
+ match peer 2001:0DB8::B
+ call A-IMPORT-FROM-B
+ route-map RSCLIENT-A-IMPORT permit 20
+ match peer 2001:0DB8::C
+ call A-IMPORT-FROM-C
+ !
+ route-map A-IMPORT-FROM-B permit 10
+ match ipv6 address prefix-list COMMON-PREFIXES
+ set metric 100
+ route-map A-IMPORT-FROM-B permit 20
+ match ipv6 address prefix-list PEER-B-PREFIXES
+ set community 65001:11111
+ !
+ route-map A-IMPORT-FROM-C permit 10
+ match ipv6 address prefix-list COMMON-PREFIXES
+ set metric 200
+ route-map A-IMPORT-FROM-C permit 20
+ match ipv6 address prefix-list PEER-C-PREFIXES
+ set community 65001:22222
+ !
+ route-map RSCLIENT-A-EXPORT permit 10
+ match peer 2001:0DB8::B
+ match ipv6 address prefix-list PEER-A-PREFIXES
+ route-map RSCLIENT-A-EXPORT permit 20
+ match peer 2001:0DB8::C
+ match ipv6 address prefix-list PEER-A-PREFIXES
+ !
+ ...
+ ...
+ ...
If you compare the initial configuration of RA with the route server
@@ -487,7 +487,7 @@ any limitation, as all kinds of filters can be included in import/export
route-maps. For example suppose that in the non-route-server scenario peer
RA had the following filters configured for input from peer B:
-::
+.. code-block:: frr
neighbor 2001:0DB8::B prefix-list LIST-1 in
neighbor 2001:0DB8::B filter-list LIST-2 in
@@ -507,7 +507,7 @@ the three filters (the community-list, the prefix-list and the
route-map). That route-map can then be used inside the Import
policy in the route server. Lets see how to do it:
-::
+.. code-block:: frr
neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import
...
diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst
index 86d88dcf9b..93a8e4396a 100644
--- a/doc/user/rpki.rst
+++ b/doc/user/rpki.rst
@@ -164,7 +164,7 @@ Validating BGP Updates
In the following example, the router prefers valid routes over invalid
prefixes because invalid routes have a lower local preference.
- ::
+ .. code-block:: frr
! Allow for invalid routes in route selection process
route bgp 60001
@@ -213,7 +213,7 @@ Displaying RPKI
RPKI Configuration Example
--------------------------
-::
+.. code-block:: frr
hostname bgpd1
password zebra
diff --git a/doc/user/snmp.rst b/doc/user/snmp.rst
index 114f1f7dfc..1a24d56cb7 100644
--- a/doc/user/snmp.rst
+++ b/doc/user/snmp.rst
@@ -42,22 +42,23 @@ master SNMP agent (snmpd) and each of the FRR daemons must be configured. In
:file:`/etc/snmp/snmpd.conf`, the ``master agentx`` directive should be added.
In each of the FRR daemons, ``agentx`` command will enable AgentX support.
-::
+:file:`/etc/snmp/snmpd.conf`:
+ #
+ # example access restrictions setup
+ #
+ com2sec readonly default public
+ group MyROGroup v1 readonly
+ view all included .1 80
+ access MyROGroup "" any noauth exact all none none
+ #
+ # enable master agent for AgentX subagents
+ #
+ master agentx
+
+:file:`/etc/frr/ospfd.conf:`
+
+ .. code-block:: frr
- /etc/snmp/snmpd.conf:
- #
- # example access restrictions setup
- #
- com2sec readonly default public
- group MyROGroup v1 readonly
- view all included .1 80
- access MyROGroup "" any noauth exact all none none
- #
- # enable master agent for AgentX subagents
- #
- master agentx
-
- /etc/frr/ospfd.conf:
! ... the rest of ospfd.conf has been omitted for clarity ...
!
agentx
@@ -69,16 +70,16 @@ each FRR daemons:
::
- 2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected
+ 2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected
Then, you can use the following command to check everything works as expected:
::
- # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
- OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
- [...]
+ # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
+ OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
+ [...]
The AgentX protocol can be transported over a Unix socket or using TCP or UDP.
@@ -88,10 +89,9 @@ need to configure FRR to use another transport, you can configure it through
::
- /etc/snmp/frr.conf:
- [snmpd]
- # Use a remote master agent
- agentXSocket tcp:192.168.15.12:705
+ [snmpd]
+ # Use a remote master agent
+ agentXSocket tcp:192.168.15.12:705
.. _smux-configuration:
@@ -112,26 +112,24 @@ In the following example the ospfd daemon will be connected to the snmpd daemon
using the password "frr_ospfd". For testing it is recommending to take exactly
the below snmpd.conf as wrong access restrictions can be hard to debug.
-::
-
- /etc/snmp/snmpd.conf:
- #
- # example access restrictions setup
- #
- com2sec readonly default public
- group MyROGroup v1 readonly
- view all included .1 80
- access MyROGroup "" any noauth exact all none none
- #
- # the following line is relevant for FRR
- #
- smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
-
- /etc/frr/ospf:
- ! ... the rest of ospfd.conf has been omitted for clarity ...
- !
- smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
- !
+:file:`/etc/snmp/snmpd.conf`:
+ #
+ # example access restrictions setup
+ #
+ com2sec readonly default public
+ group MyROGroup v1 readonly
+ view all included .1 80
+ access MyROGroup "" any noauth exact all none none
+ #
+ # the following line is relevant for FRR
+ #
+ smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
+
+:file:`/etc/frr/ospf`:
+ ! ... the rest of ospfd.conf has been omitted for clarity ...
+ !
+ smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd
+ !
After restarting snmpd and frr, a successful connection can be verified in the
diff --git a/doc/user/snmptrap.rst b/doc/user/snmptrap.rst
index 9e642a5d4d..4bc6d40122 100644
--- a/doc/user/snmptrap.rst
+++ b/doc/user/snmptrap.rst
@@ -42,7 +42,7 @@ The snmptrap_handle.sh script I personally use for handling BGP4 traps is
below. You can of course do all sorts of things when handling traps, like sound
a siren, have your display flash, etc., be creative ;).
-::
+.. code-block:: shell
#!/bin/bash
diff --git a/doc/user/vnc.rst b/doc/user/vnc.rst
index 8be4ff62de..ff6050ca68 100644
--- a/doc/user/vnc.rst
+++ b/doc/user/vnc.rst
@@ -90,7 +90,7 @@ Default values are overridden by :ref:`vnc-nve-group-configuration`.
Enter VNC configuration mode for specifying VNC default behaviors. Use
`exit-vnc` to leave VNC configuration mode. `vnc defaults` is optional.
-::
+.. code-block:: frr
vnc defaults
... various VNC defaults
@@ -142,7 +142,7 @@ Defaults section.
Enter VNC configuration mode for defining the NVE group `name`.
Use `exit` or `exit-vnc` to exit group configuration mode.
- ::
+ .. code-block:: frr
vnc nve-group group1
... configuration commands
@@ -315,7 +315,7 @@ L2 Group Configuration.
Enter VNC configuration mode for defining the L2 group `name`.
Use `exit` or `exit-vnc` to exit group configuration mode.
- ::
+ .. code-block:: frr
vnc l2-group group1
... configuration commands
@@ -851,7 +851,9 @@ Tunnel Encapsulation Attribute.
A three-way full mesh with three NVEs per NVA.
-:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100):::
+:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100):
+
+.. code-block:: frr
router bgp 64512
@@ -883,7 +885,9 @@ Tunnel Encapsulation Attribute.
exit
-:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101):::
+:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101):
+
+.. code-block:: frr
router bgp 64512
@@ -905,7 +909,9 @@ Tunnel Encapsulation Attribute.
exit-vnc
exit
-:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102):::
+:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102):
+
+.. code-block:: frr
router bgp 64512
@@ -959,7 +965,9 @@ registrations exported this way have a next-hop address of the CE behind the
connected (registering) NVE. Exporting VNC routes as IPv4 unicast is enabled
with the ``vnc export`` command below.
-The configuration for ``VNC-GW 1`` is shown below.::
+The configuration for ``VNC-GW 1`` is shown below.
+
+.. code-block:: frr
router bgp 64512
bgp router-id 192.168.1.101
@@ -994,7 +1002,9 @@ have a statement disabling the IPv4 unicast address family. IPv4 unicast is on
by default and this prevents the other VNC-GW and NVAs from learning unicast
routes advertised by the route-reflector clients.
-Configuration for ``NVA 2``:::
+Configuration for ``NVA 2``:
+
+.. code-block:: frr
router bgp 64512
bgp router-id 192.168.1.104
@@ -1077,7 +1087,9 @@ As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The
7``, and ``NVE 8`` are members of the NVE group ``group1``. The NVEs ``NVE
5``, ``NVE 6``, and ``NVE 9`` are members of the NVE group ``group2``.
-:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100:::
+:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100:
+
+.. code-block:: frr
router bgp 64512
@@ -1106,7 +1118,9 @@ As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The
exit
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:
+
+.. code-block:: frr
router bgp 64512
@@ -1126,28 +1140,30 @@ As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The
exit-vnc
exit
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102:
- router bgp 64512
+.. code-block:: frr
- bgp router-id 192.168.1.102
+ router bgp 64512
- neighbor 192.168.1.100 remote-as 64512
+ bgp router-id 192.168.1.102
- address-family ipv4 vpn
- neighbor 192.168.1.100 activate
- exit-address-family
+ neighbor 192.168.1.100 remote-as 64512
- vnc defaults
- rd 64512:1
- response-lifetime 200
- rt both 1000:1 1000:2
- exit-vnc
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
- vnc nve-group group1
- prefix vn 172.16.128.0/17
- exit-vnc
- exit
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+ exit
While not shown, an NVA can also be configured as a route reflector.
@@ -1218,7 +1234,9 @@ VNC-relevant configuration is provided.
}
}
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:
+
+.. code-block:: frr
router bgp 64512
@@ -1238,7 +1256,9 @@ VNC-relevant configuration is provided.
exit-vnc
exit
-:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:::
+:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:
+
+.. code-block:: frr
router bgp 64512
@@ -1277,7 +1297,9 @@ reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and
FRR-based NVA with redundant route reflectors
-:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100:::
+:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100:
+
+.. code-block:: frr
router bgp 64512
@@ -1304,29 +1326,33 @@ reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and
exit-address-family
exit
-:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:
- router bgp 64512
+.. code-block:: frr
- bgp router-id 192.168.1.101
+ router bgp 64512
- neighbor 192.168.1.100 remote-as 64512
- neighbor 192.168.1.104 remote-as 64512
+ bgp router-id 192.168.1.101
- address-family ipv4 vpn
- neighbor 192.168.1.100 activate
- neighbor 192.168.1.104 activate
- exit-address-family
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
- vnc nve-group group1
- prefix vn 172.16.0.0/17
- rd 64512:1
- response-lifetime 200
- rt both 1000:1 1000:2
- exit-vnc
- exit
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:
-:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:::
+.. code-block:: frr
router bgp 64512
@@ -1417,7 +1443,7 @@ reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and
}
}
-.. [#] The nve-id is carriedin the route distinguisher. It is the second octet
+.. [#] The nve-id is carried in the route distinguisher. It is the second octet
of the eight-octet route distinguisher generated for Ethernet / L2
advertisements. The first octet is a constant 0xFF, and the third
through eighth octets are set to the L2
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index 70a69cf0d4..7c886e785e 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -18,19 +18,16 @@ Besides the common invocation options (:ref:`common-invocation-options`), the
.. program:: zebra
-.. option:: -b
-.. option:: --batch
+.. option:: -b, --batch
Runs in batch mode. *zebra* parses configuration file and terminates
immediately.
-.. option:: -k
-.. option:: --keep_kernel
+.. option:: -k, --keep_kernel
When zebra starts up, don't delete old self inserted routes.
-.. option:: -r
-.. option:: --retain
+.. option:: -r, --retain
When program terminates, retain routes added by zebra.
@@ -243,7 +240,8 @@ defines static prefix and gateway.
Some example configuration:
- ::
+ .. code-block:: frr
+
ip route 10.0.0.0/8 10.0.0.2
ip route 10.0.0.0/8 ppp0
ip route 10.0.0.0/8 null0
@@ -259,7 +257,7 @@ defines static prefix and gateway.
A.B.C.D format, user must define NETMASK value with A.B.C.D
format. GATEWAY is same option as above command.
- ::
+ .. code-block:: frr
ip route 10.0.0.0 255.255.255.0 10.0.0.2
ip route 10.0.0.0 255.255.255.0 ppp0
@@ -273,9 +271,9 @@ defines static prefix and gateway.
Installs the route with the specified distance.
-Multiple nexthop static route
+Multiple nexthop static route:
-::
+.. code-block:: frr
ip route 10.0.0.1/32 10.0.0.2
ip route 10.0.0.1/32 10.0.0.3
@@ -297,7 +295,7 @@ nexthops, if the platform supports this.
* is directly connected, eth0
-::
+.. code-block:: frr
ip route 10.0.0.0/8 10.0.0.2
ip route 10.0.0.0/8 10.0.0.3
@@ -376,30 +374,30 @@ unicast topology!
MODE sets the method used to perform RPF lookups. Supported modes:
-urib-only
- Performs the lookup on the Unicast RIB. The Multicast RIB is never used.
+ urib-only
+ Performs the lookup on the Unicast RIB. The Multicast RIB is never used.
-mrib-only
- Performs the lookup on the Multicast RIB. The Unicast RIB is never used.
+ mrib-only
+ Performs the lookup on the Multicast RIB. The Unicast RIB is never used.
-mrib-then-urib
- Tries to perform the lookup on the Multicast RIB. If any route is found,
- that route is used. Otherwise, the Unicast RIB is tried.
+ mrib-then-urib
+ Tries to perform the lookup on the Multicast RIB. If any route is found,
+ that route is used. Otherwise, the Unicast RIB is tried.
-lower-distance
- Performs a lookup on the Multicast RIB and Unicast RIB each. The result
- with the lower administrative distance is used; if they're equal, the
- Multicast RIB takes precedence.
+ lower-distance
+ Performs a lookup on the Multicast RIB and Unicast RIB each. The result
+ with the lower administrative distance is used; if they're equal, the
+ Multicast RIB takes precedence.
-longer-prefix
- Performs a lookup on the Multicast RIB and Unicast RIB each. The result
- with the longer prefix length is used; if they're equal, the
- Multicast RIB takes precedence.
+ longer-prefix
+ Performs a lookup on the Multicast RIB and Unicast RIB each. The result
+ with the longer prefix length is used; if they're equal, the
+ Multicast RIB takes precedence.
- The `mrib-then-urib` setting is the default behavior if nothing is
- configured. If this is the desired behavior, it should be explicitly
- configured to make the configuration immune against possible changes in
- what the default behavior is.
+ The `mrib-then-urib` setting is the default behavior if nothing is
+ configured. If this is the desired behavior, it should be explicitly
+ configured to make the configuration immune against possible changes in
+ what the default behavior is.
.. warning::
Unreachable routes do not receive special treatment and do not cause
@@ -480,7 +478,7 @@ The following creates a prefix-list that matches all addresses, a route-map
that sets the preferred source address, and applies the route-map to all
*rip* routes.
-::
+.. code-block:: frr
ip prefix-list ANY permit 0.0.0.0/0 le 32
route-map RM1 permit 10
diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile
index 6bd7d90aef..dd29358b07 100644
--- a/docker/alpine/Dockerfile
+++ b/docker/alpine/Dockerfile
@@ -23,11 +23,13 @@ FROM alpine:3.7 as alpine-builder
RUN apk add --no-cache abuild alpine-sdk && mkdir -p /pkgs/apk
ADD docker/alpine/alpine-build.sh /usr/bin/
ADD docker/alpine/builder /etc/sudoers.d
-COPY --from=source-builder /src/*.tar.gz /src/alpine/APKBUILD /dist/
+COPY --from=source-builder /src/*.tar.gz /src/alpine/* /src/tools/etc/frr/daemons* /dist/
RUN adduser -D -G abuild builder && chown -R builder /dist /pkgs
USER builder
RUN /usr/bin/alpine-build.sh
FROM alpine:3.7
RUN mkdir -p /pkgs/apk
COPY --from=alpine-builder /pkgs/apk/ /pkgs/apk/
+RUN apk add --no-cache tini
RUN apk add --no-cache --allow-untrusted /pkgs/apk/x86_64/*.apk
+ENTRYPOINT [ "/sbin/tini", "--", "/usr/lib/frr/docker-start" ]
diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c
index e418b40343..7dd95b7bc6 100644
--- a/eigrpd/eigrp_snmp.c
+++ b/eigrpd/eigrp_snmp.c
@@ -516,7 +516,7 @@ eigrp_snmp_nbr_lookup_next(struct in_addr *nbr_addr, unsigned int *ifindex,
struct eigrp_neighbor *nbr;
struct route_node *rn;
struct eigrp_neighbor *min = NULL;
- struct eigrp *eigrp = eigrp;
+ struct eigrp *eigrp;
eigrp = eigrp_lookup();
diff --git a/lib/command.c b/lib/command.c
index f244d67e86..2bff3b17a2 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -105,6 +105,7 @@ const char *node_names[] = {
"as list", // AS_LIST_NODE,
"community list", // COMMUNITY_LIST_NODE,
"routemap", // RMAP_NODE,
+ "pbr-map", // PBRMAP_NODE,
"smux", // SMUX_NODE,
"dump", // DUMP_NODE,
"forwarding", // FORWARDING_NODE,
@@ -1312,6 +1313,7 @@ void cmd_exit(struct vty *vty)
case ISIS_NODE:
case KEYCHAIN_NODE:
case RMAP_NODE:
+ case PBRMAP_NODE:
case VTY_NODE:
vty->node = CONFIG_NODE;
break;
@@ -1409,6 +1411,7 @@ DEFUN (config_end,
case BGP_EVPN_VNI_NODE:
case BGP_IPV6L_NODE:
case RMAP_NODE:
+ case PBRMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
case LDP_NODE:
diff --git a/lib/command.h b/lib/command.h
index 8d88ea1902..bb77812350 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -128,6 +128,7 @@ enum node_type {
AS_LIST_NODE, /* AS list node. */
COMMUNITY_LIST_NODE, /* Community list node. */
RMAP_NODE, /* Route map node. */
+ PBRMAP_NODE, /* PBR map node. */
SMUX_NODE, /* SNMP configuration node. */
DUMP_NODE, /* Packet dump node. */
FORWARDING_NODE, /* IP forwarding node. */
@@ -221,6 +222,13 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
funcdecl_##funcname
+#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ funcdecl_##funcname
+
+#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
@@ -298,6 +306,9 @@ struct cmd_node {
#else /* VTYSH_EXTRACT_PL */
#define DEFPY(funcname, cmdname, cmdstr, helpstr) \
DEFUN(funcname, cmdname, cmdstr, helpstr)
+
+#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr)
#endif /* VTYSH_EXTRACT_PL */
/* Some macroes */
diff --git a/lib/command_lex.l b/lib/command_lex.l
index 530900659b..0d6e6ee7e5 100644
--- a/lib/command_lex.l
+++ b/lib/command_lex.l
@@ -23,6 +23,9 @@
*/
%{
+/* ignore flex generated code in static analyzer */
+#ifndef __clang_analyzer__
+
/* ignore harmless bugs in old versions of flex */
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
@@ -91,3 +94,5 @@ void cleanup_lexer (yyscan_t *scn)
// yy_delete_buffer (buffer, *scn);
yylex_destroy(*scn);
}
+
+#endif /* __clang_analyzer__ */
diff --git a/lib/command_match.c b/lib/command_match.c
index f6b07a0b20..99ec03e0c2 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -99,6 +99,9 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
struct listnode *head = listhead(*argv);
struct listnode *tail = listtail(*argv);
+ assert(head);
+ assert(tail);
+
// delete dummy start node
cmd_token_del((struct cmd_token *)head->data);
list_delete_node(*argv, head);
diff --git a/lib/defun_lex.l b/lib/defun_lex.l
index 87775a0e75..9c995db266 100644
--- a/lib/defun_lex.l
+++ b/lib/defun_lex.l
@@ -132,6 +132,8 @@ SPECIAL [(),]
"DEFUN_NOSH" value = strdup(yytext); return DEFUNNY;
"DEFUN_HIDDEN" value = strdup(yytext); return DEFUNNY;
"DEFPY" value = strdup(yytext); return DEFUNNY;
+"DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
+"DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
"ALIAS" value = strdup(yytext); return DEFUNNY;
"ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
"install_element" value = strdup(yytext); return INSTALL;
diff --git a/lib/graph.c b/lib/graph.c
index 945a58e688..a9cc43f7c1 100644
--- a/lib/graph.c
+++ b/lib/graph.c
@@ -34,6 +34,15 @@ struct graph *graph_new()
return graph;
}
+void graph_delete_graph(struct graph *graph)
+{
+ for (unsigned int i = vector_active(graph->nodes); i--; /**/)
+ graph_delete_node(graph, vector_slot(graph->nodes, i));
+
+ vector_free(graph->nodes);
+ XFREE(MTYPE_GRAPH, graph);
+}
+
struct graph_node *graph_new_node(struct graph *graph, void *data,
void (*del)(void *))
{
@@ -127,12 +136,24 @@ void graph_remove_edge(struct graph_node *from, struct graph_node *to)
}
}
-void graph_delete_graph(struct graph *graph)
+struct graph_node *graph_find_node(struct graph *graph, void *data)
{
- // delete each node in the graph
- for (unsigned int i = vector_active(graph->nodes); i--; /**/)
- graph_delete_node(graph, vector_slot(graph->nodes, i));
+ struct graph_node *g;
- vector_free(graph->nodes);
- XFREE(MTYPE_GRAPH, graph);
+ for (unsigned int i = vector_active(graph->nodes); i--; /**/) {
+ g = vector_slot(graph->nodes, i);
+ if (g->data == data)
+ return g;
+ }
+
+ return NULL;
+}
+
+bool graph_has_edge(struct graph_node *from, struct graph_node *to)
+{
+ for (unsigned int i = vector_active(from->to); i--; /**/)
+ if (vector_slot(from->to, i) == to)
+ return true;
+
+ return false;
}
diff --git a/lib/graph.h b/lib/graph.h
index 10ee00feda..d6dfef5a63 100644
--- a/lib/graph.h
+++ b/lib/graph.h
@@ -24,6 +24,7 @@
#ifndef _ZEBRA_COMMAND_GRAPH_H
#define _ZEBRA_COMMAND_GRAPH_H
+#include <stdbool.h>
#include "vector.h"
struct graph {
@@ -91,4 +92,23 @@ void graph_remove_edge(struct graph_node *from, struct graph_node *to);
*/
void graph_delete_graph(struct graph *graph);
+/*
+ * Finds a node in the graph.
+ *
+ * @param[in] graph the graph to search in
+ * @param[in] data the data to key off
+ * @return the first graph node whose data pointer matches `data`
+ */
+struct graph_node *graph_find_node(struct graph *graph, void *data);
+
+
+/*
+ * Determines whether two nodes have a directed edge between them.
+ *
+ * @param from
+ * @param to
+ * @return whether there is a directed edge from `from` to `to`.
+ */
+bool graph_has_edge(struct graph_node *from, struct graph_node *to);
+
#endif /* _ZEBRA_COMMAND_GRAPH_H */
diff --git a/lib/imsg.c b/lib/imsg.c
index 0ea1dd6302..6419f805ab 100644
--- a/lib/imsg.c
+++ b/lib/imsg.c
@@ -241,6 +241,8 @@ struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid,
struct ibuf *wbuf;
struct imsg_hdr hdr;
+ memset(&hdr, 0x00, IMSG_HEADER_SIZE);
+
datalen += IMSG_HEADER_SIZE;
if (datalen > MAX_IMSGSIZE) {
errno = ERANGE;
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 8d4a3ff8df..9931183634 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -531,12 +531,12 @@ struct thread_master *frr_init(void)
snprintf(p_instance, sizeof(p_instance), "-%d", di->instance);
}
if (di->pathspace)
- snprintf(p_pathspace, sizeof(p_pathspace), "/%s",
+ snprintf(p_pathspace, sizeof(p_pathspace), "%s/",
di->pathspace);
snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf",
frr_sysconfdir, p_pathspace, di->name, p_instance);
- snprintf(pidfile_default, sizeof(pidfile_default), "%s%s/%s%s.pid",
+ snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid",
frr_vtydir, p_pathspace, di->name, p_instance);
zprivs_preinit(di->privs);
diff --git a/lib/log.c b/lib/log.c
index f5aff756dd..dbfc95da86 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -968,6 +968,10 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_TABLE_MANAGER_CONNECT),
DESC_ENTRY(ZEBRA_GET_TABLE_CHUNK),
DESC_ENTRY(ZEBRA_RELEASE_TABLE_CHUNK),
+ DESC_ENTRY(ZEBRA_IPSET_CREATE),
+ DESC_ENTRY(ZEBRA_IPSET_DESTROY),
+ DESC_ENTRY(ZEBRA_IPSET_ENTRY_ADD),
+ DESC_ENTRY(ZEBRA_IPSET_ENTRY_DELETE),
};
#undef DESC_ENTRY
diff --git a/lib/netns_linux.c b/lib/netns_linux.c
index 3d61cecc03..a3fae8f39f 100644
--- a/lib/netns_linux.c
+++ b/lib/netns_linux.c
@@ -59,6 +59,26 @@ static int ns_default_ns_fd;
static int ns_debug;
+struct ns_map_nsid {
+ RB_ENTRY(ns_map_nsid) id_entry;
+ ns_id_t ns_id_external;
+ ns_id_t ns_id;
+};
+
+static inline int ns_map_compare(const struct ns_map_nsid *a,
+ const struct ns_map_nsid *b)
+{
+ return (a->ns_id - b->ns_id);
+}
+
+RB_HEAD(ns_map_nsid_head, ns_map_nsid);
+RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
+RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare);
+struct ns_map_nsid_head ns_map_nsid_list = RB_INITIALIZER(&ns_map_nsid_list);
+
+static ns_id_t ns_id_external_numbering;
+
+
#ifndef CLONE_NEWNET
#define CLONE_NEWNET 0x40000000
/* New network namespace (lo, device, names sockets, etc) */
@@ -262,6 +282,38 @@ static void ns_disable_internal(struct ns *ns)
}
}
+/* VRF list existance check by name. */
+static struct ns_map_nsid *ns_map_nsid_lookup_by_nsid(ns_id_t ns_id)
+{
+ struct ns_map_nsid ns_map;
+
+ ns_map.ns_id = ns_id;
+ return RB_FIND(ns_map_nsid_head, &ns_map_nsid_list, &ns_map);
+}
+
+ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map)
+{
+ struct ns_map_nsid *ns_map;
+ vrf_id_t ns_id_external;
+
+ ns_map = ns_map_nsid_lookup_by_nsid(ns_id);
+ if (ns_map && !map) {
+ ns_id_external = ns_map->ns_id_external;
+ RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
+ return ns_id_external;
+ }
+ if (ns_map)
+ return ns_map->ns_id_external;
+ ns_map = XCALLOC(MTYPE_NS, sizeof(struct ns_map_nsid));
+ /* increase vrf_id
+ * default vrf is the first one : 0
+ */
+ ns_map->ns_id_external = ns_id_external_numbering++;
+ ns_map->ns_id = ns_id;
+ RB_INSERT(ns_map_nsid_head, &ns_map_nsid_list, ns_map);
+ return ns_map->ns_id_external;
+}
+
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
{
return ns_get_created_internal(ns, name, ns_id);
@@ -430,7 +482,7 @@ void ns_init(void)
}
/* Initialize NS module. */
-void ns_init_management(ns_id_t default_ns_id)
+void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns)
{
int fd;
@@ -444,6 +496,8 @@ void ns_init_management(ns_id_t default_ns_id)
fd = open(NS_DEFAULT_NAME, O_RDONLY);
default_ns->fd = fd;
}
+ default_ns->internal_ns_id = internal_ns;
+
/* Set the default NS name. */
default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
if (ns_debug)
diff --git a/lib/netns_other.c b/lib/netns_other.c
index 2402dd17d6..4c7be05fab 100644
--- a/lib/netns_other.c
+++ b/lib/netns_other.c
@@ -153,6 +153,11 @@ int ns_enable(struct ns *ns, int (*func)(ns_id_t, void *))
return 0;
}
+ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool maporunmap)
+{
+ return NS_UNKNOWN;
+}
+
struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
{
return NULL;
diff --git a/lib/nexthop.c b/lib/nexthop.c
index b1e9582b20..3603050f06 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -31,6 +31,7 @@
#include "prefix.h"
#include "nexthop.h"
#include "mpls.h"
+#include "jhash.h"
DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
@@ -240,7 +241,7 @@ void nexthop_del_labels(struct nexthop *nexthop)
}
}
-const char *nexthop2str(struct nexthop *nexthop, char *str, int size)
+const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
{
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
@@ -310,3 +311,15 @@ unsigned int nexthop_level(struct nexthop *nexthop)
return rv;
}
+
+uint32_t nexthop_hash(struct nexthop *nexthop)
+{
+ uint32_t key;
+
+ key = jhash_1word(nexthop->vrf_id, 0x45afe398);
+ key = jhash_1word(nexthop->ifindex, key);
+ key = jhash_1word(nexthop->type, key);
+ key = jhash(&nexthop->gate, sizeof(union g_addr), key);
+
+ return key;
+}
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 036fc5b888..e4af405d5f 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -118,6 +118,23 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t,
mpls_label_t *);
void nexthop_del_labels(struct nexthop *);
+/*
+ * Hash a nexthop. Suitable for use with hash tables.
+ *
+ * This function uses the following values when computing the hash:
+ * - vrf_id
+ * - ifindex
+ * - type
+ * - gate
+ *
+ * nexthop
+ * The nexthop to hash
+ *
+ * Returns:
+ * 32-bit hash of nexthop
+ */
+uint32_t nexthop_hash(struct nexthop *nexthop);
+
extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2);
extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type);
@@ -126,7 +143,7 @@ extern int nexthop_same_no_recurse(const struct nexthop *next1,
extern int nexthop_labels_match(struct nexthop *nh1, struct nexthop *nh2);
extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2);
-extern const char *nexthop2str(struct nexthop *nexthop, char *str, int size);
+extern const char *nexthop2str(const struct nexthop *nexthop, char *str, int size);
extern struct nexthop *nexthop_next(struct nexthop *nexthop);
extern unsigned int nexthop_level(struct nexthop *nexthop);
#endif /*_LIB_NEXTHOP_H */
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index e486247244..1ec49c2a02 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -320,7 +320,7 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
char buf[100];
struct vrf *vrf;
- vty_out(vty, " nexthop ");
+ vty_out(vty, "nexthop ");
switch (nh->type) {
case NEXTHOP_TYPE_IFINDEX:
@@ -361,8 +361,10 @@ static int nexthop_group_write(struct vty *vty)
RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
vty_out(vty, "nexthop-group %s\n", nhgc->name);
- for (nh = nhgc->nhg.nexthop; nh; nh = nh->next)
+ for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) {
+ vty_out(vty, " ");
nexthop_group_write_nexthop(vty, nh);
+ }
vty_out(vty, "!\n");
}
diff --git a/lib/ns.h b/lib/ns.h
index fac91a40da..b3810f069b 100644
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -46,6 +46,9 @@ struct ns {
/* Identifier, same as the vector index */
ns_id_t ns_id;
+ /* Identifier, mapped on the NSID value */
+ ns_id_t internal_ns_id;
+
/* Name */
char *name;
@@ -100,7 +103,7 @@ extern void ns_terminate(void);
/* API to initialize NETNS managerment
* parameter is the default ns_id
*/
-extern void ns_init_management(ns_id_t ns_id);
+extern void ns_init_management(ns_id_t ns_id, ns_id_t internal_ns_idx);
/*
@@ -133,6 +136,11 @@ extern int ns_have_netns(void);
/* API to get context information of a NS */
extern void *ns_info_lookup(ns_id_t ns_id);
+/* API to map internal ns id value with
+ * user friendly ns id external value
+ */
+extern ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map);
+
/*
* NS init routine
* should be called from backendx
diff --git a/lib/pbr.h b/lib/pbr.h
new file mode 100644
index 0000000000..b49cb562a1
--- /dev/null
+++ b/lib/pbr.h
@@ -0,0 +1,86 @@
+/* Policy Based Routing (PBR) main header
+ * Copyright (C) 2018 6WIND
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _PBR_H
+#define _PBR_H
+
+/*
+ * A PBR filter
+ *
+ * The filter or match criteria in a PBR rule.
+ * For simplicity, all supported filters are grouped into a structure rather
+ * than delineating further. A bitmask denotes which filters are actually
+ * specified.
+ */
+struct pbr_filter {
+ uint32_t filter_bm; /* not encoded by zapi
+ */
+#define PBR_FILTER_SRC_IP (1 << 0)
+#define PBR_FILTER_DST_IP (1 << 1)
+#define PBR_FILTER_SRC_PORT (1 << 2)
+#define PBR_FILTER_DST_PORT (1 << 3)
+#define PBR_FILTER_FWMARK (1 << 4)
+
+ /* Source and Destination IP address with masks. */
+ struct prefix src_ip;
+ struct prefix dst_ip;
+
+ /* Source and Destination higher-layer (TCP/UDP) port numbers. */
+ uint16_t src_port;
+ uint16_t dst_port;
+
+ /* Filter with fwmark */
+ uint32_t fwmark;
+};
+
+/*
+ * A PBR action
+ *
+ * The action corresponding to a PBR rule.
+ * While the user specifies the action in a particular way, the forwarding
+ * plane implementation (Linux only) requires that to be encoded into a
+ * route table and the rule then point to that route table; in some cases,
+ * the user criteria may directly point to a table too.
+ */
+struct pbr_action {
+ uint32_t table;
+};
+
+/*
+ * A PBR rule
+ *
+ * This is a combination of the filter criteria and corresponding action.
+ * Rules also have a user-defined sequence number which defines the relative
+ * order amongst rules.
+ */
+struct pbr_rule {
+ vrf_id_t vrf_id;
+
+ uint32_t seq;
+ uint32_t priority;
+ uint32_t unique;
+ struct pbr_filter filter;
+ struct pbr_action action;
+ uint32_t ifindex;
+};
+
+extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
+ struct pbr_rule *zrule);
+
+#endif /* _PBR_H */
diff --git a/lib/route_types.pl b/lib/route_types.pl
index 9d50acaaed..66384fe449 100755
--- a/lib/route_types.pl
+++ b/lib/route_types.pl
@@ -54,15 +54,15 @@ while (<STDIN>) {
$_ =~ s/\s*,\s*/,/g;
- # else: 7-field line
+ # else: 8-field line
my @f = split(/,/, $_);
- unless (@f == 7 || @f == 8) {
+ unless (@f == 8 || @f == 9) {
die "invalid input on route_types line $.\n";
}
my $proto = $f[0];
$f[3] = $1 if ($f[3] =~ /^'(.*)'$/);
- $f[6] = $1 if ($f[6] =~ /^"(.*)"$/);
+ $f[7] = $1 if ($f[7] =~ /^"(.*)"$/);
$protodetail{$proto} = {
"number" => scalar @protos,
@@ -72,8 +72,9 @@ while (<STDIN>) {
"char" => $f[3],
"ipv4" => int($f[4]),
"ipv6" => int($f[5]),
- "shorthelp" => $f[6],
- "restrict2" => $f[7],
+ "redist" => int($f[6]),
+ "shorthelp" => $f[7],
+ "restrict2" => $f[8],
};
push @protos, $proto;
$daemons{$f[2]} = {
@@ -136,8 +137,9 @@ sub collect {
next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra");
next if ($protodetail{$p}->{"restrict2"} ne "" &&
$protodetail{$p}->{"restrict2"} ne $daemon);
+ next if ($protodetail{$p}->{"redist"} eq 0);
next unless (($ipv4 && $protodetail{$p}->{"ipv4"})
- || ($ipv6 && $protodetail{$p}->{"ipv6"}));
+ || ($ipv6 && $protodetail{$p}->{"ipv6"}));
push @names, $protodetail{$p}->{"cname"};
push @help, " \"".$protodetail{$p}->{"longhelp"}."\\n\"";
}
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 310a993c38..cfa55e468c 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -7,8 +7,8 @@
# Lines /beginning/ with # are comments.
#
####
-# 7 field line has format:
-# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, short desc
+# 9 field line has format:
+# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, redist, short desc, Restrictions
#
# Zserv route type: Corresponding with zebra.h. Key field.
# canonical name: Typically derived from the route type definition.
@@ -25,9 +25,11 @@
# 'X' is reserved as the 'not needed' placeholder.
# ipv4: IPv4 capable? yes/no, or 1/0.
# ipv6: IPv6 capable? ditto.
+# redist: Allow this protocol to be used in redistribution statements
# short desc: Very brief description. Used in header of
# 'show ip route'. May be specified as NULL
# if the canonical name suffices.
+# Restriction: If this cannot be used with the listed protocol for redistribution events
#
# Key fields obviously must be a unique ASCII alpha-numeric word.
# Lower-case is required, brevity is optional but highly desirable.
@@ -43,42 +45,43 @@
# If you add a new routing protocol here, make sure you also update
# meta_queue_map in zebra_rib.c
#
-## type cname daemon C 4 6 short help
-ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, "Reserved"
-ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, "kernel route"
-ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, "connected"
-ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, "static"
-ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, "RIP"
-ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, "RIPng"
-ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF"
-ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv3"
-ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS"
-ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP"
-ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM"
-ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, "EIGRP"
-ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP"
+## type cname daemon C 4 6 Redist short help Restrictions
+ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, 0, "Reserved"
+ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, 1, "kernel route"
+ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, 1, "connected"
+ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, 1, "static"
+ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, 1, "RIP"
+ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, 1, "RIPng"
+ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, 1, "OSPF"
+ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, 1, "OSPFv3"
+ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, 1, "IS-IS"
+ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, 1, "BGP"
+ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, 0, "PIM"
+ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, 1, "EIGRP"
+ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, 1, "NHRP"
# HSLS and OLSR both are AFI independent (so: 1, 1), however
# we want to disable for them for general Quagga distribution.
# This at least makes it trivial for users of these protocols
# to 'switch on' redist support (direct numeric entry remaining
# possible).
-ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS"
-ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR"
-ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, "Table"
-ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, "LDP"
+ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, 0, "HSLS"
+ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, 0, "OLSR"
+ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, 1, "Table"
+ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, 0, "LDP"
#vnc when sent to zebra
-ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, "VNC"
+ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, 1, "VNC"
# vnc when sent to bgp
-ZEBRA_ROUTE_VNC_DIRECT, vnc-direct,NULL, 'V', 1, 1, "VNC-Direct", bgpd
+ZEBRA_ROUTE_VNC_DIRECT, vnc-direct,NULL, 'V', 1, 1, 1, "VNC-Direct", bgpd
# vnc when sent to bgp (resolve NVE mode)
-ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, "VNC-RN"
+ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, 0, "VNC-RN"
# bgp unicast -> vnc
-ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct"
+ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, 0, "BGP-Direct"
# bgp unicast -> vnc
-ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC"
-ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel"
-ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP"
-ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-"
+ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, 0, "BGP2VNC"
+ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, 1, "Babel"
+ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP"
+ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR"
+ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
## help strings
@@ -103,3 +106,4 @@ ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)"
ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes"
ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)"
ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)"
+ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)"
diff --git a/lib/subdir.am b/lib/subdir.am
index 7d85b7a24d..0319f7764e 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -167,6 +167,7 @@ pkginclude_HEADERS += \
lib/zclient.h \
lib/zebra.h \
lib/logicalrouter.h \
+ lib/pbr.h \
# end
nodist_pkginclude_HEADERS += \
diff --git a/lib/vrf.c b/lib/vrf.c
index 1ed96cd0a4..b493f832f3 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -159,11 +159,13 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
if (!name && vrf_id == VRF_UNKNOWN)
return NULL;
+ /* attempt to find already available VRF
+ */
+ if (name)
+ vrf = vrf_lookup_by_name(name);
/* Try to find VRF both by ID and name */
- if (vrf_id != VRF_UNKNOWN)
+ if (!vrf && vrf_id != VRF_UNKNOWN)
vrf = vrf_lookup_by_id(vrf_id);
- if (!vrf && name)
- vrf = vrf_lookup_by_name(name);
if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
@@ -537,7 +539,8 @@ void vrf_configure_backend(int vrf_backend_netns)
vrf_backend = vrf_backend_netns;
}
-int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
+int vrf_handler_create(struct vty *vty, const char *vrfname,
+ struct vrf **vrf)
{
struct vrf *vrfp;
@@ -564,7 +567,7 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
}
int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname,
- ns_id_t ns_id)
+ ns_id_t ns_id, ns_id_t internal_ns_id)
{
struct ns *ns = NULL;
@@ -611,6 +614,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname,
return CMD_WARNING_CONFIG_FAILED;
}
ns = ns_get_created(ns, pathname, ns_id);
+ ns->internal_ns_id = internal_ns_id;
ns->vrf_ctxt = (void *)vrf;
vrf->ns_ctxt = (void *)ns;
/* update VRF netns NAME */
@@ -716,7 +720,8 @@ DEFUN_NOSH (vrf_netns,
vrf_daemon_privs->change(ZPRIVS_RAISE))
zlog_err("%s: Can't raise privileges", __func__);
- ret = vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
+ ret = vrf_netns_handler_create(vty, vrf, pathname,
+ NS_UNKNOWN, NS_UNKNOWN);
if (vrf_daemon_privs &&
vrf_daemon_privs->change(ZPRIVS_LOWER))
@@ -825,6 +830,9 @@ vrf_id_t vrf_get_default_id(void)
if (vrf)
return vrf->vrf_id;
+ /* backend netns is only known by zebra
+ * for other daemons, we return VRF_DEFAULT_INTERNAL
+ */
if (vrf_is_backend_netns())
return ns_get_default_id();
else
diff --git a/lib/vrf.h b/lib/vrf.h
index 85a5309279..8aa0fc2215 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -272,7 +272,8 @@ extern int vrf_handler_create(struct vty *vty, const char *name,
* should be called from zebra only
*/
extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
- char *pathname, ns_id_t ns_id);
+ char *pathname, ns_id_t ext_ns_id,
+ ns_id_t ns_id);
/* used internally to enable or disable VRF.
* Notify a change in the VRF ID of the VRF
diff --git a/lib/vty.c b/lib/vty.c
index 9381c6e0fd..e8463da3d7 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -719,6 +719,7 @@ static void vty_end_config(struct vty *vty)
case BGP_EVPN_NODE:
case BGP_IPV6L_NODE:
case RMAP_NODE:
+ case PBRMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
case LDP_NODE:
@@ -1115,6 +1116,7 @@ static void vty_stop_input(struct vty *vty)
case EIGRP_NODE:
case BGP_NODE:
case RMAP_NODE:
+ case PBRMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
case LDP_NODE:
diff --git a/lib/zclient.c b/lib/zclient.c
index d23f62dcd7..e1ce40ce70 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -36,6 +36,7 @@
#include "nexthop.h"
#include "mpls.h"
#include "sockopt.h"
+#include "pbr.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
@@ -1229,6 +1230,55 @@ stream_failure:
return 0;
}
+static void zapi_encode_prefix(struct stream *s,
+ struct prefix *p,
+ uint8_t family)
+{
+ struct prefix any;
+
+ if (!p) {
+ memset(&any, 0, sizeof(any));
+ any.family = family;
+ p = &any;
+ }
+
+ stream_putc(s, p->family);
+ stream_putc(s, p->prefixlen);
+ stream_put(s, &p->u.prefix, prefix_blen(p));
+}
+
+int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s,
+ struct pbr_rule *zrule)
+{
+ stream_reset(s);
+ zclient_create_header(s, cmd, zrule->vrf_id);
+
+ /*
+ * We are sending one item at a time at the moment
+ */
+ stream_putl(s, 1);
+
+ stream_putl(s, zrule->seq);
+ stream_putl(s, zrule->priority);
+ stream_putl(s, zrule->unique);
+
+ zapi_encode_prefix(s, &(zrule->filter.src_ip),
+ zrule->filter.src_ip.family);
+ stream_putw(s, zrule->filter.src_port); /* src port */
+ zapi_encode_prefix(s, &(zrule->filter.dst_ip),
+ zrule->filter.src_ip.family);
+ stream_putw(s, zrule->filter.dst_port); /* dst port */
+ stream_putw(s, zrule->filter.fwmark); /* fwmark */
+
+ stream_putl(s, zrule->action.table);
+ stream_putl(s, zrule->ifindex);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
uint32_t *tableid,
enum zapi_route_notify_owner *note)
@@ -1279,6 +1329,50 @@ stream_failure:
return false;
}
+bool zapi_ipset_notify_decode(struct stream *s,
+ uint32_t *unique,
+ enum zapi_ipset_notify_owner *note)
+{
+ uint32_t uni;
+
+ STREAM_GET(note, s, sizeof(*note));
+
+ STREAM_GETL(s, uni);
+
+ if (zclient_debug)
+ zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni);
+ *unique = uni;
+
+ return true;
+
+stream_failure:
+ return false;
+}
+
+bool zapi_ipset_entry_notify_decode(struct stream *s,
+ uint32_t *unique,
+ char *ipset_name,
+ enum zapi_ipset_entry_notify_owner *note)
+{
+ uint32_t uni;
+
+ STREAM_GET(note, s, sizeof(*note));
+
+ STREAM_GETL(s, uni);
+
+ STREAM_GET(ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ if (zclient_debug)
+ zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni);
+ *unique = uni;
+
+ return true;
+
+stream_failure:
+ return false;
+}
+
struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
{
struct nexthop *n = nexthop_new();
@@ -1991,6 +2085,40 @@ int lm_label_manager_connect(struct zclient *zclient)
return (int)result;
}
+/*
+ * Asynchronous label chunk request
+ *
+ * @param zclient Zclient used to connect to label manager (zebra)
+ * @param keep Avoid garbage collection
+ * @param chunk_size Amount of labels requested
+ * @result 0 on success, -1 otherwise
+ */
+int zclient_send_get_label_chunk(
+ struct zclient *zclient,
+ uint8_t keep,
+ uint32_t chunk_size)
+{
+ struct stream *s;
+
+ if (zclient_debug)
+ zlog_debug("Getting Label Chunk");
+
+ if (zclient->sock < 0)
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT);
+ stream_putc(s, keep);
+ stream_putl(s, chunk_size);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
/**
* Function to request a label chunk in a syncronous way
*
@@ -2604,6 +2732,12 @@ static int zclient_read(struct thread *thread)
if (zclient->rule_notify_owner)
(*zclient->rule_notify_owner)(command, zclient, length,
vrf_id);
+ break;
+ case ZEBRA_GET_LABEL_CHUNK:
+ if (zclient->label_chunk)
+ (*zclient->label_chunk)(command, zclient, length,
+ vrf_id);
+ break;
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index 9d3e5c3702..985239b326 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -47,6 +47,17 @@
*/
#define ZAPI_TCP_PATHNAME "@tcp"
+/* IPset size name stands for the name of the ipset entry
+ * that can be created by using some zapi interfaces
+ */
+#define ZEBRA_IPSET_NAME_SIZE 32
+
+/* IPTable action is defined by two values: either
+ * forward or drop
+ */
+#define ZEBRA_IPTABLES_FORWARD 0
+#define ZEBRA_IPTABLES_DROP 1
+
extern struct sockaddr_storage zclient_addr;
extern socklen_t zclient_addr_len;
@@ -135,6 +146,15 @@ typedef enum {
ZEBRA_TABLE_MANAGER_CONNECT,
ZEBRA_GET_TABLE_CHUNK,
ZEBRA_RELEASE_TABLE_CHUNK,
+ ZEBRA_IPSET_CREATE,
+ ZEBRA_IPSET_DESTROY,
+ ZEBRA_IPSET_ENTRY_ADD,
+ ZEBRA_IPSET_ENTRY_DELETE,
+ ZEBRA_IPSET_NOTIFY_OWNER,
+ ZEBRA_IPSET_ENTRY_NOTIFY_OWNER,
+ ZEBRA_IPTABLE_ADD,
+ ZEBRA_IPTABLE_DELETE,
+ ZEBRA_IPTABLE_NOTIFY_OWNER,
} zebra_message_types_t;
struct redist_proto {
@@ -223,6 +243,14 @@ struct zclient {
uint16_t length, vrf_id_t vrf_id);
int (*rule_notify_owner)(int command, struct zclient *zclient,
uint16_t length, vrf_id_t vrf_id);
+ void (*label_chunk)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
+ int (*ipset_notify_owner)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
+ int (*ipset_entry_notify_owner)(int command,
+ struct zclient *zclient,
+ uint16_t length,
+ vrf_id_t vrf_id);
};
/* Zebra API message flag. */
@@ -371,6 +399,31 @@ enum zapi_rule_notify_owner {
ZAPI_RULE_REMOVED,
};
+enum ipset_type {
+ IPSET_NET_NET = 1,
+ IPSET_NET_PORT_NET,
+ IPSET_NET_PORT,
+ IPSET_NET
+};
+
+enum zapi_ipset_notify_owner {
+ ZAPI_IPSET_FAIL_INSTALL,
+ ZAPI_IPSET_INSTALLED,
+ ZAPI_IPSET_REMOVED,
+};
+
+enum zapi_ipset_entry_notify_owner {
+ ZAPI_IPSET_ENTRY_FAIL_INSTALL,
+ ZAPI_IPSET_ENTRY_INSTALLED,
+ ZAPI_IPSET_ENTRY_REMOVED,
+};
+
+enum zapi_iptable_notify_owner {
+ ZAPI_IPTABLE_FAIL_INSTALL,
+ ZAPI_IPTABLE_INSTALLED,
+ ZAPI_IPTABLE_REMOVED,
+};
+
/* Zebra MAC types */
#define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/
#define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/
@@ -535,6 +588,11 @@ extern int zapi_ipv4_route(uint8_t, struct zclient *, struct prefix_ipv4 *,
extern struct interface *zebra_interface_link_params_read(struct stream *);
extern size_t zebra_interface_link_params_write(struct stream *,
struct interface *);
+extern int zclient_send_get_label_chunk(
+ struct zclient *zclient,
+ uint8_t keep,
+ uint32_t chunk_size);
+
extern int lm_label_manager_connect(struct zclient *zclient);
extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep,
uint32_t chunk_size, uint32_t *start,
@@ -605,6 +663,17 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno,
uint32_t *priority, uint32_t *unique,
ifindex_t *ifindex,
enum zapi_rule_notify_owner *note);
+bool zapi_ipset_notify_decode(struct stream *s,
+ uint32_t *unique,
+ enum zapi_ipset_notify_owner *note);
+
+#define ZEBRA_IPSET_NAME_SIZE 32
+
+bool zapi_ipset_entry_notify_decode(struct stream *s,
+ uint32_t *unique,
+ char *ipset_name,
+ enum zapi_ipset_entry_notify_owner *note);
+
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
extern bool zapi_nexthop_update_decode(struct stream *s,
struct zapi_route *nhr);
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index 01b8055b66..b895b5ad8b 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -786,6 +786,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
/* (3) if the prefix is equal to an active configured address range */
/* or if the NU bit is set in the prefix */
if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) {
+ /* must have been set in previous block */
+ assert(prefix_lsa);
+
range = ospf6_route_lookup(&prefix, oa->range_table);
if (range) {
if (is_debug)
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 7c60749d7e..cc8577b94e 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -349,28 +349,6 @@ void ospf6_interface_if_add(struct interface *ifp)
ospf6_interface_state_update(oi->interface);
}
-void ospf6_interface_if_del(struct interface *ifp)
-{
- struct ospf6_interface *oi;
-
- oi = (struct ospf6_interface *)ifp->info;
- if (oi == NULL)
- return;
-
- /* interface stop */
- if (oi->area)
- thread_execute(master, interface_down, oi, 0);
-
- listnode_delete(oi->area->if_list, oi);
- oi->area = (struct ospf6_area *)NULL;
-
- /* cut link */
- oi->interface = NULL;
- ifp->info = NULL;
-
- ospf6_interface_delete(oi);
-}
-
void ospf6_interface_state_update(struct interface *ifp)
{
struct ospf6_interface *oi;
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
index 553e89a227..8fd43f099a 100644
--- a/ospf6d/ospf6_interface.h
+++ b/ospf6d/ospf6_interface.h
@@ -176,7 +176,6 @@ extern void ospf6_interface_enable(struct ospf6_interface *);
extern void ospf6_interface_disable(struct ospf6_interface *);
extern void ospf6_interface_if_add(struct interface *);
-extern void ospf6_interface_if_del(struct interface *);
extern void ospf6_interface_state_update(struct interface *);
extern void ospf6_interface_connected_route_update(struct interface *);
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index 581a899bcd..de4ee2e1ac 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -1314,12 +1314,246 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
return 0;
}
+void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
+ struct ospf6_route *old,
+ struct ospf6_route *route)
+{
+ struct ospf6_route *old_route;
+ struct ospf6_path *ecmp_path, *o_path = NULL;
+ struct listnode *anode, *anext;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ char buf[PREFIX2STR_BUFFER];
+ bool route_found = false;
+
+ /* check for old entry match with new route origin,
+ * delete old entry.
+ */
+ for (old_route = old; old_route; old_route = old_route->next) {
+ bool route_updated = false;
+
+ if (!ospf6_route_is_same(old_route, route) ||
+ (old_route->path.type != route->path.type))
+ continue;
+
+ /* Current and New route has same origin,
+ * delete old entry.
+ */
+ for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext,
+ o_path)) {
+ /* Check old route path and route has same
+ * origin.
+ */
+ if (o_path->area_id != route->path.area_id ||
+ (memcmp(&(o_path)->origin, &(route)->path.origin,
+ sizeof(struct ospf6_ls_origin)) != 0))
+ continue;
+
+ /* Cost is not same then delete current path */
+ if (o_path->cost == route->path.cost)
+ continue;
+
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&old_route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: route %s cost old %u new %u is not same, replace route",
+ __PRETTY_FUNCTION__, buf,
+ o_path->cost, route->path.cost);
+ }
+
+ /* Remove selected current rout path's nh from
+ * effective nh list.
+ */
+ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(old_route->nh_list,
+ rnode, rnext, rnh)) {
+ if (!ospf6_nexthop_is_same(rnh, nh))
+ continue;
+ listnode_delete(old_route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(rnh);
+ route_updated = true;
+ }
+ }
+
+ listnode_delete(old_route->paths, o_path);
+ ospf6_path_free(o_path);
+
+ /* Current route's path (adv_router info) is similar
+ * to route being added.
+ * Replace current route's path with paths list head.
+ * Update FIB with effective NHs.
+ */
+ if (listcount(old_route->paths)) {
+ if (old_route->path.origin.id ==
+ route->path.origin.id &&
+ old_route->path.origin.adv_router ==
+ route->path.origin.adv_router) {
+ struct ospf6_path *h_path;
+
+ h_path = (struct ospf6_path *)
+ listgetdata(listhead(old_route->paths));
+ old_route->path.origin.type =
+ h_path->origin.type;
+ old_route->path.origin.id =
+ h_path->origin.id;
+ old_route->path.origin.adv_router =
+ h_path->origin.adv_router;
+ }
+
+ if (route_updated) {
+ for (ALL_LIST_ELEMENTS(old_route->paths,
+ anode, anext, o_path)) {
+ ospf6_merge_nexthops(
+ old_route->nh_list,
+ o_path->nh_list);
+ }
+ /* Update ospf6 route table and
+ * RIB/FIB with effective
+ * nh_list
+ */
+ if (oa->route_table->hook_add)
+ (*oa->route_table->hook_add)
+ (old_route);
+ break;
+ }
+ } else {
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&old_route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: route %s old cost %u new cost %u, delete old entry.",
+ __PRETTY_FUNCTION__, buf,
+ old_route->path.cost,
+ route->path.cost);
+ }
+ ospf6_route_remove(old_route,
+ oa->route_table);
+ break;
+ }
+ }
+ if (route_updated)
+ break;
+ }
+
+ for (old_route = old; old_route; old_route = old_route->next) {
+
+ if (!ospf6_route_is_same(old_route, route) ||
+ (old_route->path.type != route->path.type))
+ continue;
+
+ /* Old Route and New Route have Equal Cost, Merge NHs */
+ if (old_route->path.cost == route->path.cost) {
+ route_found = true;
+
+ /* check if this path exists already in
+ * route->paths list, if so, replace nh_list.
+ */
+ for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
+ o_path)) {
+ if (o_path->area_id == route->path.area_id &&
+ (memcmp(&(o_path)->origin,
+ &(route)->path.origin,
+ sizeof(struct ospf6_ls_origin)) == 0))
+ break;
+ }
+ /* If path is not found in old_route paths's list,
+ * add a new path to route paths list and merge
+ * nexthops in route->path->nh_list.
+ * Otherwise replace existing path's nh_list.
+ */
+ if (o_path == NULL) {
+ ecmp_path = ospf6_path_dup(&route->path);
+
+ /* Add a nh_list to new ecmp path */
+ ospf6_copy_nexthops(ecmp_path->nh_list,
+ route->nh_list);
+ /* Merge nexthop to existing route's nh_list */
+ ospf6_route_merge_nexthops(old_route, route);
+ /* Add the new path to route's path list */
+ listnode_add_sort(old_route->paths, ecmp_path);
+
+ UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
+ SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE);
+ /* Update RIB/FIB */
+ if (oa->route_table->hook_add)
+ (*oa->route_table->hook_add)
+ (old_route);
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: route %s %p another path added with nh %u, effective paths %u nh %u",
+ __PRETTY_FUNCTION__, buf,
+ (void *)old_route,
+ listcount(ecmp_path->nh_list),
+ old_route->paths ?
+ listcount(old_route->paths)
+ : 0,
+ listcount(old_route->nh_list));
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
+ nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(
+ old_route->nh_list,
+ rnode, rnext, rnh)) {
+ if (!ospf6_nexthop_is_same(rnh,
+ nh))
+ continue;
+
+ listnode_delete(
+ old_route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(rnh);
+ }
+ }
+ list_delete_all_node(o_path->nh_list);
+ ospf6_copy_nexthops(o_path->nh_list,
+ route->nh_list);
+
+ /* Merge nexthop to existing route's nh_list */
+ ospf6_route_merge_nexthops(old_route,
+ route);
+
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix,
+ buf, sizeof(buf));
+ zlog_debug("%s: existing route %s %p with effective paths %u nh count %u",
+ __PRETTY_FUNCTION__, buf,
+ (void *)old_route,
+ listcount(old_route->paths),
+ old_route->nh_list ?
+ listcount(old_route->nh_list)
+ : 0);
+ }
+
+ UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
+ SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE);
+ /* Update ospf6 route table and RIB/FIB */
+ if (oa->route_table->hook_add)
+ (*oa->route_table->hook_add)
+ (old_route);
+ }
+ /* Delete the new route its info added to existing
+ * route.
+ */
+ ospf6_route_delete(route);
+
+ break;
+ }
+ }
+
+ if (!route_found) {
+ /* Add new route to existing node in ospf6 route table. */
+ ospf6_route_add(route, oa->route_table);
+ }
+}
+
void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
{
struct ospf6_area *oa;
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
struct prefix ls_prefix;
- struct ospf6_route *route, *ls_entry;
+ struct ospf6_route *route, *ls_entry, *old;
int prefix_num;
struct ospf6_prefix *op;
char *start, *current, *end;
@@ -1422,14 +1656,27 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
ospf6_copy_nexthops(path->nh_list, route->path.nh_list);
listnode_add_sort(route->paths, path);
- if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
- prefix2str(&route->prefix, buf, sizeof(buf));
- zlog_debug(" route %s add with cost %u nh count %u",
- buf, route->path.cost,
- listcount(route->nh_list));
+ old = ospf6_route_lookup(&route->prefix, oa->route_table);
+ if (old && (ospf6_route_cmp(route, old) == 0)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug(" Update route: %s old cost %u new cost %u nh count %u paths %u",
+ buf,
+ old->path.cost, route->path.cost,
+ listcount(route->nh_list),
+ listcount(route->paths));
+ }
+ ospf6_intra_prefix_route_ecmp_path(oa, old, route);
+ } else {
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug(" route %s add with cost %u nh %u paths %u",
+ buf, route->path.cost,
+ listcount(route->nh_list),
+ listcount(route->paths));
+ }
+ ospf6_route_add(route, oa->route_table);
}
-
- ospf6_route_add(route, oa->route_table);
prefix_num--;
}
@@ -1442,14 +1689,15 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
struct ospf6_area *oa;
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
struct prefix prefix;
- struct ospf6_route *route, *nroute;
+ struct ospf6_route *route, *nroute, *route_to_del;
int prefix_num;
struct ospf6_prefix *op;
char *start, *current, *end;
char buf[PREFIX2STR_BUFFER];
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
- zlog_debug("%s disappearing", lsa->name);
+ zlog_debug("%s: %s disappearing", __PRETTY_FUNCTION__,
+ lsa->name);
oa = OSPF6_AREA(lsa->lsdb->data);
@@ -1469,6 +1717,22 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
break;
prefix_num--;
+ route_to_del = ospf6_route_create();
+
+ memset(&route_to_del->prefix, 0, sizeof(struct prefix));
+ route_to_del->prefix.family = AF_INET6;
+ route_to_del->prefix.prefixlen = op->prefix_length;
+ ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, op);
+
+ route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
+ route_to_del->path.origin.type = lsa->header->type;
+ route_to_del->path.origin.id = lsa->header->id;
+ route_to_del->path.origin.adv_router = lsa->header->adv_router;
+ route_to_del->path.prefix_options = op->prefix_options;
+ route_to_del->path.area_id = oa->area_id;
+ route_to_del->path.type = OSPF6_PATH_TYPE_INTRA;
+ route_to_del->path.metric_type = 1;
+
memset(&prefix, 0, sizeof(struct prefix));
prefix.family = AF_INET6;
prefix.prefixlen = op->prefix_length;
@@ -1488,20 +1752,162 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
continue;
if (route->path.type != OSPF6_PATH_TYPE_INTRA)
continue;
- if (route->path.origin.type != lsa->header->type
- || route->path.origin.id != lsa->header->id
- || route->path.origin.adv_router
- != lsa->header->adv_router)
+ /* Route has multiple ECMP paths, remove matching
+ * path. Update current route's effective nh list
+ * after removal of one of the path.
+ */
+ if (listcount(route->paths) > 1) {
+ struct listnode *anode, *anext;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ struct ospf6_path *o_path;
+ bool nh_updated = false;
+
+ /* Iterate all paths of route to find maching
+ * with LSA remove info.
+ * If route->path is same, replace
+ * from paths list.
+ */
+ for (ALL_LIST_ELEMENTS(route->paths, anode,
+ anext, o_path)) {
+ if ((o_path->origin.type !=
+ lsa->header->type) ||
+ (o_path->origin.adv_router !=
+ lsa->header->adv_router) ||
+ (o_path->origin.id !=
+ lsa->header->id))
+ continue;
+
+ if (IS_OSPF6_DEBUG_EXAMIN
+ (INTRA_PREFIX)) {
+ prefix2str(&prefix, buf,
+ sizeof(buf));
+ zlog_debug(
+ "%s: route %s path found with cost %u nh %u to remove.",
+ __PRETTY_FUNCTION__,
+ buf, o_path->cost,
+ listcount(
+ o_path->nh_list));
+ }
+ /* Remove old route from global
+ * ospf6 route table.
+ * nh_update section will add
+ * back with effective nh.
+ */
+ if (oa->route_table->hook_remove)
+ (*oa->route_table->hook_remove)
+ (route);
+ /* Remove found path's nh_list from
+ * the route's nh_list.
+ */
+ for (ALL_LIST_ELEMENTS_RO(
+ o_path->nh_list,
+ nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(
+ route->nh_list,
+ rnode, rnext, rnh)) {
+ if (
+ !ospf6_nexthop_is_same(
+ rnh, nh))
+ continue;
+ listnode_delete(
+ route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(
+ rnh);
+ }
+ }
+ /* Delete the path from route's
+ * path list
+ */
+ listnode_delete(route->paths, o_path);
+ ospf6_path_free(o_path);
+ nh_updated = true;
+ break;
+ }
+
+ if (nh_updated) {
+
+ /* Iterate all paths and merge nexthop,
+ * unlesss any of the nexthop similar to
+ * ones deleted as part of path
+ * deletion.
+ */
+ for (ALL_LIST_ELEMENTS(route->paths,
+ anode, anext, o_path)) {
+ ospf6_merge_nexthops(
+ route->nh_list,
+ o_path->nh_list);
+ }
+
+ if (IS_OSPF6_DEBUG_EXAMIN(
+ INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf,
+ sizeof(buf));
+ assert(route->nh_list);
+ zlog_debug("%s: route %s update paths %u nh %u"
+ , __PRETTY_FUNCTION__,
+ buf,
+ listcount(route->paths),
+ listcount(
+ route->nh_list));
+ }
+
+ /* route's primary path is similar
+ * to LSA, replace route's primary
+ * path with route's paths list
+ * head.
+ */
+ if ((route->path.origin.id ==
+ lsa->header->id) &&
+ (route->path.origin.adv_router
+ == lsa->header->adv_router)) {
+ struct ospf6_path *h_path;
+
+ h_path = (struct ospf6_path *)
+ listgetdata(listhead(
+ route->paths));
+ route->path.origin.type =
+ h_path->origin.type;
+ route->path.origin.id =
+ h_path->origin.id;
+ route->path.origin.adv_router =
+ h_path->origin.adv_router;
+ }
+
+ /* Update Global Route table and
+ * RIB/FIB with effective
+ * nh_list
+ */
+ if (oa->route_table->hook_add)
+ (*oa->route_table->hook_add)
+ (route);
+ }
continue;
- if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
- prefix2str(&route->prefix, buf, sizeof(buf));
- zlog_debug("remove %s", buf);
+ } else {
+
+ if (route->path.origin.type != lsa->header->type
+ || route->path.origin.id != lsa->header->id
+ || route->path.origin.adv_router
+ != lsa->header->adv_router)
+ continue;
+
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("route remove %s with path %u cost %u nh %u",
+ buf, route->path.type,
+ route->path.cost,
+ listcount(route->nh_list));
+ }
+ ospf6_route_remove(route, oa->route_table);
}
- ospf6_route_remove(route, oa->route_table);
}
if (route)
ospf6_route_unlock(route);
+
+ ospf6_route_delete(route_to_del);
}
if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h
index 2abcce8c8d..672e288bf3 100644
--- a/ospf6d/ospf6_intra.h
+++ b/ospf6d/ospf6_intra.h
@@ -231,6 +231,9 @@ extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa);
extern int ospf6_orig_as_external_lsa(struct thread *thread);
extern void ospf6_intra_route_calculation(struct ospf6_area *oa);
extern void ospf6_intra_brouter_calculation(struct ospf6_area *oa);
+extern void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
+ struct ospf6_route *old,
+ struct ospf6_route *route);
extern void ospf6_intra_init(void);
diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c
index 6455f606bd..b5a1812ffa 100644
--- a/ospf6d/ospf6_neighbor.c
+++ b/ospf6d/ospf6_neighbor.c
@@ -193,8 +193,9 @@ static void ospf6_neighbor_state_change(uint8_t next_state,
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area);
- if (prev_state == OSPF6_NEIGHBOR_LOADING
- && next_state == OSPF6_NEIGHBOR_FULL) {
+ if ((prev_state == OSPF6_NEIGHBOR_LOADING ||
+ prev_state == OSPF6_NEIGHBOR_EXCHANGE) &&
+ next_state == OSPF6_NEIGHBOR_FULL) {
OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if);
on->ospf6_if->area->full_nbrs++;
}
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 67b9b9df96..8be00d9b41 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -382,6 +382,20 @@ struct ospf6_path *ospf6_path_dup(struct ospf6_path *path)
return new;
}
+void ospf6_copy_paths(struct list *dst, struct list *src)
+{
+ struct ospf6_path *path_new, *path;
+ struct listnode *node;
+
+ if (dst && src) {
+ for (ALL_LIST_ELEMENTS_RO(src, node, path)) {
+ path_new = ospf6_path_dup(path);
+ ospf6_copy_nexthops(path_new->nh_list, path->nh_list);
+ listnode_add_sort(dst, path_new);
+ }
+ }
+}
+
struct ospf6_route *ospf6_route_create(void)
{
struct ospf6_route *route;
@@ -420,6 +434,7 @@ struct ospf6_route *ospf6_route_copy(struct ospf6_route *route)
new->linkstate_id = route->linkstate_id;
new->path = route->path;
ospf6_copy_nexthops(new->nh_list, route->nh_list);
+ ospf6_copy_paths(new->paths, route->paths);
new->rnode = NULL;
new->prev = NULL;
new->next = NULL;
@@ -649,10 +664,11 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug(
- "%s %p: route add %p cost %u: update of %p old cost %u",
+ "%s %p: route add %p cost %u nh %u: update of %p old cost %u nh %u",
ospf6_route_table_name(table), (void *)table,
- (void *)route, route->path.cost, (void *)old,
- old->path.cost);
+ (void *)route, route->path.cost,
+ listcount(route->nh_list), (void *)old,
+ old->path.cost, listcount(old->nh_list));
else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: update",
ospf6_route_table_name(table));
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index 7ad1f19fc6..a69e9a920f 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -246,6 +246,7 @@ extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX];
((ra)->type == (rb)->type \
&& memcmp(&(ra)->prefix, &(rb)->prefix, sizeof(struct prefix)) == 0 \
&& memcmp(&(ra)->path, &(rb)->path, sizeof(struct ospf6_path)) == 0 \
+ && listcount(ra->paths) == listcount(rb->paths) \
&& ospf6_route_cmp_nexthops(ra, rb) == 0)
#define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST))
@@ -340,5 +341,6 @@ extern void ospf6_route_init(void);
extern void ospf6_clean(void);
extern void ospf6_path_free(struct ospf6_path *op);
extern struct ospf6_path *ospf6_path_dup(struct ospf6_path *path);
+extern void ospf6_copy_paths(struct list *dst, struct list *src);
#endif /* OSPF6_ROUTE_H */
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 8c2e706d17..8458d19952 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -127,13 +127,6 @@ static int ospf6_zebra_if_del(int command, struct zclient *zclient,
zlog_debug("Zebra Interface delete: %s index %d mtu %d",
ifp->name, ifp->ifindex, ifp->mtu6);
-#if 0
- /* XXX: ospf6_interface_if_del is not the right way to handle this,
- * because among other thinkable issues, it will also clear all
- * settings as they are contained in the struct ospf6_interface. */
- ospf6_interface_if_del (ifp);
-#endif /*0*/
-
if_set_index(ifp, IFINDEX_INTERNAL);
return 0;
}
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index fd3da45f78..6623790837 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -1756,6 +1756,7 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf,
ei.route_map_set.metric = -1;
ei.route_map_set.metric_type = -1;
ei.tag = 0;
+ ei.instance = 0;
if ((new = ospf_external_lsa_new(ospf, &ei, &type7->data->id))
== NULL) {
diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c
index 7464b14b1f..6825be83ac 100644
--- a/ospfd/ospf_snmp.c
+++ b/ospfd/ospf_snmp.c
@@ -2117,7 +2117,7 @@ static struct ospf_neighbor *ospf_snmp_nbr_lookup_next(struct in_addr *nbr_addr,
struct ospf_neighbor *nbr;
struct route_node *rn;
struct ospf_neighbor *min = NULL;
- struct ospf *ospf = ospf;
+ struct ospf *ospf;
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index 05adc5aa4f..26df7a24cd 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -1161,16 +1161,13 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf,
/* Set opaque-LSA header fields depending of the type of RFC */
if (IS_INTER_AS(lp->type)) {
- if
- IS_FLOOD_AS(lp->type)
- {
- options |= OSPF_OPTION_E; /* Enable AS external
- as we flood
- Inter-AS with
- Opaque Type 11 */
- lsa_type = OSPF_OPAQUE_AS_LSA;
- }
- else {
+ if (IS_FLOOD_AS(lp->type)) {
+ /* Enable AS external as we flood Inter-AS with Opaque
+ * Type 11
+ */
+ options |= OSPF_OPTION_E;
+ lsa_type = OSPF_OPAQUE_AS_LSA;
+ } else {
options |= LSA_OPTIONS_GET(
area); /* Get area default option */
options |= LSA_OPTIONS_NSSA_GET(area);
@@ -1210,12 +1207,12 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf,
/* Now, create an OSPF LSA instance. */
if ((new = ospf_lsa_new()) == NULL) {
- zlog_warn("ospf_mpls_te_lsa_new: ospf_lsa_new() ?");
+ zlog_warn("%s: ospf_lsa_new() ?", __func__);
stream_free(s);
return NULL;
}
if ((new->data = ospf_lsa_data_new(length)) == NULL) {
- zlog_warn("ospf_mpls_te_lsa_new: ospf_lsa_data_new() ?");
+ zlog_warn("%s: ospf_lsa_data_new() ?", __func__);
ospf_lsa_unlock(&new);
new = NULL;
stream_free(s);
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 23d00633d4..6487596706 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -118,7 +118,8 @@ static int ospf_interface_add(int command, struct zclient *zclient,
assert(ifp->info);
- if (!OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) {
+ if (IF_DEF_PARAMS(ifp)
+ && !OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) {
SET_IF_PARAM(IF_DEF_PARAMS(ifp), type);
IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp);
}
diff --git a/pbrd/.gitignore b/pbrd/.gitignore
new file mode 100644
index 0000000000..ff95d88527
--- /dev/null
+++ b/pbrd/.gitignore
@@ -0,0 +1,15 @@
+!Makefile
+Makefile.in
+libpbr.a
+pbrd
+tags
+TAGS
+.deps
+*.o
+*.lo
+*.la
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
diff --git a/pbrd/Makefile b/pbrd/Makefile
new file mode 100644
index 0000000000..e8999c3409
--- /dev/null
+++ b/pbrd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. pbrd/pbrd
+%: ALWAYS
+ @$(MAKE) -s -C .. pbrd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/pbrd/pbr_debug.c b/pbrd/pbr_debug.c
new file mode 100644
index 0000000000..e9b4a52955
--- /dev/null
+++ b/pbrd/pbr_debug.c
@@ -0,0 +1,173 @@
+/*
+ * PBR - debugging
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "debug.h"
+#include "command.h"
+#include "vector.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "pbrd/pbr_debug_clippy.c"
+#endif
+#include "pbrd/pbr_debug.h"
+
+struct debug pbr_dbg_map = {0, "PBR map"};
+struct debug pbr_dbg_zebra = {0, "PBR Zebra communications"};
+struct debug pbr_dbg_nht = {0, "PBR nexthop tracking"};
+struct debug pbr_dbg_event = {0, "PBR events"};
+
+struct debug *pbr_debugs[] = {&pbr_dbg_map, &pbr_dbg_zebra, &pbr_dbg_nht,
+ &pbr_dbg_event};
+
+const char *pbr_debugs_conflines[] = {
+ "debug pbr map",
+ "debug pbr zebra",
+ "debug pbr nht",
+ "debug pbr events",
+};
+
+/*
+ * Set or unset flags on all debugs for pbrd.
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+static void pbr_debug_set_all(uint32_t flags, bool set)
+{
+ for (unsigned int i = 0; i < array_size(pbr_debugs); i++) {
+ DEBUG_FLAGS_SET(pbr_debugs[i], flags, set);
+
+ /* if all modes have been turned off, don't preserve options */
+ if (!DEBUG_MODE_CHECK(pbr_debugs[i], DEBUG_MODE_ALL))
+ DEBUG_CLEAR(pbr_debugs[i]);
+ }
+}
+
+/*
+ * Check flags on all debugs for pbrd.
+ *
+ * flags
+ * The flags to set
+ *
+ * Returns:
+ * The subset of the given flags that were set in all pbrd debugs
+ */
+static uint32_t pbr_debug_check_all(uint32_t flags)
+{
+ uint32_t mode = DEBUG_MODE_ALL;
+
+ for (unsigned int i = 0; i < array_size(pbr_debugs); i++)
+ mode &= DEBUG_MODE_CHECK(pbr_debugs[i], flags);
+ return mode;
+}
+
+static int pbr_debug_config_write_helper(struct vty *vty, bool config)
+{
+ uint32_t mode = DEBUG_MODE_ALL;
+
+ if (config)
+ mode = DEBUG_MODE_CONF;
+
+ if (pbr_debug_check_all(DEBUG_MODE_CONF) == mode) {
+ vty_out(vty, "debug pbr\n");
+ return 0;
+ }
+
+ for (unsigned int i = 0; i < array_size(pbr_debugs); i++)
+ if (DEBUG_MODE_CHECK(pbr_debugs[i], mode))
+ vty_out(vty, "%s\n", pbr_debugs_conflines[i]);
+ return 0;
+}
+
+int pbr_debug_config_write(struct vty *vty)
+{
+ return pbr_debug_config_write_helper(vty, true);
+}
+
+/* PBR debugging CLI ------------------------------------------------------- */
+/* clang-format off */
+
+DEFPY(debug_pbr,
+ debug_pbr_cmd,
+ "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
+ NO_STR
+ DEBUG_STR
+ "Policy Based Routing\n"
+ "Policy maps\n"
+ "PBRD <-> Zebra communications\n"
+ "Nexthop tracking\n"
+ "Events\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+ if (map)
+ DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
+ if (zebra)
+ DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
+ if (nht)
+ DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
+ if (events)
+ DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
+
+ /* no specific debug --> act on all of them */
+ if (strmatch(argv[argc - 1]->text, "pbr"))
+ pbr_debug_set_all(mode, !no);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH(show_debugging_pbr,
+ show_debugging_pbr_cmd,
+ "show debugging [pbr]",
+ SHOW_STR
+ DEBUG_STR
+ "Policy Based Routing\n")
+{
+ vty_out(vty, "PBR debugging status:\n");
+
+ pbr_debug_config_write_helper(vty, false);
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format on */
+/* ------------------------------------------------------------------------- */
+
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+
+struct debug_callbacks pbr_dbg_cbs = {.debug_set_all = pbr_debug_set_all};
+
+void pbr_debug_init(void)
+{
+ debug_init(&pbr_dbg_cbs);
+}
+
+void pbr_debug_init_vty(void)
+{
+ install_node(&debug_node, pbr_debug_config_write);
+
+ install_element(VIEW_NODE, &debug_pbr_cmd);
+ install_element(CONFIG_NODE, &debug_pbr_cmd);
+
+ install_element(VIEW_NODE, &show_debugging_pbr_cmd);
+}
diff --git a/pbrd/pbr_debug.h b/pbrd/pbr_debug.h
new file mode 100644
index 0000000000..2744724629
--- /dev/null
+++ b/pbrd/pbr_debug.h
@@ -0,0 +1,53 @@
+/*
+ * PBR - debugging
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __PBR_DEBUG_H__
+#define __PBR_DEBUG_H__
+
+#include <zebra.h>
+
+#include "debug.h"
+
+/* PBR debugging records */
+extern struct debug pbr_dbg_map;
+extern struct debug pbr_dbg_zebra;
+extern struct debug pbr_dbg_nht;
+extern struct debug pbr_dbg_event;
+
+/*
+ * Initialize PBR debugging.
+ *
+ * Installs VTY commands and registers callbacks.
+ */
+void pbr_debug_init(void);
+
+/*
+ * Install PBR debugging VTY commands.
+ */
+void pbr_debug_init_vty(void);
+
+/*
+ * Print PBR debugging configuration.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int pbr_debug_config_write(struct vty *vty);
+
+#endif /* __PBR_DEBUG_H__ */
diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c
new file mode 100644
index 0000000000..ba09621083
--- /dev/null
+++ b/pbrd/pbr_main.c
@@ -0,0 +1,170 @@
+/*
+ * PBR - main code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "if.h"
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+#include "filter.h"
+#include "plist.h"
+#include "stream.h"
+#include "log.h"
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "keychain.h"
+#include "distribute.h"
+#include "libfrr.h"
+#include "routemap.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+
+#include "pbr_nht.h"
+#include "pbr_map.h"
+#include "pbr_zebra.h"
+#include "pbr_vty.h"
+#include "pbr_debug.h"
+
+zebra_capabilities_t _caps_p[] = {
+ ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN,
+};
+
+struct zebra_privs_t pbr_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0};
+
+struct option longopts[] = { { 0 } };
+
+/* Master of threads. */
+struct thread_master *master;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received");
+}
+
+/* SIGINT / SIGTERM handler. */
+static void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct quagga_signal_t pbr_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+#define PBR_VTY_PORT 2615
+
+FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT,
+
+ .proghelp = "Implementation of PBR.",
+
+ .signals = pbr_signals,
+ .n_signals = array_size(pbr_signals),
+
+ .privs = &pbr_privs,)
+
+int main(int argc, char **argv, char **envp)
+{
+ frr_preinit(&pbrd_di, argc, argv);
+ frr_opt_add("", longopts, "");
+
+ while (1) {
+ int opt;
+
+ opt = frr_getopt(argc, argv, NULL);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ default:
+ frr_help_exit(1);
+ break;
+ }
+ }
+
+ master = frr_init();
+
+ pbr_debug_init();
+
+ vrf_init(NULL, NULL, NULL, NULL);
+ nexthop_group_init(pbr_nhgroup_add_cb,
+ pbr_nhgroup_add_nexthop_cb,
+ pbr_nhgroup_del_nexthop_cb,
+ pbr_nhgroup_delete_cb);
+
+ /*
+ * So we safely ignore these commands since
+ * we are getting them at this point in time
+ */
+ access_list_init();
+ pbr_nht_init();
+ pbr_map_init();
+ pbr_zebra_init();
+ pbr_vty_init();
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c
new file mode 100644
index 0000000000..ea79320a71
--- /dev/null
+++ b/pbrd/pbr_map.c
@@ -0,0 +1,569 @@
+/*
+ * PBR-map Code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "thread.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "table.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "memory.h"
+#include "log.h"
+#include "vty.h"
+
+#include "pbr_nht.h"
+#include "pbr_map.h"
+#include "pbr_zebra.h"
+#include "pbr_memory.h"
+#include "pbr_debug.h"
+
+DEFINE_MTYPE_STATIC(PBRD, PBR_MAP, "PBR Map")
+DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_SEQNO, "PBR Map Sequence")
+DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_INTERFACE, "PBR Map Interface")
+
+static uint32_t pbr_map_sequence_unique;
+
+static inline int pbr_map_compare(const struct pbr_map *pbrmap1,
+ const struct pbr_map *pbrmap2);
+
+RB_GENERATE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare)
+
+struct pbr_map_entry_head pbr_maps = RB_INITIALIZER(&pbr_maps);
+
+DEFINE_QOBJ_TYPE(pbr_map_sequence)
+
+static inline int pbr_map_compare(const struct pbr_map *pbrmap1,
+ const struct pbr_map *pbrmap2)
+{
+ return strcmp(pbrmap1->name, pbrmap2->name);
+}
+
+static int pbr_map_sequence_compare(const struct pbr_map_sequence *pbrms1,
+ const struct pbr_map_sequence *pbrms2)
+{
+ if (pbrms1->seqno == pbrms2->seqno)
+ return 0;
+
+ if (pbrms1->seqno < pbrms2->seqno)
+ return -1;
+
+ return 1;
+}
+
+static void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->internal_nhg_name)
+ XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
+
+ XFREE(MTYPE_PBR_MAP_SEQNO, pbrms);
+}
+
+static int pbr_map_interface_compare(const struct pbr_map_interface *pmi1,
+ const struct pbr_map_interface *pmi2)
+{
+ return strcmp(pmi1->ifp->name, pmi2->ifp->name);
+}
+
+static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi)
+{
+ struct pbr_map_interface *pmi_int;
+ struct listnode *node, *nnode;
+ struct pbr_map *pbrm;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ for (ALL_LIST_ELEMENTS(pbrm->incoming, node, nnode, pmi_int)) {
+ if (pmi == pmi_int) {
+ pbr_map_policy_delete(pbrm, pmi);
+ return;
+ }
+ }
+ }
+}
+
+static const char *pbr_map_reason_str[] = {
+ "Invalid NH-group", "Invalid NH", "No Nexthops",
+ "Both NH and NH-Group", "Invalid Src or Dst", "Deleting Sequence",
+};
+
+void pbr_map_reason_string(unsigned int reason, char *buf, int size)
+{
+ unsigned int bit;
+ int len = 0;
+
+ if (!buf)
+ return;
+
+ for (bit = 0; bit < array_size(pbr_map_reason_str); bit++) {
+ if ((reason & (1 << bit)) && (len < size)) {
+ len += snprintf((buf + len), (size - len), "%s%s",
+ (len > 0) ? ", " : "",
+ pbr_map_reason_str[bit]);
+ }
+ }
+}
+
+
+void pbr_map_interface_delete(struct pbr_map *pbrm, struct interface *ifp_del)
+{
+
+ struct listnode *node;
+ struct pbr_map_interface *pmi;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) {
+ if (ifp_del == pmi->ifp)
+ break;
+ }
+
+ if (pmi)
+ pbr_map_policy_delete(pbrm, pmi);
+}
+
+void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp_add)
+{
+ struct listnode *node;
+ struct pbr_map_interface *pmi;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) {
+ if (ifp_add == pmi->ifp)
+ return;
+ }
+
+ pmi = XCALLOC(MTYPE_PBR_MAP_INTERFACE, sizeof(*pmi));
+ pmi->ifp = ifp_add;
+ pmi->pbrm = pbrm;
+ listnode_add_sort(pbrm->incoming, pmi);
+
+ pbr_map_check_valid(pbrm->name);
+ if (pbrm->valid && !pbrm->installed)
+ pbr_map_install(pbrm);
+}
+
+void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp)
+{
+ struct pbr_interface *pbr_ifp = ifp->info;
+
+ if (pbr_ifp
+ && strncmp(pbr_ifp->mapname, "", sizeof(pbr_ifp->mapname)) != 0)
+ vty_out(vty, " pbr-policy %s\n", pbr_ifp->mapname);
+}
+
+struct pbr_map *pbrm_find(const char *name)
+{
+ struct pbr_map pbrm;
+
+ strlcpy(pbrm.name, name, sizeof(pbrm.name));
+
+ return RB_FIND(pbr_map_entry_head, &pbr_maps, &pbrm);
+}
+
+extern void pbr_map_delete(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_map *pbrm;
+ struct listnode *inode;
+ struct pbr_map_interface *pmi;
+
+ pbrm = pbrms->parent;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ pbr_send_pbr_map(pbrms, pmi, false);
+
+ if (pbrms->nhg)
+ pbr_nht_delete_individual_nexthop(pbrms);
+
+ listnode_delete(pbrm->seqnumbers, pbrms);
+
+ if (pbrm->seqnumbers->count == 0) {
+ RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm);
+ XFREE(MTYPE_PBR_MAP, pbrm);
+ }
+}
+
+void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_map *pbrm = pbrms->parent;
+ struct listnode *node;
+ struct pbr_map_interface *pmi;
+
+ if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi))
+ pbr_send_pbr_map(pbrms, pmi, false);
+ }
+
+ pbrm->valid = false;
+ pbrms->nhs_installed = false;
+ pbrms->installed = false;
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+ pbrms->nhgrp_name = NULL;
+}
+
+struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique,
+ ifindex_t ifindex)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *snode, *inode;
+ struct pbr_map_interface *pmi;
+ struct pbr_map *pbrm;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) {
+ if (pmi->ifp->ifindex != ifindex)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode,
+ pbrms)) {
+ DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u",
+ __PRETTY_FUNCTION__, pbrms->unique,
+ unique);
+ if (pbrms->unique == unique)
+ return pbrms;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void pbr_map_add_interfaces(struct pbr_map *pbrm)
+{
+ struct interface *ifp;
+ struct pbr_interface *pbr_ifp;
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (ifp->info) {
+ pbr_ifp = ifp->info;
+ if (strcmp(pbrm->name, pbr_ifp->mapname) == 0)
+ pbr_map_add_interface(pbrm, ifp);
+ }
+ }
+ }
+}
+
+struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
+{
+ struct pbr_map *pbrm;
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ pbrm = pbrm_find(name);
+ if (!pbrm) {
+ pbrm = XCALLOC(MTYPE_PBR_MAP, sizeof(*pbrm));
+ strcpy(pbrm->name, name);
+
+ pbrm->seqnumbers = list_new();
+ pbrm->seqnumbers->cmp =
+ (int (*)(void *, void *))pbr_map_sequence_compare;
+ pbrm->seqnumbers->del =
+ (void (*)(void *))pbr_map_sequence_delete;
+
+ pbrm->incoming = list_new();
+ pbrm->incoming->cmp =
+ (int (*)(void *, void *))pbr_map_interface_compare;
+ pbrm->incoming->del =
+ (void (*)(void *))pbr_map_interface_list_delete;
+
+ RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm);
+
+ pbr_map_add_interfaces(pbrm);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ if (pbrms->seqno == seqno)
+ break;
+
+ }
+
+ if (!pbrms) {
+ pbrms = XCALLOC(MTYPE_PBR_MAP_SEQNO, sizeof(*pbrms));
+ pbrms->unique = pbr_map_sequence_unique++;
+ pbrms->seqno = seqno;
+ pbrms->ruleno = pbr_nht_get_next_rule(seqno);
+ pbrms->parent = pbrm;
+ pbrms->reason =
+ PBR_MAP_INVALID_SRCDST |
+ PBR_MAP_INVALID_NO_NEXTHOPS;
+
+ QOBJ_REG(pbrms, pbr_map_sequence);
+ listnode_add_sort(pbrm->seqnumbers, pbrms);
+
+ pbrm->installed = false;
+ }
+
+ return pbrms;
+}
+
+static void
+pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)
+{
+ /*
+ * Check validness of the nexthop or nexthop-group
+ */
+ if (!pbrms->nhg && !pbrms->nhgrp_name)
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+
+ if (pbrms->nhg && pbrms->nhgrp_name)
+ pbrms->reason |= PBR_MAP_INVALID_BOTH_NHANDGRP;
+
+ if (pbrms->nhg &&
+ !pbr_nht_nexthop_group_valid(pbrms->internal_nhg_name))
+ pbrms->reason |= PBR_MAP_INVALID_NEXTHOP;
+
+ if (pbrms->nhgrp_name) {
+ if (!pbr_nht_nexthop_group_valid(pbrms->nhgrp_name))
+ pbrms->reason |= PBR_MAP_INVALID_NEXTHOP_GROUP;
+ else
+ pbrms->nhs_installed = true;
+ }
+}
+
+static void pbr_map_sequence_check_src_dst_valid(struct pbr_map_sequence *pbrms)
+{
+ if (!pbrms->src && !pbrms->dst)
+ pbrms->reason |= PBR_MAP_INVALID_SRCDST;
+}
+
+/*
+ * Checks to see if we think that the pbmrs is valid. If we think
+ * the config is valid return true.
+ */
+static void pbr_map_sequence_check_valid(struct pbr_map_sequence *pbrms)
+{
+ pbr_map_sequence_check_nexthops_valid(pbrms);
+
+ pbr_map_sequence_check_src_dst_valid(pbrms);
+}
+
+static bool pbr_map_check_valid_internal(struct pbr_map *pbrm)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ pbrm->valid = true;
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ pbrms->reason = 0;
+ pbr_map_sequence_check_valid(pbrms);
+ /*
+ * A pbr_map_sequence that is invalid causes
+ * the whole shebang to be invalid
+ */
+ if (pbrms->reason != 0)
+ pbrm->valid = false;
+ }
+
+ return pbrm->valid;
+}
+
+/*
+ * For a given PBR-MAP check to see if we think it is a
+ * valid config or not. If so note that it is and return
+ * that we are valid.
+ */
+bool pbr_map_check_valid(const char *name)
+{
+ struct pbr_map *pbrm;
+
+ pbrm = pbrm_find(name);
+ if (!pbrm) {
+ DEBUGD(&pbr_dbg_map,
+ "%s: Specified PBR-MAP(%s) does not exist?",
+ __PRETTY_FUNCTION__, name);
+ return false;
+ }
+
+ pbr_map_check_valid_internal(pbrm);
+ return pbrm->valid;
+}
+
+void pbr_map_schedule_policy_from_nhg(const char *nh_group)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __PRETTY_FUNCTION__,
+ pbrm->name);
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ DEBUGD(&pbr_dbg_map, "\tNH Grp name: %s",
+ pbrms->nhgrp_name ? pbrms->nhgrp_name : "NULL");
+
+ if (pbrms->nhgrp_name
+ && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) {
+ pbrms->nhs_installed = true;
+
+ pbr_map_check(pbrms);
+ }
+
+ if (pbrms->nhg
+ && (strcmp(nh_group, pbrms->internal_nhg_name)
+ == 0)) {
+ pbrms->nhs_installed = true;
+
+ pbr_map_check(pbrms);
+ }
+ }
+ }
+}
+
+void pbr_map_policy_install(const char *name)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node, *inode;
+ struct pbr_map_interface *pmi;
+
+ DEBUGD(&pbr_dbg_map, "%s: for %s", __PRETTY_FUNCTION__, name);
+ pbrm = pbrm_find(name);
+ if (!pbrm)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ DEBUGD(&pbr_dbg_map,
+ "%s: Looking at what to install %s(%u) %d %d",
+ __PRETTY_FUNCTION__, name, pbrms->seqno, pbrm->valid,
+ pbrms->nhs_installed);
+
+ if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) {
+ DEBUGD(&pbr_dbg_map, "\tInstalling %s %u",
+ pbrm->name, pbrms->seqno);
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ pbr_send_pbr_map(pbrms, pmi, true);
+ }
+ }
+}
+
+void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi)
+{
+ struct listnode *node;
+ struct pbr_map_sequence *pbrms;
+
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ pbr_send_pbr_map(pbrms, pmi, false);
+
+ listnode_delete(pbrm->incoming, pmi);
+ pmi->pbrm = NULL;
+ XFREE(MTYPE_PBR_MAP_INTERFACE, pmi);
+}
+
+/*
+ * For a nexthop group specified, see if any of the pbr-maps
+ * are using it and if so, check to see that we are still
+ * valid for usage. If we are valid then schedule the installation/deletion
+ * of the pbr-policy.
+ */
+void pbr_map_check_nh_group_change(const char *nh_group)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node, *inode;
+ struct pbr_map_interface *pmi;
+ bool found_name;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ found_name = false;
+ if (pbrms->nhgrp_name)
+ found_name =
+ !strcmp(nh_group, pbrms->nhgrp_name);
+ else if (pbrms->nhg)
+ found_name = !strcmp(nh_group,
+ pbrms->internal_nhg_name);
+
+ if (found_name) {
+ bool original = pbrm->valid;
+
+ pbr_map_check_valid_internal(pbrm);
+
+ if (pbrm->valid && (original != pbrm->valid))
+ pbr_map_install(pbrm);
+
+ if (pbrm->valid == false)
+ for (ALL_LIST_ELEMENTS_RO(
+ pbrm->incoming, inode,
+ pmi))
+ pbr_send_pbr_map(pbrms, pmi,
+ false);
+ }
+ }
+ }
+}
+
+void pbr_map_check(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_map *pbrm;
+ struct listnode *inode;
+ struct pbr_map_interface *pmi;
+ bool install;
+
+ pbrm = pbrms->parent;
+ DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __PRETTY_FUNCTION__,
+ pbrm->name, pbrms->seqno);
+ if (pbr_map_check_valid(pbrm->name))
+ DEBUGD(&pbr_dbg_map, "We are totally valid %s\n",
+ pbrm->name);
+
+ DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64,
+ __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, pbrms->reason);
+
+ if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) {
+ install = true;
+ DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64,
+ __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno,
+ pbrms->reason);
+ DEBUGD(&pbr_dbg_map,
+ "\tSending PBR_MAP_POLICY_INSTALL event");
+ } else {
+ install = false;
+ DEBUGD(&pbr_dbg_map,
+ "%s: Removing %s(%u) reason: %" PRIu64,
+ __PRETTY_FUNCTION__, pbrm->name,
+ pbrms->seqno, pbrms->reason);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ pbr_send_pbr_map(pbrms, pmi, install);
+}
+
+void pbr_map_install(struct pbr_map *pbrm)
+{
+ struct listnode *node, *inode;
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map_interface *pmi;
+
+ if (!pbrm->incoming->count)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ pbr_send_pbr_map(pbrms, pmi, true);
+
+ pbrm->installed = true;
+}
+
+void pbr_map_init(void)
+{
+ RB_INIT(pbr_map_entry_head, &pbr_maps);
+
+ pbr_map_sequence_unique = 1;
+}
diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h
new file mode 100644
index 0000000000..5cb22d7429
--- /dev/null
+++ b/pbrd/pbr_map.h
@@ -0,0 +1,162 @@
+/*
+ * PBR-map Header
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __PBR_MAP_H__
+#define __PBR_MAP_H__
+
+struct pbr_map {
+ /*
+ * RB Tree of the pbr_maps
+ */
+ RB_ENTRY(pbr_map) pbr_map_entry;
+
+ /*
+ * The name of the PBR_MAP
+ */
+#define PBR_MAP_NAMELEN 100
+ char name[PBR_MAP_NAMELEN];
+
+ struct list *seqnumbers;
+
+ /*
+ * The list of incoming interfaces that
+ * we will apply this policy map onto
+ */
+ struct list *incoming;
+
+ /*
+ * If valid is true we think the pbr_map is valid,
+ * If false, look in individual pbrms to see
+ * what we think is the invalid reason
+ */
+ bool valid;
+
+ bool installed;
+};
+
+RB_HEAD(pbr_map_entry_head, pbr_map);
+RB_PROTOTYPE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare)
+
+struct pbr_map_interface {
+ struct interface *ifp;
+
+ struct pbr_map *pbrm;
+
+ bool delete;
+};
+
+struct pbr_map_sequence {
+ struct pbr_map *parent;
+
+ /*
+ * The Unique identifier of this specific pbrms
+ */
+ uint32_t unique;
+
+ /*
+ * The sequence of where we are for display
+ */
+ uint32_t seqno;
+
+ /*
+ * The rule number to install into
+ */
+ uint32_t ruleno;
+
+ /*
+ * Our policy Catchers
+ */
+ struct prefix *src;
+ struct prefix *dst;
+
+ /*
+ * Family of the src/dst. Needed when deleting since we clear them
+ */
+ unsigned char family;
+
+ /*
+ * The nexthop group we auto create
+ * for when the user specifies a individual
+ * nexthop
+ */
+ struct nexthop_group *nhg;
+ char *internal_nhg_name;
+
+ /*
+ * The name of the nexthop group
+ * configured in the pbr-map
+ */
+ char *nhgrp_name;
+
+ /*
+ * Do we think are nexthops are installed
+ */
+ bool nhs_installed;
+
+ /*
+ * Are we installed
+ */
+ bool installed;
+
+ /*
+ * A reason of 0 means we think the pbr_map_sequence is good to go
+ * We can accumuluate multiple failure states
+ */
+#define PBR_MAP_VALID_SEQUENCE_NUMBER 0
+#define PBR_MAP_INVALID_NEXTHOP_GROUP (1 << 0)
+#define PBR_MAP_INVALID_NEXTHOP (1 << 1)
+#define PBR_MAP_INVALID_NO_NEXTHOPS (1 << 2)
+#define PBR_MAP_INVALID_BOTH_NHANDGRP (1 << 3)
+#define PBR_MAP_INVALID_SRCDST (1 << 4)
+ uint64_t reason;
+
+ QOBJ_FIELDS
+};
+
+DECLARE_QOBJ_TYPE(pbr_map_sequence)
+
+extern struct pbr_map_entry_head pbr_maps;
+
+extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno);
+extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique,
+ ifindex_t ifindex);
+
+extern struct pbr_map *pbrm_find(const char *name);
+extern void pbr_map_delete(struct pbr_map_sequence *pbrms);
+extern void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms);
+extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp);
+extern void pbr_map_interface_delete(struct pbr_map *pbrm,
+ struct interface *ifp);
+extern void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp);
+extern void pbr_map_init(void);
+
+extern bool pbr_map_check_valid(const char *name);
+
+extern void pbr_map_check(struct pbr_map_sequence *pbrms);
+extern void pbr_map_check_nh_group_change(const char *nh_group);
+extern void pbr_map_reason_string(unsigned int reason, char *buf, int size);
+
+extern void pbr_map_schedule_policy_from_nhg(const char *nh_group);
+
+extern void pbr_map_install(struct pbr_map *pbrm);
+
+extern void pbr_map_policy_install(const char *name);
+extern void pbr_map_policy_delete(struct pbr_map *pbrm,
+ struct pbr_map_interface *pmi);
+#endif
diff --git a/pbrd/pbr_memory.c b/pbrd/pbr_memory.c
new file mode 100644
index 0000000000..febe406ca7
--- /dev/null
+++ b/pbrd/pbr_memory.c
@@ -0,0 +1,27 @@
+/*
+ * PBR memory code.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <memory.h>
+
+#include "pbrd/pbr_memory.h"
+
+
+DEFINE_MGROUP(PBRD, "pbrd")
diff --git a/pbrd/pbr_memory.h b/pbrd/pbr_memory.h
new file mode 100644
index 0000000000..a87d519099
--- /dev/null
+++ b/pbrd/pbr_memory.h
@@ -0,0 +1,24 @@
+/*
+ * pbr memory code.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __PBR_MEMORY_H__
+
+DECLARE_MGROUP(PBRD)
+
+#endif
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
new file mode 100644
index 0000000000..1ce8c2104d
--- /dev/null
+++ b/pbrd/pbr_nht.c
@@ -0,0 +1,842 @@
+/*
+ * PBR-nht Code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <log.h>
+#include <nexthop.h>
+#include <nexthop_group.h>
+#include <hash.h>
+#include <jhash.h>
+#include <vty.h>
+#include <zclient.h>
+#include <debug.h>
+
+#include "pbrd/pbr_nht.h"
+#include "pbrd/pbr_map.h"
+#include "pbrd/pbr_zebra.h"
+#include "pbrd/pbr_memory.h"
+#include "pbrd/pbr_debug.h"
+
+DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups")
+
+static struct hash *pbr_nhg_hash;
+static struct hash *pbr_nhrc_hash;
+
+static uint32_t pbr_nhg_low_table;
+static uint32_t pbr_nhg_high_table;
+static uint32_t pbr_nhg_low_rule;
+static uint32_t pbr_nhg_high_rule;
+static bool nhg_tableid[65535];
+
+static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
+ struct nexthop_group nhg);
+static void
+pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
+ struct nexthop_group nhg,
+ enum nexthop_types_t nh_afi);
+
+/*
+ * Nexthop refcount.
+ */
+struct nhrc {
+ struct nexthop nexthop;
+ unsigned int refcount;
+};
+
+/* Hash functions for pbr_nhrc_hash ---------------------------------------- */
+
+static void *pbr_nhrc_hash_alloc(void *p)
+{
+ struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc));
+ nhrc->nexthop = *(struct nexthop *)p;
+ return nhrc;
+}
+
+static int pbr_nhrc_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct nexthop *nh1, *nh2;
+
+ nh1 = arg1;
+ nh2 = arg2;
+
+ return nexthop_same(nh1, nh2);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void *pbr_nh_alloc(void *p)
+{
+ struct pbr_nexthop_cache *new;
+ struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p;
+ struct nhrc *nhrc;
+
+ new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
+ nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc);
+ new->nexthop = &nhrc->nexthop;
+
+ /* Decremented again in pbr_nh_delete */
+ ++nhrc->refcount;
+
+ DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra",
+ __PRETTY_FUNCTION__);
+
+ pbr_send_rnh(new->nexthop, true);
+
+ new->valid = false;
+ return new;
+}
+
+static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc)
+{
+ struct nhrc *nhrc;
+
+ nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop);
+
+ if (nhrc)
+ --nhrc->refcount;
+ if (!nhrc || nhrc->refcount == 0) {
+ DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra",
+ __PRETTY_FUNCTION__);
+ pbr_send_rnh((*pnhc)->nexthop, false);
+ }
+ if (nhrc && nhrc->refcount == 0) {
+ hash_release(pbr_nhrc_hash, nhrc);
+ XFREE(MTYPE_PBR_NHG, nhrc);
+ }
+
+ XFREE(MTYPE_PBR_NHG, *pnhc);
+}
+
+static void pbr_nh_delete_iterate(struct hash_backet *b, void *p)
+{
+ pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
+}
+
+static uint32_t pbr_nh_hash_key(void *arg)
+{
+ uint32_t key;
+ struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg;
+
+ key = nexthop_hash(pbrnc->nexthop);
+
+ return key;
+}
+
+static int pbr_nh_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct pbr_nexthop_cache *pbrnc1 =
+ (const struct pbr_nexthop_cache *)arg1;
+ const struct pbr_nexthop_cache *pbrnc2 =
+ (const struct pbr_nexthop_cache *)arg2;
+
+ if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id)
+ return 0;
+
+ if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex)
+ return 0;
+
+ if (pbrnc1->nexthop->type != pbrnc2->nexthop->type)
+ return 0;
+
+ switch (pbrnc1->nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ return 1;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4:
+ return pbrnc1->nexthop->gate.ipv4.s_addr
+ == pbrnc2->nexthop->gate.ipv4.s_addr;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ return !memcmp(&pbrnc1->nexthop->gate.ipv6,
+ &pbrnc2->nexthop->gate.ipv6, 16);
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type;
+ }
+
+ /*
+ * We should not get here
+ */
+ return 0;
+}
+
+static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p)
+{
+ hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL);
+ hash_free(p->nhh);
+ XFREE(MTYPE_PBR_NHG, p);
+}
+
+static void *pbr_nhgc_alloc(void *p)
+{
+ struct pbr_nexthop_group_cache *new;
+ struct pbr_nexthop_group_cache *pnhgc =
+ (struct pbr_nexthop_group_cache *)p;
+
+ new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
+
+ strcpy(new->name, pnhgc->name);
+ new->table_id = pbr_nht_get_next_tableid();
+
+ DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u",
+ __PRETTY_FUNCTION__, new->name, new->table_id);
+
+ new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal,
+ "PBR NH Cache Hash");
+ return new;
+}
+
+
+void pbr_nhgroup_add_cb(const char *name)
+{
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct nexthop_group_cmd *nhgc;
+
+ nhgc = nhgc_find(name);
+ pnhgc = pbr_nht_add_group(name);
+
+ DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__,
+ name);
+
+ pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
+ pbr_map_check_nh_group_change(name);
+}
+
+void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+ char debugstr[256];
+ struct pbr_nexthop_group_cache pnhgc_find = {};
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_cache pnhc_find = {};
+ struct pbr_nexthop_cache *pnhc;
+
+ /* find pnhgc by name */
+ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
+ pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
+
+ /* create & insert new pnhc into pnhgc->nhh */
+ pnhc_find.nexthop = (struct nexthop *)nhop;
+ pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc);
+ pnhc_find.nexthop = NULL;
+
+ /* set parent pnhgc */
+ pnhc->parent = pnhgc;
+
+ if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
+ nexthop2str(nhop, debugstr, sizeof(debugstr));
+ DEBUGD(&pbr_dbg_nht, "%s: Added %s to nexthop-group %s",
+ __PRETTY_FUNCTION__, debugstr, nhgc->name);
+ }
+
+ pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
+ pbr_map_check_nh_group_change(nhgc->name);
+}
+
+void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+ char debugstr[256];
+ struct pbr_nexthop_group_cache pnhgc_find = {};
+ 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;
+
+ /* find pnhgc by name */
+ strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
+ pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
+
+ /* delete pnhc from pnhgc->nhh */
+ pnhc_find.nexthop = (struct nexthop *)nhop;
+ pnhc = hash_release(pnhgc->nhh, &pnhc_find);
+
+ /* delete pnhc */
+ pbr_nh_delete(&pnhc);
+
+ if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
+ nexthop2str(nhop, debugstr, sizeof(debugstr));
+ DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s",
+ __PRETTY_FUNCTION__, debugstr, nhgc->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_map_check_nh_group_change(nhgc->name);
+}
+
+void pbr_nhgroup_delete_cb(const char *name)
+{
+ DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s",
+ __PRETTY_FUNCTION__, name);
+
+ /* delete group from all pbrms's */
+ pbr_nht_delete_group(name);
+
+ pbr_map_check_nh_group_change(name);
+}
+
+#if 0
+static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
+{
+ return NULL;
+}
+#endif
+
+static void pbr_nht_find_nhg_from_table_install(struct hash_backet *b,
+ void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc =
+ (struct pbr_nexthop_group_cache *)b->data;
+ uint32_t *table_id = (uint32_t *)data;
+
+ if (pnhgc->table_id == *table_id) {
+ DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s",
+ __PRETTY_FUNCTION__, *table_id, pnhgc->name);
+ pnhgc->installed = true;
+ pbr_map_schedule_policy_from_nhg(pnhgc->name);
+ }
+}
+
+void pbr_nht_route_installed_for_table(uint32_t table_id)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
+ &table_id);
+}
+
+static void pbr_nht_find_nhg_from_table_remove(struct hash_backet *b,
+ void *data)
+{
+ ;
+}
+
+void pbr_nht_route_removed_for_table(uint32_t table_id)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
+ &table_id);
+}
+
+/*
+ * Loop through all nexthops in a nexthop group to check that they are all the
+ * same. If they are not all the same, log this peculiarity.
+ *
+ * nhg
+ * The nexthop group to check
+ *
+ * Returns:
+ * - AFI of last nexthop in the group
+ * - AFI_MAX on error
+ */
+static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
+ enum nexthop_types_t nh_afi)
+{
+ struct nexthop *nexthop;
+ afi_t install_afi = AFI_MAX;
+ bool v6, v4, bh;
+
+ v6 = v4 = bh = false;
+
+ if (!nh_afi) {
+ for (ALL_NEXTHOPS(nhg, nexthop)) {
+ nh_afi = nexthop->type;
+ 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;
+ install_afi = AFI_MAX;
+ break;
+ }
+
+ if (!bh && v6 && v4)
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Saw both V6 and V4 nexthops...using %s",
+ __PRETTY_FUNCTION__, afi2str(install_afi));
+ if (bh && (v6 || v4))
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
+ __PRETTY_FUNCTION__, v4 ? "v4" : "",
+ (v4 && v6) ? " and " : "", v6 ? "v6" : "");
+
+ return install_afi;
+}
+
+static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
+ struct nexthop_group nhg)
+{
+ afi_t install_afi;
+ enum nexthop_types_t nh_afi = 0;
+
+ install_afi = pbr_nht_which_afi(nhg, nh_afi);
+
+ pnhgc->installed = false;
+
+ 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)
+{
+ afi_t install_afi;
+
+ install_afi = pbr_nht_which_afi(nhg, nh_afi);
+
+ pnhgc->installed = false;
+ pnhgc->valid = false;
+ route_delete(pnhgc, install_afi);
+}
+
+void pbr_nht_change_group(const char *name)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_group_cache find;
+ struct nexthop *nhop;
+
+ nhgc = nhgc_find(name);
+ if (!nhgc)
+ return;
+
+ memset(&find, 0, sizeof(find));
+ strcpy(find.name, name);
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ if (!pnhgc) {
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Could not find nexthop-group cache w/ name '%s'",
+ __PRETTY_FUNCTION__, name);
+ return;
+ }
+
+ for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
+ struct pbr_nexthop_cache lookup;
+ struct pbr_nexthop_cache *pnhc;
+
+ lookup.nexthop = nhop;
+ pnhc = hash_lookup(pnhgc->nhh, &lookup);
+ if (!pnhc) {
+ pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
+ pnhc->parent = pnhgc;
+ }
+ }
+ pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
+}
+
+char *pbr_nht_nexthop_make_name(char *name, size_t l,
+ uint32_t seqno, char *buffer)
+{
+ snprintf(buffer, l, "%s%u", name, seqno);
+ return buffer;
+}
+
+void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_group_cache find;
+ struct pbr_nexthop_cache *pnhc;
+ struct pbr_nexthop_cache lookup;
+
+ memset(&find, 0, sizeof(find));
+ pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_MAP_NAMELEN,
+ pbrms->seqno, find.name);
+ if (!pbrms->internal_nhg_name)
+ pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
+
+ pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
+
+ lookup.nexthop = pbrms->nhg->nexthop;
+ pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
+ pnhc->parent = pnhgc;
+ pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
+}
+
+void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
+{
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_group_cache find;
+ struct pbr_nexthop_cache *pnhc;
+ struct pbr_nexthop_cache lup;
+ struct pbr_map *pbrm = pbrms->parent;
+ struct listnode *node;
+ struct pbr_map_interface *pmi;
+ struct nexthop *nh;
+ enum nexthop_types_t nh_afi = 0;
+
+ if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi))
+ pbr_send_pbr_map(pbrms, pmi, false);
+ }
+
+ pbrm->valid = false;
+ pbrms->nhs_installed = false;
+ pbrms->installed = false;
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+
+ memset(&find, 0, sizeof(find));
+ strcpy(&find.name[0], pbrms->internal_nhg_name);
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ nh = pbrms->nhg->nexthop;
+ nh_afi = 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);
+
+ hash_release(pbr_nhg_hash, pnhgc);
+
+ nexthop_del(pbrms->nhg, nh);
+ nexthop_free(nh);
+ nexthop_group_delete(&pbrms->nhg);
+ XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
+}
+
+struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
+{
+ struct nexthop *nhop;
+ struct nexthop_group_cmd *nhgc;
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_group_cache lookup;
+
+ nhgc = nhgc_find(name);
+
+ if (!nhgc) {
+ zlog_warn("%s: Could not find group %s to add",
+ __PRETTY_FUNCTION__, name);
+ return NULL;
+ }
+
+ strcpy(lookup.name, name);
+ pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
+ DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__,
+ pnhgc);
+
+ for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
+ struct pbr_nexthop_cache lookup;
+ struct pbr_nexthop_cache *pnhc;
+
+ lookup.nexthop = nhop;
+ pnhc = hash_lookup(pnhgc->nhh, &lookup);
+ if (!pnhc) {
+ pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
+ pnhc->parent = pnhgc;
+ }
+ }
+
+ return pnhgc;
+}
+
+void pbr_nht_delete_group(const char *name)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *snode;
+ struct pbr_map *pbrm;
+ struct pbr_nexthop_group_cache pnhgc_find;
+ struct pbr_nexthop_group_cache *pnhgc;
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
+ if (pbrms->nhgrp_name
+ && strmatch(pbrms->nhgrp_name, name)) {
+ pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
+ nexthop_group_delete(&pbrms->nhg);
+ pbrms->nhg = NULL;
+ pbrms->internal_nhg_name = NULL;
+ pbrm->valid = false;
+ }
+ }
+ }
+
+ strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
+ pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
+ pbr_nhgc_delete(pnhgc);
+}
+
+bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
+{
+ DEBUGD(&pbr_dbg_nht, "%s: %p", __PRETTY_FUNCTION__, nhg);
+ return true;
+}
+
+bool pbr_nht_nexthop_group_valid(const char *name)
+{
+ struct pbr_nexthop_group_cache *pnhgc;
+ struct pbr_nexthop_group_cache lookup;
+
+ DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name);
+
+ strcpy(lookup.name, name);
+ pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
+ if (!pnhgc)
+ return false;
+ DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __PRETTY_FUNCTION__, pnhgc->valid,
+ pnhgc->installed);
+ if (pnhgc->valid && pnhgc->installed)
+ return true;
+
+ return false;
+}
+
+struct pbr_nht_individual {
+ struct zapi_route *nhr;
+
+ uint32_t valid;
+};
+
+static void pbr_nht_individual_nexthop_update_lookup(struct hash_backet *b,
+ void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct pbr_nht_individual *pnhi = data;
+ char buf[PREFIX_STRLEN];
+ bool old_valid;
+
+ old_valid = pnhc->valid;
+
+ switch (pnhi->nhr->prefix.family) {
+ case AF_INET:
+ if (pnhc->nexthop->gate.ipv4.s_addr
+ == pnhi->nhr->prefix.u.prefix4.s_addr)
+ pnhc->valid = !!pnhi->nhr->nexthop_num;
+ break;
+ case AF_INET6:
+ if (memcmp(&pnhc->nexthop->gate.ipv6,
+ &pnhi->nhr->prefix.u.prefix6, 16)
+ == 0)
+ pnhc->valid = !!pnhi->nhr->nexthop_num;
+ break;
+ }
+
+ DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d",
+ prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid,
+ pnhc->valid);
+
+ if (pnhc->valid)
+ pnhi->valid += 1;
+}
+
+static void pbr_nht_nexthop_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.nhr = (struct zapi_route *)data;
+ pnhi.valid = 0;
+ hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_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_update(struct zapi_route *nhr)
+{
+ hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
+}
+
+static uint32_t pbr_nhg_hash_key(void *arg)
+{
+ struct pbr_nexthop_group_cache *nhgc =
+ (struct pbr_nexthop_group_cache *)arg;
+
+ return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
+}
+
+static int pbr_nhg_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct pbr_nexthop_group_cache *nhgc1 =
+ (const struct pbr_nexthop_group_cache *)arg1;
+ const struct pbr_nexthop_group_cache *nhgc2 =
+ (const struct pbr_nexthop_group_cache *)arg2;
+
+ return !strcmp(nhgc1->name, nhgc2->name);
+}
+
+
+uint32_t pbr_nht_get_next_tableid(void)
+{
+ uint32_t i;
+ bool found = false;
+
+ for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
+ if (nhg_tableid[i] == false) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ nhg_tableid[i] = true;
+ return i;
+ } else
+ return 0;
+}
+
+void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
+{
+ pbr_nhg_low_table = low;
+ pbr_nhg_high_table = high;
+}
+
+void pbr_nht_write_table_range(struct vty *vty)
+{
+ if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
+ || pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
+ vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
+ pbr_nhg_high_table);
+ }
+}
+
+uint32_t pbr_nht_get_next_rule(uint32_t seqno)
+{
+ return seqno + pbr_nhg_low_rule - 1;
+}
+void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
+{
+ pbr_nhg_low_rule = low;
+ pbr_nhg_high_rule = high;
+}
+
+void pbr_nht_write_rule_range(struct vty *vty)
+{
+ if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
+ || pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
+ vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
+ pbr_nhg_high_rule);
+ }
+}
+
+uint32_t pbr_nht_get_table(const char *name)
+{
+ struct pbr_nexthop_group_cache find;
+ struct pbr_nexthop_group_cache *pnhgc;
+
+ memset(&find, 0, sizeof(find));
+ strcpy(find.name, name);
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ if (!pnhgc) {
+ DEBUGD(&pbr_dbg_nht,
+ "%s: Could not find nexthop-group cache w/ name '%s'",
+ __PRETTY_FUNCTION__, name);
+ return 5000;
+ }
+
+ return pnhgc->table_id;
+}
+
+bool pbr_nht_get_installed(const char *name)
+{
+ struct pbr_nexthop_group_cache find;
+ struct pbr_nexthop_group_cache *pnhgc;
+
+ memset(&find, 0, sizeof(find));
+ strcpy(find.name, name);
+
+ pnhgc = hash_lookup(pbr_nhg_hash, &find);
+
+ if (!pnhgc)
+ return false;
+
+ return pnhgc->installed;
+}
+
+static void pbr_nht_show_nhg_nexthops(struct hash_backet *b, void *data)
+{
+ struct pbr_nexthop_cache *pnhc = b->data;
+ struct vty *vty = data;
+
+ vty_out(vty, "\tValid: %d ", pnhc->valid);
+ nexthop_group_write_nexthop(vty, pnhc->nexthop);
+}
+
+struct pbr_nht_show {
+ struct vty *vty;
+ const char *name;
+};
+
+static void pbr_nht_show_nhg(struct hash_backet *b, void *data)
+{
+ struct pbr_nexthop_group_cache *pnhgc = b->data;
+ struct pbr_nht_show *pns = data;
+ struct vty *vty;
+
+ if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
+ return;
+
+ vty = pns->vty;
+ vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
+ pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
+
+ hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
+}
+
+void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
+{
+ struct pbr_nht_show pns;
+
+ pns.vty = vty;
+ pns.name = name;
+
+ hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
+}
+
+void pbr_nht_init(void)
+{
+ pbr_nhg_hash = hash_create_size(
+ 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
+ pbr_nhrc_hash =
+ hash_create_size(16, (unsigned int (*)(void *))nexthop_hash,
+ pbr_nhrc_hash_equal, "PBR NH Hash");
+
+ pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
+ pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
+ pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
+ pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
+ memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
+}
diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h
new file mode 100644
index 0000000000..2cfb076f00
--- /dev/null
+++ b/pbrd/pbr_nht.h
@@ -0,0 +1,115 @@
+/*
+ * PBR-nht Header
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __PBR_NHT_H__
+#define __PBR_NHT_H__
+
+#include <lib/zclient.h>
+#include <lib/nexthop_group.h>
+
+#include "pbr_map.h"
+
+struct pbr_nexthop_group_cache {
+ char name[PBR_MAP_NAMELEN];
+
+ uint32_t table_id;
+
+ struct hash *nhh;
+
+ /*
+ * If all nexthops are considered valid
+ */
+ bool valid;
+
+ bool installed;
+};
+
+struct pbr_nexthop_cache {
+ struct pbr_nexthop_group_cache *parent;
+
+ struct nexthop *nexthop;
+
+ bool valid;
+};
+
+extern void pbr_nht_write_table_range(struct vty *vty);
+#define PBR_NHT_DEFAULT_LOW_TABLEID 10000
+#define PBR_NHT_DEFAULT_HIGH_TABLEID 11000
+extern void pbr_nht_set_tableid_range(uint32_t low, uint32_t high);
+
+/*
+ * Get the next tableid to use for installation
+ */
+extern uint32_t pbr_nht_get_next_tableid(void);
+/*
+ * Get the next rule number to use for installation
+ */
+extern void pbr_nht_write_rule_range(struct vty *vty);
+
+#define PBR_NHT_DEFAULT_LOW_RULE 300
+#define PBR_NHT_DEFAULT_HIGH_RULE 1300
+extern void pbr_nht_set_rule_range(uint32_t low, uint32_t high);
+
+extern uint32_t pbr_nht_get_next_rule(uint32_t seqno);
+
+extern void pbr_nhgroup_add_cb(const char *name);
+extern void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhg,
+ const struct nexthop *nhop);
+extern void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhg,
+ const struct nexthop *nhop);
+extern void pbr_nhgroup_delete_cb(const char *name);
+
+extern bool pbr_nht_nexthop_valid(struct nexthop_group *nhg);
+extern bool pbr_nht_nexthop_group_valid(const char *name);
+
+extern struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name);
+extern void pbr_nht_change_group(const char *name);
+extern void pbr_nht_delete_group(const char *name);
+
+extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms);
+extern void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms);
+/*
+ * Given the tableid of the installed default
+ * route, find the nexthop-group associated with
+ * it, then find all pbr-maps that use it and
+ * install/delete them as well.
+ */
+extern void pbr_nht_route_installed_for_table(uint32_t table_id);
+extern void pbr_nht_route_removed_for_table(uint32_t table_id);
+
+/*
+ * Given the nexthop group name, lookup the associated
+ * tableid with it
+ */
+extern uint32_t pbr_nht_get_table(const char *name);
+
+extern bool pbr_nht_get_installed(const char *name);
+
+extern char *pbr_nht_nexthop_make_name(char *name, size_t l, uint32_t seqno,
+ char *buffer);
+
+extern void pbr_nht_show_nexthop_group(struct vty *vty, const char *name);
+
+/*
+ * When we get a callback from zebra about a nexthop changing
+ */
+extern void pbr_nht_nexthop_update(struct zapi_route *nhr);
+
+extern void pbr_nht_init(void);
+#endif
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
new file mode 100644
index 0000000000..87ec3804a5
--- /dev/null
+++ b/pbrd/pbr_vty.c
@@ -0,0 +1,585 @@
+/*
+ * PBR - vty code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "vty.h"
+#include "command.h"
+#include "prefix.h"
+#include "vrf.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+#include "log.h"
+#include "json.h"
+#include "debug.h"
+
+#include "pbrd/pbr_nht.h"
+#include "pbrd/pbr_map.h"
+#include "pbrd/pbr_zebra.h"
+#include "pbrd/pbr_vty.h"
+#include "pbrd/pbr_debug.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "pbrd/pbr_vty_clippy.c"
+#endif
+
+DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)",
+ "Create pbr-map or enter pbr-map command mode\n"
+ "The name of the PBR MAP\n"
+ "Sequence to insert in existing pbr-map entry\n"
+ "Sequence number\n")
+{
+ const char *pbrm_name = argv[1]->arg;
+ uint32_t seqno = atoi(argv[3]->arg);
+ struct pbr_map_sequence *pbrms;
+
+ pbrms = pbrms_get(pbrm_name, seqno);
+ VTY_PUSH_CONTEXT(PBRMAP_NODE, pbrms);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
+ NO_STR
+ "Delete pbr-map\n"
+ "The name of the PBR MAP\n"
+ "Sequence to delete from existing pbr-map entry\n"
+ "Sequence number\n")
+{
+ const char *pbrm_name = argv[2]->arg;
+ uint32_t seqno = 0;
+ struct pbr_map *pbrm = pbrm_find(pbrm_name);
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node, *next_node;
+
+ if (argc > 3)
+ seqno = atoi(argv[4]->arg);
+
+ if (!pbrm) {
+ vty_out(vty, "pbr-map %s not found\n", pbrm_name);
+ return CMD_SUCCESS;
+ }
+
+ for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) {
+ if (seqno && pbrms->seqno != seqno)
+ continue;
+
+ pbr_map_delete(pbrms);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
+ "[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the src ip or ipv6 prefix to use\n"
+ "v4 Prefix\n"
+ "v6 Prefix\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ pbrms->family = prefix->family;
+
+ if (!no) {
+ if (prefix_same(pbrms->src, prefix))
+ return CMD_SUCCESS;
+
+ if (!pbrms->src)
+ pbrms->src = prefix_new();
+ prefix_copy(pbrms->src, prefix);
+ } else {
+ prefix_free(pbrms->src);
+ pbrms->src = 0;
+ }
+
+ pbr_map_check(pbrms);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
+ "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the src ip or ipv6 prefix to use\n"
+ "v4 Prefix\n"
+ "v6 Prefix\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ pbrms->family = prefix->family;
+
+ if (!no) {
+ if (prefix_same(pbrms->dst, prefix))
+ return CMD_SUCCESS;
+
+ if (!pbrms->dst)
+ pbrms->dst = prefix_new();
+ prefix_copy(pbrms->dst, prefix);
+ } else {
+ prefix_free(pbrms->dst);
+ pbrms->dst = NULL;
+ }
+
+ pbr_map_check(pbrms);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
+ "[no] set nexthop-group NAME$name",
+ NO_STR
+ "Set for the PBR-MAP\n"
+ "nexthop-group to use\n"
+ "The name of the nexthop-group\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ struct nexthop_group_cmd *nhgc;
+
+ if (pbrms->nhg) {
+ vty_out(vty,
+ "A `set nexthop XX` command already exists, please remove that first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nhgc = nhgc_find(name);
+ if (!nhgc) {
+ vty_out(vty, "Specified nexthop-group %s does not exist\n",
+ name);
+ vty_out(vty, "PBR-MAP will not be applied until it is created\n");
+ }
+
+ if (no) {
+ if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
+ pbr_map_delete_nexthop_group(pbrms);
+ else {
+ vty_out(vty,
+ "Nexthop Group specified: %s does not exist to remove",
+ name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ } else {
+ if (pbrms->nhgrp_name) {
+ if (strcmp(name, pbrms->nhgrp_name) != 0) {
+ vty_out(vty,
+ "Please delete current nexthop group before modifying current one");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+ }
+ pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
+ pbr_map_check(pbrms);
+ }
+
+ return CMD_SUCCESS;
+}
+
+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_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"
+ "If the nexthop is in a different vrf tell us\n"
+ "The nexthop-vrf Name\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ struct vrf *vrf;
+ struct nexthop nhop;
+ struct nexthop *nh;
+
+ if (pbrms->nhgrp_name) {
+ vty_out(vty,
+ "Please unconfigure the nexthop group before adding an individual nexthop");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (name)
+ vrf = vrf_lookup_by_name(name);
+ else
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+
+ if (!vrf) {
+ vty_out(vty, "Specified: %s is non-existent\n", name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ memset(&nhop, 0, sizeof(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) {
+ 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;
+ }
+ } else
+ nhop.type = NEXTHOP_TYPE_IPV6;
+ }
+
+ if (pbrms->nhg)
+ nh = nexthop_exists(pbrms->nhg, &nhop);
+ else {
+ char buf[100];
+
+ if (no) {
+ vty_out(vty, "No nexthops to delete");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ pbrms->nhg = nexthop_group_new();
+ pbrms->internal_nhg_name =
+ XSTRDUP(MTYPE_TMP,
+ pbr_nht_nexthop_make_name(pbrms->parent->name,
+ PBR_MAP_NAMELEN,
+ pbrms->seqno,
+ buf));
+ nh = NULL;
+ }
+
+ if (no) {
+ if (nh)
+ pbr_nht_delete_individual_nexthop(pbrms);
+ } else if (!nh) {
+
+ if (pbrms->nhg->nexthop) {
+ vty_out(vty,
+ "If you would like more than one nexthop please use nexthop-groups");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* must be adding new nexthop since !no and !nexthop_exists */
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&pbrms->nhg->nexthop, nh);
+
+ pbr_nht_add_individual_nexthop(pbrms);
+ pbr_map_check(pbrms);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (pbr_policy,
+ pbr_policy_cmd,
+ "[no] pbr-policy NAME$mapname",
+ NO_STR
+ "Policy to use\n"
+ "Name of the pbr-map to apply\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pbr_map *pbrm, *old_pbrm;
+ struct pbr_interface *pbr_ifp = ifp->info;
+
+ pbrm = pbrm_find(mapname);
+
+ if (!pbr_ifp) {
+ /*
+ * Some one could have fat fingered the interface
+ * name
+ */
+ pbr_ifp = pbr_if_new(ifp);
+ }
+
+ if (no) {
+ if (strcmp(pbr_ifp->mapname, mapname) == 0) {
+ strcpy(pbr_ifp->mapname, "");
+
+ if (pbrm)
+ pbr_map_interface_delete(pbrm, ifp);
+ }
+ } else {
+ if (strcmp(pbr_ifp->mapname, "") == 0) {
+ strcpy(pbr_ifp->mapname, mapname);
+
+ if (pbrm)
+ pbr_map_add_interface(pbrm, ifp);
+ } else {
+ if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) {
+ old_pbrm = pbrm_find(pbr_ifp->mapname);
+ if (old_pbrm)
+ pbr_map_interface_delete(old_pbrm, ifp);
+ strcpy(pbr_ifp->mapname, mapname);
+ if (pbrm)
+ pbr_map_add_interface(pbrm, ifp);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_pbr,
+ show_pbr_cmd,
+ "show pbr [json$json]",
+ SHOW_STR
+ "Policy Based Routing\n"
+ JSON_STR)
+{
+ pbr_nht_write_table_range(vty);
+ pbr_nht_write_rule_range(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_pbr_map,
+ show_pbr_map_cmd,
+ "show pbr map [NAME$name] [detail$detail] [json$json]",
+ SHOW_STR
+ "Policy Based Routing\n"
+ "PBR Map\n"
+ "PBR Map Name\n"
+ "Detailed information\n"
+ JSON_STR)
+{
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map *pbrm;
+ struct listnode *node;
+ char buf[PREFIX_STRLEN];
+ char rbuf[64];
+
+ RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
+ if (name && strcmp(name, pbrm->name) != 0)
+ continue;
+
+ vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name,
+ pbrm->valid);
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
+ if (pbrms->reason)
+ pbr_map_reason_string(pbrms->reason, rbuf,
+ sizeof(rbuf));
+ vty_out(vty,
+ " Seq: %u rule: %u Installed: %d(%u) Reason: %s\n",
+ pbrms->seqno, pbrms->ruleno, pbrms->installed,
+ pbrms->unique, pbrms->reason ? rbuf : "Valid");
+
+ if (pbrms->src)
+ vty_out(vty, "\tSRC Match: %s\n",
+ prefix2str(pbrms->src, buf,
+ sizeof(buf)));
+ if (pbrms->dst)
+ vty_out(vty, "\tDST Match: %s\n",
+ prefix2str(pbrms->dst, buf,
+ sizeof(buf)));
+
+ if (pbrms->nhgrp_name) {
+ vty_out(vty,
+ "\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
+ pbrms->nhgrp_name,
+ pbr_nht_get_table(pbrms->nhgrp_name),
+ pbrms->nhs_installed,
+ pbr_nht_get_installed(
+ pbrms->nhgrp_name));
+ } else if (pbrms->nhg) {
+ vty_out(vty, " ");
+ nexthop_group_write_nexthop(
+ vty, pbrms->nhg->nexthop);
+ vty_out(vty,
+ "\tInstalled: %u(%d) Tableid: %d\n",
+ pbrms->nhs_installed,
+ pbr_nht_get_installed(
+ pbrms->internal_nhg_name),
+ pbr_nht_get_table(
+ pbrms->internal_nhg_name));
+ } else {
+ vty_out(vty,
+ "\tNexthop-Group: Unknown Installed: 0(0)\n");
+ }
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_pbr_nexthop_group,
+ show_pbr_nexthop_group_cmd,
+ "show pbr nexthop-groups [WORD$word]",
+ SHOW_STR
+ "Policy Based Routing\n"
+ "Nexthop Groups\n"
+ "Optional Name of the nexthop group\n")
+{
+ pbr_nht_show_nexthop_group(vty, word);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_pbr_interface,
+ show_pbr_interface_cmd,
+ "show pbr interface [NAME$name] [json$json]",
+ SHOW_STR
+ "Policy Based Routing\n"
+ "PBR Interface\n"
+ "PBR Interface Name\n"
+ JSON_STR)
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+ struct pbr_interface *pbr_ifp;
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ FOR_ALL_INTERFACES(vrf, ifp) {
+ struct pbr_map *pbrm;
+
+ if (!ifp->info)
+ continue;
+
+ if (name && strcmp(ifp->name, name) != 0)
+ continue;
+
+ pbr_ifp = ifp->info;
+
+ if (strcmp(pbr_ifp->mapname, "") == 0)
+ continue;
+
+ pbrm = pbrm_find(pbr_ifp->mapname);
+ vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
+ ifp->ifindex, pbr_ifp->mapname);
+ if (!pbrm)
+ vty_out(vty, " (map doesn't exist)");
+ vty_out(vty, "\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node interface_node = {
+ INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
+};
+
+static int pbr_interface_config_write(struct vty *vty)
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ vty_frame(vty, "interface %s\n", ifp->name);
+ else
+ vty_frame(vty, "interface %s vrf %s\n",
+ ifp->name, vrf->name);
+
+ pbr_map_write_interfaces(vty, ifp);
+
+ vty_endframe(vty, "!\n");
+ }
+ }
+
+ return 1;
+}
+
+/* PBR map node structure. */
+static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1};
+
+static int pbr_vty_map_config_write_sequence(struct vty *vty,
+ struct pbr_map *pbrm,
+ struct pbr_map_sequence *pbrms)
+{
+ char buff[PREFIX_STRLEN];
+
+ vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
+
+ if (pbrms->src)
+ 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",
+ prefix2str(pbrms->dst, buff, sizeof(buff)));
+
+ if (pbrms->nhgrp_name)
+ vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
+
+ if (pbrms->nhg) {
+ vty_out(vty, " set ");
+ nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
+ }
+
+ vty_out(vty, "!\n");
+ return 1;
+}
+
+static int pbr_vty_map_config_write(struct vty *vty)
+{
+ struct pbr_map *pbrm;
+
+ pbr_nht_write_table_range(vty);
+ pbr_nht_write_rule_range(vty);
+
+ RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) {
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ pbr_vty_map_config_write_sequence(vty, pbrm, pbrms);
+ }
+
+ return 1;
+}
+
+void pbr_vty_init(void)
+{
+ install_node(&interface_node,
+ pbr_interface_config_write);
+ if_cmd_init();
+
+ install_node(&pbr_map_node,
+ pbr_vty_map_config_write);
+
+ install_default(PBRMAP_NODE);
+
+ install_element(CONFIG_NODE, &pbr_map_cmd);
+ install_element(CONFIG_NODE, &no_pbr_map_cmd);
+ install_element(INTERFACE_NODE, &pbr_policy_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
+ install_element(VIEW_NODE, &show_pbr_cmd);
+ install_element(VIEW_NODE, &show_pbr_map_cmd);
+ install_element(VIEW_NODE, &show_pbr_interface_cmd);
+ install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd);
+
+ pbr_debug_init_vty();
+}
diff --git a/pbrd/pbr_vty.h b/pbrd/pbr_vty.h
new file mode 100644
index 0000000000..6e345fd7e2
--- /dev/null
+++ b/pbrd/pbr_vty.h
@@ -0,0 +1,24 @@
+/*
+ * VTY library for PBR
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __PBR_VTY_H__
+#define __PBR_VTY_H__
+
+extern void pbr_vty_init(void);
+#endif
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
new file mode 100644
index 0000000000..a1a2d34ac1
--- /dev/null
+++ b/pbrd/pbr_zebra.c
@@ -0,0 +1,527 @@
+/*
+ * Zebra connect code.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "thread.h"
+#include "command.h"
+#include "network.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "table.h"
+#include "stream.h"
+#include "memory.h"
+#include "zclient.h"
+#include "filter.h"
+#include "plist.h"
+#include "log.h"
+#include "nexthop.h"
+#include "nexthop_group.h"
+
+#include "pbr_nht.h"
+#include "pbr_map.h"
+#include "pbr_memory.h"
+#include "pbr_zebra.h"
+#include "pbr_debug.h"
+
+DEFINE_MTYPE_STATIC(PBRD, PBR_INTERFACE, "PBR Interface")
+
+/* Zebra structure to hold current status. */
+struct zclient *zclient;
+
+static struct interface *zebra_interface_if_lookup(struct stream *s)
+{
+ char ifname_tmp[INTERFACE_NAMSIZ];
+
+ /* Read interface name. */
+ stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
+
+ /* And look it up. */
+ return if_lookup_by_name(ifname_tmp, VRF_DEFAULT);
+}
+
+struct pbr_interface *pbr_if_new(struct interface *ifp)
+{
+ struct pbr_interface *pbr_ifp;
+
+ zassert(ifp);
+ zassert(!ifp->info);
+
+ pbr_ifp = XCALLOC(MTYPE_PBR_INTERFACE, sizeof(*pbr_ifp));
+
+ if (!pbr_ifp) {
+ zlog_err("%s: PBR XCALLOC(%zu) failure", __PRETTY_FUNCTION__,
+ sizeof(*pbr_ifp));
+ return 0;
+ }
+
+ return (pbr_ifp);
+}
+
+/* Inteface addition message from zebra. */
+static int interface_add(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
+
+ if (!ifp)
+ return 0;
+
+ if (!ifp->info) {
+ struct pbr_interface *pbr_ifp;
+
+ pbr_ifp = pbr_if_new(ifp);
+ ifp->info = pbr_ifp;
+ }
+
+ return 0;
+}
+
+static int interface_delete(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+ struct stream *s;
+
+ s = zclient->ibuf;
+ /* zebra_interface_state_read () updates interface structure in iflist
+ */
+ ifp = zebra_interface_state_read(s, vrf_id);
+
+ if (ifp == NULL)
+ return 0;
+
+ if_set_index(ifp, IFINDEX_INTERNAL);
+
+ return 0;
+}
+
+static int interface_address_add(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+
+ return 0;
+}
+
+static int interface_address_delete(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct connected *c;
+
+ c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
+
+ if (!c)
+ return 0;
+
+ connected_free(c);
+ return 0;
+}
+
+static int interface_state_up(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+
+ zebra_interface_if_lookup(zclient->ibuf);
+
+ return 0;
+}
+
+static int interface_state_down(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+
+ zebra_interface_state_read(zclient->ibuf, vrf_id);
+
+ return 0;
+}
+
+static int route_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct prefix p;
+ enum zapi_route_notify_owner note;
+ uint32_t table_id;
+ char buf[PREFIX_STRLEN];
+
+ prefix2str(&p, buf, sizeof(buf));
+
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, &note))
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%s] Route install failure for table: %u",
+ __PRETTY_FUNCTION__, buf, table_id);
+ break;
+ case ZAPI_ROUTE_BETTER_ADMIN_WON:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%s] Route better admin distance won for table: %u",
+ __PRETTY_FUNCTION__, buf, table_id);
+ break;
+ case ZAPI_ROUTE_INSTALLED:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%s] Route installed succeeded for table: %u",
+ __PRETTY_FUNCTION__, buf, table_id);
+ pbr_nht_route_installed_for_table(table_id);
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%s] Route Removed succeeded for table: %u",
+ __PRETTY_FUNCTION__, buf, table_id);
+ pbr_nht_route_removed_for_table(table_id);
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: [%s] Route remove fail for table: %u",
+ __PRETTY_FUNCTION__, buf, table_id);
+ break;
+ }
+
+ return 0;
+}
+
+static int rule_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ uint32_t seqno, priority, unique;
+ enum zapi_rule_notify_owner note;
+ struct pbr_map_sequence *pbrms;
+ ifindex_t ifi;
+
+ if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
+ &ifi, &note))
+ return -1;
+
+ pbrms = pbrms_lookup_unique(unique, ifi);
+ if (!pbrms) {
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Failure to lookup pbrms based upon %u",
+ __PRETTY_FUNCTION__, unique);
+ return 0;
+ }
+
+ switch (note) {
+ case ZAPI_RULE_FAIL_INSTALL:
+ DEBUGD(&pbr_dbg_zebra, "%s: Recieved RULE_FAIL_INSTALL",
+ __PRETTY_FUNCTION__);
+ pbrms->installed = false;
+ break;
+ case ZAPI_RULE_INSTALLED:
+ pbrms->installed = true;
+ DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED",
+ __PRETTY_FUNCTION__);
+ break;
+ case ZAPI_RULE_REMOVED:
+ DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+
+ return 0;
+}
+
+static void zebra_connected(struct zclient *zclient)
+{
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+}
+
+static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg,
+ uint8_t install_afi)
+{
+ struct zapi_nexthop *api_nh;
+ struct nexthop *nhop;
+ int i;
+
+ api->prefix.family = install_afi;
+
+ i = 0;
+ for (ALL_NEXTHOPS(nhg, nhop)) {
+ api_nh = &api->nexthops[i];
+ api_nh->vrf_id = nhop->vrf_id;
+ api_nh->type = nhop->type;
+ switch (nhop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ api_nh->gate.ipv4 = nhop->gate.ipv4;
+ break;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ api_nh->gate.ipv4 = nhop->gate.ipv4;
+ api_nh->ifindex = nhop->ifindex;
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ api_nh->ifindex = nhop->ifindex;
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6, 16);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ api_nh->ifindex = nhop->ifindex;
+ memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6, 16);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ api_nh->bh_type = nhop->bh_type;
+ break;
+ }
+ i++;
+ }
+ api->nexthop_num = i;
+
+ zclient_route_send(ZEBRA_ROUTE_ADD, zclient, api);
+}
+
+/*
+ * This function assumes a default route is being
+ * installed into the appropriate tableid
+ */
+void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg,
+ afi_t install_afi)
+{
+ struct zapi_route api;
+
+ memset(&api, 0, sizeof(api));
+
+ api.vrf_id = VRF_DEFAULT;
+ api.type = ZEBRA_ROUTE_PBR;
+ api.safi = SAFI_UNICAST;
+ /*
+ * Sending a default route
+ */
+ api.tableid = pnhgc->table_id;
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ switch (install_afi) {
+ case AFI_MAX:
+ route_add_helper(&api, nhg, AF_INET);
+ route_add_helper(&api, nhg, AF_INET6);
+ break;
+ case AFI_IP:
+ route_add_helper(&api, nhg, AF_INET);
+ break;
+ case AFI_IP6:
+ route_add_helper(&api, nhg, AF_INET6);
+ break;
+ case AFI_L2VPN:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to install unsupported route type: L2VPN",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+}
+
+/*
+ * This function assumes a default route is being
+ * removed from the appropriate tableid
+ */
+void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi)
+{
+ struct zapi_route api;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = VRF_DEFAULT;
+ api.type = ZEBRA_ROUTE_PBR;
+ api.safi = SAFI_UNICAST;
+
+ api.tableid = pnhgc->table_id;
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+
+ switch (afi) {
+ case AFI_IP:
+ api.prefix.family = AF_INET;
+ zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+ break;
+ case AFI_IP6:
+ api.prefix.family = AF_INET6;
+ zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+ break;
+ case AFI_MAX:
+ api.prefix.family = AF_INET;
+ zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+ api.prefix.family = AF_INET6;
+ zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+ break;
+ case AFI_L2VPN:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to delete unsupported route type: L2VPN",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+}
+
+static int pbr_zebra_nexthop_update(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct zapi_route nhr;
+ char buf[PREFIX2STR_BUFFER];
+ uint32_t i;
+
+ zapi_nexthop_update_decode(zclient->ibuf, &nhr);
+
+ if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) {
+
+ DEBUGD(&pbr_dbg_zebra, "%s: Received Nexthop update: %s",
+ __PRETTY_FUNCTION__,
+ prefix2str(&nhr.prefix, buf, sizeof(buf)));
+
+ DEBUGD(&pbr_dbg_zebra, "%s: (\tNexthops(%u)",
+ __PRETTY_FUNCTION__, nhr.nexthop_num);
+
+ for (i = 0; i < nhr.nexthop_num; i++) {
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: \tType: %d: vrf: %d, ifindex: %d gate: %s",
+ __PRETTY_FUNCTION__, nhr.nexthops[i].type,
+ nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex,
+ inet_ntoa(nhr.nexthops[i].gate.ipv4));
+ }
+ }
+
+ pbr_nht_nexthop_update(&nhr);
+ return 1;
+}
+
+extern struct zebra_privs_t pbr_privs;
+
+void pbr_zebra_init(void)
+{
+ struct zclient_options opt = { .receive_notify = true };
+
+ zclient = zclient_new_notify(master, &opt);
+
+ zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs);
+ zclient->zebra_connected = zebra_connected;
+ zclient->interface_add = interface_add;
+ zclient->interface_delete = interface_delete;
+ zclient->interface_up = interface_state_up;
+ zclient->interface_down = interface_state_down;
+ zclient->interface_address_add = interface_address_add;
+ zclient->interface_address_delete = interface_address_delete;
+ zclient->route_notify_owner = route_notify_owner;
+ zclient->rule_notify_owner = rule_notify_owner;
+ zclient->nexthop_update = pbr_zebra_nexthop_update;
+}
+
+void pbr_send_rnh(struct nexthop *nhop, bool reg)
+{
+ uint32_t command;
+ struct prefix p;
+
+ command = (reg) ?
+ ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER;
+
+ memset(&p, 0, sizeof(p));
+ switch (nhop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ p.family = AF_INET;
+ p.u.prefix4.s_addr = nhop->gate.ipv4.s_addr;
+ p.prefixlen = 32;
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ p.family = AF_INET6;
+ memcpy(&p.u.prefix6, &nhop->gate.ipv6, 16);
+ p.prefixlen = 128;
+ break;
+ }
+
+ if (zclient_send_rnh(zclient, command, &p,
+ false, nhop->vrf_id) < 0) {
+ zlog_warn("%s: Failure to send nexthop to zebra",
+ __PRETTY_FUNCTION__);
+ }
+}
+
+static void pbr_encode_pbr_map_sequence_prefix(struct stream *s,
+ struct prefix *p,
+ unsigned char family)
+{
+ struct prefix any;
+
+ if (!p) {
+ memset(&any, 0, sizeof(any));
+ any.family = family;
+ p = &any;
+ }
+
+ stream_putc(s, p->family);
+ stream_putc(s, p->prefixlen);
+ stream_put(s, &p->u.prefix, prefix_blen(p));
+}
+
+static void pbr_encode_pbr_map_sequence(struct stream *s,
+ struct pbr_map_sequence *pbrms,
+ struct interface *ifp)
+{
+ unsigned char family;
+
+ family = AF_INET;
+ if (pbrms->family)
+ family = pbrms->family;
+
+ stream_putl(s, pbrms->seqno);
+ stream_putl(s, pbrms->ruleno);
+ stream_putl(s, pbrms->unique);
+ pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
+ stream_putw(s, 0); /* src port */
+ pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
+ stream_putw(s, 0); /* dst port */
+ if (pbrms->nhgrp_name)
+ stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name));
+ else if (pbrms->nhg)
+ stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name));
+ stream_putl(s, ifp->ifindex);
+}
+
+void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
+ struct pbr_map_interface *pmi, bool install)
+{
+ struct pbr_map *pbrm = pbrms->parent;
+ struct stream *s;
+
+ DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name,
+ install);
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
+ VRF_DEFAULT);
+
+ /*
+ * We are sending one item at a time at the moment
+ */
+ stream_putl(s, 1);
+
+ DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u",
+ __PRETTY_FUNCTION__, install ? "Installing" : "Deleting",
+ pbrm->name, install, pmi->ifp->name, pmi->delete);
+
+ pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zclient_send_message(zclient);
+}
diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h
new file mode 100644
index 0000000000..4cbefe2636
--- /dev/null
+++ b/pbrd/pbr_zebra.h
@@ -0,0 +1,42 @@
+/*
+ * Zebra connect library for PBR
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __PBR_ZEBRA_H__
+#define __PBR_ZEBRA_H__
+
+struct pbr_interface {
+ char mapname[100];
+};
+
+extern struct thread_master *master;
+
+extern void pbr_zebra_init(void);
+
+extern void route_add(struct pbr_nexthop_group_cache *pnhgc,
+ struct nexthop_group nhg, afi_t install_afi);
+extern void route_delete(struct pbr_nexthop_group_cache *pnhgc,
+ afi_t install_afi);
+
+extern void pbr_send_rnh(struct nexthop *nhop, bool reg);
+
+extern void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
+ struct pbr_map_interface *pmi, bool install);
+
+extern struct pbr_interface *pbr_if_new(struct interface *ifp);
+#endif
diff --git a/pbrd/pbrd.conf.sample b/pbrd/pbrd.conf.sample
new file mode 100644
index 0000000000..bb1c2edca8
--- /dev/null
+++ b/pbrd/pbrd.conf.sample
@@ -0,0 +1,3 @@
+!
+!
+log stdout
diff --git a/pbrd/subdir.am b/pbrd/subdir.am
new file mode 100644
index 0000000000..42ab393218
--- /dev/null
+++ b/pbrd/subdir.am
@@ -0,0 +1,37 @@
+#
+# pbrd
+#
+
+if PBRD
+noinst_LIBRARIES += pbrd/libpbr.a
+sbin_PROGRAMS += pbrd/pbrd
+dist_examples_DATA += pbrd/pbrd.conf.sample
+endif
+
+pbrd_libpbr_a_SOURCES = \
+ pbrd/pbr_zebra.c \
+ pbrd/pbr_vty.c \
+ pbrd/pbr_map.c \
+ pbrd/pbr_memory.c \
+ pbrd/pbr_nht.c \
+ pbrd/pbr_debug.c \
+ # end
+
+noinst_HEADERS += \
+ pbrd/pbr_map.h \
+ pbrd/pbr_memory.h \
+ pbrd/pbr_nht.h \
+ pbrd/pbr_vty.h \
+ pbrd/pbr_zebra.h \
+ pbrd/pbr_debug.h \
+ # end
+
+pbrd/pbr_vty_clippy.c: $(CLIPPY_DEPS)
+pbrd/pbr_vty.$(OBJEXT): pbrd/pbr_vty_clippy.c
+
+pbrd/pbr_debug_clippy.c: $(CLIPPY_DEPS)
+pbrd/pbr_debug.$(OBJEXT): pbrd/pbr_debug_clippy.c
+
+pbrd_pbrd_SOURCES = pbrd/pbr_main.c
+pbrd_pbrd_LDADD = pbrd/libpbr.a lib/libfrr.la @LIBCAP@
+
diff --git a/python/clidef.py b/python/clidef.py
index fe01a89162..2e55909647 100644
--- a/python/clidef.py
+++ b/python/clidef.py
@@ -186,7 +186,7 @@ def process_file(fn, ofd, dumpfd, all_defun):
filedata = clippy.parse(fn)
for entry in filedata['data']:
- if entry['type'] == 'DEFPY' or (all_defun and entry['type'].startswith('DEFUN')):
+ if entry['type'].startswith('DEFPY') or (all_defun and entry['type'].startswith('DEFUN')):
cmddef = entry['args'][2]
cmddef = ''.join([i[1:-1] for i in cmddef])
diff --git a/redhat/daemons b/redhat/daemons
index 889e288e57..1dd7c95d23 100644
--- a/redhat/daemons
+++ b/redhat/daemons
@@ -50,6 +50,7 @@ nhrpd=no
eigrpd=no
babeld=no
sharpd=no
+pbrd=no
#
# Command line options for the daemons
#
@@ -66,6 +67,7 @@ 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")
#
# If the vtysh_enable is yes, then the unified config is read
diff --git a/redhat/frr.init b/redhat/frr.init
index a62647b258..19b282fe1a 100755
--- a/redhat/frr.init
+++ b/redhat/frr.init
@@ -33,7 +33,7 @@ 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 ldpd nhrpd eigrpd babeld"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd pimd pbrd ldpd nhrpd eigrpd babeld"
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index 14fb910219..f83c08e611 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -27,6 +27,7 @@
%{!?with_bgp_vnc: %global with_bgp_vnc 0 }
%{!?with_pimd: %global with_pimd 1 }
%{!?with_rpki: %global with_rpki 0 }
+%{!?with_pbrd: %global with_pbrd 1 }
# path defines
%define _sysconfdir /etc/frr
@@ -86,7 +87,7 @@
%{!?frr_gid: %global frr_gid 92 }
%{!?vty_gid: %global vty_gid 85 }
-%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d
+%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd
%if %{with_ldpd}
%define daemon_ldpd ldpd
@@ -100,6 +101,12 @@
%define daemon_pimd ""
%endif
+%if %{with_pbrd}
+%define daemon_pbrd pbrd
+%else
+%define daemon_pbrd ""
+%endif
+
%if %{with_nhrpd}
%define daemon_nhrpd nhrpd
%else
@@ -124,7 +131,7 @@
%define daemon_watchfrr ""
%endif
-%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr}
+%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd}
# allow build dir to be kept
%{!?keep_build: %global keep_build 0 }
@@ -182,7 +189,7 @@ protocol. It takes multi-server and multi-thread approach to resolve
the current complexity of the Internet.
FRRouting supports BGP4, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM, LDP
-NHRP, Babel and EIGRP.
+NHRP, Babel, PBR and EIGRP.
FRRouting is a fork of Quagga.
@@ -266,6 +273,11 @@ developing OSPF-API and frr applications.
%else
--disable-pimd \
%endif
+%if %{with_pbrd}
+ --enable-pbrd \
+%else
+ --disable-pbrd \
+%endif
%if %{with_nhrpd}
--enable-nhrpd \
%else
@@ -414,6 +426,9 @@ zebra_spec_add_service nhrpd 2610/tcp "NHRPd vty"
%if %{with_pimd}
zebra_spec_add_service pimd 2611/tcp "PIMd vty"
%endif
+%if %{with_pbrd}
+zebra_spec_add_service pbrd 2615/tcp "PBRd vty"
+%endif
%if %{with_ldpd}
zebra_spec_add_service ldpd 2612/tcp "LDPd vty"
%endif
@@ -547,6 +562,9 @@ rm -rf %{buildroot}
%if %{with_pimd}
%{_sbindir}/pimd
%endif
+%if %{with_pbrd}
+ %{_sbindir}/pbrd
+%endif
%{_sbindir}/isisd
%if %{with_ldpd}
%{_sbindir}/ldpd
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
index 917e532e0a..19367638cc 100644
--- a/ripd/rip_snmp.c
+++ b/ripd/rip_snmp.c
@@ -207,7 +207,7 @@ static int rip_snmp_ifaddr_del(struct connected *ifc)
if (!rn)
return 0;
i = rn->info;
- if (rn && !strncmp(i->name, ifp->name, INTERFACE_NAMSIZ)) {
+ if (!strncmp(i->name, ifp->name, INTERFACE_NAMSIZ)) {
rn->info = NULL;
route_unlock_node(rn);
route_unlock_node(rn);
diff --git a/tests/ospf6d/test_lsdb.c b/tests/ospf6d/test_lsdb.c
index 633e88e769..ec0835c719 100644
--- a/tests/ospf6d/test_lsdb.c
+++ b/tests/ospf6d/test_lsdb.c
@@ -38,9 +38,15 @@ static size_t lsa_count = 0;
static void lsa_check_resize(size_t len)
{
+ struct ospf6_lsa **templsas;
+
if (lsa_count >= len)
return;
- lsas = realloc(lsas, len * sizeof(lsas[0]));
+ templsas = realloc(lsas, len * sizeof(lsas[0]));
+ if (templsas)
+ lsas = templsas;
+ else
+ return;
memset(lsas + lsa_count, 0, sizeof(lsas[0]) * (len - lsa_count));
lsa_count = len;
diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons
index ac17fed03a..9a96c0490a 100644
--- a/tools/etc/frr/daemons
+++ b/tools/etc/frr/daemons
@@ -34,3 +34,4 @@ nhrpd=no
eigrpd=no
babeld=no
sharpd=no
+pbrd=no
diff --git a/tools/etc/frr/daemons.conf b/tools/etc/frr/daemons.conf
index 783d167318..e6c0cde968 100644
--- a/tools/etc/frr/daemons.conf
+++ b/tools/etc/frr/daemons.conf
@@ -17,6 +17,7 @@ nhrpd_options=" --daemon -A 127.0.0.1"
eigrpd_options=" --daemon -A 127.0.0.1"
babeld_options=" --daemon -A 127.0.0.1"
sharpd_options=" --daemon -A 127.0.0.1"
+pbrd_options=" --daemon -A 127.0.0.1"
# The list of daemons to watch is automatically generated by the init script.
watchfrr_enable=yes
diff --git a/tools/frr b/tools/frr
index 6de19a4bde..27136bb762 100755
--- a/tools/frr
+++ b/tools/frr
@@ -21,11 +21,13 @@ 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 babeld pimd ldpd nhrpd eigrpd sharpd"
+DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd"
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py
-. /lib/lsb/init-functions
+if [ -e /lib/lsb/init-functions ]; then
+ . /lib/lsb/init-functions
+fi
if [ -f /usr/lib/frr/ssd ]; then
SSD=/usr/lib/frr/ssd
diff --git a/tools/indent.py b/tools/indent.py
index 139049b670..d2c41e1865 100755
--- a/tools/indent.py
+++ b/tools/indent.py
@@ -6,7 +6,7 @@ import sys, re, subprocess, os
# find all DEFUNs
defun_re = re.compile(
- r'^((DEF(UN(|_ATTR|_CMD_(ELEMENT|FUNC_(DECL|TEXT))|_DEPRECATED|_NOSH|_HIDDEN|SH(|_ATTR|_DEPRECATED|_HIDDEN))?|PY)|ALIAS)\s*\(.*?)^(?=\s*\{)',
+ r'^((DEF(UN(|_ATTR|_CMD_(ELEMENT|FUNC_(DECL|TEXT))|_DEPRECATED|_NOSH|_HIDDEN|SH(|_ATTR|_DEPRECATED|_HIDDEN))?|PY|PY_ATTR|PY_HIDDEN)|ALIAS)\s*\(.*?)^(?=\s*\{)',
re.M | re.S)
define_re = re.compile(
r'((^#\s*define[^\n]+[^\\]\n)+)',
diff --git a/tools/lsan-suppressions.txt b/tools/lsan-suppressions.txt
index dd5577bd24..5184b55699 100644
--- a/tools/lsan-suppressions.txt
+++ b/tools/lsan-suppressions.txt
@@ -1 +1,5 @@
leak:clippy
+leak:PyObject_Malloc
+leak:PyObject_Realloc
+leak:PyList_Append
+leak:malloc
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 33d34fc0dd..52641de72c 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -141,6 +141,11 @@ if SNMP
vtysh_scan += $(top_srcdir)/lib/agentx.c
endif
+if PBRD
+vtysh_scan += $(top_srcdir)/pbrd/pbr_vty.c
+vtysh_scan += $(top_srcdir)/pbrd/pbr_debug.c
+endif
+
vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
$(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \
@@ -148,6 +153,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/vrf.c \
$(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
$(top_srcdir)/lib/logicalrouter.c \
+ $(top_srcdir)/lib/nexthop_group.c \
$(top_srcdir)/zebra/interface.c \
$(top_srcdir)/zebra/irdp_interface.c \
$(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index bdee0d47e2..c0277b3d61 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -99,6 +99,9 @@ foreach (@ARGV) {
elsif ($file =~ /lib\/ns\.c$/) {
$protocol = "VTYSH_ZEBRA";
}
+ elsif ($file =~ /lib\/nexthop_group\.c$/) {
+ $protocol = "VTYSH_PBRD";
+ }
elsif ($file =~ /lib\/plist\.c$/) {
if ($defun_array[1] =~ m/ipv6/) {
$protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD|VTYSH_ISISD";
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 14c7c2fed6..2400908183 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -79,6 +79,7 @@ struct vtysh_client vtysh_client[] = {
{.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL},
{.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL},
{.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL},
+ {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL},
};
enum vtysh_write_integrated vtysh_write_integrated =
@@ -141,21 +142,22 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
bufvalid += nread;
+ /*
+ * We expect string output from daemons, so instead of looking
+ * for the full 3 null bytes of the terminator, we check for
+ * just one instead and assume it is the first byte of the
+ * terminator. The presence of the full terminator is checked
+ * later.
+ */
if (bufvalid - buf >= 4)
- end = memmem(bufvalid - 4, 4, terminator,
- sizeof(terminator));
-
- if (end && end + sizeof(terminator) + 1 > bufvalid)
- /* found \0\0\0 but return code hasn't been read yet */
- end = NULL;
- if (end)
- ret = end[sizeof(terminator)];
+ end = memmem(bufvalid - 4, 4, "\0", 1);
/*
* calculate # bytes we have, up to & not including the
* terminator if present
*/
size_t textlen = (end ? end : bufvalid) - buf;
+ bool b = false;
/* feed line processing callback if present */
while (callback && bufvalid > buf && (end > buf || !end)) {
@@ -165,16 +167,38 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
/* line break */
*eol++ = '\0';
else if (end == buf)
- /* no line break, end of input, no text left
- * before end
- * => don't insert an empty line at the end */
- break;
+ /*
+ * no line break, end of input, no text left
+ * before end; nothing to write
+ */
+ b = true;
else if (end)
- /* no line break, end of input, but some text
- * left */
+ /* no nl, end of input, but some text left */
eol = end;
- else
- /* continue reading */
+ else if (bufvalid == buf + bufsz) {
+ /*
+ * no nl, no end of input, no buffer space;
+ * realloc
+ */
+ char *new;
+
+ bufsz *= 2;
+ if (buf == stackbuf) {
+ new = XMALLOC(MTYPE_TMP, bufsz);
+ memcpy(new, stackbuf, sizeof(stackbuf));
+ } else
+ new = XREALLOC(MTYPE_TMP, buf, bufsz);
+
+ bufvalid = bufvalid - buf + new;
+ buf = new;
+ /* if end != NULL, we won't be reading more
+ * data... */
+ assert(end == NULL);
+ b = true;
+ } else
+ b = true;
+
+ if (b)
break;
/* eol is at line end now, either \n => \0 or \0\0\0 */
@@ -187,10 +211,7 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
if (callback)
callback(cbarg, buf);
- if (eol == end)
- /* \n\0\0\0 */
- break;
-
+ /* shift back data and adjust bufvalid */
memmove(buf, eol, bufvalid - eol);
bufvalid -= eol - buf;
if (end)
@@ -203,23 +224,28 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
fwrite(buf, 1, textlen, fp);
memmove(buf, buf + textlen, bufvalid - buf - textlen);
bufvalid -= textlen;
+ if (end)
+ end -= textlen;
+
+ /*
+ * ----------------------------------------------------
+ * At this point `buf` should be in one of two states:
+ * - Empty (i.e. buf == bufvalid)
+ * - Contains up to 4 bytes of the terminator
+ * ----------------------------------------------------
+ */
+ assert(((buf == bufvalid)
+ || (bufvalid - buf <= 4 && buf[0] == 0x00)));
}
- if (bufvalid == buf + bufsz) {
- char *new;
- bufsz *= 2;
- if (buf == stackbuf) {
- new = XMALLOC(MTYPE_TMP, bufsz);
- memcpy(new, stackbuf, sizeof(stackbuf));
- } else
- new = XREALLOC(MTYPE_TMP, buf, bufsz);
-
- bufvalid = bufvalid - buf + new;
- buf = new;
- /* if end != NULL, we won't be reading more data... */
- assert(end == NULL);
+ /* if we have the terminator, break */
+ if (end && bufvalid - buf == 4) {
+ assert(!memcmp(buf, terminator, 3));
+ ret = buf[3];
+ break;
}
- } while (!end);
+
+ } while (true);
goto out;
out_err:
@@ -997,8 +1023,15 @@ static struct cmd_node vrf_node = {
VRF_NODE, "%s(config-vrf)# ",
};
+static struct cmd_node nh_group_node = {
+ NH_GROUP_NODE,
+ "%s(config-nh-group)# ",
+};
+
static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# "};
+static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# "};
+
static struct cmd_node zebra_node = {ZEBRA_NODE, "%s(config-router)# "};
static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE,
@@ -1504,6 +1537,24 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd,
return CMD_SUCCESS;
}
+DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd,
+ "pbr-map NAME seq (1-1000)",
+ "Create pbr-map or enter pbr-map command mode\n"
+ "The name of the PBR MAP\n"
+ "Sequence to insert to/delete from existing pbr-map entry\n"
+ "Sequence number\n")
+{
+ vty->node = PBRMAP_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
+ NO_STR
+ "Delete pbr-map\n"
+ "The name of the PBR MAP\n"
+ "Sequence to delete from existing pbr-map entry\n"
+ "Sequence number\n")
+
DEFUNSH(VTYSH_ALL, vtysh_line_vty, vtysh_line_vty_cmd, "line vty",
"Configure a terminal line\n"
"Virtual terminal\n")
@@ -1563,6 +1614,7 @@ static int vtysh_exit(struct vty *vty)
case LDP_L2VPN_NODE:
case ISIS_NODE:
case RMAP_NODE:
+ case PBRMAP_NODE:
case VTY_NODE:
case KEYCHAIN_NODE:
vtysh_execute("end");
@@ -1709,6 +1761,18 @@ DEFUNSH(VTYSH_RMAP, vtysh_quit_rmap, vtysh_quit_rmap_cmd, "quit",
return vtysh_exit_rmap(self, vty, argc, argv);
}
+DEFUNSH(VTYSH_PBRD, vtysh_exit_pbr_map, vtysh_exit_pbr_map_cmd, "exit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_PBRD, vtysh_quit_pbr_map, vtysh_quit_pbr_map_cmd, "quit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit_rmap(self, vty, argc, argv);
+}
+
DEFUNSH(VTYSH_BGPD, vtysh_exit_bgpd, vtysh_exit_bgpd_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
@@ -1852,6 +1916,20 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd,
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
+DEFUNSH(VTYSH_PBRD, vtysh_nexthop_group, vtysh_nexthop_group_cmd,
+ "nexthop-group NAME",
+ "Nexthop Group configuration\n"
+ "Name of the Nexthop Group\n")
+{
+ vty->node = NH_GROUP_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFSH(VTYSH_PBRD, vtysh_no_nexthop_group_cmd, "no nexthop-group NAME",
+ NO_STR
+ "Nexthop Group Configuration\n"
+ "Name of the Nexthop Group\n")
+
DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
"Select a VRF to configure\n"
"VRF's name\n")
@@ -1890,6 +1968,18 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit",
return vtysh_exit_vrf(self, vty, argc, argv);
}
+DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd,
+ "exit", "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_VRF, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd,
+ "quit", "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit_nexthop_group(self, vty, argc, argv);
+}
+
/* TODO Implement interface description commands in ripngd, ospf6d
* and isisd. */
DEFSH(VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_OSPFD | VTYSH_EIGRPD,
@@ -1965,7 +2055,7 @@ DEFUN (vtysh_show_work_queues,
DEFUN (vtysh_show_work_queues_daemon,
vtysh_show_work_queues_daemon_cmd,
- "show work-queues <zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd>",
+ "show work-queues <zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pbrd>",
SHOW_STR
"Work Queue information\n"
"For the zebra daemon\n"
@@ -1974,7 +2064,8 @@ DEFUN (vtysh_show_work_queues_daemon,
"For the ospf daemon\n"
"For the ospfv6 daemon\n"
"For the bgp daemon\n"
- "For the isis daemon\n")
+ "For the isis daemon\n"
+ "For the pbr daemon\n")
{
int idx_protocol = 2;
unsigned int i;
@@ -3130,7 +3221,9 @@ void vtysh_init_vty(void)
install_node(&link_params_node, NULL);
install_node(&logicalrouter_node, NULL);
install_node(&vrf_node, NULL);
+ install_node(&nh_group_node, NULL);
install_node(&rmap_node, NULL);
+ install_node(&pbr_map_node, NULL);
install_node(&zebra_node, NULL);
install_node(&bgp_vpnv4_node, NULL);
install_node(&bgp_vpnv6_node, NULL);
@@ -3259,6 +3352,8 @@ void vtysh_init_vty(void)
install_element(KEYCHAIN_KEY_NODE, &vtysh_quit_ripd_cmd);
install_element(RMAP_NODE, &vtysh_exit_rmap_cmd);
install_element(RMAP_NODE, &vtysh_quit_rmap_cmd);
+ install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd);
+ install_element(PBRMAP_NODE, &vtysh_quit_pbr_map_cmd);
install_element(VTY_NODE, &vtysh_exit_line_vty_cmd);
install_element(VTY_NODE, &vtysh_quit_line_vty_cmd);
@@ -3299,6 +3394,7 @@ void vtysh_init_vty(void)
install_element(KEYCHAIN_NODE, &vtysh_end_all_cmd);
install_element(KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd);
install_element(RMAP_NODE, &vtysh_end_all_cmd);
+ install_element(PBRMAP_NODE, &vtysh_end_all_cmd);
install_element(VTY_NODE, &vtysh_end_all_cmd);
install_element(INTERFACE_NODE, &vtysh_interface_desc_cmd);
@@ -3321,6 +3417,11 @@ void vtysh_init_vty(void)
install_element(LOGICALROUTER_NODE, &vtysh_exit_logicalrouter_cmd);
install_element(LOGICALROUTER_NODE, &vtysh_quit_logicalrouter_cmd);
+ install_element(CONFIG_NODE, &vtysh_nexthop_group_cmd);
+ install_element(NH_GROUP_NODE, &vtysh_end_all_cmd);
+ install_element(NH_GROUP_NODE, &vtysh_exit_nexthop_group_cmd);
+ install_element(NH_GROUP_NODE, &vtysh_quit_nexthop_group_cmd);
+
install_element(VRF_NODE, &vtysh_end_all_cmd);
install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
install_element(VRF_NODE, &vtysh_quit_vrf_cmd);
@@ -3394,6 +3495,8 @@ void vtysh_init_vty(void)
install_element(CONFIG_NODE, &key_chain_cmd);
install_element(CONFIG_NODE, &vtysh_route_map_cmd);
+ install_element(CONFIG_NODE, &vtysh_pbr_map_cmd);
+ install_element(CONFIG_NODE, &vtysh_no_pbr_map_cmd);
install_element(CONFIG_NODE, &vtysh_line_vty_cmd);
install_element(KEYCHAIN_NODE, &key_cmd);
install_element(KEYCHAIN_NODE, &key_chain_cmd);
@@ -3410,6 +3513,7 @@ void vtysh_init_vty(void)
install_element(CONFIG_NODE, &vtysh_vrf_cmd);
install_element(CONFIG_NODE, &vtysh_no_vrf_cmd);
+ install_element(CONFIG_NODE, &vtysh_no_nexthop_group_cmd);
/* "write terminal" command. */
install_element(ENABLE_NODE, &vtysh_write_terminal_cmd);
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index 9b21c3376a..f3e58f309e 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -24,29 +24,30 @@
#include "memory.h"
DECLARE_MGROUP(MVTYSH)
-#define VTYSH_ZEBRA 0x01
-#define VTYSH_RIPD 0x02
-#define VTYSH_RIPNGD 0x04
-#define VTYSH_OSPFD 0x08
-#define VTYSH_OSPF6D 0x10
-#define VTYSH_BGPD 0x20
-#define VTYSH_ISISD 0x40
-#define VTYSH_PIMD 0x100
-#define VTYSH_LDPD 0x200
-#define VTYSH_WATCHFRR 0x400
-#define VTYSH_NHRPD 0x800
-#define VTYSH_EIGRPD 0x1000
-#define VTYSH_BABELD 0x2000
-#define VTYSH_SHARPD 0x4000
+#define VTYSH_ZEBRA 0x0001
+#define VTYSH_RIPD 0x0002
+#define VTYSH_RIPNGD 0x0004
+#define VTYSH_OSPFD 0x0008
+#define VTYSH_OSPF6D 0x0010
+#define VTYSH_BGPD 0x0020
+#define VTYSH_ISISD 0x0040
+#define VTYSH_PIMD 0x0080
+#define VTYSH_LDPD 0x0100
+#define VTYSH_WATCHFRR 0x0200
+#define VTYSH_NHRPD 0x0400
+#define VTYSH_EIGRPD 0x0800
+#define VTYSH_BABELD 0x1000
+#define VTYSH_SHARPD 0x2000
+#define VTYSH_PBRD 0x4000
/* commands in REALLYALL are crucial to correct vtysh operation */
#define VTYSH_REALLYALL ~0U
/* watchfrr is not in ALL since library CLI functions should not be
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
-#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD
+#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD
-#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD
+#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD
#define VTYSH_NS VTYSH_ZEBRA
#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index e94bd139e5..3748fef54c 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -205,6 +205,9 @@ void vtysh_config_parse_line(void *arg, const char *line)
config = config_get(LOGICALROUTER_NODE, line);
else if (strncmp(line, "vrf", strlen("vrf")) == 0)
config = config_get(VRF_NODE, line);
+ else if (strncmp(line, "nexthop-group", strlen("nexthop-group"))
+ == 0)
+ config = config_get(NH_GROUP_NODE, line);
else if (strncmp(line, "router-id", strlen("router-id")) == 0)
config = config_get(ZEBRA_NODE, line);
else if (strncmp(line, "router rip", strlen("router rip")) == 0)
@@ -235,6 +238,8 @@ void vtysh_config_parse_line(void *arg, const char *line)
config = config_get(ISIS_NODE, line);
else if (strncmp(line, "route-map", strlen("route-map")) == 0)
config = config_get(RMAP_NODE, line);
+ else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0)
+ config = config_get(PBRMAP_NODE, line);
else if (strncmp(line, "access-list", strlen("access-list"))
== 0)
config = config_get(ACCESS_NODE, line);
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 4de671b7fa..cd59d8094b 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -306,6 +306,7 @@ int main(int argc, char **argv, char **env)
char *homedir = NULL;
int ditch_suid = 0;
char sysconfdir[MAXPATHLEN];
+ const char *pathspace_arg = NULL;
char pathspace[MAXPATHLEN] = "";
/* SUID: drop down to calling user & go back up when needed */
@@ -363,7 +364,8 @@ int main(int argc, char **argv, char **env)
"slashes or dots are not permitted in the --pathspace option.\n");
exit(1);
}
- snprintf(pathspace, sizeof(pathspace), "/%s", optarg);
+ pathspace_arg = optarg;
+ snprintf(pathspace, sizeof(pathspace), "%s/", optarg);
break;
case 'd':
daemon_name = optarg;
@@ -419,7 +421,11 @@ int main(int argc, char **argv, char **env)
pathspace, VTYSH_CONFIG_NAME);
snprintf(frr_config, sizeof(frr_config), "%s%s%s", sysconfdir,
pathspace, FRR_CONFIG_NAME);
- strlcat(vtydir, pathspace, sizeof(vtydir));
+
+ if (pathspace_arg) {
+ strlcat(vtydir, "/", sizeof(vtydir));
+ strlcat(vtydir, pathspace_arg, sizeof(vtydir));
+ }
/* Initialize user input buffer. */
line_read = NULL;
diff --git a/zebra/client_main.c b/zebra/client_main.c
index 4035e53f7c..1ead7ee1fd 100644
--- a/zebra/client_main.c
+++ b/zebra/client_main.c
@@ -148,9 +148,8 @@ void zebra_sim(FILE *fp)
continue;
}
- for (i = 0; i < 10; i++) {
- if (!zebra_type[i].str)
- break;
+ i = 0;
+ while (zebra_type[i++].str) {
if (strcmp(zebra_type[i].str, str) == 0) {
type = zebra_type[i].type;
break;
diff --git a/zebra/connected.c b/zebra/connected.c
index 2198ddf5ea..23f2f666a0 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -495,7 +495,8 @@ void connected_delete_ipv4(struct interface *ifp, int flags,
/* Add connected IPv6 route to the interface. */
void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
- uint8_t prefixlen, const char *label)
+ struct in6_addr *broad, uint8_t prefixlen,
+ const char *label)
{
struct prefix_ipv6 *p;
struct connected *ifc;
@@ -518,6 +519,20 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
p->prefixlen = prefixlen;
ifc->address = (struct prefix *)p;
+ if (broad) {
+ p = prefix_ipv6_new();
+ p->family = AF_INET6;
+ IPV6_ADDR_COPY(&p->prefix, broad);
+ p->prefixlen = prefixlen;
+ ifc->destination = (struct prefix *)p;
+ } else {
+ if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) {
+ zlog_warn("warning: %s called for interface %s with peer flag set, but no peer address supplied",
+ __func__, ifp->name);
+ UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
+ }
+ }
+
/* Label of this address. */
if (label)
ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
@@ -536,9 +551,9 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
}
void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
- uint8_t prefixlen)
+ struct in6_addr *broad, uint8_t prefixlen)
{
- struct prefix p;
+ struct prefix p, d;
struct connected *ifc;
memset(&p, 0, sizeof(struct prefix));
@@ -546,7 +561,14 @@ void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
memcpy(&p.u.prefix6, address, sizeof(struct in6_addr));
p.prefixlen = prefixlen;
- ifc = connected_check(ifp, &p);
+ if (broad) {
+ memset(&d, 0, sizeof(struct prefix));
+ d.family = AF_INET6;
+ IPV6_ADDR_COPY(&d.u.prefix, broad);
+ d.prefixlen = prefixlen;
+ ifc = connected_check_ptp(ifp, &p, &d);
+ } else
+ ifc = connected_check_ptp(ifp, &p, NULL);
connected_delete_helper(ifc, &p);
}
diff --git a/zebra/connected.h b/zebra/connected.h
index 9b69a3f246..2a2b093395 100644
--- a/zebra/connected.h
+++ b/zebra/connected.h
@@ -42,10 +42,11 @@ extern void connected_up(struct interface *ifp, struct connected *ifc);
extern void connected_down(struct interface *ifp, struct connected *ifc);
extern void connected_add_ipv6(struct interface *ifp, int flags,
- struct in6_addr *address, uint8_t prefixlen,
- const char *label);
+ struct in6_addr *address, struct in6_addr *broad,
+ uint8_t prefixlen, const char *label);
extern void connected_delete_ipv6(struct interface *ifp,
- struct in6_addr *address, uint8_t prefixlen);
+ struct in6_addr *address,
+ struct in6_addr *broad, uint8_t prefixlen);
extern int connected_is_unnumbered(struct interface *);
diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c
index b506315ebf..f5ed945527 100644
--- a/zebra/if_ioctl.c
+++ b/zebra/if_ioctl.c
@@ -249,7 +249,7 @@ static int if_getaddrs(void)
}
#endif
- connected_add_ipv6(ifp, flags, &addr->sin6_addr,
+ connected_add_ipv6(ifp, flags, &addr->sin6_addr, NULL,
prefixlen, NULL);
}
}
diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c
index e9182304dd..6cf98e85f5 100644
--- a/zebra/if_ioctl_solaris.c
+++ b/zebra/if_ioctl_solaris.c
@@ -315,7 +315,7 @@ static int if_get_addr(struct interface *ifp, struct sockaddr *addr,
connected_add_ipv4(ifp, flags, &SIN(addr)->sin_addr, prefixlen,
(struct in_addr *)dest_pnt, label);
else if (af == AF_INET6)
- connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr,
+ connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr, NULL,
prefixlen, label);
return 0;
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 4a37c14b92..e28c189f86 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -1006,9 +1006,11 @@ int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h,
& (IFA_F_DADFAILED | IFA_F_TENTATIVE)))
connected_add_ipv6(ifp, flags,
(struct in6_addr *)addr,
+ (struct in6_addr *)broad,
ifa->ifa_prefixlen, label);
} else
connected_delete_ipv6(ifp, (struct in6_addr *)addr,
+ (struct in6_addr *)broad,
ifa->ifa_prefixlen);
}
diff --git a/zebra/interface.c b/zebra/interface.c
index 4f761a5e99..6f59a2d399 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -825,6 +825,7 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
struct in6_addr *address, int add)
{
struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id);
+ struct zebra_if *zif = ifp->info;
char buf[16] = "169.254.0.1";
struct in_addr ipv4_ll;
char mac[6];
@@ -845,6 +846,16 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
/* Add arp record */
kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
+
+ /*
+ * We need to note whether or not we originated a v6
+ * neighbor entry for this interface. So that when
+ * someone unwisely accidently deletes this entry
+ * we can shove it back in.
+ */
+ zif->v6_2_v4_ll_neigh_entry = !!add;
+ memcpy(&zif->v6_2_v4_ll_addr6, address, sizeof(*address));
+
zvrf->neigh_updates++;
}
diff --git a/zebra/interface.h b/zebra/interface.h
index fba3201c5c..9634bfdb3f 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -272,6 +272,13 @@ struct zebra_if {
struct interface *link;
struct thread *speed_update;
+
+ /*
+ * Does this interface have a v6 to v4 ll neighbor entry
+ * for bgp unnumbered?
+ */
+ bool v6_2_v4_ll_neigh_entry;
+ struct in6_addr v6_2_v4_ll_addr6;
};
DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp),
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 4ac3bed4b4..1a94807317 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -771,10 +771,11 @@ int ifam_read(struct ifa_msghdr *ifam)
if (ifam->ifam_type == RTM_NEWADDR)
connected_add_ipv6(ifp, flags, &addr.sin6.sin6_addr,
+ NULL,
ip6_masklen(mask.sin6.sin6_addr),
(isalias ? ifname : NULL));
else
- connected_delete_ipv6(ifp, &addr.sin6.sin6_addr,
+ connected_delete_ipv6(ifp, &addr.sin6.sin6_addr, NULL,
ip6_masklen(mask.sin6.sin6_addr));
break;
default:
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 15e7d4437a..6a7a75f209 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -531,6 +531,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re,
re->tag, rmap_name);
if (ret != RMAP_MATCH) {
+ UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED);
zebra_del_import_table_entry(rn, re);
return 0;
}
@@ -547,8 +548,10 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re,
break;
}
- if (same)
+ if (same) {
+ UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED);
zebra_del_import_table_entry(rn, same);
+ }
newre = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
newre->type = ZEBRA_ROUTE_TABLE;
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 3053c56d1e..df53a06bc2 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -397,14 +397,14 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
if (IS_ZEBRA_DEBUG_KERNEL) {
char buf[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
- zlog_debug("%s %s%s%s vrf %u metric: %d Admin Distance: %d",
+ zlog_debug("%s %s%s%s vrf %u(%u) metric: %d Admin Distance: %d",
nl_msg_type_to_str(h->nlmsg_type),
prefix2str(&p, buf, sizeof(buf)),
src_p.prefixlen ? " from " : "",
src_p.prefixlen
? prefix2str(&src_p, buf2, sizeof(buf2))
: "",
- vrf_id, metric, distance);
+ vrf_id, table, metric, distance);
}
afi_t afi = AFI_IP;
@@ -1253,23 +1253,21 @@ _netlink_mpls_build_multipath(const char *routedesc, zebra_nhlfe_t *nhlfe,
*
* @param cmd: Netlink command which is to be processed
* @param p: Prefix for which the change is due
- * @param nexthop: Nexthop which is currently processed
- * @param routedesc: Semantic annotation for nexthop
- * (recursive, multipath, etc.)
* @param family: Address family which the change concerns
+ * @param zvrf: The vrf we are in
+ * @param tableid: The table we are working on
*/
static void _netlink_route_debug(int cmd, struct prefix *p,
- struct nexthop *nexthop, const char *routedesc,
int family, struct zebra_vrf *zvrf,
uint32_t tableid)
{
if (IS_ZEBRA_DEBUG_KERNEL) {
char buf[PREFIX_STRLEN];
zlog_debug(
- "netlink_route_multipath() (%s): %s %s vrf %u(%u) type %s",
- routedesc, nl_msg_type_to_str(cmd),
- prefix2str(p, buf, sizeof(buf)), zvrf_id(zvrf), tableid,
- (nexthop) ? nexthop_type_to_str(nexthop->type) : "UNK");
+ "netlink_route_multipath(): %s %s vrf %u(%u)",
+ nl_msg_type_to_str(cmd),
+ prefix2str(p, buf, sizeof(buf)),
+ zvrf_id(zvrf), tableid);
}
}
@@ -1321,7 +1319,6 @@ static int netlink_route_multipath(int cmd, struct prefix *p,
struct sockaddr_nl snl;
struct nexthop *nexthop = NULL;
unsigned int nexthop_num;
- int discard = 0;
int family = PREFIX_FAMILY(p);
const char *routedesc;
int setsrc = 0;
@@ -1380,7 +1377,15 @@ static int netlink_route_multipath(int cmd, struct prefix *p,
addattr32(&req.n, sizeof req, RTA_TABLE, re->table);
}
- if (discard)
+ _netlink_route_debug(cmd, p, family, zvrf, re->table);
+
+ /*
+ * If we are not updating the route and we have received
+ * a route delete, then all we need to fill in is the
+ * prefix information to tell the kernel to schwack
+ * it.
+ */
+ if (!update && cmd == RTM_DELROUTE)
goto skip;
if (re->mtu || re->nexthop_mtu) {
@@ -1486,8 +1491,6 @@ static int netlink_route_multipath(int cmd, struct prefix *p,
? "recursive, single-path"
: "single-path";
- _netlink_route_debug(cmd, p, nexthop, routedesc,
- family, zvrf, re->table);
_netlink_route_build_singlepath(
routedesc, bytelen, nexthop, &req.n,
&req.r, sizeof req, cmd);
@@ -1570,8 +1573,6 @@ static int netlink_route_multipath(int cmd, struct prefix *p,
: "multipath";
nexthop_num++;
- _netlink_route_debug(cmd, p, nexthop, routedesc,
- family, zvrf, re->table);
_netlink_route_build_multipath(
routedesc, bytelen, nexthop, rta, rtnh,
&req.r, &src1);
@@ -2112,21 +2113,53 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
ndm = NLMSG_DATA(h);
- /* We only process neigh notifications if EVPN is enabled */
- if (!is_evpn_enabled())
- return 0;
-
/* The interface should exist. */
ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
ndm->ndm_ifindex);
if (!ifp || !ifp->info)
return 0;
- /* Drop "permanent" entries. */
- if (ndm->ndm_state & NUD_PERMANENT)
+ zif = (struct zebra_if *)ifp->info;
+
+ /* Parse attributes and extract fields of interest. */
+ memset(tb, 0, sizeof tb);
+ netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len);
+
+ if (!tb[NDA_DST]) {
+ zlog_warn("%s family %s IF %s(%u) - no DST",
+ nl_msg_type_to_str(h->nlmsg_type),
+ nl_family_to_str(ndm->ndm_family), ifp->name,
+ ndm->ndm_ifindex);
return 0;
+ }
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
+ memcpy(&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
+
+ /* Drop some "permanent" entries. */
+ if (ndm->ndm_state & NUD_PERMANENT) {
+ char buf[16] = "169.254.0.1";
+ struct in_addr ipv4_ll;
+
+ if (ndm->ndm_family != AF_INET)
+ return 0;
+
+ if (!zif->v6_2_v4_ll_neigh_entry)
+ return 0;
+
+ if (h->nlmsg_type != RTM_DELNEIGH)
+ return 0;
+
+ inet_pton(AF_INET, buf, &ipv4_ll);
+ if (ipv4_ll.s_addr != ip.ip._v4_addr.s_addr)
+ return 0;
+
+ if_nbr_ipv6ll_to_ipv4ll_neigh_update(
+ ifp, &zif->v6_2_v4_ll_addr6, true);
+ return 0;
+ }
- zif = (struct zebra_if *)ifp->info;
/* The neighbor is present on an SVI. From this, we locate the
* underlying
* bridge because we're only interested in neighbors on a VxLAN bridge.
@@ -2148,22 +2181,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
else
return 0;
- /* Parse attributes and extract fields of interest. */
- memset(tb, 0, sizeof tb);
- netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len);
-
- if (!tb[NDA_DST]) {
- zlog_warn("%s family %s IF %s(%u) - no DST",
- nl_msg_type_to_str(h->nlmsg_type),
- nl_family_to_str(ndm->ndm_family), ifp->name,
- ndm->ndm_ifindex);
- return 0;
- }
memset(&mac, 0, sizeof(struct ethaddr));
- memset(&ip, 0, sizeof(struct ipaddr));
- ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
- memcpy(&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
-
if (h->nlmsg_type == RTM_NEWNEIGH) {
if (tb[NDA_LLADDR]) {
if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) {
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index 2122f9f5fa..310f0952fa 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -66,7 +66,7 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
char buf2[PREFIX_STRLEN];
memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
- family = PREFIX_FAMILY(&rule->filter.src_ip);
+ family = PREFIX_FAMILY(&rule->rule.filter.src_ip);
bytelen = (family == AF_INET ? 4 : 16);
req.n.nlmsg_type = cmd;
@@ -77,11 +77,8 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
req.frh.family = family;
req.frh.action = FR_ACT_TO_TBL;
- if (cmd == RTM_NEWRULE)
- req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
-
/* rule's pref # */
- addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->priority);
+ addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority);
/* interface on which applied */
if (rule->ifp)
@@ -90,24 +87,24 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
/* source IP, if specified */
if (IS_RULE_FILTERING_ON_SRC_IP(rule)) {
- req.frh.src_len = rule->filter.src_ip.prefixlen;
+ req.frh.src_len = rule->rule.filter.src_ip.prefixlen;
addattr_l(&req.n, sizeof(req), FRA_SRC,
- &rule->filter.src_ip.u.prefix, bytelen);
+ &rule->rule.filter.src_ip.u.prefix, bytelen);
}
/* destination IP, if specified */
if (IS_RULE_FILTERING_ON_DST_IP(rule)) {
- req.frh.dst_len = rule->filter.dst_ip.prefixlen;
+ req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen;
addattr_l(&req.n, sizeof(req), FRA_DST,
- &rule->filter.dst_ip.u.prefix, bytelen);
+ &rule->rule.filter.dst_ip.u.prefix, bytelen);
}
/* Route table to use to forward, if filter criteria matches. */
- if (rule->action.table < 256)
- req.frh.table = rule->action.table;
+ if (rule->rule.action.table < 256)
+ req.frh.table = rule->rule.action.table;
else {
req.frh.table = RT_TABLE_UNSPEC;
addattr32(&req.n, sizeof(req), FRA_TABLE,
- rule->action.table);
+ rule->rule.action.table);
}
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -115,10 +112,12 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
"Tx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
nl_msg_type_to_str(cmd), nl_family_to_str(family),
rule->ifp ? rule->ifp->name : "Unknown",
- rule->ifp ? rule->ifp->ifindex : 0, rule->priority,
- prefix2str(&rule->filter.src_ip, buf1, sizeof(buf1)),
- prefix2str(&rule->filter.dst_ip, buf2, sizeof(buf2)),
- rule->action.table);
+ rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority,
+ prefix2str(&rule->rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&rule->rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ rule->rule.action.table);
/* Ship off the message.
* Note: Currently, netlink_talk() is a blocking call which returns
@@ -213,44 +212,46 @@ int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
memset(&rule, 0, sizeof(rule));
if (tb[FRA_PRIORITY])
- rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
+ rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
if (tb[FRA_SRC]) {
if (frh->family == AF_INET)
- memcpy(&rule.filter.src_ip.u.prefix4,
+ memcpy(&rule.rule.filter.src_ip.u.prefix4,
RTA_DATA(tb[FRA_SRC]), 4);
else
- memcpy(&rule.filter.src_ip.u.prefix6,
+ memcpy(&rule.rule.filter.src_ip.u.prefix6,
RTA_DATA(tb[FRA_SRC]), 16);
- rule.filter.src_ip.prefixlen = frh->src_len;
- rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
+ rule.rule.filter.src_ip.prefixlen = frh->src_len;
+ rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
}
if (tb[FRA_DST]) {
if (frh->family == AF_INET)
- memcpy(&rule.filter.dst_ip.u.prefix4,
+ memcpy(&rule.rule.filter.dst_ip.u.prefix4,
RTA_DATA(tb[FRA_DST]), 4);
else
- memcpy(&rule.filter.dst_ip.u.prefix6,
+ memcpy(&rule.rule.filter.dst_ip.u.prefix6,
RTA_DATA(tb[FRA_DST]), 16);
- rule.filter.dst_ip.prefixlen = frh->dst_len;
- rule.filter.filter_bm |= PBR_FILTER_DST_IP;
+ rule.rule.filter.dst_ip.prefixlen = frh->dst_len;
+ rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
}
if (tb[FRA_TABLE])
- rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]);
+ rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]);
else
- rule.action.table = frh->table;
+ rule.rule.action.table = frh->table;
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
nl_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(frh->family), rule.ifp->name,
- rule.ifp->ifindex, rule.priority,
- prefix2str(&rule.filter.src_ip, buf1, sizeof(buf1)),
- prefix2str(&rule.filter.dst_ip, buf2, sizeof(buf2)),
- rule.action.table);
+ rule.ifp->ifindex, rule.rule.priority,
+ prefix2str(&rule.rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&rule.rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ rule.rule.action.table);
return kernel_pbr_rule_del(&rule);
}
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
index f9beb32ac4..30f850597c 100644
--- a/zebra/zebra_netns_notify.c
+++ b/zebra/zebra_netns_notify.c
@@ -70,7 +70,7 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)
char *netnspath = ns_netns_pathname(NULL, name);
struct vrf *vrf;
int ret;
- ns_id_t ns_id;
+ ns_id_t ns_id, ns_id_external;
if (netnspath == NULL)
return;
@@ -80,8 +80,9 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)
ns_id = zebra_ns_id_get(netnspath);
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
+ ns_id_external = ns_map_nsid_with_external(ns_id, true);
/* if VRF with NS ID already present */
- vrf = vrf_lookup_by_id((vrf_id_t)ns_id);
+ vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external);
if (vrf) {
zlog_warn(
"NS notify : same NSID used by VRF %s. Ignore NS %s creation",
@@ -90,15 +91,18 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)
}
if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) {
zlog_warn("NS notify : failed to create VRF %s", name);
+ ns_map_nsid_with_external(ns_id, false);
return;
}
if (zserv_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
- ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id);
+ ret = vrf_netns_handler_create(NULL, vrf, netnspath,
+ ns_id_external, ns_id);
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
if (ret != CMD_SUCCESS) {
zlog_warn("NS notify : failed to create NS %s", netnspath);
+ ns_map_nsid_with_external(ns_id, false);
return;
}
zlog_info("NS notify : created VRF %s NS %s", name, netnspath);
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index 7393f767af..5c62e366a6 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -140,6 +140,20 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
hash_create_size(8, zebra_pbr_rules_hash_key,
zebra_pbr_rules_hash_equal, "Rules Hash");
+ zns->ipset_hash =
+ hash_create_size(8, zebra_pbr_ipset_hash_key,
+ zebra_pbr_ipset_hash_equal, "IPset Hash");
+
+ zns->ipset_entry_hash =
+ hash_create_size(8, zebra_pbr_ipset_entry_hash_key,
+ zebra_pbr_ipset_entry_hash_equal,
+ "IPset Hash Entry");
+
+ zns->iptable_hash =
+ hash_create_size(8, zebra_pbr_iptable_hash_key,
+ zebra_pbr_iptable_hash_equal,
+ "IPtable Hash Entry");
+
#if defined(HAVE_RTADV)
rtadv_init(zns);
#endif
@@ -248,6 +262,15 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
hash_clean(zns->rules_hash, zebra_pbr_rules_free);
hash_free(zns->rules_hash);
+ hash_clean(zns->ipset_hash, zebra_pbr_ipset_free);
+ hash_free(zns->ipset_hash);
+ hash_clean(zns->ipset_entry_hash,
+ zebra_pbr_ipset_entry_free),
+ hash_free(zns->ipset_entry_hash);
+ hash_clean(zns->iptable_hash,
+ zebra_pbr_iptable_free);
+ hash_free(zns->iptable_hash);
+
while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) {
znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables);
@@ -274,6 +297,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
int zebra_ns_init(void)
{
ns_id_t ns_id;
+ ns_id_t ns_id_external;
dzns = zebra_ns_alloc();
@@ -282,8 +306,8 @@ int zebra_ns_init(void)
ns_id = zebra_ns_id_get_default();
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
-
- ns_init_management(ns_id);
+ ns_id_external = ns_map_nsid_with_external(ns_id, true);
+ ns_init_management(ns_id_external, ns_id);
logicalrouter_init(logicalrouter_config_write);
@@ -295,7 +319,7 @@ int zebra_ns_init(void)
zebra_vrf_init();
/* Default NS is activated */
- zebra_ns_enable(ns_id, (void **)&dzns);
+ zebra_ns_enable(ns_id_external, (void **)&dzns);
if (vrf_is_backend_netns()) {
ns_add_hook(NS_NEW_HOOK, zebra_ns_new);
diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h
index 66b73148d2..fbf88ae6ea 100644
--- a/zebra/zebra_ns.h
+++ b/zebra/zebra_ns.h
@@ -73,6 +73,12 @@ struct zebra_ns {
struct hash *rules_hash;
+ struct hash *ipset_hash;
+
+ struct hash *ipset_entry_hash;
+
+ struct hash *iptable_hash;
+
/* Back pointer */
struct ns *ns;
};
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index 4b93168846..27daa2d807 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -50,16 +50,22 @@ uint32_t zebra_pbr_rules_hash_key(void *arg)
uint32_t key;
rule = (struct zebra_pbr_rule *)arg;
- key = jhash_3words(rule->seq, rule->priority, rule->action.table,
- prefix_hash_key(&rule->filter.src_ip));
+ key = jhash_3words(rule->rule.seq, rule->rule.priority,
+ rule->rule.action.table,
+ prefix_hash_key(&rule->rule.filter.src_ip));
if (rule->ifp)
key = jhash_1word(rule->ifp->ifindex, key);
else
key = jhash_1word(0, key);
- return jhash_3words(rule->filter.src_port, rule->filter.dst_port,
- prefix_hash_key(&rule->filter.dst_ip),
- jhash_1word(rule->unique, key));
+ if (rule->rule.filter.fwmark)
+ key = jhash_1word(rule->rule.filter.fwmark, key);
+ else
+ key = jhash_1word(0, key);
+ return jhash_3words(rule->rule.filter.src_port,
+ rule->rule.filter.dst_port,
+ prefix_hash_key(&rule->rule.filter.dst_ip),
+ jhash_1word(rule->rule.unique, key));
}
int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
@@ -69,28 +75,31 @@ int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
r1 = (const struct zebra_pbr_rule *)arg1;
r2 = (const struct zebra_pbr_rule *)arg2;
- if (r1->seq != r2->seq)
+ if (r1->rule.seq != r2->rule.seq)
return 0;
- if (r1->priority != r2->priority)
+ if (r1->rule.priority != r2->rule.priority)
return 0;
- if (r1->unique != r2->unique)
+ if (r1->rule.unique != r2->rule.unique)
return 0;
- if (r1->action.table != r2->action.table)
+ if (r1->rule.action.table != r2->rule.action.table)
return 0;
- if (r1->filter.src_port != r2->filter.src_port)
+ if (r1->rule.filter.src_port != r2->rule.filter.src_port)
return 0;
- if (r1->filter.dst_port != r2->filter.dst_port)
+ if (r1->rule.filter.dst_port != r2->rule.filter.dst_port)
return 0;
- if (!prefix_same(&r1->filter.src_ip, &r2->filter.src_ip))
+ if (r1->rule.filter.fwmark != r2->rule.filter.fwmark)
return 0;
- if (!prefix_same(&r1->filter.dst_ip, &r2->filter.dst_ip))
+ if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip))
+ return 0;
+
+ if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip))
return 0;
if (r1->ifp != r2->ifp)
@@ -99,17 +108,17 @@ int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
return 1;
}
-struct pbr_unique_lookup {
+struct pbr_rule_unique_lookup {
struct zebra_pbr_rule *rule;
uint32_t unique;
};
static int pbr_rule_lookup_unique_walker(struct hash_backet *b, void *data)
{
- struct pbr_unique_lookup *pul = data;
+ struct pbr_rule_unique_lookup *pul = data;
struct zebra_pbr_rule *rule = b->data;
- if (pul->unique == rule->unique) {
+ if (pul->unique == rule->rule.unique) {
pul->rule = rule;
return HASHWALK_ABORT;
}
@@ -120,7 +129,7 @@ static int pbr_rule_lookup_unique_walker(struct hash_backet *b, void *data)
static struct zebra_pbr_rule *pbr_rule_lookup_unique(struct zebra_ns *zns,
uint32_t unique)
{
- struct pbr_unique_lookup pul;
+ struct pbr_rule_unique_lookup pul;
pul.unique = unique;
pul.rule = NULL;
@@ -129,6 +138,126 @@ static struct zebra_pbr_rule *pbr_rule_lookup_unique(struct zebra_ns *zns,
return pul.rule;
}
+void zebra_pbr_ipset_free(void *arg)
+{
+ struct zebra_pbr_ipset *ipset;
+
+ ipset = (struct zebra_pbr_ipset *)arg;
+
+ XFREE(MTYPE_TMP, ipset);
+}
+
+uint32_t zebra_pbr_ipset_hash_key(void *arg)
+{
+ struct zebra_pbr_ipset *ipset = (struct zebra_pbr_ipset *)arg;
+ uint32_t *pnt = (uint32_t *)&ipset->ipset_name;
+
+ return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de);
+}
+
+int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_ipset *r1, *r2;
+
+ r1 = (const struct zebra_pbr_ipset *)arg1;
+ r2 = (const struct zebra_pbr_ipset *)arg2;
+
+ if (r1->type != r2->type)
+ return 0;
+ if (r1->unique != r2->unique)
+ return 0;
+ if (strncmp(r1->ipset_name, r2->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ return 0;
+ return 1;
+}
+
+void zebra_pbr_ipset_entry_free(void *arg)
+{
+ struct zebra_pbr_ipset_entry *ipset;
+
+ ipset = (struct zebra_pbr_ipset_entry *)arg;
+
+ XFREE(MTYPE_TMP, ipset);
+}
+
+uint32_t zebra_pbr_ipset_entry_hash_key(void *arg)
+{
+ struct zebra_pbr_ipset_entry *ipset;
+ uint32_t key;
+
+ ipset = (struct zebra_pbr_ipset_entry *)arg;
+ key = prefix_hash_key(&ipset->src);
+ key = jhash_1word(ipset->unique, key);
+ key = jhash_1word(prefix_hash_key(&ipset->dst), key);
+
+ return key;
+}
+
+int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_ipset_entry *r1, *r2;
+
+ r1 = (const struct zebra_pbr_ipset_entry *)arg1;
+ r2 = (const struct zebra_pbr_ipset_entry *)arg2;
+
+ if (r1->unique != r2->unique)
+ return 0;
+
+ if (!prefix_same(&r1->src, &r2->src))
+ return 0;
+
+ if (!prefix_same(&r1->dst, &r2->dst))
+ return 0;
+
+ return 1;
+}
+
+void zebra_pbr_iptable_free(void *arg)
+{
+ struct zebra_pbr_iptable *iptable;
+
+ iptable = (struct zebra_pbr_iptable *)arg;
+
+ XFREE(MTYPE_TMP, iptable);
+}
+
+uint32_t zebra_pbr_iptable_hash_key(void *arg)
+{
+ struct zebra_pbr_iptable *iptable = (struct zebra_pbr_iptable *)arg;
+ uint32_t *pnt = (uint32_t *)&(iptable->ipset_name);
+ uint32_t key;
+
+ key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE,
+ 0x63ab42de);
+ key = jhash_1word(iptable->fwmark, key);
+ return jhash_3words(iptable->filter_bm, iptable->type,
+ iptable->unique, key);
+}
+
+int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct zebra_pbr_iptable *r1, *r2;
+
+ r1 = (const struct zebra_pbr_iptable *)arg1;
+ r2 = (const struct zebra_pbr_iptable *)arg2;
+
+ if (r1->type != r2->type)
+ return 0;
+ if (r1->unique != r2->unique)
+ return 0;
+ if (r1->filter_bm != r2->filter_bm)
+ return 0;
+ if (r1->fwmark != r2->fwmark)
+ return 0;
+ if (r1->action != r2->action)
+ return 0;
+ if (strncmp(r1->ipset_name, r2->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE))
+ return 0;
+ return 1;
+}
+
static void *pbr_rule_alloc_intern(void *arg)
{
struct zebra_pbr_rule *zpr;
@@ -146,7 +275,7 @@ static void *pbr_rule_alloc_intern(void *arg)
void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule)
{
struct zebra_pbr_rule *unique =
- pbr_rule_lookup_unique(zns, rule->unique);
+ pbr_rule_lookup_unique(zns, rule->rule.unique);
(void)hash_get(zns->rules_hash, rule, pbr_rule_alloc_intern);
kernel_add_pbr_rule(rule);
@@ -194,6 +323,162 @@ void zebra_pbr_client_close_cleanup(int sock)
hash_iterate(zns->rules_hash, zebra_pbr_cleanup_rules, &sock);
}
+static void *pbr_ipset_alloc_intern(void *arg)
+{
+ struct zebra_pbr_ipset *zpi;
+ struct zebra_pbr_ipset *new;
+
+ zpi = (struct zebra_pbr_ipset *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_create_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset)
+{
+ (void)hash_get(zns->ipset_hash, ipset, pbr_ipset_alloc_intern);
+ /* TODO:
+ * - Netlink call
+ */
+}
+
+void zebra_pbr_destroy_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset)
+{
+ struct zebra_pbr_ipset *lookup;
+
+ lookup = hash_lookup(zns->ipset_hash, ipset);
+ /* TODO:
+ * - Netlink destroy from kernel
+ * - ?? destroy ipset entries before
+ */
+ if (lookup)
+ XFREE(MTYPE_TMP, lookup);
+ else
+ zlog_warn("%s: IPSet Entry being deleted we know nothing about",
+ __PRETTY_FUNCTION__);
+}
+
+struct pbr_ipset_name_lookup {
+ struct zebra_pbr_ipset *ipset;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+};
+
+static int zebra_pbr_ipset_pername_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct pbr_ipset_name_lookup *pinl =
+ (struct pbr_ipset_name_lookup *)arg;
+ struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)backet->data;
+
+ if (!strncmp(pinl->ipset_name, zpi->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE)) {
+ pinl->ipset = zpi;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(struct zebra_ns *zns,
+ char *ipsetname)
+{
+ struct pbr_ipset_name_lookup pinl;
+ struct pbr_ipset_name_lookup *ptr = &pinl;
+
+ if (!ipsetname)
+ return NULL;
+ memset(ptr, 0, sizeof(struct pbr_ipset_name_lookup));
+ snprintf((char *)ptr->ipset_name, ZEBRA_IPSET_NAME_SIZE, "%s",
+ ipsetname);
+ hash_walk(zns->ipset_hash, zebra_pbr_ipset_pername_walkcb, ptr);
+ return ptr->ipset;
+}
+
+static void *pbr_ipset_entry_alloc_intern(void *arg)
+{
+ struct zebra_pbr_ipset_entry *zpi;
+ struct zebra_pbr_ipset_entry *new;
+
+ zpi = (struct zebra_pbr_ipset_entry *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset_entry));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_add_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset)
+{
+ (void)hash_get(zns->ipset_entry_hash, ipset,
+ pbr_ipset_entry_alloc_intern);
+ /* TODO:
+ * - attach to ipset list
+ * - Netlink add to kernel
+ */
+}
+
+void zebra_pbr_del_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset)
+{
+ struct zebra_pbr_ipset_entry *lookup;
+
+ lookup = hash_lookup(zns->ipset_entry_hash, ipset);
+ /* TODO:
+ * - Netlink destroy
+ * - detach from ipset list
+ * - ?? if no more entres, delete ipset
+ */
+ if (lookup)
+ XFREE(MTYPE_TMP, lookup);
+ else
+ zlog_warn("%s: IPSet being deleted we know nothing about",
+ __PRETTY_FUNCTION__);
+}
+
+static void *pbr_iptable_alloc_intern(void *arg)
+{
+ struct zebra_pbr_iptable *zpi;
+ struct zebra_pbr_iptable *new;
+
+ zpi = (struct zebra_pbr_iptable *)arg;
+
+ new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
+
+ memcpy(new, zpi, sizeof(*zpi));
+
+ return new;
+}
+
+void zebra_pbr_add_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable)
+{
+ (void)hash_get(zns->iptable_hash, iptable,
+ pbr_iptable_alloc_intern);
+ /* TODO call netlink layer */
+}
+
+void zebra_pbr_del_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable)
+{
+ struct zebra_pbr_ipset_entry *lookup;
+
+ lookup = hash_lookup(zns->iptable_hash, iptable);
+ /* TODO:
+ * - call netlink layer
+ * - detach from iptable list
+ */
+ if (lookup)
+ XFREE(MTYPE_TMP, lookup);
+ else
+ zlog_warn("%s: IPTable being deleted we know nothing about",
+ __PRETTY_FUNCTION__);
+}
+
/*
* Handle success or failure of rule (un)install in the kernel.
*/
@@ -215,6 +500,69 @@ void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule,
}
/*
+ * Handle success or failure of ipset (un)install in the kernel.
+ */
+void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset,
+ enum southbound_results res)
+{
+ switch (res) {
+ case SOUTHBOUND_INSTALL_SUCCESS:
+ zsend_ipset_notify_owner(ipset, ZAPI_IPSET_INSTALLED);
+ break;
+ case SOUTHBOUND_INSTALL_FAILURE:
+ zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_INSTALL);
+ break;
+ case SOUTHBOUND_DELETE_SUCCESS:
+ case SOUTHBOUND_DELETE_FAILURE:
+ /* TODO : handling of delete event */
+ break;
+ }
+}
+
+/*
+ * Handle success or failure of ipset (un)install in the kernel.
+ */
+void kernel_pbr_ipset_entry_add_del_status(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum southbound_results res)
+{
+ switch (res) {
+ case SOUTHBOUND_INSTALL_SUCCESS:
+ zsend_ipset_entry_notify_owner(ipset,
+ ZAPI_IPSET_ENTRY_INSTALLED);
+ break;
+ case SOUTHBOUND_INSTALL_FAILURE:
+ zsend_ipset_entry_notify_owner(ipset,
+ ZAPI_IPSET_ENTRY_FAIL_INSTALL);
+ break;
+ case SOUTHBOUND_DELETE_SUCCESS:
+ case SOUTHBOUND_DELETE_FAILURE:
+ /* TODO : handling of delete event */
+ break;
+ }
+}
+
+/*
+ * Handle success or failure of ipset (un)install in the kernel.
+ */
+void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
+ enum southbound_results res)
+{
+ switch (res) {
+ case SOUTHBOUND_INSTALL_SUCCESS:
+ zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_INSTALLED);
+ break;
+ case SOUTHBOUND_INSTALL_FAILURE:
+ zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_FAIL_INSTALL);
+ break;
+ case SOUTHBOUND_DELETE_SUCCESS:
+ case SOUTHBOUND_DELETE_FAILURE:
+ /* TODO : handling of delete event */
+ break;
+ }
+}
+
+/*
* Handle rule delete notification from kernel.
*/
int kernel_pbr_rule_del(struct zebra_pbr_rule *rule)
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index f910d8e742..0ac629cc65 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -30,77 +30,114 @@
#include "if.h"
#include "rt.h"
+#include "pbr.h"
-/*
- * A PBR filter
- *
- * The filter or match criteria in a PBR rule.
- * For simplicity, all supported filters are grouped into a structure rather
- * than delineating further. A bitmask denotes which filters are actually
- * specified.
- */
-struct zebra_pbr_filter {
- uint32_t filter_bm;
-#define PBR_FILTER_SRC_IP (1 << 0)
-#define PBR_FILTER_DST_IP (1 << 1)
-#define PBR_FILTER_SRC_PORT (1 << 2)
-#define PBR_FILTER_DST_PORT (1 << 3)
-
- /* Source and Destination IP address with masks. */
- struct prefix src_ip;
- struct prefix dst_ip;
-
- /* Source and Destination higher-layer (TCP/UDP) port numbers. */
- uint16_t src_port;
- uint16_t dst_port;
+struct zebra_pbr_rule {
+ int sock;
+
+ struct pbr_rule rule;
+
+ struct interface *ifp;
};
#define IS_RULE_FILTERING_ON_SRC_IP(r) \
- (r->filter.filter_bm & PBR_FILTER_SRC_IP)
+ (r->rule.filter.filter_bm & PBR_FILTER_SRC_IP)
#define IS_RULE_FILTERING_ON_DST_IP(r) \
- (r->filter.filter_bm & PBR_FILTER_DST_IP)
+ (r->rule.filter.filter_bm & PBR_FILTER_DST_IP)
#define IS_RULE_FILTERING_ON_SRC_PORT(r) \
- (r->filter.filter_bm & PBR_FILTER_SRC_PORT)
+ (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT)
#define IS_RULE_FILTERING_ON_DST_PORT(r) \
- (r->filter.filter_bm & PBR_FILTER_DST_PORT)
+ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT)
/*
- * A PBR action
+ * An IPSet Entry Filter
*
- * The action corresponding to a PBR rule.
- * While the user specifies the action in a particular way, the forwarding
- * plane implementation (Linux only) requires that to be encoded into a
- * route table and the rule then point to that route table; in some cases,
- * the user criteria may directly point to a table too.
+ * This is a filter mapped on ipset entries
*/
-struct zebra_pbr_action {
- uint32_t table;
+struct zebra_pbr_ipset {
+ /*
+ * Originating zclient sock fd, so we can know who to send
+ * back to.
+ */
+ int sock;
+
+ uint32_t unique;
+
+ /* type is encoded as uint32_t
+ * but value is an enum ipset_type
+ */
+ uint32_t type;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
/*
- * A PBR rule
+ * An IPSet Entry Filter
*
- * This is a combination of the filter criteria and corresponding action.
- * Rules also have a user-defined sequence number which defines the relative
- * order amongst rules.
+ * This is a filter mapped on ipset entries
*/
-struct zebra_pbr_rule {
+struct zebra_pbr_ipset_entry {
/*
* Originating zclient sock fd, so we can know who to send
* back to.
*/
int sock;
- uint32_t seq;
- uint32_t priority;
- struct interface *ifp;
uint32_t unique;
- struct zebra_pbr_filter filter;
- struct zebra_pbr_action action;
+
+ struct prefix src;
+ struct prefix dst;
+
+ uint32_t filter_bm;
+
+ struct zebra_pbr_ipset *backpointer;
+};
+
+/*
+ * An IPTables Action
+ *
+ * This is a filter mapped on ipset entries
+ */
+struct zebra_pbr_iptable {
+ /*
+ * Originating zclient sock fd, so we can know who to send
+ * back to.
+ */
+ int sock;
+
+ uint32_t unique;
+
+ /* include ipset type
+ */
+ uint32_t type;
+
+ /* include which IP is to be filtered
+ */
+ uint32_t filter_bm;
+
+ uint32_t fwmark;
+
+ uint32_t action;
+
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
};
void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule);
void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule);
+void zebra_pbr_create_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset);
+void zebra_pbr_destroy_ipset(struct zebra_ns *zns,
+ struct zebra_pbr_ipset *ipset);
+struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(struct zebra_ns *zns,
+ char *ipsetname);
+void zebra_pbr_add_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset);
+void zebra_pbr_del_ipset_entry(struct zebra_ns *zns,
+ struct zebra_pbr_ipset_entry *ipset);
+
+void zebra_pbr_add_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable);
+void zebra_pbr_del_iptable(struct zebra_ns *zns,
+ struct zebra_pbr_iptable *iptable);
/*
* Install specified rule for a specific interface.
@@ -128,6 +165,19 @@ extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule,
enum southbound_results res);
/*
+ * Handle success or failure of ipset kinds (un)install in the kernel.
+ */
+extern void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset,
+ enum southbound_results res);
+
+extern void kernel_pbr_ipset_entry_add_del_status(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum southbound_results res);
+
+extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable,
+ enum southbound_results res);
+
+/*
* Handle rule delete notification from kernel.
*/
extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule);
@@ -138,4 +188,21 @@ extern void zebra_pbr_rules_free(void *arg);
extern uint32_t zebra_pbr_rules_hash_key(void *arg);
extern int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2);
+/* has operates on 32bit pointer
+ * and field is a string of 8bit
+ */
+#define ZEBRA_IPSET_NAME_HASH_SIZE (ZEBRA_IPSET_NAME_SIZE / 4)
+
+extern void zebra_pbr_ipset_free(void *arg);
+extern uint32_t zebra_pbr_ipset_hash_key(void *arg);
+extern int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2);
+
+extern void zebra_pbr_ipset_entry_free(void *arg);
+extern uint32_t zebra_pbr_ipset_entry_hash_key(void *arg);
+extern int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2);
+
+extern void zebra_pbr_iptable_free(void *arg);
+extern uint32_t zebra_pbr_iptable_hash_key(void *arg);
+extern int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2);
+
#endif /* _ZEBRA_PBR_H */
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index effe672a9d..6ad60a6fff 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -2121,8 +2121,8 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
/* free RE and nexthops */
if (re->type == ZEBRA_ROUTE_STATIC)
- zebra_deregister_rnh_static_nexthops(re->vrf_id, re->ng.nexthop,
- rn);
+ zebra_deregister_rnh_static_nexthops(re->ng.nexthop->vrf_id,
+ re->ng.nexthop, rn);
nexthops_free(re->ng.nexthop);
XFREE(MTYPE_RE, re);
}
@@ -2491,8 +2491,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
* If we can show that this code path is
* dead then we can remove it.
*/
- if (fib && type == ZEBRA_ROUTE_KERNEL
- && CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE)) {
+ if (fib && CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE)) {
if (IS_ZEBRA_DEBUG_RIB) {
rnode_debug(
rn, vrf_id,
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index 65df4e15aa..879da092f0 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -514,6 +514,59 @@ static void zebra_rnh_notify_protocol_clients(vrf_id_t vrfid, int family,
}
}
+static void zebra_rnh_process_pbr_tables(int family,
+ struct route_node *nrn,
+ struct rnh *rnh,
+ struct route_node *prn,
+ struct route_entry *re)
+{
+ struct zebra_ns_table *znst;
+ struct route_entry *o_re;
+ struct route_node *o_rn;
+ struct listnode *node;
+ struct zserv *client;
+ struct zebra_ns *zns;
+ afi_t afi = AFI_IP;
+
+ if (family == AF_INET6)
+ afi = AFI_IP6;
+
+ /*
+ * We are only concerned about nexthops that change for
+ * anyone using PBR
+ */
+ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
+ if (client->proto == ZEBRA_ROUTE_PBR)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ RB_FOREACH (znst, zebra_ns_table_head, &zns->ns_tables) {
+ if (afi != znst->afi)
+ continue;
+
+ for (o_rn = route_top(znst->table);
+ o_rn; o_rn = srcdest_route_next(o_rn)) {
+ RNODE_FOREACH_RE (o_rn, o_re) {
+ if (o_re->type == ZEBRA_ROUTE_PBR)
+ break;
+
+ }
+
+ /*
+ * If we have a PBR route and a nexthop changes
+ * just rethink it. Yes this is a hammer, but
+ * a small one
+ */
+ if (o_re)
+ rib_queue_add(o_rn);
+ }
+ }
+}
+
static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family,
struct route_node *nrn,
struct rnh *rnh,
@@ -752,6 +805,9 @@ static void zebra_rnh_eval_nexthop_entry(vrf_id_t vrfid, int family, int force,
zebra_rnh_process_static_routes(vrfid, family, nrn, rnh, prn,
rnh->state);
+ zebra_rnh_process_pbr_tables(family, nrn, rnh, prn,
+ rnh->state);
+
/* Process pseudowires attached to this nexthop */
zebra_rnh_process_pseudowires(vrfid, rnh);
}
diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c
index 82b3ee9743..914b049c05 100644
--- a/zebra/zebra_static.c
+++ b/zebra/zebra_static.c
@@ -46,12 +46,26 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p,
struct prefix nh_p;
struct nexthop *nexthop = NULL;
enum blackhole_type bh_type = 0;
+ struct vrf *nh_vrf;
/* Lookup table. */
table = zebra_vrf_table(afi, safi, si->vrf_id);
if (!table)
return;
+ /*
+ * If a specific vrf is coming up and the nexthop vrf we are
+ * looking at using hasn't been brought up yet, just don't
+ * install the static route yet.
+ * When the nexthop vrf comes up we will get another call
+ * back to do the right thing. I'm putting this check
+ * here because we are calling static_install_route a bunch
+ * from a bunch of different callpaths.
+ */
+ nh_vrf = vrf_lookup_by_id(si->nh_vrf_id);
+ if (!nh_vrf)
+ return;
+
memset(&nh_p, 0, sizeof(nh_p));
if (si->type == STATIC_BLACKHOLE) {
switch (si->bh_type) {
@@ -369,7 +383,7 @@ void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p,
nh_p.u.prefix6 = nexthop->gate.ipv6;
}
route_entry_nexthop_delete(re, nexthop);
- zebra_deregister_rnh_static_nh(si->vrf_id, &nh_p, rn);
+ zebra_deregister_rnh_static_nh(si->nh_vrf_id, &nh_p, rn);
nexthop_free(nexthop);
}
/* Unlock node. */
@@ -442,6 +456,7 @@ int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p,
si->tag = tag;
si->vrf_id = zvrf_id(zvrf);
si->nh_vrf_id = zvrf_id(nh_zvrf);
+ strcpy(si->nh_vrfname, nh_zvrf->vrf->name);
if (ifname)
strlcpy(si->ifname, ifname, sizeof(si->ifname));
@@ -573,33 +588,233 @@ static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi,
safi_t safi)
{
struct route_table *stable;
- struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
struct route_node *rn;
struct static_route *si;
struct prefix *p, *src_pp;
struct prefix_ipv6 *src_p;
+ struct vrf *vrf;
- stable = zebra_vrf_static_table(afi, safi, zvrf);
- if (!stable)
- return;
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf->info;
- for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {
- srcdest_rnode_prefixes(rn, &p, &src_pp);
- src_p = (struct prefix_ipv6 *)src_pp;
+ stable = zebra_vrf_static_table(afi, safi, zvrf);
+ if (!stable)
+ continue;
+
+ for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {
+ srcdest_rnode_prefixes(rn, &p, &src_pp);
+ src_p = (struct prefix_ipv6 *)src_pp;
+ for (si = rn->info; si; si = si->next) {
+ if (!si->ifname[0])
+ continue;
+ if (up) {
+ if (strcmp(si->ifname, ifp->name))
+ continue;
+ si->ifindex = ifp->ifindex;
+ static_install_route(afi, safi, p, src_p, si);
+ } else {
+ if (si->ifindex != ifp->ifindex)
+ continue;
+ static_uninstall_route(afi, safi, p, src_p, si);
+ si->ifindex = IFINDEX_INTERNAL;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This function looks at a zvrf's stable and notices if any of the
+ * nexthops we are using are part of the vrf coming up.
+ * If we are using them then cleanup the nexthop vrf id
+ * to be the new value and then re-installs them
+ *
+ *
+ * stable -> The table we are looking at.
+ * zvrf -> The newly changed vrf.
+ * afi -> The afi to look at
+ * safi -> the safi to look at
+ */
+static void static_fixup_vrf(struct zebra_vrf *zvrf,
+ struct route_table *stable, afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_route *si;
+ struct interface *ifp;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
for (si = rn->info; si; si = si->next) {
- if (!si->ifname[0])
+ if (strcmp(zvrf->vrf->name, si->nh_vrfname) != 0)
continue;
- if (up) {
- if (strcmp(si->ifname, ifp->name))
+
+ si->nh_vrf_id = zvrf->vrf->vrf_id;
+ if (si->ifindex) {
+ ifp = if_lookup_by_name(si->ifname,
+ si->nh_vrf_id);
+ if (ifp)
+ si->ifindex = ifp->ifindex;
+ else
continue;
- si->ifindex = ifp->ifindex;
- static_install_route(afi, safi, p, src_p, si);
- } else {
- if (si->ifindex != ifp->ifindex)
+ }
+ static_install_route(afi, safi, &rn->p, NULL, si);
+ }
+ }
+}
+
+/*
+ * This function enables static routes in a zvrf as it
+ * is coming up. It sets the new vrf_id as appropriate.
+ *
+ * zvrf -> The zvrf that is being brought up and enabled by the kernel
+ * stable -> The stable we are looking at.
+ * afi -> the afi in question
+ * safi -> the safi in question
+ */
+static void static_enable_vrf(struct zebra_vrf *zvrf,
+ struct route_table *stable,
+ afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_route *si;
+ struct interface *ifp;
+ struct vrf *vrf = zvrf->vrf;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ for (si = rn->info; si; si = si->next) {
+ si->vrf_id = vrf->vrf_id;
+ if (si->ifindex) {
+ ifp = if_lookup_by_name(si->ifname,
+ si->nh_vrf_id);
+ if (ifp)
+ si->ifindex = ifp->ifindex;
+ else
+ continue;
+ }
+ static_install_route(afi, safi, &rn->p, NULL, si);
+ }
+ }
+}
+
+/*
+ * When a vrf is being enabled by the kernel, go through all the
+ * static routes in the system that use this vrf (both nexthops vrfs
+ * and the routes vrf )
+ *
+ * enable_zvrf -> the vrf being enabled
+ */
+void static_fixup_vrf_ids(struct zebra_vrf *enable_zvrf)
+{
+ struct route_table *stable;
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf->info;
+ /* Install any static routes configured for this VRF. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+ stable = zvrf->stable[afi][safi];
+ if (!stable)
+ continue;
+
+ static_fixup_vrf(enable_zvrf, stable,
+ afi, safi);
+
+ if (enable_zvrf == zvrf)
+ static_enable_vrf(zvrf, stable,
+ afi, safi);
+ }
+ }
+ }
+}
+
+/*
+ * Look at the specified stable and if any of the routes in
+ * this table are using the zvrf as the nexthop, uninstall
+ * those routes.
+ *
+ * zvrf -> the vrf being disabled
+ * stable -> the table we need to look at.
+ * afi -> the afi in question
+ * safi -> the safi in question
+ */
+static void static_cleanup_vrf(struct zebra_vrf *zvrf,
+ struct route_table *stable,
+ afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_route *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ for (si = rn->info; si; si = si->next) {
+ if (strcmp(zvrf->vrf->name, si->nh_vrfname) != 0)
+ continue;
+
+ static_uninstall_route(afi, safi, &rn->p, NULL, si);
+ }
+ }
+}
+
+/*
+ * Look at all static routes in this table and uninstall
+ * them.
+ *
+ * stable -> The table to uninstall from
+ * afi -> The afi in question
+ * safi -> the safi in question
+ */
+static void static_disable_vrf(struct route_table *stable,
+ afi_t afi, safi_t safi)
+{
+ struct route_node *rn;
+ struct static_route *si;
+
+ for (rn = route_top(stable); rn; rn = route_next(rn)) {
+ for (si = rn->info; si; si = si->next) {
+ static_uninstall_route(afi, safi, &rn->p, NULL, si);
+ }
+ }
+}
+
+/*
+ * When the disable_zvrf is shutdown by the kernel, we call
+ * this function and it cleans up all static routes using
+ * this vrf as a nexthop as well as all static routes
+ * in it's stables.
+ *
+ * disable_zvrf - The vrf being disabled
+ */
+void static_cleanup_vrf_ids(struct zebra_vrf *disable_zvrf)
+{
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf->info;
+
+ /* Uninstall any static routes configured for this VRF. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+ struct route_table *stable;
+
+ stable = zvrf->stable[afi][safi];
+ if (!stable)
continue;
- static_uninstall_route(afi, safi, p, src_p, si);
- si->ifindex = IFINDEX_INTERNAL;
+
+ static_cleanup_vrf(disable_zvrf, stable,
+ afi, safi);
+
+ if (disable_zvrf == zvrf)
+ static_disable_vrf(stable, afi, safi);
}
}
}
diff --git a/zebra/zebra_static.h b/zebra/zebra_static.h
index a12e465e79..341905f422 100644
--- a/zebra/zebra_static.h
+++ b/zebra/zebra_static.h
@@ -55,6 +55,7 @@ struct static_route {
/* VRF identifier. */
vrf_id_t vrf_id;
vrf_id_t nh_vrf_id;
+ char nh_vrfname[VRF_NAMSIZ + 1];
/* Administrative distance. */
uint8_t distance;
@@ -102,4 +103,6 @@ extern int static_delete_route(afi_t, safi_t safi, uint8_t type,
extern void static_ifindex_update(struct interface *ifp, bool up);
+extern void static_cleanup_vrf_ids(struct zebra_vrf *zvrf);
+extern void static_fixup_vrf_ids(struct zebra_vrf *zvrf);
#endif
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index fe1b100575..76dc5657d4 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -102,11 +102,7 @@ static int zebra_vrf_new(struct vrf *vrf)
static int zebra_vrf_enable(struct vrf *vrf)
{
struct zebra_vrf *zvrf = vrf->info;
- struct route_table *stable;
- struct route_node *rn;
- struct static_route *si;
struct route_table *table;
- struct interface *ifp;
afi_t afi;
safi_t safi;
@@ -138,29 +134,7 @@ static int zebra_vrf_enable(struct vrf *vrf)
zvrf->import_check_table[afi] = table;
}
- /* Install any static routes configured for this VRF. */
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
- stable = zvrf->stable[afi][safi];
- if (!stable)
- continue;
-
- for (rn = route_top(stable); rn; rn = route_next(rn))
- for (si = rn->info; si; si = si->next) {
- si->vrf_id = vrf->vrf_id;
- if (si->ifindex) {
- ifp = if_lookup_by_name(
- si->ifname, si->vrf_id);
- if (ifp)
- si->ifindex =
- ifp->ifindex;
- else
- continue;
- }
- static_install_route(afi, safi, &rn->p,
- NULL, si);
- }
- }
+ static_fixup_vrf_ids(zvrf);
/*
* We may have static routes that are now possible to
@@ -178,9 +152,6 @@ static int zebra_vrf_enable(struct vrf *vrf)
static int zebra_vrf_disable(struct vrf *vrf)
{
struct zebra_vrf *zvrf = vrf->info;
- struct route_table *stable;
- struct route_node *rn;
- struct static_route *si;
struct route_table *table;
struct interface *ifp;
afi_t afi;
@@ -192,18 +163,7 @@ static int zebra_vrf_disable(struct vrf *vrf)
zlog_debug("VRF %s id %u is now inactive", zvrf_name(zvrf),
zvrf_id(zvrf));
- /* Uninstall any static routes configured for this VRF. */
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
- stable = zvrf->stable[afi][safi];
- if (!stable)
- continue;
-
- for (rn = route_top(stable); rn; rn = route_next(rn))
- for (si = rn->info; si; si = si->next)
- static_uninstall_route(
- afi, safi, &rn->p, NULL, si);
- }
+ static_cleanup_vrf_ids(zvrf);
/* Stop any VxLAN-EVPN processing. */
zebra_vxlan_vrf_disable(zvrf);
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 44f77f84b1..84fc76d7f6 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -91,6 +91,9 @@ struct static_hold_route {
char *tag_str;
char *distance_str;
char *label_str;
+
+ /* processed & masked destination, used for config display */
+ struct prefix dest;
};
static struct list *static_list;
@@ -160,11 +163,7 @@ static int static_list_compare(void *arg1, void *arg2)
if (ret)
return ret;
- ret = static_list_compare_helper(shr1->dest_str, shr2->dest_str);
- if (ret)
- return ret;
-
- ret = static_list_compare_helper(shr1->mask_str, shr2->mask_str);
+ ret = prefix_cmp(&shr1->dest, &shr2->dest);
if (ret)
return ret;
@@ -198,15 +197,12 @@ static int static_list_compare(void *arg1, void *arg2)
/* General function for static route. */
-static int zebra_static_route_holdem(struct zebra_vrf *zvrf,
- struct zebra_vrf *nh_zvrf,
- afi_t afi, safi_t safi,
- const char *negate, const char *dest_str,
- const char *mask_str, const char *src_str,
- const char *gate_str, const char *ifname,
- const char *flag_str, const char *tag_str,
- const char *distance_str,
- const char *label_str)
+static int zebra_static_route_holdem(
+ struct zebra_vrf *zvrf, struct zebra_vrf *nh_zvrf, afi_t afi,
+ safi_t safi, const char *negate, struct prefix *dest,
+ const char *dest_str, const char *mask_str, const char *src_str,
+ const char *gate_str, const char *ifname, const char *flag_str,
+ const char *tag_str, const char *distance_str, const char *label_str)
{
struct static_hold_route *shr, *lookup;
struct listnode *node;
@@ -216,6 +212,8 @@ static int zebra_static_route_holdem(struct zebra_vrf *zvrf,
shr->nhvrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, nh_zvrf->vrf->name);
shr->afi = afi;
shr->safi = safi;
+ if (dest)
+ prefix_copy(&shr->dest, dest);
if (dest_str)
shr->dest_str = XSTRDUP(MTYPE_STATIC_ROUTE, dest_str);
if (mask_str)
@@ -294,14 +292,6 @@ static int zebra_static_route_leak(
return CMD_WARNING_CONFIG_FAILED;
}
- if (zvrf->vrf->vrf_id == VRF_UNKNOWN
- || nh_zvrf->vrf->vrf_id == VRF_UNKNOWN) {
- vrf_set_user_cfged(zvrf->vrf);
- return zebra_static_route_holdem(
- zvrf, nh_zvrf, afi, safi, negate, dest_str, mask_str,
- src_str, gate_str, ifname, flag_str, tag_str,
- distance_str, label_str);
- }
switch (afi) {
case AFI_IP:
/* Cisco like mask notation. */
@@ -343,6 +333,15 @@ static int zebra_static_route_leak(
/* Apply mask for given prefix. */
apply_mask(&p);
+ if (zvrf->vrf->vrf_id == VRF_UNKNOWN
+ || nh_zvrf->vrf->vrf_id == VRF_UNKNOWN) {
+ vrf_set_user_cfged(zvrf->vrf);
+ return zebra_static_route_holdem(
+ zvrf, nh_zvrf, afi, safi, negate, &p, dest_str,
+ mask_str, src_str, gate_str, ifname, flag_str, tag_str,
+ distance_str, label_str);
+ }
+
/* Administrative distance. */
if (distance_str)
distance = atoi(distance_str);
@@ -1108,7 +1107,10 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
struct vrf *vrf =
vrf_lookup_by_id(nexthop->vrf_id);
- vty_out(vty, "(vrf %s)", vrf->name);
+ if (vrf)
+ vty_out(vty, "(vrf %s)", vrf->name);
+ else
+ vty_out(vty, "(vrf UKNOWN)");
}
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
@@ -1468,7 +1470,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
if (nexthop->vrf_id != re->vrf_id) {
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
- vty_out(vty, "(vrf %s)", vrf->name);
+ if (vrf)
+ vty_out(vty, "(vrf %s)", vrf->name);
+ else
+ vty_out(vty, "(vrf UKNOWN)");
}
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
@@ -2221,11 +2226,13 @@ int static_config(struct vty *vty, struct zebra_vrf *zvrf, afi_t afi,
if (strcmp(zvrf->vrf->name, shr->vrf_name) != 0)
continue;
+ char dest_str[PREFIX_STRLEN];
+
+ prefix2str(&shr->dest, dest_str, sizeof(dest_str));
+
vty_out(vty, "%s ", spacing);
if (shr->dest_str)
- vty_out(vty, "%s ", shr->dest_str);
- if (shr->mask_str)
- vty_out(vty, "%s ", shr->mask_str);
+ vty_out(vty, "%s ", dest_str);
if (shr->src_str)
vty_out(vty, "from %s ", shr->src_str);
if (shr->gate_str)
@@ -2303,11 +2310,7 @@ int static_config(struct vty *vty, struct zebra_vrf *zvrf, afi_t afi,
sizeof buf, 0));
if (si->nh_vrf_id != si->vrf_id) {
- struct vrf *vrf;
-
- vrf = vrf_lookup_by_id(si->nh_vrf_id);
- vty_out(vty, " nexthop-vrf %s",
- (vrf) ? vrf->name : "Unknown");
+ vty_out(vty, " nexthop-vrf %s", si->nh_vrfname);
}
vty_out(vty, "\n");
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 7784403fff..fa8f837408 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -5774,10 +5774,13 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
NULL; /* link info for the SVI = bridge info */
svi_if_zif = ifp->info;
- svi_if_link = if_lookup_by_index_per_ns(
- zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex);
- if (svi_if_zif && svi_if_link)
- zvni = zvni_from_svi(ifp, svi_if_link);
+ if (svi_if_zif) {
+ svi_if_link = if_lookup_by_index_per_ns(
+ zebra_ns_lookup(NS_DEFAULT),
+ svi_if_zif->link_ifindex);
+ if (svi_if_link)
+ zvni = zvni_from_svi(ifp, svi_if_link);
+ }
} else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
zvni = zvni_from_svi(ifp, ifp);
}
diff --git a/zebra/zserv.c b/zebra/zserv.c
index c06efbfb4b..94c20c1d13 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -725,10 +725,9 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
struct zserv *client;
struct stream *s;
- if (IS_ZEBRA_DEBUG_PACKET) {
+ if (IS_ZEBRA_DEBUG_PACKET)
zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
- rule->unique);
- }
+ rule->rule.unique);
for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
if (rule->sock == client->sock)
@@ -739,13 +738,12 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
return;
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
- stream_reset(s);
zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT);
stream_put(s, &note, sizeof(note));
- stream_putl(s, rule->seq);
- stream_putl(s, rule->priority);
- stream_putl(s, rule->unique);
+ stream_putl(s, rule->rule.seq);
+ stream_putl(s, rule->rule.priority);
+ stream_putl(s, rule->rule.unique);
if (rule->ifp)
stream_putl(s, rule->ifp->ifindex);
else
@@ -756,6 +754,98 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
zebra_server_send_message(client, s);
}
+void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset,
+ enum zapi_ipset_notify_owner note)
+{
+ struct listnode *node;
+ struct zserv *client;
+ struct stream *s;
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
+ ipset->unique);
+
+ for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+ if (ipset->sock == client->sock)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_IPSET_NOTIFY_OWNER, VRF_DEFAULT);
+ stream_put(s, &note, sizeof(note));
+ stream_putl(s, ipset->unique);
+ stream_put(s, ipset->ipset_name, ZEBRA_IPSET_NAME_SIZE);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zebra_server_send_message(client, s);
+}
+
+void zsend_ipset_entry_notify_owner(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum zapi_ipset_entry_notify_owner note)
+{
+ struct listnode *node;
+ struct zserv *client;
+ struct stream *s;
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
+ ipset->unique);
+
+ for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+ if (ipset->sock == client->sock)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_IPSET_ENTRY_NOTIFY_OWNER,
+ VRF_DEFAULT);
+ stream_put(s, &note, sizeof(note));
+ stream_putl(s, ipset->unique);
+ stream_put(s, ipset->backpointer->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zebra_server_send_message(client, s);
+}
+
+void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
+ enum zapi_iptable_notify_owner note)
+{
+ struct listnode *node;
+ struct zserv *client;
+ struct stream *s;
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__,
+ iptable->unique);
+
+ for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) {
+ if (iptable->sock == client->sock)
+ break;
+ }
+
+ if (!client)
+ return;
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_IPTABLE_NOTIFY_OWNER, VRF_DEFAULT);
+ stream_put(s, &note, sizeof(note));
+ stream_putl(s, iptable->unique);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ zebra_server_send_message(client, s);
+}
+
/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */
int zsend_router_id_update(struct zserv *client, struct prefix *p,
vrf_id_t vrf_id)
@@ -2667,39 +2757,47 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
memset(&zpr, 0, sizeof(zpr));
zpr.sock = client->sock;
- STREAM_GETL(s, zpr.seq);
- STREAM_GETL(s, zpr.priority);
- STREAM_GETL(s, zpr.unique);
- STREAM_GETC(s, zpr.filter.src_ip.family);
- STREAM_GETC(s, zpr.filter.src_ip.prefixlen);
- STREAM_GET(&zpr.filter.src_ip.u.prefix, s,
- prefix_blen(&zpr.filter.src_ip));
- STREAM_GETW(s, zpr.filter.src_port);
- STREAM_GETC(s, zpr.filter.dst_ip.family);
- STREAM_GETC(s, zpr.filter.dst_ip.prefixlen);
- STREAM_GET(&zpr.filter.dst_ip.u.prefix, s,
- prefix_blen(&zpr.filter.dst_ip));
- STREAM_GETW(s, zpr.filter.dst_port);
- STREAM_GETL(s, zpr.action.table);
+ zpr.rule.vrf_id = hdr->vrf_id;
+ STREAM_GETL(s, zpr.rule.seq);
+ STREAM_GETL(s, zpr.rule.priority);
+ STREAM_GETL(s, zpr.rule.unique);
+ STREAM_GETC(s, zpr.rule.filter.src_ip.family);
+ STREAM_GETC(s, zpr.rule.filter.src_ip.prefixlen);
+ STREAM_GET(&zpr.rule.filter.src_ip.u.prefix, s,
+ prefix_blen(&zpr.rule.filter.src_ip));
+ STREAM_GETW(s, zpr.rule.filter.src_port);
+ STREAM_GETC(s, zpr.rule.filter.dst_ip.family);
+ STREAM_GETC(s, zpr.rule.filter.dst_ip.prefixlen);
+ STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s,
+ prefix_blen(&zpr.rule.filter.dst_ip));
+ STREAM_GETW(s, zpr.rule.filter.dst_port);
+ STREAM_GETL(s, zpr.rule.filter.fwmark);
+ STREAM_GETL(s, zpr.rule.action.table);
STREAM_GETL(s, ifindex);
- zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN);
- if (!zpr.ifp) {
- zlog_debug("FAiled to lookup ifindex: %u", ifindex);
- return;
+ if (ifindex) {
+ zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN);
+ if (!zpr.ifp) {
+ zlog_debug("Failed to lookup ifindex: %u",
+ ifindex);
+ return;
+ }
}
- if (!is_default_prefix(&zpr.filter.src_ip))
- zpr.filter.filter_bm |= PBR_FILTER_SRC_IP;
+ if (!is_default_prefix(&zpr.rule.filter.src_ip))
+ zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
+
+ if (!is_default_prefix(&zpr.rule.filter.dst_ip))
+ zpr.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
- if (!is_default_prefix(&zpr.filter.dst_ip))
- zpr.filter.filter_bm |= PBR_FILTER_DST_IP;
+ if (zpr.rule.filter.src_port)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_PORT;
- if (zpr.filter.src_port)
- zpr.filter.filter_bm |= PBR_FILTER_SRC_PORT;
+ if (zpr.rule.filter.dst_port)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT;
- if (zpr.filter.dst_port)
- zpr.filter.filter_bm |= PBR_FILTER_DST_PORT;
+ if (zpr.rule.filter.fwmark)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
if (hdr->command == ZEBRA_RULE_ADD)
zebra_pbr_add_rule(zvrf->zns, &zpr);
@@ -2711,6 +2809,107 @@ stream_failure:
return;
}
+
+static inline void zread_ipset(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_pbr_ipset zpi;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&zpi, 0, sizeof(zpi));
+
+ zpi.sock = client->sock;
+ STREAM_GETL(s, zpi.unique);
+ STREAM_GETL(s, zpi.type);
+ STREAM_GET(&zpi.ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ if (hdr->command == ZEBRA_IPSET_CREATE)
+ zebra_pbr_create_ipset(zvrf->zns, &zpi);
+ else
+ zebra_pbr_destroy_ipset(zvrf->zns, &zpi);
+ }
+
+stream_failure:
+ return;
+}
+
+static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_pbr_ipset_entry zpi;
+ struct zebra_pbr_ipset ipset;
+ struct stream *s;
+ uint32_t total, i;
+
+ s = msg;
+ STREAM_GETL(s, total);
+
+ for (i = 0; i < total; i++) {
+ memset(&zpi, 0, sizeof(zpi));
+ memset(&ipset, 0, sizeof(ipset));
+
+ zpi.sock = client->sock;
+ STREAM_GETL(s, zpi.unique);
+ STREAM_GET(&ipset.ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+ STREAM_GETC(s, zpi.src.family);
+ STREAM_GETC(s, zpi.src.prefixlen);
+ STREAM_GET(&zpi.src.u.prefix, s,
+ prefix_blen(&zpi.src));
+ STREAM_GETC(s, zpi.dst.family);
+ STREAM_GETC(s, zpi.dst.prefixlen);
+ STREAM_GET(&zpi.dst.u.prefix, s,
+ prefix_blen(&zpi.dst));
+
+ if (!is_default_prefix(&zpi.src))
+ zpi.filter_bm |= PBR_FILTER_SRC_IP;
+
+ if (!is_default_prefix(&zpi.dst))
+ zpi.filter_bm |= PBR_FILTER_DST_IP;
+
+ /* calculate backpointer */
+ zpi.backpointer = zebra_pbr_lookup_ipset_pername(zvrf->zns,
+ ipset.ipset_name);
+ if (hdr->command == ZEBRA_IPSET_ENTRY_ADD)
+ zebra_pbr_add_ipset_entry(zvrf->zns, &zpi);
+ else
+ zebra_pbr_del_ipset_entry(zvrf->zns, &zpi);
+ }
+
+stream_failure:
+ return;
+}
+
+static inline void zread_iptable(ZAPI_HANDLER_ARGS)
+{
+ struct zebra_pbr_iptable zpi;
+ struct stream *s;
+
+ s = msg;
+
+ memset(&zpi, 0, sizeof(zpi));
+
+ zpi.sock = client->sock;
+ STREAM_GETL(s, zpi.unique);
+ STREAM_GETL(s, zpi.type);
+ STREAM_GETL(s, zpi.filter_bm);
+ STREAM_GETL(s, zpi.action);
+ STREAM_GETL(s, zpi.fwmark);
+ STREAM_GET(&zpi.ipset_name, s,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ if (hdr->command == ZEBRA_IPTABLE_ADD)
+ zebra_pbr_add_iptable(zvrf->zns, &zpi);
+ else
+ zebra_pbr_del_iptable(zvrf->zns, &zpi);
+stream_failure:
+ return;
+}
+
void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,
[ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,
@@ -2771,6 +2970,12 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_TABLE_MANAGER_CONNECT] = zread_table_manager_request,
[ZEBRA_GET_TABLE_CHUNK] = zread_table_manager_request,
[ZEBRA_RELEASE_TABLE_CHUNK] = zread_table_manager_request,
+ [ZEBRA_IPSET_CREATE] = zread_ipset,
+ [ZEBRA_IPSET_DESTROY] = zread_ipset,
+ [ZEBRA_IPSET_ENTRY_ADD] = zread_ipset_entry,
+ [ZEBRA_IPSET_ENTRY_DELETE] = zread_ipset_entry,
+ [ZEBRA_IPTABLE_ADD] = zread_iptable,
+ [ZEBRA_IPTABLE_DELETE] = zread_iptable,
};
static inline void zserv_handle_commands(struct zserv *client,
diff --git a/zebra/zserv.h b/zebra/zserv.h
index 947e11e35b..503d85f5b6 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -28,6 +28,7 @@
#include "routemap.h"
#include "vty.h"
#include "zclient.h"
+#include "pbr.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_pw.h"
@@ -187,9 +188,21 @@ extern int zsend_pw_update(struct zserv *, struct zebra_pw *);
extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
enum zapi_route_notify_owner note);
+struct zebra_pbr_ipset;
+struct zebra_pbr_ipset_entry;
+struct zebra_pbr_iptable;
struct zebra_pbr_rule;
extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
enum zapi_rule_notify_owner note);
+extern void zsend_ipset_notify_owner(
+ struct zebra_pbr_ipset *ipset,
+ enum zapi_ipset_notify_owner note);
+extern void zsend_ipset_entry_notify_owner(
+ struct zebra_pbr_ipset_entry *ipset,
+ enum zapi_ipset_entry_notify_owner note);
+extern void zsend_iptable_notify_owner(
+ struct zebra_pbr_iptable *iptable,
+ enum zapi_iptable_notify_owner note);
extern void zserv_nexthop_num_warn(const char *, const struct prefix *,
const unsigned int);