summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COMMUNITY.md1
-rw-r--r--Makefile.am1
-rw-r--r--README4
-rwxr-xr-xREADME.NetBSD46
-rw-r--r--REPORTING-BUGS35
-rw-r--r--babeld/babel_main.c5
-rw-r--r--bgpd/Makefile.am4
-rw-r--r--bgpd/bgp_attr.c100
-rw-r--r--bgpd/bgp_attr.h6
-rw-r--r--bgpd/bgp_attr_evpn.c14
-rw-r--r--bgpd/bgp_debug.c53
-rw-r--r--bgpd/bgp_debug.h4
-rw-r--r--bgpd/bgp_ecommunity.c86
-rw-r--r--bgpd/bgp_ecommunity.h6
-rw-r--r--bgpd/bgp_evpn.c202
-rw-r--r--bgpd/bgp_evpn_private.h48
-rw-r--r--bgpd/bgp_evpn_vty.c53
-rw-r--r--bgpd/bgp_flowspec_util.c334
-rw-r--r--bgpd/bgp_flowspec_util.h3
-rw-r--r--bgpd/bgp_fsm.c21
-rw-r--r--bgpd/bgp_io.c2
-rw-r--r--bgpd/bgp_label.h1
-rw-r--r--bgpd/bgp_main.c2
-rw-r--r--bgpd/bgp_mplsvpn.c304
-rw-r--r--bgpd/bgp_mplsvpn.h12
-rw-r--r--bgpd/bgp_nexthop.h1
-rw-r--r--bgpd/bgp_pbr.c1140
-rw-r--r--bgpd/bgp_pbr.h256
-rw-r--r--bgpd/bgp_rd.c12
-rw-r--r--bgpd/bgp_rd.h2
-rw-r--r--bgpd/bgp_route.c252
-rw-r--r--bgpd/bgp_route.h10
-rw-r--r--bgpd/bgp_routemap.c2
-rw-r--r--bgpd/bgp_rpki.c3
-rw-r--r--bgpd/bgp_snmp.c42
-rw-r--r--bgpd/bgp_updgrp_packet.c3
-rw-r--r--bgpd/bgp_vty.c671
-rw-r--r--bgpd/bgp_zebra.c743
-rw-r--r--bgpd/bgp_zebra.h17
-rw-r--r--bgpd/bgpd.c191
-rw-r--r--bgpd/bgpd.h79
-rw-r--r--bgpd/rfapi/rfapi_vty.c10
-rwxr-xr-xconfigure.ac17
-rw-r--r--debianpkg/README.deb_build.md127
-rw-r--r--debianpkg/backports/ubuntu14.04/debian/frr.postinst2
-rw-r--r--debianpkg/frr-doc.docs1
-rw-r--r--debianpkg/frr.postinst2
-rw-r--r--doc/Makefile.am8
-rw-r--r--doc/developer/.gitignore2
-rw-r--r--doc/developer/Makefile1
-rw-r--r--doc/developer/Makefile.in8
-rw-r--r--doc/developer/building-frr-on-openbsd6.rst2
-rw-r--r--doc/developer/building.rst2
-rw-r--r--doc/developer/conf.py10
-rw-r--r--doc/developer/index.rst6
-rw-r--r--doc/developer/library.rst2
-rw-r--r--doc/developer/packaging-debian.rst173
-rw-r--r--doc/developer/packaging.rst8
-rw-r--r--doc/developer/process-architecture.rst320
-rw-r--r--doc/figures/threadmaster-multiple.pngbin0 -> 62628 bytes
-rw-r--r--doc/figures/threadmaster-single.pngbin0 -> 26152 bytes
-rw-r--r--doc/figures/threadmaster.svg42
-rw-r--r--doc/frr-sphinx.mk9
-rw-r--r--doc/manpages/.gitignore2
-rw-r--r--doc/manpages/Makefile.in (renamed from doc/manpages/Makefile)9
-rw-r--r--doc/manpages/conf.py1
-rw-r--r--doc/manpages/index.rst4
-rw-r--r--doc/manpages/sharpd.rst38
-rw-r--r--doc/user/.gitignore2
-rw-r--r--doc/user/Makefile.in (renamed from doc/user/Makefile)9
-rw-r--r--doc/user/basic.rst19
-rw-r--r--doc/user/bgp.rst25
-rw-r--r--doc/user/bugs.rst67
-rw-r--r--doc/user/conf.py10
-rw-r--r--doc/user/index.rst6
-rw-r--r--doc/user/installation.rst13
-rw-r--r--doc/user/ospf6d.rst2
-rw-r--r--doc/user/pbr.rst2
-rw-r--r--doc/user/sharp.rst67
-rw-r--r--doc/user/snmp.rst9
-rw-r--r--doc/user/zebra.rst173
-rw-r--r--eigrpd/eigrp_hello.c2
-rw-r--r--eigrpd/eigrp_macros.h2
-rw-r--r--eigrpd/eigrp_packet.c3
-rw-r--r--eigrpd/eigrp_query.c5
-rw-r--r--eigrpd/eigrp_reply.c2
-rw-r--r--eigrpd/eigrp_siaquery.c2
-rw-r--r--eigrpd/eigrp_siareply.c2
-rw-r--r--eigrpd/eigrp_topology.c29
-rw-r--r--eigrpd/eigrp_update.c20
-rw-r--r--isisd/isis_redist.c14
-rw-r--r--lib/command.c226
-rw-r--r--lib/command.h6
-rw-r--r--lib/command_graph.c5
-rw-r--r--lib/ipaddr.h10
-rw-r--r--lib/plist.c48
-rw-r--r--lib/plist_int.h2
-rw-r--r--lib/prefix.c128
-rw-r--r--lib/prefix.h91
-rw-r--r--lib/routemap.c14
-rw-r--r--lib/routemap.h4
-rw-r--r--lib/stream.c4
-rw-r--r--lib/zclient.c50
-rw-r--r--lib/zclient.h11
-rw-r--r--ospf6d/ospf6_abr.c74
-rw-r--r--ospf6d/ospf6_area.h3
-rw-r--r--ospf6d/ospf6_intra.c495
-rw-r--r--ospf6d/ospf6_route.c16
-rw-r--r--ospf6d/ospf6_route.h3
-rw-r--r--ospf6d/ospf6_top.c11
-rw-r--r--ospf6d/ospf6d.c2
-rw-r--r--ospfd/ospf_interface.c12
-rw-r--r--pbrd/pbr_nht.c14
-rw-r--r--pbrd/pbr_vty.c8
-rw-r--r--pbrd/pbr_zebra.c1
-rw-r--r--pimd/COMMANDS1
-rw-r--r--pimd/pim_cmd.c98
-rw-r--r--pimd/pim_iface.c34
-rw-r--r--pimd/pim_iface.h4
-rw-r--r--pimd/pim_ifchannel.c7
-rw-r--r--pimd/pim_ifchannel.h1
-rw-r--r--pimd/pim_igmp.c23
-rw-r--r--pimd/pim_igmp.h3
-rw-r--r--pimd/pim_igmp_mtrace.c6
-rw-r--r--pimd/pim_igmp_stats.c42
-rw-r--r--pimd/pim_igmp_stats.h41
-rw-r--r--pimd/pim_igmpv2.c6
-rw-r--r--pimd/pim_igmpv3.c3
-rw-r--r--pimd/pim_instance.c14
-rw-r--r--pimd/pim_msdp.c12
-rw-r--r--pimd/pim_pim.c4
-rw-r--r--pimd/pim_upstream.c35
-rw-r--r--pimd/pim_upstream.h1
-rw-r--r--pimd/pim_zebra.c35
-rw-r--r--pimd/subdir.am2
-rw-r--r--redhat/frr.service2
-rw-r--r--redhat/frr.spec.in2
-rw-r--r--snapcraft/defaults/babeld.conf.default0
-rw-r--r--snapcraft/defaults/eigrpd.conf.default0
-rw-r--r--snapcraft/defaults/pbrd.conf.default0
-rw-r--r--snapcraft/scripts/Makefile3
-rw-r--r--snapcraft/scripts/babeld-service13
-rw-r--r--snapcraft/scripts/eigrpd-service13
-rw-r--r--snapcraft/scripts/pbrd-service13
-rw-r--r--snapcraft/snapcraft.yaml.in50
-rw-r--r--tests/bgpd/test_mp_attr.c42
-rw-r--r--tools/etc/iproute2/rt_protos.d/frr.conf3
-rwxr-xr-xtools/frr13
-rw-r--r--tools/frr.service2
-rw-r--r--vtysh/Makefile.am1
-rw-r--r--vtysh/vtysh.c33
-rw-r--r--zebra/connected.c4
-rw-r--r--zebra/if_netlink.c41
-rw-r--r--zebra/interface.c13
-rw-r--r--zebra/kernel_netlink.c2
-rw-r--r--zebra/kernel_netlink.h1
-rw-r--r--zebra/kernel_socket.c10
-rw-r--r--zebra/redistribute.c2
-rw-r--r--zebra/rib.h3
-rw-r--r--zebra/rt_netlink.c41
-rw-r--r--zebra/rt_netlink.h1
-rw-r--r--zebra/zapi_msg.c11
-rw-r--r--zebra/zebra_ptm.c6
-rw-r--r--zebra/zebra_rib.c5
-rw-r--r--zebra/zebra_vty.c19
-rw-r--r--zebra/zebra_vxlan.c79
-rw-r--r--zebra/zebra_vxlan.h1
167 files changed, 6496 insertions, 1605 deletions
diff --git a/COMMUNITY.md b/COMMUNITY.md
deleted file mode 100644
index fa00310851..0000000000
--- a/COMMUNITY.md
+++ /dev/null
@@ -1 +0,0 @@
-Moved to doc/developer/workflow.rst
diff --git a/Makefile.am b/Makefile.am
index 6cac1a7ba3..3b8deb5884 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -83,7 +83,6 @@ rc_SCRIPTS = \
endif
EXTRA_DIST += \
- REPORTING-BUGS \
SERVICES \
aclocal.m4 \
update-autotools \
diff --git a/README b/README
index af14795a6a..95ed0d3fc2 100644
--- a/README
+++ b/README
@@ -5,9 +5,9 @@ Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, RIPv2, RIPng,
IS-IS, PIM-SM/MSDP, LDP and Babel as well as very early support for EIGRP and
NHRP.
-See the file REPORTING-BUGS to report bugs.
+See doc/user/bugs.rst for information on how to report bugs.
-See COMMUNITY.md for information on contributing.
+See doc/developer/workflow.rst for information on contributing.
Free RRRouting is free software. See the file COPYING for copying conditions.
diff --git a/README.NetBSD b/README.NetBSD
deleted file mode 100755
index c97e3bcb38..0000000000
--- a/README.NetBSD
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-
-# This file is helpful for building FRR from cvs on NetBSD, and
-# probably on any system using pkgsrc.
-# One should have readline installed already (pkgsrc/devel/readline).
-
-MAKE=make
-# FRR is currently documented not to require GNU make, but sometimes
-# BSD make fails. Enable this if statement as a workaround.
-if false; then
- MAKE=gmake
- echo "WARNING: using gmake to work around nonportable makefiles"
-fi
-
-# Use /usr/frr to be independent, and /usr/pkg to overwrite pkgsrc.
-PREFIX=/usr/pkg
-
-case $1 in
-
- build)
- # Omitted because it is now default:
- # --enable-opaque-lsa
- ./bootstrap.sh
- LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" CPPFLAGS="-I/usr/pkg/include" \
- ./configure --prefix=${PREFIX} \
- --sysconfdir=/etc/zebra --localstatedir=/var/run/zebra \
- --enable-exampledir=${PREFIX}/share/examples/zebra \
- --enable-pkgsrcrcdir=${PREFIX}/etc/rc.d \
- --enable-vtysh
- ${MAKE}
- ;;
-
- install)
- ${MAKE} install
- ;;
-
- clean)
- ${MAKE} clean
- ;;
-
- *)
- echo "Usage: README.NetBSD (build|install|clean)"
- exit 1
- ;;
-
-esac
diff --git a/REPORTING-BUGS b/REPORTING-BUGS
deleted file mode 100644
index 339ebc24d5..0000000000
--- a/REPORTING-BUGS
+++ /dev/null
@@ -1,35 +0,0 @@
-This file describes the procedure for reporting FRRouting bugs. You are not
-obliged to follow this format, but it would be great help for FRRouting developers
-if you report a bug as described below.
-
-Bugs submitted with woefully incomplete information may be summarily
-closed. Submitters of bugs against old versions may be asked to
-retest against the latest release. Submitters may be asked for
-additional information. Bugs may be closed after 30 days of
-non-response to requests to reconfirm or supply additional
-information.
-
-Report bugs on Github Issue Tracker at
- https://github.com/frrouting/frr/issues
-
-Please supply the following information:
-1. Your FRRouting version or if it is from git then the commit reference.
- Please try to report bugs against git master or the latest release.
-2. FRR daemons you run e.g. bgpd or ripd and full name of your OS. Any
- specific options you compiled FRR with.
-3. Problem description. Copy and paste relative commands and their output to
- describe your network setup e.g. "zebra>show ip route".
- Please, also give your simple network layout and output of relative OS
- commands (e.g., ifconfig (BSD) or ip (Linux)).
-4. All FRR configuration files you use. If you don't want to publish your
- network numbers change 2 middle bytes in IPv4 address to be XXX (e.g.
- 192.XXX.XXX.32/24). Similar could be done with IPv6.
-5. If any FRR daemon core dumped, please, supply stack trace using the
- following commands: host> gdb exec_file core_file , (gdb) bt .
-6. Run all FRR daemons with full debugging on (see documentation on
- debugging) and send _only_ part of logs which are relative to your problem.
-7. If the problem is difficult to reproduce please send a shell script to
- reproduce it.
-8. Patches, workarounds, fixes are always welcome.
-
-Thank You.
diff --git a/babeld/babel_main.c b/babeld/babel_main.c
index 48f6994d82..9ea123c8f9 100644
--- a/babeld/babel_main.c
+++ b/babeld/babel_main.c
@@ -73,7 +73,6 @@ int protocol_port; /* babel's port */
int protocol_socket = -1; /* socket: communicate with others babeld */
static char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG;
-static char *babel_config_file = NULL;
static char *babel_vty_addr = NULL;
static int babel_vty_port = BABEL_VTY_PORT;
@@ -198,7 +197,7 @@ main(int argc, char **argv)
babelz_zebra_init ();
/* Get zebra configuration file. */
- vty_read_config (babel_config_file, babel_config_default);
+ vty_read_config (babeld_di.config_file, babel_config_default);
/* init buffer */
rc = resize_receive_buffer(1500);
@@ -389,7 +388,7 @@ show_babel_main_configuration (struct vty *vty)
"id = %s\n"
"kernel_metric = %d\n",
state_file,
- babel_config_file ? babel_config_file : babel_config_default,
+ babeld_di.config_file ? babeld_di.config_file : babel_config_default,
format_address(protocol_group),
protocol_port,
babel_vty_addr ? babel_vty_addr : "None",
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index a2880b7b94..8a410adca1 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_labelpool.c
+ bgp_flowspec_vty.c bgp_labelpool.c bgp_pbr.c
noinst_HEADERS = \
bgp_memory.h \
@@ -101,7 +101,7 @@ noinst_HEADERS = \
$(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_labelpool.h
+ bgp_labelpool.h bgp_pbr.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index a8ba892f39..276a7054e3 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -520,6 +520,8 @@ unsigned int attrhash_key_make(void *p)
MIX(attr->mp_nexthop_len);
key = jhash(attr->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key);
key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
+ MIX(attr->nh_ifindex);
+ MIX(attr->nh_lla_ifindex);
return key;
}
@@ -559,7 +561,9 @@ int attrhash_cmp(const void *p1, const void *p2)
&attr2->mp_nexthop_global_in)
&& IPV4_ADDR_SAME(&attr1->originator_id,
&attr2->originator_id)
- && overlay_index_same(attr1, attr2))
+ && overlay_index_same(attr1, attr2)
+ && attr1->nh_ifindex == attr2->nh_ifindex
+ && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex)
return 1;
}
@@ -593,9 +597,9 @@ static void attr_show_all_iterator(struct hash_backet *backet, struct vty *vty)
vty_out(vty, "attr[%ld] nexthop %s\n", attr->refcnt,
inet_ntoa(attr->nexthop));
- vty_out(vty, "\tflags: %" PRIu64 " med: %u local_pref: %u origin: %u weight: %u\n",
+ vty_out(vty, "\tflags: %" PRIu64 " med: %u local_pref: %u origin: %u weight: %u label: %u\n",
attr->flag, attr->med, attr->local_pref, attr->origin,
- attr->weight);
+ attr->weight, attr->label);
}
void attr_show_all(struct vty *vty)
@@ -1683,6 +1687,8 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
stream_getl(s); /* RD low */
}
stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global))
+ attr->nh_ifindex = peer->nexthop.ifp->ifindex;
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
@@ -1692,6 +1698,8 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
stream_getl(s); /* RD low */
}
stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global))
+ attr->nh_ifindex = peer->nexthop.ifp->ifindex;
if (attr->mp_nexthop_len
== BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
stream_getl(s); /* RD high */
@@ -1715,6 +1723,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
}
+ attr->nh_lla_ifindex = peer->nexthop.ifp->ifindex;
break;
default:
zlog_info("%s: (%s) Wrong multiprotocol next hop length: %d",
@@ -2012,36 +2021,32 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
return 0;
}
-/* Prefix SID attribute
- * draft-ietf-idr-bgp-prefix-sid-05
+/*
+ * Read an individual SID value returning how much data we have read
+ * Returns 0 if there was an error that needs to be passed up the stack
*/
-static bgp_attr_parse_ret_t
-bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
- struct bgp_nlri *mp_update)
+static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
+ int32_t length,
+ struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update)
{
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
- int type;
- int length;
uint32_t label_index;
struct in6_addr ipv6_sid;
uint32_t srgb_base;
uint32_t srgb_range;
int srgb_count;
- attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
-
- type = stream_getc(peer->curr);
- length = stream_getw(peer->curr);
-
if (type == BGP_PREFIX_SID_LABEL_INDEX) {
if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
zlog_err(
- "Prefix SID label index length is %d instead of %d",
- length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- args->total);
+ "Prefix SID label index length is %d instead of %d",
+ length,
+ BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
}
/* Ignore flags and reserved */
@@ -2051,9 +2056,8 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
/* Fetch the label index and see if it is valid. */
label_index = stream_getl(peer->curr);
if (label_index == BGP_INVALID_LABEL_INDEX)
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
- args->total);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
/* Store label index; subsequently, we'll check on
* address-family */
@@ -2074,9 +2078,9 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
if (length != BGP_PREFIX_SID_IPV6_LENGTH) {
zlog_err("Prefix SID IPv6 length is %d instead of %d",
length, BGP_PREFIX_SID_IPV6_LENGTH);
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- args->total);
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
}
/* Ignore reserved */
@@ -2113,6 +2117,47 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
return BGP_ATTR_PARSE_PROCEED;
}
+/* Prefix SID attribute
+ * draft-ietf-idr-bgp-prefix-sid-05
+ */
+bgp_attr_parse_ret_t
+bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ bgp_attr_parse_ret_t ret;
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
+
+ while (tlength) {
+ int32_t type, length;
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ ret = bgp_attr_psid_sub(type, length, args, mp_update);
+
+ if (ret != BGP_ATTR_PARSE_PROCEED)
+ return ret;
+ /*
+ * Subtract length + the T and the L
+ * since length is the Vector portion
+ */
+ tlength -= length + 3;
+
+ if (tlength < 0) {
+ zlog_err("Prefix SID internal length %d causes us to read beyond the total Prefix SID length",
+ length);
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/* PMSI tunnel attribute (RFC 6514)
* Basic validation checks done here.
*/
@@ -2489,7 +2534,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
startp);
break;
case BGP_ATTR_PREFIX_SID:
- ret = bgp_attr_prefix_sid(&attr_args, mp_update);
+ ret = bgp_attr_prefix_sid(length,
+ &attr_args, mp_update);
break;
case BGP_ATTR_PMSI_TUNNEL:
ret = bgp_attr_pmsi_tunnel(&attr_args);
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 758db4a447..f17c2a68e4 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -144,6 +144,9 @@ struct attr {
struct in6_addr mp_nexthop_global;
struct in6_addr mp_nexthop_local;
+ /* ifIndex corresponding to mp_nexthop_local. */
+ ifindex_t nh_lla_ifindex;
+
/* Extended Communities attribute. */
struct ecommunity *ecommunity;
@@ -305,6 +308,9 @@ extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
struct bgp_nlri *);
extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
struct bgp_nlri *);
+extern bgp_attr_parse_ret_t
+bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update);
extern struct bgp_attr_encap_subtlv *
encap_tlv_dup(struct bgp_attr_encap_subtlv *orig);
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index d2a61b93fe..14ff01ada5 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -227,16 +227,18 @@ extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag,
dst->family = AF_EVPN;
p_evpn_p->route_type = evpn_type;
if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
- p_evpn_p->eth_tag = eth_tag;
- p_evpn_p->ip_prefix_length = p2.prefixlen;
+ p_evpn_p->prefix_addr.eth_tag = eth_tag;
+ p_evpn_p->prefix_addr.ip_prefix_length = p2.prefixlen;
if (src->family == AF_INET) {
- SET_IPADDR_V4(&p_evpn_p->ip);
- memcpy(&p_evpn_p->ip.ipaddr_v4, &src->u.prefix4,
+ SET_IPADDR_V4(&p_evpn_p->prefix_addr.ip);
+ memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v4,
+ &src->u.prefix4,
sizeof(struct in_addr));
dst->prefixlen = (uint8_t)PREFIX_LEN_ROUTE_TYPE_5_IPV4;
} else {
- SET_IPADDR_V6(&p_evpn_p->ip);
- memcpy(&p_evpn_p->ip.ipaddr_v6, &src->u.prefix6,
+ SET_IPADDR_V6(&p_evpn_p->prefix_addr.ip);
+ memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v6,
+ &src->u.prefix6,
sizeof(struct in6_addr));
dst->prefixlen = (uint8_t)PREFIX_LEN_ROUTE_TYPE_5_IPV6;
}
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 29ac5f520d..3e3fbcbfe8 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -59,6 +59,7 @@ 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 conf_bgp_debug_pbr;
unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
@@ -75,6 +76,7 @@ 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;
+unsigned long term_bgp_debug_pbr;
struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
@@ -1653,7 +1655,40 @@ DEFUN (no_debug_bgp_vpn,
if (vty->node != CONFIG_NODE)
vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text);
+ return CMD_SUCCESS;
+}
+/* debug bgp pbr */
+DEFUN (debug_bgp_pbr,
+ debug_bgp_pbr_cmd,
+ "debug bgp pbr",
+ DEBUG_STR
+ BGP_STR
+ "BGP policy based routing\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(pbr, PBR);
+ else {
+ TERM_DEBUG_ON(pbr, PBR);
+ vty_out(vty, "BGP policy based routing is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_pbr,
+ no_debug_bgp_pbr_cmd,
+ "no debug bgp pbr",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP policy based routing\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(pbr, PBR);
+ else {
+ TERM_DEBUG_OFF(pbr, PBR);
+ vty_out(vty, "BGP policy based routing is off\n");
+ }
return CMD_SUCCESS;
}
@@ -1733,6 +1768,7 @@ DEFUN (no_debug_bgp,
TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
TERM_DEBUG_OFF(flowspec, FLOWSPEC);
TERM_DEBUG_OFF(labelpool, LABELPOOL);
+ TERM_DEBUG_OFF(pbr, PBR);
vty_out(vty, "All possible debugging has been turned off\n");
return CMD_SUCCESS;
@@ -1808,6 +1844,9 @@ DEFUN_NOSH (show_debugging_bgp,
if (BGP_DEBUG(labelpool, LABELPOOL))
vty_out(vty, " BGP labelpool debugging is on\n");
+ if (BGP_DEBUG(pbr, PBR))
+ vty_out(vty, " BGP policy based routing debugging is on\n");
+
vty_out(vty, "\n");
return CMD_SUCCESS;
}
@@ -1865,6 +1904,9 @@ int bgp_debug_count(void)
if (BGP_DEBUG(labelpool, LABELPOOL))
ret++;
+ if (BGP_DEBUG(pbr, PBR))
+ ret++;
+
return ret;
}
@@ -1966,6 +2008,10 @@ static int bgp_config_write_debug(struct vty *vty)
write++;
}
+ if (CONF_BGP_DEBUG(pbr, PBR)) {
+ vty_out(vty, "debug bgp pbr\n");
+ write++;
+ }
return write;
}
@@ -2069,6 +2115,13 @@ void bgp_debug_init(void)
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);
+
+ /* debug bgp pbr */
+ install_element(ENABLE_NODE, &debug_bgp_pbr_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_pbr_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_pbr_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 ad476ee918..d5d8fbb505 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -75,6 +75,7 @@ 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 conf_bgp_debug_pbr;
extern unsigned long term_bgp_debug_as4;
extern unsigned long term_bgp_debug_neighbor_events;
@@ -89,6 +90,7 @@ 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 unsigned long term_bgp_debug_pbr;
extern struct list *bgp_debug_neighbor_events_peers;
extern struct list *bgp_debug_keepalive_peers;
@@ -123,6 +125,8 @@ struct bgp_debug_filter {
#define BGP_DEBUG_VPN_LEAK_LABEL 0x08
#define BGP_DEBUG_FLOWSPEC 0x01
#define BGP_DEBUG_LABELPOOL 0x01
+#define BGP_DEBUG_PBR 0x01
+#define BGP_DEBUG_PBR_ERROR 0x02
#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 80166dd32b..85b9ffd8ca 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -34,6 +34,7 @@
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_flowspec_private.h"
+#include "bgpd/bgp_pbr.h"
/* struct used to dump the rate contained in FS set traffic-rate EC */
union traffic_rate {
@@ -895,3 +896,88 @@ extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
ecom->val = p;
return 1;
}
+
+/*
+ * Remove specified extended community value from extended community.
+ * Returns 1 if value was present (and hence, removed), 0 otherwise.
+ */
+int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
+{
+ uint8_t *p;
+ int c, found = 0;
+
+ /* Make sure specified value exists. */
+ if (ecom == NULL || ecom->val == NULL)
+ return 0;
+ c = 0;
+ for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
+ if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0)
+ return 0;
+
+ /* Delete the selected value */
+ ecom->size--;
+ p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
+ if (c != 0)
+ memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
+ if ((ecom->size - c) != 0)
+ memcpy(p + (c)*ECOMMUNITY_SIZE,
+ ecom->val + (c + 1) * ECOMMUNITY_SIZE,
+ (ecom->size - c) * ECOMMUNITY_SIZE);
+ XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
+ ecom->val = p;
+ return 1;
+}
+
+int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
+ struct bgp_pbr_entry_action *api)
+{
+ if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
+ api->action = ACTION_TRAFFICRATE;
+ api->u.r.rate_info[3] = ecom_eval->val[4];
+ api->u.r.rate_info[2] = ecom_eval->val[5];
+ api->u.r.rate_info[1] = ecom_eval->val[6];
+ api->u.r.rate_info[0] = ecom_eval->val[7];
+ } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
+ api->action = ACTION_TRAFFIC_ACTION;
+ /* else distribute code is set by default */
+ if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
+ api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
+ else
+ api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
+ if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
+ api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
+
+ } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
+ api->action = ACTION_MARKING;
+ api->u.marking_dscp = ecom_eval->val[7];
+ } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
+ /* must use external function */
+ return 0;
+ } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
+ /* see draft-ietf-idr-flowspec-redirect-ip-02
+ * Q1: how come a ext. community can host ipv6 address
+ * Q2 : from cisco documentation:
+ * Announces the reachability of one or more flowspec NLRI.
+ * When a BGP speaker receives an UPDATE message with the
+ * redirect-to-IP extended community, it is expected to
+ * create a traffic filtering rule for every flow-spec
+ * NLRI in the message that has this path as its best
+ * path. The filter entry matches the IP packets
+ * described in the NLRI field and redirects them or
+ * copies them towards the IPv4 or IPv6 address specified
+ * in the 'Network Address of Next- Hop'
+ * field of the associated MP_REACH_NLRI.
+ */
+ struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
+ ecom_eval + 2;
+
+ api->u.zr.redirect_ip_v4 = ip_ecom->ip;
+ } else
+ return -1;
+ return 0;
+}
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 0c22c5a149..88bdb5e2ae 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -170,4 +170,10 @@ extern int ecommunity_add_val(struct ecommunity *, struct ecommunity_val *);
extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
uint8_t subtype);
extern struct ecommunity *ecommunity_new(void);
+extern int ecommunity_del_val(struct ecommunity *ecom,
+ struct ecommunity_val *eval);
+struct bgp_pbr_entry_action;
+extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
+ struct bgp_pbr_entry_action *api);
+
#endif /* _QUAGGA_BGP_ECOMMUNITY_H */
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 483d65be71..ee53beb191 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -29,7 +29,6 @@
#include "stream.h"
#include "hash.h"
#include "jhash.h"
-#include "bitfield.h"
#include "zclient.h"
#include "bgpd/bgp_attr_evpn.h"
@@ -511,15 +510,15 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL,
bgp->vrf_id);
stream_putl(s, vpn->vni);
- stream_put(s, &p->prefix.mac.octet, ETH_ALEN); /* Mac Addr */
+ stream_put(s, &p->prefix.macip_addr.mac.octet, ETH_ALEN); /* Mac Addr */
/* IP address length and IP address, if any. */
- if (IS_EVPN_PREFIX_IPADDR_NONE(p))
+ if (is_evpn_prefix_ipaddr_none(p))
stream_putl(s, 0);
else {
- ipa_len = IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BYTELEN
+ ipa_len = is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BYTELEN
: IPV6_MAX_BYTELEN;
stream_putl(s, ipa_len);
- stream_put(s, &p->prefix.ip.ip.addr, ipa_len);
+ stream_put(s, &p->prefix.macip_addr.ip.ip.addr, ipa_len);
}
stream_put_in_addr(s, &remote_vtep_ip);
@@ -533,8 +532,10 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
zlog_debug(
"Tx %s MACIP, VNI %u MAC %s IP %s (flags: 0x%x) remote VTEP %s",
add ? "ADD" : "DEL", vpn->vni,
- prefix_mac2str(&p->prefix.mac, buf1, sizeof(buf1)),
- ipaddr2str(&p->prefix.ip, buf3, sizeof(buf3)), flags,
+ prefix_mac2str(&p->prefix.macip_addr.mac,
+ buf1, sizeof(buf1)),
+ ipaddr2str(&p->prefix.macip_addr.ip,
+ buf3, sizeof(buf3)), flags,
inet_ntop(AF_INET, &remote_vtep_ip, buf2,
sizeof(buf2)));
@@ -564,9 +565,9 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
s, add ? ZEBRA_REMOTE_VTEP_ADD : ZEBRA_REMOTE_VTEP_DEL,
bgp->vrf_id);
stream_putl(s, vpn->vni);
- if (IS_EVPN_PREFIX_IPADDR_V4(p))
- stream_put_in_addr(s, &p->prefix.ip.ipaddr_v4);
- else if (IS_EVPN_PREFIX_IPADDR_V6(p)) {
+ if (is_evpn_prefix_ipaddr_v4(p))
+ stream_put_in_addr(s, &p->prefix.imet_addr.ip.ipaddr_v4);
+ else if (is_evpn_prefix_ipaddr_v6(p)) {
zlog_err(
"Bad remote IP when trying to %s remote VTEP for VNI %u",
add ? "ADD" : "DEL", vpn->vni);
@@ -578,7 +579,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
if (bgp_debug_zebra(NULL))
zlog_debug("Tx %s Remote VTEP, VNI %u remote VTEP %s",
add ? "ADD" : "DEL", vpn->vni,
- inet_ntoa(p->prefix.ip.ipaddr_v4));
+ inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4));
return zclient_send_message(zclient);
}
@@ -1294,8 +1295,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* these routes.
*/
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
- (IS_EVPN_PREFIX_IPADDR_V4(p) ||
- !IN6_IS_ADDR_LINKLOCAL(&p->prefix.ip.ipaddr_v6)) &&
+ (is_evpn_prefix_ipaddr_v4(p) ||
+ !IN6_IS_ADDR_LINKLOCAL(&p->prefix.macip_addr.ip.ipaddr_v6)) &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS))
add_l3_ecomm = 1;
@@ -1540,8 +1541,8 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
- if (IS_EVPN_PREFIX_IPADDR_V6(evp) &&
- IN6_IS_ADDR_LINKLOCAL(&evp->prefix.ip.ipaddr_v6))
+ if (is_evpn_prefix_ipaddr_v6(evp) &&
+ IN6_IS_ADDR_LINKLOCAL(&evp->prefix.macip_addr.ip.ipaddr_v6))
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr_ip6_ll, 0, 1, &ri, 0);
else {
@@ -1793,10 +1794,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
char buf1[PREFIX_STRLEN];
memset(pp, 0, sizeof(struct prefix));
- if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
- ip_prefix_from_type2_prefix(evp, pp);
- else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
- ip_prefix_from_type5_prefix(evp, pp);
+ ip_prefix_from_evpn_prefix(evp, pp);
if (bgp_debug_zebra(NULL)) {
zlog_debug(
@@ -1808,11 +1806,11 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
/* Create (or fetch) route within the VRF. */
/* NOTE: There is no RD here. */
- if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
afi = AFI_IP;
safi = SAFI_UNICAST;
rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
- } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ } else if (is_evpn_prefix_ipaddr_v6(evp)) {
afi = AFI_IP6;
safi = SAFI_UNICAST;
rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
@@ -1821,12 +1819,15 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
/* EVPN routes currently only support a IPv4 next hop which corresponds
* to the remote VTEP. When importing into a VRF, if it is IPv6 host
- * route, we have to convert the next hop to an IPv4-mapped address
- * for the rest of the code to flow through.
+ * or prefix route, we have to convert the next hop to an IPv4-mapped
+ * address for the rest of the code to flow through. In the case of IPv4,
+ * make sure to set the flag for next hop attribute.
*/
bgp_attr_dup(&attr, parent_ri->attr);
if (afi == AFI_IP6)
evpn_convert_nexthop_to_ipv6(&attr);
+ else
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
/* Check if route entry is already present. */
for (ri = rn->info; ri; ri = ri->next)
@@ -1843,7 +1844,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
parent_ri->peer, attr_new, rn);
SET_FLAG(ri->flags, BGP_INFO_VALID);
bgp_info_extra_get(ri);
- ri->extra->parent = parent_ri;
+ ri->extra->parent = bgp_info_lock(parent_ri);
if (parent_ri->extra) {
memcpy(&ri->extra->label, &parent_ri->extra->label,
sizeof(ri->extra->label));
@@ -1915,7 +1916,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
parent_ri->peer, attr_new, rn);
SET_FLAG(ri->flags, BGP_INFO_VALID);
bgp_info_extra_get(ri);
- ri->extra->parent = parent_ri;
+ ri->extra->parent = bgp_info_lock(parent_ri);
if (parent_ri->extra) {
memcpy(&ri->extra->label, &parent_ri->extra->label,
sizeof(ri->extra->label));
@@ -1971,10 +1972,7 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
char buf1[PREFIX_STRLEN];
memset(pp, 0, sizeof(struct prefix));
- if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
- ip_prefix_from_type2_prefix(evp, pp);
- else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
- ip_prefix_from_type5_prefix(evp, pp);
+ ip_prefix_from_evpn_prefix(evp, pp);
if (bgp_debug_zebra(NULL)) {
zlog_debug(
@@ -1986,7 +1984,7 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
/* Locate route within the VRF. */
/* NOTE: There is no RD here. */
- if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
afi = AFI_IP;
safi = SAFI_UNICAST;
rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
@@ -2233,8 +2231,8 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install)
continue;
/* if not a mac+ip route skip this route */
- if (!(IS_EVPN_PREFIX_IPADDR_V4(evp)
- || IS_EVPN_PREFIX_IPADDR_V6(evp)))
+ if (!(is_evpn_prefix_ipaddr_v4(evp)
+ || is_evpn_prefix_ipaddr_v6(evp)))
continue;
for (ri = rn->info; ri; ri = ri->next) {
@@ -2424,8 +2422,8 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
/* if it is type-2 route and not a mac+ip route skip this route */
if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
- && !(IS_EVPN_PREFIX_IPADDR_V4(evp)
- || IS_EVPN_PREFIX_IPADDR_V6(evp)))
+ && !(is_evpn_prefix_ipaddr_v4(evp)
+ || is_evpn_prefix_ipaddr_v6(evp)))
return 0;
for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) {
@@ -2851,7 +2849,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
/* Copy Ethernet Tag */
memcpy(&eth_tag, pfx, 4);
- p.prefix.eth_tag = ntohl(eth_tag);
+ p.prefix.macip_addr.eth_tag = ntohl(eth_tag);
pfx += 4;
/* Get the MAC Addr len */
@@ -2859,7 +2857,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
/* Get the MAC Addr */
if (macaddr_len == (ETH_ALEN * 8)) {
- memcpy(&p.prefix.mac.octet, pfx, ETH_ALEN);
+ memcpy(&p.prefix.macip_addr.mac.octet, pfx, ETH_ALEN);
pfx += ETH_ALEN;
} else {
zlog_err(
@@ -2881,10 +2879,10 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
if (ipaddr_len) {
ipaddr_len /= 8; /* Convert to bytes. */
- p.prefix.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN)
+ p.prefix.macip_addr.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN)
? IPADDR_V4
: IPADDR_V6;
- memcpy(&p.prefix.ip.ip.addr, pfx, ipaddr_len);
+ memcpy(&p.prefix.macip_addr.ip.ip.addr, pfx, ipaddr_len);
}
pfx += ipaddr_len;
@@ -2966,14 +2964,14 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
/* Copy Ethernet Tag */
memcpy(&eth_tag, pfx, 4);
- p.prefix.eth_tag = ntohl(eth_tag);
+ p.prefix.imet_addr.eth_tag = ntohl(eth_tag);
pfx += 4;
/* Get the IP. */
ipaddr_len = *pfx++;
if (ipaddr_len == IPV4_MAX_BITLEN) {
- p.prefix.ip.ipa_type = IPADDR_V4;
- memcpy(&p.prefix.ip.ip.addr, pfx, IPV4_MAX_BYTELEN);
+ p.prefix.imet_addr.ip.ipa_type = IPADDR_V4;
+ memcpy(&p.prefix.imet_addr.ip.ip.addr, pfx, IPV4_MAX_BYTELEN);
} else {
zlog_err(
"%u:%s - Rx EVPN Type-3 NLRI with unsupported IP address length %d",
@@ -3040,7 +3038,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
/* Fetch Ethernet Tag. */
memcpy(&eth_tag, pfx, 4);
- p.prefix.eth_tag = ntohl(eth_tag);
+ p.prefix.prefix_addr.eth_tag = ntohl(eth_tag);
pfx += 4;
/* Fetch IP prefix length. */
@@ -3051,21 +3049,21 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
peer->bgp->vrf_id, peer->host, ippfx_len);
return -1;
}
- p.prefix.ip_prefix_length = ippfx_len;
+ p.prefix.prefix_addr.ip_prefix_length = ippfx_len;
/* Determine IPv4 or IPv6 prefix */
/* Since the address and GW are from the same family, this just becomes
* a simple check on the total size.
*/
if (psize == 34) {
- SET_IPADDR_V4(&p.prefix.ip);
- memcpy(&p.prefix.ip.ipaddr_v4, pfx, 4);
+ SET_IPADDR_V4(&p.prefix.prefix_addr.ip);
+ memcpy(&p.prefix.prefix_addr.ip.ipaddr_v4, pfx, 4);
pfx += 4;
memcpy(&evpn.gw_ip.ipv4, pfx, 4);
pfx += 4;
} else {
- SET_IPADDR_V6(&p.prefix.ip);
- memcpy(&p.prefix.ip.ipaddr_v6, pfx, 16);
+ SET_IPADDR_V6(&p.prefix.prefix_addr.ip);
+ memcpy(&p.prefix.prefix_addr.ip.ipaddr_v6, pfx, 16);
pfx += 16;
memcpy(&evpn.gw_ip.ipv6, pfx, 16);
pfx += 16;
@@ -3110,7 +3108,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p,
/* len denites the total len of IP and GW-IP in the route
IP and GW-IP have to be both ipv4 or ipv6
*/
- if (IS_IPADDR_V4(&p_evpn_p->ip))
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
len = 8; /* IP and GWIP are both ipv4 */
else
len = 32; /* IP and GWIP are both ipv6 */
@@ -3121,20 +3119,20 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p,
stream_put(s, &(attr->evpn_overlay.eth_s_id), 10);
else
stream_put(s, &temp, 10);
- stream_putl(s, p_evpn_p->eth_tag);
- stream_putc(s, p_evpn_p->ip_prefix_length);
- if (IS_IPADDR_V4(&p_evpn_p->ip))
- stream_put_ipv4(s, p_evpn_p->ip.ipaddr_v4.s_addr);
+ stream_putl(s, p_evpn_p->prefix_addr.eth_tag);
+ stream_putc(s, p_evpn_p->prefix_addr.ip_prefix_length);
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
+ stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr);
else
- stream_put(s, &p_evpn_p->ip.ipaddr_v6, 16);
+ stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16);
if (attr) {
- if (IS_IPADDR_V4(&p_evpn_p->ip))
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
stream_put_ipv4(s,
attr->evpn_overlay.gw_ip.ipv4.s_addr);
else
stream_put(s, &(attr->evpn_overlay.gw_ip.ipv6), 16);
} else {
- if (IS_IPADDR_V4(&p_evpn_p->ip))
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
stream_put_ipv4(s, 0);
else
stream_put(s, &temp, 16);
@@ -3584,44 +3582,49 @@ void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json)
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
json_object_int_add(json, "routeType", p->prefix.route_type);
- json_object_int_add(json, "ethTag", p->prefix.eth_tag);
+ json_object_int_add(json, "ethTag",
+ p->prefix.imet_addr.eth_tag);
json_object_int_add(json, "ipLen",
- IS_EVPN_PREFIX_IPADDR_V4(p)
+ is_evpn_prefix_ipaddr_v4(p)
? IPV4_MAX_BITLEN
: IPV6_MAX_BITLEN);
json_object_string_add(json, "ip",
- inet_ntoa(p->prefix.ip.ipaddr_v4));
+ inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4));
} else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
- if (IS_EVPN_PREFIX_IPADDR_NONE(p)) {
+ if (is_evpn_prefix_ipaddr_none(p)) {
json_object_int_add(json, "routeType",
p->prefix.route_type);
- json_object_int_add(json, "ethTag", p->prefix.eth_tag);
+ json_object_int_add(json, "ethTag",
+ p->prefix.macip_addr.eth_tag);
json_object_int_add(json, "macLen", 8 * ETH_ALEN);
json_object_string_add(json, "mac",
- prefix_mac2str(&p->prefix.mac,
+ prefix_mac2str(&p->prefix.macip_addr.mac,
buf1,
sizeof(buf1)));
} else {
uint8_t family;
- family = IS_EVPN_PREFIX_IPADDR_V4(p) ? AF_INET
+ family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET
: AF_INET6;
json_object_int_add(json, "routeType",
p->prefix.route_type);
- json_object_int_add(json, "ethTag", p->prefix.eth_tag);
+ json_object_int_add(json, "ethTag",
+ p->prefix.macip_addr.eth_tag);
json_object_int_add(json, "macLen", 8 * ETH_ALEN);
json_object_string_add(json, "mac",
- prefix_mac2str(&p->prefix.mac,
+ prefix_mac2str(&p->prefix.macip_addr.mac,
buf1,
sizeof(buf1)));
json_object_int_add(json, "ipLen",
- IS_EVPN_PREFIX_IPADDR_V4(p)
+ is_evpn_prefix_ipaddr_v4(p)
? IPV4_MAX_BITLEN
: IPV6_MAX_BITLEN);
json_object_string_add(
json, "ip",
- inet_ntop(family, &p->prefix.ip.ip.addr, buf2,
+ inet_ntop(family,
+ &p->prefix.macip_addr.ip.ip.addr,
+ buf2,
PREFIX2STR_BUFFER));
}
} else {
@@ -3640,42 +3643,44 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type,
- p->prefix.eth_tag,
- IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BITLEN
+ p->prefix.imet_addr.eth_tag,
+ is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN
: IPV6_MAX_BITLEN,
- inet_ntoa(p->prefix.ip.ipaddr_v4));
+ inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4));
} else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
- if (IS_EVPN_PREFIX_IPADDR_NONE(p))
+ if (is_evpn_prefix_ipaddr_none(p))
snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]",
p->prefix.route_type,
- p->prefix.eth_tag,
+ p->prefix.macip_addr.eth_tag,
8 * ETH_ALEN,
- prefix_mac2str(&p->prefix.mac, buf1,
+ prefix_mac2str(&p->prefix.macip_addr.mac, buf1,
sizeof(buf1)));
else {
uint8_t family;
- family = IS_EVPN_PREFIX_IPADDR_V4(p) ? AF_INET
+ family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET
: AF_INET6;
snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]:[%d]:[%s]",
p->prefix.route_type,
- p->prefix.eth_tag,
+ p->prefix.macip_addr.eth_tag,
8 * ETH_ALEN,
- prefix_mac2str(&p->prefix.mac, buf1,
+ prefix_mac2str(&p->prefix.macip_addr.mac, buf1,
sizeof(buf1)),
family == AF_INET ? IPV4_MAX_BITLEN
: IPV6_MAX_BITLEN,
- inet_ntop(family, &p->prefix.ip.ip.addr, buf2,
+ inet_ntop(family,
+ &p->prefix.macip_addr.ip.ip.addr,
+ buf2,
PREFIX2STR_BUFFER));
}
} else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]",
p->prefix.route_type,
- p->prefix.eth_tag,
- p->prefix.ip_prefix_length,
- IS_EVPN_PREFIX_IPADDR_V4(p)
- ? inet_ntoa(p->prefix.ip.ipaddr_v4)
- : inet6_ntoa(p->prefix.ip.ipaddr_v6));
+ p->prefix.prefix_addr.eth_tag,
+ p->prefix.prefix_addr.ip_prefix_length,
+ is_evpn_prefix_ipaddr_v4(p)
+ ? inet_ntoa(p->prefix.prefix_addr.ip.ipaddr_v4)
+ : inet6_ntoa(p->prefix.prefix_addr.ip.ipaddr_v6));
} else {
/* For EVPN route types not supported yet. */
snprintf(buf, len, "(unsupported route type %d)",
@@ -3704,9 +3709,9 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
switch (evp->prefix.route_type) {
case BGP_EVPN_MAC_IP_ROUTE:
- if (IS_EVPN_PREFIX_IPADDR_V4(evp))
+ if (is_evpn_prefix_ipaddr_v4(evp))
ipa_len = IPV4_MAX_BYTELEN;
- else if (IS_EVPN_PREFIX_IPADDR_V6(evp))
+ else if (is_evpn_prefix_ipaddr_v6(evp))
ipa_len = IPV6_MAX_BYTELEN;
/* RD, ESI, EthTag, MAC+len, IP len, [IP], 1 VNI */
len = 8 + 10 + 4 + 1 + 6 + 1 + ipa_len + 3;
@@ -3718,12 +3723,13 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
stream_put(s, &attr->evpn_overlay.eth_s_id, ESI_LEN);
else
stream_put(s, 0, 10);
- stream_putl(s, evp->prefix.eth_tag); /* Ethernet Tag ID */
+ stream_putl(s, evp->prefix.macip_addr.eth_tag); /* Ethernet Tag ID */
stream_putc(s, 8 * ETH_ALEN); /* Mac Addr Len - bits */
- stream_put(s, evp->prefix.mac.octet, 6); /* Mac Addr */
- stream_putc(s, 8 * ipa_len); /* IP address Length */
- if (ipa_len) /* IP */
- stream_put(s, &evp->prefix.ip.ip.addr, ipa_len);
+ stream_put(s, evp->prefix.macip_addr.mac.octet, 6); /* Mac Addr */
+ stream_putc(s, 8 * ipa_len); /* IP address Length */
+ if (ipa_len) /* IP */
+ stream_put(s, &evp->prefix.macip_addr.ip.ip.addr,
+ ipa_len);
/* 1st label is the L2 VNI */
stream_put(s, label, BGP_LABEL_BYTES);
/* Include 2nd label (L3 VNI) if advertising MAC+IP */
@@ -3734,10 +3740,10 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
case BGP_EVPN_IMET_ROUTE:
stream_putc(s, 17); // TODO: length - assumes IPv4 address
stream_put(s, prd->val, 8); /* RD */
- stream_putl(s, evp->prefix.eth_tag); /* Ethernet Tag ID */
+ stream_putl(s, evp->prefix.imet_addr.eth_tag); /* Ethernet Tag ID */
stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */
/* Originating Router's IP Addr */
- stream_put_in_addr(s, &evp->prefix.ip.ipaddr_v4);
+ stream_put_in_addr(s, &evp->prefix.imet_addr.ip.ipaddr_v4);
break;
case BGP_EVPN_IP_PREFIX_ROUTE:
@@ -3988,12 +3994,7 @@ void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp)
{
- char buf[100];
-
- bgp->vrf_prd.family = AF_UNSPEC;
- bgp->vrf_prd.prefixlen = 64;
- sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), bgp->vrf_rd_id);
- (void)str2prefix_rd(buf, &bgp->vrf_prd);
+ form_auto_rd(bgp->router_id, bgp->vrf_rd_id, &bgp->vrf_prd);
}
/*
@@ -4427,8 +4428,10 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
/* Locate VNI hash */
vpn = bgp_evpn_lookup_vni(bgp, vni);
if (!vpn) {
- zlog_warn("%u: VNI hash entry for VNI %u not found at DEL",
- bgp->vrf_id, vni);
+ if (bgp_debug_zebra(NULL))
+ zlog_warn(
+ "%u: VNI hash entry for VNI %u not found at DEL",
+ bgp->vrf_id, vni);
return 0;
}
@@ -4577,7 +4580,6 @@ void bgp_evpn_cleanup(struct bgp *bgp)
list_delete_and_null(&bgp->vrf_export_rtl);
if (bgp->l2vnis)
list_delete_and_null(&bgp->l2vnis);
- bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
}
/*
@@ -4585,7 +4587,6 @@ void bgp_evpn_cleanup(struct bgp *bgp)
* Create
* VNI hash table
* hash for RT to VNI
- * assign a unique rd id for auto derivation of vrf_prd
*/
void bgp_evpn_init(struct bgp *bgp)
{
@@ -4606,7 +4607,6 @@ void bgp_evpn_init(struct bgp *bgp)
(int (*)(void *, void *))evpn_route_target_cmp;
bgp->l2vnis = list_new();
bgp->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp;
- bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
}
void bgp_evpn_vrf_delete(struct bgp *bgp_vrf)
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index 1eecb9ecf7..1efde3a719 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -272,15 +272,15 @@ static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp,
struct prefix *ip)
{
memset(ip, 0, sizeof(struct prefix));
- if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
ip->family = AF_INET;
- ip->prefixlen = evp->prefix.ip_prefix_length;
- memcpy(&(ip->u.prefix4), &(evp->prefix.ip.ip),
+ ip->prefixlen = evp->prefix.prefix_addr.ip_prefix_length;
+ memcpy(&(ip->u.prefix4), &(evp->prefix.prefix_addr.ip.ip),
IPV4_MAX_BYTELEN);
- } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ } else if (is_evpn_prefix_ipaddr_v6(evp)) {
ip->family = AF_INET6;
- ip->prefixlen = evp->prefix.ip_prefix_length;
- memcpy(&(ip->u.prefix6), &(evp->prefix.ip.ip),
+ ip->prefixlen = evp->prefix.prefix_addr.ip_prefix_length;
+ memcpy(&(ip->u.prefix6), &(evp->prefix.prefix_addr.ip.ip),
IPV6_MAX_BYTELEN);
}
}
@@ -290,26 +290,36 @@ static inline int is_evpn_prefix_default(struct prefix *evp)
if (evp->family != AF_EVPN)
return 0;
- return ((evp->u.prefix_evpn.ip_prefix_length == 0) ? 1 : 0);
+ return ((evp->u.prefix_evpn.prefix_addr.ip_prefix_length == 0) ?
+ 1 : 0);
}
static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp,
struct prefix *ip)
{
memset(ip, 0, sizeof(struct prefix));
- if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
ip->family = AF_INET;
ip->prefixlen = IPV4_MAX_BITLEN;
- memcpy(&(ip->u.prefix4), &(evp->prefix.ip.ip),
+ memcpy(&(ip->u.prefix4), &(evp->prefix.macip_addr.ip.ip),
IPV4_MAX_BYTELEN);
- } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ } else if (is_evpn_prefix_ipaddr_v6(evp)) {
ip->family = AF_INET6;
ip->prefixlen = IPV6_MAX_BITLEN;
- memcpy(&(ip->u.prefix6), &(evp->prefix.ip.ip),
+ memcpy(&(ip->u.prefix6), &(evp->prefix.macip_addr.ip.ip),
IPV6_MAX_BYTELEN);
}
}
+static inline void ip_prefix_from_evpn_prefix(struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ip_prefix_from_type2_prefix(evp, ip);
+ else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ ip_prefix_from_type5_prefix(evp, ip);
+}
+
static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
struct ethaddr *mac,
struct ipaddr *ip)
@@ -318,10 +328,10 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
p->family = AF_EVPN;
p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
- memcpy(&p->prefix.mac.octet, mac->octet, ETH_ALEN);
- p->prefix.ip.ipa_type = IPADDR_NONE;
+ memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN);
+ p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE;
if (ip)
- memcpy(&p->prefix.ip, ip, sizeof(*ip));
+ memcpy(&p->prefix.macip_addr.ip, ip, sizeof(*ip));
}
static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
@@ -343,10 +353,10 @@ static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
memset(evp, 0, sizeof(struct prefix_evpn));
evp->family = AF_EVPN;
evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
- evp->prefix.ip_prefix_length = ip_prefix->prefixlen;
evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
- evp->prefix.ip.ipa_type = ip.ipa_type;
- memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr));
+ evp->prefix.prefix_addr.ip_prefix_length = ip_prefix->prefixlen;
+ evp->prefix.prefix_addr.ip.ipa_type = ip.ipa_type;
+ memcpy(&evp->prefix.prefix_addr.ip, &ip, sizeof(struct ipaddr));
}
static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
@@ -356,8 +366,8 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
p->family = AF_EVPN;
p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
p->prefix.route_type = BGP_EVPN_IMET_ROUTE;
- p->prefix.ip.ipa_type = IPADDR_V4;
- p->prefix.ip.ipaddr_v4 = originator_ip;
+ p->prefix.imet_addr.ip.ipa_type = IPADDR_V4;
+ p->prefix.imet_addr.ip.ipaddr_v4 = originator_ip;
}
static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index c74a1bfb7c..fd3c229472 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -311,7 +311,7 @@ static void bgp_evpn_show_route_rd_header(struct vty *vty,
}
static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
- json_object *json)
+ uint64_t tbl_ver, json_object *json)
{
char ri_header[] =
" Network Next Hop Metric LocPrf Weight Path\n";
@@ -319,9 +319,8 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
if (json)
return;
-
- vty_out(vty, "BGP table version is 0, local router ID is %s\n",
- inet_ntoa(bgp->router_id));
+ vty_out(vty, "BGP table version is %" PRIu64 ", local router ID is %s\n",
+ tbl_ver, inet_ntoa(bgp->router_id));
vty_out(vty,
"Status codes: s suppressed, d damped, h history, "
"* valid, > best, i - internal\n");
@@ -494,12 +493,16 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
{
struct bgp_node *rn;
struct bgp_info *ri;
+ struct bgp_table *table;
int header = 1;
+ uint64_t tbl_ver;
uint32_t prefix_cnt, path_cnt;
prefix_cnt = path_cnt = 0;
- for (rn = bgp_table_top(vpn->route_table); rn;
+ table = vpn->route_table;
+ tbl_ver = table->version;
+ for (rn = bgp_table_top(table); rn;
rn = bgp_route_next(rn)) {
struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
int add_prefix_to_json = 0;
@@ -519,7 +522,8 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
if (rn->info) {
/* Overall header/legend displayed once. */
if (header) {
- bgp_evpn_show_route_header(vty, bgp, json);
+ bgp_evpn_show_route_header(vty, bgp,
+ tbl_ver, json);
header = 0;
}
@@ -603,9 +607,9 @@ static void show_vni_routes_hash(struct hash_backet *backet, void *arg)
static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
json_object *json)
{
- json_object *json_vni;
- json_object *json_import_rtl;
- json_object *json_export_rtl;
+ json_object *json_vni = NULL;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
char buf1[10];
char buf2[INET6_ADDRSTRLEN];
char rt_buf[25];
@@ -862,6 +866,8 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
for (rn = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); rn;
rn = bgp_route_next(rn)) {
+ uint64_t tbl_ver;
+
if (use_json)
continue; /* XXX json TODO */
@@ -872,6 +878,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
continue;
rd_header = 1;
+ tbl_ver = table->version;
for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm))
for (ri = rm->info; ri; ri = ri->next) {
@@ -891,7 +898,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
json_object_int_add(
json,
"bgpTableVersion",
- 0);
+ tbl_ver);
json_object_string_add(
json,
"bgpLocalRouterId",
@@ -917,7 +924,8 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
V4_HEADER_OVERLAY);
else {
vty_out(vty,
- "BGP table version is 0, local router ID is %s\n",
+ "BGP table version is %" PRIu64 ", local router ID is %s\n",
+ tbl_ver,
inet_ntoa(
bgp->router_id));
vty_out(vty,
@@ -2201,11 +2209,13 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
char rd_str[RD_ADDRSTRLEN];
json_object *json_rd = NULL; /* contains routes for an RD */
int add_rd_to_json = 0;
+ uint64_t tbl_ver;
table = (struct bgp_table *)rd_rn->info;
if (table == NULL)
continue;
+ tbl_ver = table->version;
prefix_rd2str((struct prefix_rd *)&rd_rn->p, rd_str,
sizeof(rd_str));
@@ -2236,6 +2246,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
/* Overall header/legend displayed once. */
if (header) {
bgp_evpn_show_route_header(vty, bgp,
+ tbl_ver,
json);
header = 0;
}
@@ -2873,7 +2884,7 @@ DEFUN (no_bgp_evpn_advertise_type5,
if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
- UNSET_FLAG(bgp_vrf->vrf_flags,
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST);
}
}
@@ -4382,12 +4393,22 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
vty_out(vty, " advertise-default-gw\n");
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST))
- vty_out(vty, " advertise ipv4 unicast\n");
+ BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
+ if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
+ vty_out(vty, " advertise ipv4 unicast route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
+ else
+ vty_out(vty, " advertise ipv4 unicast\n");
+ }
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))
- vty_out(vty, " advertise ipv6 unicast\n");
+ BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
+ if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
+ vty_out(vty, " advertise ipv6 unicast route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
+ else
+ vty_out(vty, " advertise ipv6 unicast\n");
+ }
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4))
diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c
index 007b27f17e..956cf28c21 100644
--- a/bgpd/bgp_flowspec_util.c
+++ b/bgpd/bgp_flowspec_util.c
@@ -25,6 +25,7 @@
#include "bgp_table.h"
#include "bgp_flowspec_util.h"
#include "bgp_flowspec_private.h"
+#include "bgp_pbr.h"
static void hex2bin(uint8_t *hex, int *bin)
{
@@ -50,6 +51,109 @@ static int hexstr2num(uint8_t *hexstr, int len)
return num;
}
+/* call bgp_flowspec_op_decode
+ * returns offset
+ */
+static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
+ struct bgp_pbr_match_val *mval,
+ uint8_t *match_num, int *error)
+{
+ int ret;
+
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content,
+ len,
+ mval, error);
+ if (*error < 0)
+ zlog_err("%s: flowspec_op_decode error %d",
+ __func__, *error);
+ else
+ *match_num = *error;
+ return ret;
+}
+
+static bool bgp_flowspec_contains_prefix(struct prefix *pfs,
+ struct prefix *input,
+ int prefix_check)
+{
+ uint32_t offset = 0;
+ int type;
+ int ret = 0, error = 0;
+ uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
+ size_t len = pfs->u.prefix_flowspec.prefixlen;
+ struct prefix compare;
+
+ error = 0;
+ while (offset < len-1 && error >= 0) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ memset(&compare, 0, sizeof(struct prefix));
+ ret = bgp_flowspec_ip_address(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content+offset,
+ len - offset,
+ &compare, &error);
+ if (ret <= 0)
+ break;
+ if (prefix_check &&
+ compare.prefixlen != input->prefixlen)
+ break;
+ if (compare.family != input->family)
+ break;
+ if ((input->family == AF_INET) &&
+ IPV4_ADDR_SAME(&input->u.prefix4,
+ &compare.u.prefix4))
+ return true;
+ if ((input->family == AF_INET6) &&
+ IPV6_ADDR_SAME(&input->u.prefix6.s6_addr,
+ &compare.u.prefix6.s6_addr))
+ return true;
+ break;
+ case FLOWSPEC_IP_PROTOCOL:
+ case FLOWSPEC_PORT:
+ case FLOWSPEC_DEST_PORT:
+ case FLOWSPEC_SRC_PORT:
+ case FLOWSPEC_ICMP_TYPE:
+ case FLOWSPEC_ICMP_CODE:
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_tcpflags_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_PKT_LEN:
+ case FLOWSPEC_DSCP:
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL,
+ &error);
+ break;
+ case FLOWSPEC_FRAGMENT:
+ ret = bgp_flowspec_fragment_type_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL,
+ &error);
+ break;
+ default:
+ error = -1;
+ break;
+ }
+ offset += ret;
+ }
+ return false;
+}
/*
* handle the flowspec address src/dst or generic address NLRI
@@ -122,9 +226,12 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
+ struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
*error = 0;
do {
+ if (loop > BGP_PBR_MATCH_VAL_MAX)
+ *error = -2;
hex2bin(&nlri_ptr[offset], op);
offset++;
len = 2*op[2]+op[3];
@@ -168,7 +275,24 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
ptr += len_written;
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
- /* TODO : FS OPAQUE */
+ /* limitation: stop converting */
+ if (*error == -2)
+ break;
+ mval->value = value;
+ if (op[5] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_LESS_THAN;
+ if (op[6] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_GREATER_THAN;
+ if (op[7] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EQUAL_TO;
+ if (op[1] == 1)
+ mval->unary_operator = OPERATOR_UNARY_AND;
+ else
+ mval->unary_operator = OPERATOR_UNARY_OR;
+ mval++;
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
@@ -203,12 +327,15 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type,
int op[8];
int len, value_size, loop = 0, value;
char *ptr = (char *)result; /* for return_string */
+ struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
*error = 0;
do {
+ if (loop > BGP_PBR_MATCH_VAL_MAX)
+ *error = -2;
hex2bin(&nlri_ptr[offset], op);
/* if first element, AND bit can not be set */
if (op[1] == 1 && loop == 0)
@@ -252,7 +379,29 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type,
ptr += len_written;
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
- /* TODO : FS OPAQUE */
+ /* limitation: stop converting */
+ if (*error == -2)
+ break;
+ mval->value = value;
+ if (op[6] == 1) {
+ /* different from */
+ mval->compare_operator |=
+ OPERATOR_COMPARE_LESS_THAN;
+ mval->compare_operator |=
+ OPERATOR_COMPARE_GREATER_THAN;
+ } else
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EQUAL_TO;
+ if (op[7] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EXACT_MATCH;
+ if (op[1] == 1)
+ mval->unary_operator =
+ OPERATOR_UNARY_AND;
+ else
+ mval->unary_operator =
+ OPERATOR_UNARY_OR;
+ mval++;
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
@@ -284,6 +433,8 @@ int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
int op[8];
int len, value, value_size, loop = 0;
char *ptr = (char *)result; /* for return_string */
+ struct bgp_pbr_fragment_val *mval =
+ (struct bgp_pbr_fragment_val *)result;
uint32_t offset = 0;
int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
int len_written;
@@ -340,7 +491,7 @@ int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
}
break;
case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
- /* TODO : FS OPAQUE */
+ mval->bitmask = (uint8_t)value;
break;
case BGP_FLOWSPEC_VALIDATE_ONLY:
default:
@@ -354,89 +505,158 @@ int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
return offset;
}
-
-static bool bgp_flowspec_contains_prefix(struct prefix *pfs,
- struct prefix *input,
- int prefix_check)
+int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
+ struct bgp_pbr_entry_main *bpem)
{
- uint32_t offset = 0;
- int type;
- int ret = 0, error = 0;
- uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
- size_t len = pfs->u.prefix_flowspec.prefixlen;
- struct prefix compare;
+ int offset = 0, error = 0;
+ struct prefix *prefix;
+ struct bgp_pbr_match_val *mval;
+ uint8_t *match_num;
+ uint8_t bitmask = 0;
+ int ret = 0, type;
- error = 0;
- while (offset < len-1 && error >= 0) {
+ while (offset < len - 1 && error >= 0) {
type = nlri_content[offset];
offset++;
switch (type) {
case FLOWSPEC_DEST_PREFIX:
case FLOWSPEC_SRC_PREFIX:
- memset(&compare, 0, sizeof(struct prefix));
+ bitmask = 0;
+ if (type == FLOWSPEC_DEST_PREFIX) {
+ bitmask |= PREFIX_DST_PRESENT;
+ prefix = &bpem->dst_prefix;
+ } else {
+ bitmask |= PREFIX_SRC_PRESENT;
+ prefix = &bpem->src_prefix;
+ }
ret = bgp_flowspec_ip_address(
BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
- nlri_content+offset,
+ nlri_content + offset,
len - offset,
- &compare, &error);
- if (ret <= 0)
- break;
- if (prefix_check &&
- compare.prefixlen != input->prefixlen)
- break;
- if (compare.family != input->family)
- break;
- if ((input->family == AF_INET) &&
- IPV4_ADDR_SAME(&input->u.prefix4,
- &compare.u.prefix4))
- return true;
- if ((input->family == AF_INET6) &&
- IPV6_ADDR_SAME(&input->u.prefix6.s6_addr,
- &compare.u.prefix6.s6_addr))
- return true;
+ prefix, &error);
+ if (error < 0)
+ zlog_err("%s: flowspec_ip_address error %d",
+ __func__, error);
+ else
+ bpem->match_bitmask |= bitmask;
+ offset += ret;
break;
case FLOWSPEC_IP_PROTOCOL:
+ match_num = &(bpem->match_protocol_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->protocol);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
case FLOWSPEC_PORT:
+ match_num = &(bpem->match_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
case FLOWSPEC_DEST_PORT:
+ match_num = &(bpem->match_dst_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->dst_port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
case FLOWSPEC_SRC_PORT:
+ match_num = &(bpem->match_src_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->src_port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
case FLOWSPEC_ICMP_TYPE:
- case FLOWSPEC_ICMP_CODE:
- ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
- nlri_content+offset,
- len - offset,
- NULL, &error);
+ match_num = &(bpem->match_icmp_type_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->icmp_type);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
break;
- case FLOWSPEC_TCP_FLAGS:
- ret = bgp_flowspec_tcpflags_decode(
- BGP_FLOWSPEC_VALIDATE_ONLY,
- nlri_content+offset,
- len - offset,
- NULL, &error);
+ case FLOWSPEC_ICMP_CODE:
+ match_num = &(bpem->match_icmp_code_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->icmp_code);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
break;
case FLOWSPEC_PKT_LEN:
+ match_num =
+ &(bpem->match_packet_length_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->packet_length);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
case FLOWSPEC_DSCP:
- ret = bgp_flowspec_op_decode(
- BGP_FLOWSPEC_VALIDATE_ONLY,
- nlri_content + offset,
- len - offset, NULL,
- &error);
+ match_num = &(bpem->match_dscp_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->dscp);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_tcpflags_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset,
+ &bpem->tcpflags, &error);
+ if (error < 0)
+ zlog_err("%s: flowspec_tcpflags_decode error %d",
+ __func__, error);
+ else
+ bpem->match_tcpflags_num = error;
+ /* contains the number of slots used */
+ offset += ret;
break;
case FLOWSPEC_FRAGMENT:
ret = bgp_flowspec_fragment_type_decode(
- BGP_FLOWSPEC_VALIDATE_ONLY,
- nlri_content + offset,
- len - offset, NULL,
- &error);
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset, &bpem->fragment,
+ &error);
+ if (error < 0)
+ zlog_err("%s: flowspec_fragment_type_decode error %d",
+ __func__, error);
+ else
+ bpem->match_bitmask |= FRAGMENT_PRESENT;
+ offset += ret;
break;
default:
- error = -1;
- break;
+ zlog_err("%s: unknown type %d\n", __func__, type);
}
- offset += ret;
}
- return false;
+ return error;
}
+
struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi,
struct bgp_table *rib,
struct prefix *match,
diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h
index aa21461102..e4454ab4db 100644
--- a/bgpd/bgp_flowspec_util.h
+++ b/bgpd/bgp_flowspec_util.h
@@ -50,6 +50,9 @@ extern int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type,
uint8_t *nlri_ptr,
uint32_t max_len,
void *result, int *error);
+struct bgp_pbr_entry_main;
+extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
+ struct bgp_pbr_entry_main *bpem);
extern struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi,
struct bgp_table *rib,
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index a11a4f78f7..3f5ff12cbc 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -52,6 +52,7 @@
#include "bgpd/bgp_memory.h"
#include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_zebra.h"
DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer))
DEFINE_HOOK(peer_established, (struct peer * peer), (peer))
@@ -1256,7 +1257,8 @@ static int bgp_connect_check(struct thread *thread)
/* If getsockopt is fail, this is fatal error. */
if (ret < 0) {
- zlog_info("can't get sockopt for nonblocking connect");
+ zlog_info("can't get sockopt for nonblocking connect: %d(%s)",
+ errno, safe_strerror(errno));
BGP_EVENT_ADD(peer, TCP_fatal_error);
return -1;
}
@@ -1267,8 +1269,8 @@ static int bgp_connect_check(struct thread *thread)
return 1;
} else {
if (bgp_debug_neighbor_events(peer))
- zlog_debug("%s [Event] Connect failed (%s)", peer->host,
- safe_strerror(errno));
+ zlog_debug("%s [Event] Connect failed %d(%s)",
+ peer->host, status, safe_strerror(status));
BGP_EVENT_ADD(peer, TCP_connection_open_failed);
return 0;
}
@@ -1397,13 +1399,14 @@ int bgp_start(struct peer *peer)
if (!bgp_find_or_add_nexthop(peer->bgp, peer->bgp,
family2afi(peer->su.sa.sa_family), NULL,
peer, connected)) {
-#if defined(HAVE_CUMULUS)
- if (bgp_debug_neighbor_events(peer))
- zlog_debug("%s [FSM] Waiting for NHT", peer->host);
+ if (bgp_zebra_num_connects()) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Waiting for NHT",
+ peer->host);
- BGP_EVENT_ADD(peer, TCP_connection_open_failed);
- return 0;
-#endif
+ BGP_EVENT_ADD(peer, TCP_connection_open_failed);
+ return 0;
+ }
}
assert(!peer->t_write);
diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c
index 3882ff8b56..69c92e829c 100644
--- a/bgpd/bgp_io.c
+++ b/bgpd/bgp_io.c
@@ -179,7 +179,7 @@ static int bgp_process_reads(struct thread *thread)
peer = THREAD_ARG(thread);
- if (peer->fd < 0)
+ if (peer->fd < 0 || bm->terminating)
return -1;
struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h
index 01bf8b372b..2b2525dd0e 100644
--- a/bgpd/bgp_label.h
+++ b/bgpd/bgp_label.h
@@ -24,6 +24,7 @@
#define BGP_LABEL_BYTES 3
#define BGP_LABEL_BITS 24
#define BGP_WITHDRAW_LABEL 0x800000
+#define BGP_PREVENT_VRF_2_VRF_LEAK 0xFFFFFFFE
struct bgp_node;
struct bgp_info;
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 5158717b5d..004bdd90a2 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -143,6 +143,8 @@ void sighup(void)
__attribute__((__noreturn__)) void sigint(void)
{
zlog_notice("Terminating on signal");
+ assert(bm->terminating == false);
+ bm->terminating = true; /* global flag that shutting down */
if (!retain_mode)
bgp_terminate();
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 01068d17f4..55365df299 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -87,6 +87,10 @@ void encode_label(mpls_label_t label, mpls_label_t *label_pnt)
uint8_t *pnt = (uint8_t *)label_pnt;
if (pnt == NULL)
return;
+ if (label == BGP_PREVENT_VRF_2_VRF_LEAK) {
+ *label_pnt = label;
+ return;
+ }
*pnt++ = (label >> 12) & 0xff;
*pnt++ = (label >> 4) & 0xff;
*pnt++ = ((label << 4) + 1) & 0xff; /* S=1 */
@@ -565,7 +569,7 @@ leak_update(
setlabels(new, label, num_labels);
bgp_info_extra_get(new);
- new->extra->parent = parent;
+ new->extra->parent = bgp_info_lock(parent);
if (bgp_orig)
new->extra->bgp_orig = bgp_orig;
@@ -640,8 +644,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */
char *s = ecommunity_ecom2str(info_vrf->attr->ecommunity,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
- zlog_debug("%s: info_vrf->type=%d, EC{%s}", __func__,
- info_vrf->type, s);
+ zlog_debug("%s: %s info_vrf->type=%d, EC{%s}", __func__,
+ bgp_vrf->name, info_vrf->type, s);
XFREE(MTYPE_ECOMMUNITY_STR, s);
}
@@ -654,14 +658,15 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */
return;
}
- /* loop check */
- if (info_vrf->extra && info_vrf->extra->bgp_orig == bgp_vpn)
+ /* loop check - should not be an imported route. */
+ if (info_vrf->extra && info_vrf->extra->bgp_orig)
return;
if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) {
if (debug)
- zlog_debug("%s: skipping: %s", __func__, debugmsg);
+ zlog_debug("%s: %s skipping: %s", __func__,
+ bgp_vrf->name, debugmsg);
return;
}
@@ -737,9 +742,9 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */
/* if policy nexthop not set, use 0 */
if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) {
-
struct prefix *nexthop =
&bgp_vrf->vpn_policy[afi].tovpn_nexthop;
+
switch (nexthop->family) {
case AF_INET:
/* prevent mp_nexthop_global_in <- self in bgp_route.c
@@ -759,12 +764,38 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */
assert(0);
}
} else {
- if (afi == AFI_IP) {
- /* For ipv4, copy to multiprotocol nexthop field */
- static_attr.mp_nexthop_global_in = static_attr.nexthop;
- static_attr.mp_nexthop_len = 4;
- /* XXX Leave static_attr.nexthop intact for NHT */
- static_attr.flag &= ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ if (afi == AFI_IP) {
+ /*
+ * For ipv4, copy to multiprotocol
+ * nexthop field
+ */
+ static_attr.mp_nexthop_global_in =
+ static_attr.nexthop;
+ static_attr.mp_nexthop_len = 4;
+ /*
+ * XXX Leave static_attr.nexthop
+ * intact for NHT
+ */
+ static_attr.flag &=
+ ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ } else {
+ /* Update based on next-hop family to account for
+ * RFC 5549 (BGP unnumbered) scenario. Note that
+ * specific action is only needed for the case of
+ * IPv4 nexthops as the attr has been copied
+ * otherwise.
+ */
+ if (afi == AFI_IP &&
+ !BGP_ATTR_NEXTHOP_AFI_IP6(info_vrf->attr)) {
+ static_attr.mp_nexthop_global_in.s_addr =
+ static_attr.nexthop.s_addr;
+ static_attr.mp_nexthop_len = 4;
+ static_attr.flag |=
+ ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
}
nexthop_self_flag = 1;
}
@@ -838,14 +869,9 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */
info_vrf->type, info_vrf->sub_type);
}
- if (info_vrf->type != ZEBRA_ROUTE_BGP) {
- if (debug)
- zlog_debug("%s: wrong type %d", __func__,
- info_vrf->type);
- return;
- }
if (info_vrf->sub_type != BGP_ROUTE_NORMAL
- && info_vrf->sub_type != BGP_ROUTE_STATIC) {
+ && info_vrf->sub_type != BGP_ROUTE_STATIC
+ && info_vrf->sub_type != BGP_ROUTE_REDISTRIBUTE) {
if (debug)
zlog_debug("%s: wrong sub_type %d", __func__,
@@ -1036,22 +1062,35 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */
nexthop_orig.family = nhfamily;
switch (nhfamily) {
-
case AF_INET:
/* save */
nexthop_orig.u.prefix4 = info_vpn->attr->mp_nexthop_global_in;
nexthop_orig.prefixlen = 32;
+
+ if (CHECK_FLAG(bgp_vrf->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ static_attr.nexthop.s_addr =
+ nexthop_orig.u.prefix4.s_addr;
+
+ static_attr.mp_nexthop_global_in =
+ info_vpn->attr->mp_nexthop_global_in;
+ static_attr.mp_nexthop_len =
+ info_vpn->attr->mp_nexthop_len;
+ }
static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
break;
-
case AF_INET6:
/* save */
nexthop_orig.u.prefix6 = info_vpn->attr->mp_nexthop_global;
nexthop_orig.prefixlen = 128;
+
+ if (CHECK_FLAG(bgp_vrf->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ static_attr.mp_nexthop_global = nexthop_orig.u.prefix6;
+ }
break;
}
-
/*
* route map handling
*/
@@ -1101,28 +1140,34 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */
* labels for these routes enables the non-labeled nexthops
* from the originating VRF to be considered valid for this route.
*/
+ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ /* work back to original route */
+ for (bi_ultimate = info_vpn;
+ bi_ultimate->extra && bi_ultimate->extra->parent;
+ bi_ultimate = bi_ultimate->extra->parent)
+ ;
- /* 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;
+ /*
+ * 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;
- }
+ 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;
+ /* 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) {
@@ -1132,6 +1177,10 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */
num_labels);
}
+ /*
+ * For VRF-2-VRF route-leaking,
+ * the source will be the originating VRF.
+ */
if (info_vpn->extra && info_vpn->extra->bgp_orig)
src_vrf = info_vpn->extra->bgp_orig;
else
@@ -1255,18 +1304,17 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */
struct bgp_info *bi;
safi_t safi = SAFI_UNICAST;
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
- struct bgp *bgp_vpn = bgp_get_default();
if (debug)
zlog_debug("%s: entry", __func__);
/*
- * Walk vrf table, delete bi with bgp_orig == bgp_vpn
+ * Walk vrf table, delete bi with bgp_orig in a different vrf
*/
for (bn = bgp_table_top(bgp_vrf->rib[afi][safi]); bn;
bn = bgp_route_next(bn)) {
for (bi = bn->info; bi; bi = bi->next) {
- if (bi->extra && bi->extra->bgp_orig == bgp_vpn) {
+ if (bi->extra && bi->extra->bgp_orig != bgp_vrf) {
/* delete route */
bgp_aggregate_decrement(bgp_vrf, &bn->p, bi,
@@ -1409,6 +1457,172 @@ void vpn_policy_routemap_event(const char *rmap_name)
vpn_policy_routemap_update(bgp, rmap_name);
}
+void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi)
+{
+ const char *export_name;
+ vpn_policy_direction_t idir, edir;
+ char *vname;
+ char buf[1000];
+ struct ecommunity *ecom;
+ bool first_export = false;
+
+ export_name = to_bgp->name ? to_bgp->name : BGP_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ /*
+ * Cross-ref both VRFs. Also, note if this is the first time
+ * any VRF is importing from "import_vrf".
+ */
+ vname = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name)
+ : XSTRDUP(MTYPE_TMP, BGP_DEFAULT_NAME));
+
+ listnode_add(to_bgp->vpn_policy[afi].import_vrf, vname);
+
+ if (!listcount(from_bgp->vpn_policy[afi].export_vrf))
+ first_export = true;
+ vname = XSTRDUP(MTYPE_TMP, export_name);
+ listnode_add(from_bgp->vpn_policy[afi].export_vrf, vname);
+
+ /* Update import RT for current VRF using export RT of the VRF we're
+ * importing from. First though, make sure "import_vrf" has that
+ * set.
+ */
+ if (first_export) {
+ form_auto_rd(from_bgp->router_id, from_bgp->vrf_rd_id,
+ &from_bgp->vrf_prd_auto);
+ from_bgp->vpn_policy[afi].tovpn_rd = from_bgp->vrf_prd_auto;
+ SET_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd,
+ buf, sizeof(buf));
+ from_bgp->vpn_policy[afi].rtlist[edir] =
+ ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0);
+ SET_FLAG(from_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT);
+ from_bgp->vpn_policy[afi].tovpn_label =
+ BGP_PREVENT_VRF_2_VRF_LEAK;
+ }
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ if (to_bgp->vpn_policy[afi].rtlist[idir])
+ to_bgp->vpn_policy[afi].rtlist[idir] =
+ ecommunity_merge(to_bgp->vpn_policy[afi]
+ .rtlist[idir], ecom);
+ else
+ to_bgp->vpn_policy[afi].rtlist[idir] = ecommunity_dup(ecom);
+ SET_FLAG(to_bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT);
+
+ /* Does "import_vrf" first need to export its routes or that
+ * is already done and we just need to import those routes
+ * from the global table?
+ */
+ if (first_export)
+ vpn_leak_postchange(edir, afi, bgp_get_default(), from_bgp);
+ else
+ vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp);
+}
+
+void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi)
+{
+ const char *export_name, *tmp_name;
+ vpn_policy_direction_t idir, edir;
+ char *vname;
+ struct ecommunity *ecom;
+ struct listnode *node;
+
+ export_name = to_bgp->name ? to_bgp->name : BGP_DEFAULT_NAME;
+ tmp_name = from_bgp->name ? from_bgp->name : BGP_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ /* Were we importing from "import_vrf"? */
+ for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, node,
+ vname)) {
+ if (strcmp(vname, tmp_name) == 0)
+ break;
+ }
+
+ /*
+ * We do not check in the cli if the passed in bgp
+ * instance is actually imported into us before
+ * we call this function. As such if we do not
+ * find this in the import_vrf list than
+ * we just need to return safely.
+ */
+ if (!vname)
+ return;
+
+ /* Remove "import_vrf" from our import list. */
+ listnode_delete(to_bgp->vpn_policy[afi].import_vrf, vname);
+ XFREE(MTYPE_TMP, vname);
+
+ /* Remove routes imported from "import_vrf". */
+ /* TODO: In the current logic, we have to first remove all
+ * imported routes and then (if needed) import back routes
+ */
+ vpn_leak_prechange(idir, afi, bgp_get_default(), to_bgp);
+
+ if (to_bgp->vpn_policy[afi].import_vrf->count == 0) {
+ UNSET_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT);
+ ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]);
+ } else {
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir],
+ (struct ecommunity_val *)ecom->val);
+ vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp);
+ }
+
+ /*
+ * What?
+ * So SA is assuming that since the ALL_LIST_ELEMENTS_RO
+ * below is checking for NULL that export_vrf can be
+ * NULL, consequently it is complaining( like a cabbage )
+ * that we could dereference and crash in the listcount(..)
+ * check below.
+ * So make it happy, under protest, with liberty and justice
+ * for all.
+ */
+ assert(from_bgp->vpn_policy[afi].export_vrf);
+
+ /* Remove us from "import_vrf's" export list. If no other VRF
+ * is importing from "import_vrf", cleanup appropriately.
+ */
+ for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf,
+ node, vname)) {
+ if (strcmp(vname, export_name) == 0)
+ break;
+ }
+
+ /*
+ * If we have gotten to this point then the vname must
+ * exist. If not, we are in a world of trouble and
+ * have slag sitting around.
+ *
+ * import_vrf and export_vrf must match in having
+ * the in/out names as appropriate.
+ */
+ assert(vname);
+
+ listnode_delete(from_bgp->vpn_policy[afi].export_vrf, vname);
+ XFREE(MTYPE_TMP, vname);
+
+ if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) {
+ vpn_leak_prechange(edir, afi, bgp_get_default(), from_bgp);
+ ecommunity_free(&from_bgp->vpn_policy[afi].rtlist[edir]);
+ UNSET_FLAG(from_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT);
+ memset(&from_bgp->vpn_policy[afi].tovpn_rd, 0,
+ sizeof(struct prefix_rd));
+ UNSET_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ from_bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+
+ }
+}
+
/* For testing purpose, static route of MPLS-VPN. */
DEFUN (vpnv4_network,
vpnv4_network_cmd,
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index c13030c6c8..384108dc0c 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -78,6 +78,10 @@ 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);
+extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi);
+void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi);
static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
const char **pmsg)
@@ -92,7 +96,9 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
/* Is vrf configured to export to vpn? */
if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ && !CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
if (pmsg)
*pmsg = "export not set";
return 0;
@@ -147,7 +153,9 @@ static inline int vpn_leak_from_vpn_active(struct bgp *bgp_vrf, afi_t afi,
/* Is vrf configured to import from vpn? */
if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
- BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
+ && !CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
if (pmsg)
*pmsg = "import not set";
return 0;
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index 519f092762..a771bead23 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -29,6 +29,7 @@
(((nexthop_len) == 4 || (nexthop_len) == 12 \
? AF_INET \
: ((nexthop_len) == 16 || (nexthop_len) == 24 \
+ || (nexthop_len) == 32 \
|| (nexthop_len) == 48 \
? AF_INET6 \
: AF_UNSPEC)))
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
new file mode 100644
index 0000000000..04d6314fd7
--- /dev/null
+++ b/bgpd/bgp_pbr.c
@@ -0,0 +1,1140 @@
+/*
+ * BGP pbr
+ * Copyright (C) 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 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 "prefix.h"
+#include "zclient.h"
+#include "jhash.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_mplsvpn.h"
+
+DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry")
+DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match")
+DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action")
+
+static int bgp_pbr_match_counter_unique;
+static int bgp_pbr_match_entry_counter_unique;
+static int bgp_pbr_action_counter_unique;
+static int bgp_pbr_match_iptable_counter_unique;
+
+struct bgp_pbr_match_iptable_unique {
+ uint32_t unique;
+ struct bgp_pbr_match *bpm_found;
+};
+
+struct bgp_pbr_match_entry_unique {
+ uint32_t unique;
+ struct bgp_pbr_match_entry *bpme_found;
+};
+
+struct bgp_pbr_action_unique {
+ uint32_t unique;
+ struct bgp_pbr_action *bpa_found;
+};
+
+static int bgp_pbr_action_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct bgp_pbr_action *bpa = (struct bgp_pbr_action *)backet->data;
+ struct bgp_pbr_action_unique *bpau = (struct bgp_pbr_action_unique *)
+ arg;
+ uint32_t unique = bpau->unique;
+
+ if (bpa->unique == unique) {
+ bpau->bpa_found = bpa;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int bgp_pbr_match_entry_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct bgp_pbr_match_entry *bpme =
+ (struct bgp_pbr_match_entry *)backet->data;
+ struct bgp_pbr_match_entry_unique *bpmeu =
+ (struct bgp_pbr_match_entry_unique *)arg;
+ uint32_t unique = bpmeu->unique;
+
+ if (bpme->unique == unique) {
+ bpmeu->bpme_found = bpme;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct bgp_pbr_match_ipsetname {
+ char *ipsetname;
+ struct bgp_pbr_match *bpm_found;
+};
+
+static int bgp_pbr_match_pername_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
+ struct bgp_pbr_match_ipsetname *bpmi =
+ (struct bgp_pbr_match_ipsetname *)arg;
+ char *ipset_name = bpmi->ipsetname;
+
+ if (!strncmp(ipset_name, bpm->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE)) {
+ bpmi->bpm_found = bpm;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int bgp_pbr_match_iptable_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
+ struct bgp_pbr_match_iptable_unique *bpmiu =
+ (struct bgp_pbr_match_iptable_unique *)arg;
+ uint32_t unique = bpmiu->unique;
+
+ if (bpm->unique2 == unique) {
+ bpmiu->bpm_found = bpm;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct bgp_pbr_match_unique {
+ uint32_t unique;
+ struct bgp_pbr_match *bpm_found;
+};
+
+static int bgp_pbr_match_walkcb(struct hash_backet *backet, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
+ struct bgp_pbr_match_unique *bpmu = (struct bgp_pbr_match_unique *)
+ arg;
+ uint32_t unique = bpmu->unique;
+
+ if (bpm->unique == unique) {
+ bpmu->bpm_found = bpm;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval,
+ const char *prepend)
+{
+ char *ptr = str;
+
+ if (prepend)
+ ptr += sprintf(ptr, "%s", prepend);
+ else {
+ if (mval->unary_operator & OPERATOR_UNARY_OR)
+ ptr += sprintf(ptr, ", or ");
+ if (mval->unary_operator & OPERATOR_UNARY_AND)
+ ptr += sprintf(ptr, ", and ");
+ }
+ if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN)
+ ptr += sprintf(ptr, "<");
+ if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN)
+ ptr += sprintf(ptr, ">");
+ if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO)
+ ptr += sprintf(ptr, "=");
+ if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH)
+ ptr += sprintf(ptr, "match");
+ ptr += sprintf(ptr, " %u", mval->value);
+ return (int)(ptr - str);
+}
+
+#define INCREMENT_DISPLAY(_ptr, _cnt) do { \
+ if (_cnt) \
+ (_ptr) += sprintf((_ptr), "; "); \
+ _cnt++; \
+ } while (0)
+
+/* return 1 if OK, 0 if validation should stop) */
+static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
+{
+ /* because bgp pbr entry may contain unsupported
+ * combinations, a message will be displayed here if
+ * not supported.
+ * for now, only match/set supported is
+ * - combination src/dst => redirect nexthop [ + rate]
+ * - combination src/dst => redirect VRF [ + rate]
+ * - combination src/dst => drop
+ */
+ if (api->match_src_port_num || api->match_dst_port_num
+ || api->match_port_num || api->match_protocol_num
+ || api->match_icmp_type_num || api->match_icmp_type_num
+ || api->match_packet_length_num || api->match_dscp_num
+ || api->match_tcpflags_num) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_debug("BGP: some SET actions not supported by Zebra. ignoring.");
+ }
+ return 0;
+ }
+ if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
+ !(api->match_bitmask & PREFIX_DST_PRESENT)) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_debug("BGP: match actions without src"
+ " or dst address can not operate."
+ " ignoring.");
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/* return -1 if build or validation failed */
+static int bgp_pbr_build_and_validate_entry(struct prefix *p,
+ struct bgp_info *info,
+ struct bgp_pbr_entry_main *api)
+{
+ int ret;
+ int i, action_count = 0;
+ struct ecommunity *ecom;
+ struct ecommunity_val *ecom_eval;
+ struct bgp_pbr_entry_action *api_action;
+ struct prefix *src = NULL, *dst = NULL;
+ int valid_prefix = 0;
+ afi_t afi = AFI_IP;
+
+ /* extract match from flowspec entries */
+ ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
+ p->u.prefix_flowspec.prefixlen, api);
+ if (ret < 0)
+ return -1;
+ /* extract actiosn from flowspec ecom list */
+ if (info && info->attr && info->attr->ecommunity) {
+ ecom = info->attr->ecommunity;
+ for (i = 0; i < ecom->size; i++) {
+ ecom_eval = (struct ecommunity_val *)
+ ecom->val + (i * ECOMMUNITY_SIZE);
+
+ if (action_count > ACTIONS_MAX_NUM) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ zlog_err("%s: flowspec actions exceeds limit (max %u)",
+ __func__, action_count);
+ break;
+ }
+ api_action = &api->actions[action_count];
+
+ if ((ecom_eval->val[1] ==
+ (char)ECOMMUNITY_REDIRECT_VRF) &&
+ (ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_TRANS_EXP ||
+ ecom_eval->val[0] ==
+ (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
+ ecom_eval->val[0] ==
+ (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) {
+ struct ecommunity *eckey = ecommunity_new();
+ struct ecommunity_val ecom_copy;
+
+ memcpy(&ecom_copy, ecom_eval,
+ sizeof(struct ecommunity_val));
+ ecom_copy.val[0] &=
+ ~ECOMMUNITY_ENCODE_TRANS_EXP;
+ ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
+ ecommunity_add_val(eckey, &ecom_copy);
+
+ api_action->action = ACTION_REDIRECT;
+ api_action->u.redirect_vrf =
+ get_first_vrf_for_redirect_with_rt(
+ eckey);
+ ecommunity_free(&eckey);
+ } else if ((ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) &&
+ (ecom_eval->val[1] ==
+ (char)ECOMMUNITY_REDIRECT_IP_NH)) {
+ api_action->action = ACTION_REDIRECT_IP;
+ api_action->u.zr.redirect_ip_v4.s_addr =
+ info->attr->nexthop.s_addr;
+ api_action->u.zr.duplicate = ecom_eval->val[7];
+ } else {
+ if (ecom_eval->val[0] !=
+ (char)ECOMMUNITY_ENCODE_TRANS_EXP)
+ continue;
+ ret = ecommunity_fill_pbr_action(ecom_eval,
+ api_action);
+ if (ret != 0)
+ continue;
+ }
+ api->action_num++;
+ }
+ }
+
+ /* validate if incoming matc/action is compatible
+ * with our policy routing engine
+ */
+ if (!bgp_pbr_validate_policy_route(api))
+ return -1;
+
+ /* check inconsistency in the match rule */
+ if (api->match_bitmask & PREFIX_SRC_PRESENT) {
+ src = &api->src_prefix;
+ afi = family2afi(src->family);
+ valid_prefix = 1;
+ }
+ if (api->match_bitmask & PREFIX_DST_PRESENT) {
+ dst = &api->dst_prefix;
+ if (valid_prefix && afi != family2afi(dst->family)) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_debug("%s: inconsistency:"
+ " no match for afi src and dst (%u/%u)",
+ __func__, afi, family2afi(dst->family));
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void bgp_pbr_match_entry_free(void *arg)
+{
+ struct bgp_pbr_match_entry *bpme;
+
+ bpme = (struct bgp_pbr_match_entry *)arg;
+
+ if (bpme->installed) {
+ bgp_send_pbr_ipset_entry_match(bpme, false);
+ bpme->installed = false;
+ bpme->backpointer = NULL;
+ }
+ XFREE(MTYPE_PBR_MATCH_ENTRY, bpme);
+}
+
+static void bgp_pbr_match_free(void *arg)
+{
+ struct bgp_pbr_match *bpm;
+
+ bpm = (struct bgp_pbr_match *)arg;
+
+ hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free);
+
+ if (hashcount(bpm->entry_hash) == 0) {
+ /* delete iptable entry first */
+ /* then delete ipset match */
+ if (bpm->installed) {
+ if (bpm->installed_in_iptable) {
+ bgp_send_pbr_iptable(bpm->action,
+ bpm, false);
+ bpm->installed_in_iptable = false;
+ bpm->action->refcnt--;
+ }
+ bgp_send_pbr_ipset_match(bpm, false);
+ bpm->installed = false;
+ bpm->action = NULL;
+ }
+ }
+ hash_free(bpm->entry_hash);
+
+ XFREE(MTYPE_PBR_MATCH, bpm);
+}
+
+static void *bgp_pbr_match_alloc_intern(void *arg)
+{
+ struct bgp_pbr_match *bpm, *new;
+
+ bpm = (struct bgp_pbr_match *)arg;
+
+ new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
+ memcpy(new, bpm, sizeof(*bpm));
+
+ return new;
+}
+
+static void bgp_pbr_action_free(void *arg)
+{
+ struct bgp_pbr_action *bpa;
+
+ bpa = (struct bgp_pbr_action *)arg;
+
+ if (bpa->refcnt == 0) {
+ if (bpa->installed && bpa->table_id != 0) {
+ bgp_send_pbr_rule_action(bpa, false);
+ bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
+ AFI_IP,
+ bpa->table_id,
+ false);
+ }
+ }
+ XFREE(MTYPE_PBR_ACTION, bpa);
+}
+
+static void *bgp_pbr_action_alloc_intern(void *arg)
+{
+ struct bgp_pbr_action *bpa, *new;
+
+ bpa = (struct bgp_pbr_action *)arg;
+
+ new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
+
+ memcpy(new, bpa, sizeof(*bpa));
+
+ return new;
+}
+
+static void *bgp_pbr_match_entry_alloc_intern(void *arg)
+{
+ struct bgp_pbr_match_entry *bpme, *new;
+
+ bpme = (struct bgp_pbr_match_entry *)arg;
+
+ new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
+
+ memcpy(new, bpme, sizeof(*bpme));
+
+ return new;
+}
+
+uint32_t bgp_pbr_match_hash_key(void *arg)
+{
+ struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg;
+ uint32_t key;
+
+ key = jhash_1word(pbm->vrf_id, 0x4312abde);
+ key = jhash_1word(pbm->flags, key);
+ return jhash_1word(pbm->type, key);
+}
+
+int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_match *r1, *r2;
+
+ r1 = (const struct bgp_pbr_match *)arg1;
+ r2 = (const struct bgp_pbr_match *)arg2;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return 0;
+
+ if (r1->type != r2->type)
+ return 0;
+
+ if (r1->flags != r2->flags)
+ return 0;
+
+ if (r1->action != r2->action)
+ return 0;
+
+ return 1;
+}
+
+uint32_t bgp_pbr_match_entry_hash_key(void *arg)
+{
+ struct bgp_pbr_match_entry *pbme;
+ uint32_t key;
+
+ pbme = (struct bgp_pbr_match_entry *)arg;
+ key = prefix_hash_key(&pbme->src);
+ key = jhash_1word(prefix_hash_key(&pbme->dst), key);
+
+ return key;
+}
+
+int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_match_entry *r1, *r2;
+
+ r1 = (const struct bgp_pbr_match_entry *)arg1;
+ r2 = (const struct bgp_pbr_match_entry *)arg2;
+
+ /* on updates, comparing
+ * backpointer is not necessary
+ */
+
+ /* unique value is self calculated
+ */
+
+ /* rate is ignored for now
+ */
+
+ if (!prefix_same(&r1->src, &r2->src))
+ return 0;
+
+ if (!prefix_same(&r1->dst, &r2->dst))
+ return 0;
+
+ return 1;
+}
+
+uint32_t bgp_pbr_action_hash_key(void *arg)
+{
+ struct bgp_pbr_action *pbra;
+ uint32_t key;
+
+ pbra = (struct bgp_pbr_action *)arg;
+ key = jhash_1word(pbra->table_id, 0x4312abde);
+ key = jhash_1word(pbra->fwmark, key);
+ return key;
+}
+
+int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_action *r1, *r2;
+
+ r1 = (const struct bgp_pbr_action *)arg1;
+ r2 = (const struct bgp_pbr_action *)arg2;
+
+ /* unique value is self calculated
+ * table and fwmark is self calculated
+ */
+ if (r1->rate != r2->rate)
+ return 0;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return 0;
+
+ if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop)))
+ return 0;
+ return 1;
+}
+
+struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_action_unique bpau;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpau.unique = unique;
+ bpau.bpa_found = NULL;
+ hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau);
+ return bpau.bpa_found;
+}
+
+struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_match_unique bpmu;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpmu.unique = unique;
+ bpmu.bpm_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu);
+ return bpmu.bpm_found;
+}
+
+struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
+ char *ipset_name,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_match_entry_unique bpmeu;
+ struct bgp_pbr_match_ipsetname bpmi;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE);
+ snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name);
+ bpmi.bpm_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi);
+ XFREE(MTYPE_TMP, bpmi.ipsetname);
+ if (!bpmi.bpm_found)
+ return NULL;
+ bpmeu.bpme_found = NULL;
+ bpmeu.unique = unique;
+ hash_walk(bpmi.bpm_found->entry_hash,
+ bgp_pbr_match_entry_walkcb, &bpmeu);
+ return bpmeu.bpme_found;
+}
+
+struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_match_iptable_unique bpmiu;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpmiu.unique = unique;
+ bpmiu.bpm_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu);
+ return bpmiu.bpm_found;
+}
+
+void bgp_pbr_cleanup(struct bgp *bgp)
+{
+ if (bgp->pbr_match_hash) {
+ hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free);
+ hash_free(bgp->pbr_match_hash);
+ bgp->pbr_match_hash = NULL;
+ }
+ if (bgp->pbr_action_hash) {
+ hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free);
+ hash_free(bgp->pbr_action_hash);
+ bgp->pbr_action_hash = NULL;
+ }
+}
+
+void bgp_pbr_init(struct bgp *bgp)
+{
+ bgp->pbr_match_hash =
+ hash_create_size(8, bgp_pbr_match_hash_key,
+ bgp_pbr_match_hash_equal,
+ "Match Hash");
+ bgp->pbr_action_hash =
+ hash_create_size(8, bgp_pbr_action_hash_key,
+ bgp_pbr_action_hash_equal,
+ "Match Hash Entry");
+}
+
+void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
+{
+ int i = 0;
+ char return_string[512];
+ char *ptr = return_string;
+ char buff[64];
+ int nb_items = 0;
+
+ ptr += sprintf(ptr, "MATCH : ");
+ if (api->match_bitmask & PREFIX_SRC_PRESENT) {
+ struct prefix *p = &(api->src_prefix);
+
+ ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64));
+ INCREMENT_DISPLAY(ptr, nb_items);
+ }
+ if (api->match_bitmask & PREFIX_DST_PRESENT) {
+ struct prefix *p = &(api->dst_prefix);
+
+ INCREMENT_DISPLAY(ptr, nb_items);
+ ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64));
+ }
+
+ if (api->match_protocol_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_protocol_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i],
+ i > 0 ? NULL : "@proto ");
+
+ if (api->match_src_port_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_src_port_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i],
+ i > 0 ? NULL : "@srcport ");
+
+ if (api->match_dst_port_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_dst_port_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i],
+ i > 0 ? NULL : "@dstport ");
+
+ if (api->match_port_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_port_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i],
+ i > 0 ? NULL : "@port ");
+
+ if (api->match_icmp_type_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_icmp_type_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i],
+ i > 0 ? NULL : "@icmptype ");
+
+ if (api->match_icmp_code_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_icmp_code_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i],
+ i > 0 ? NULL : "@icmpcode ");
+
+ if (api->match_packet_length_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_packet_length_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i],
+ i > 0 ? NULL : "@plen ");
+
+ if (api->match_dscp_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_dscp_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i],
+ i > 0 ? NULL : "@dscp ");
+
+ if (api->match_tcpflags_num)
+ INCREMENT_DISPLAY(ptr, nb_items);
+ for (i = 0; i < api->match_tcpflags_num; i++)
+ ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i],
+ i > 0 ? NULL : "@tcpflags ");
+
+ if (api->match_bitmask & FRAGMENT_PRESENT) {
+ INCREMENT_DISPLAY(ptr, nb_items);
+ ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask);
+ }
+ if (!nb_items)
+ ptr = return_string;
+ else
+ ptr += sprintf(ptr, "; ");
+ if (api->action_num)
+ ptr += sprintf(ptr, "SET : ");
+ nb_items = 0;
+ for (i = 0; i < api->action_num; i++) {
+ switch (api->actions[i].action) {
+ case ACTION_TRAFFICRATE:
+ INCREMENT_DISPLAY(ptr, nb_items);
+ ptr += sprintf(ptr, "@set rate %f",
+ api->actions[i].u.r.rate);
+ break;
+ case ACTION_TRAFFIC_ACTION:
+ INCREMENT_DISPLAY(ptr, nb_items);
+ ptr += sprintf(ptr, "@action ");
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_TERMINATE)
+ ptr += sprintf(ptr,
+ " terminate (apply filter(s))");
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_DISTRIBUTE)
+ ptr += sprintf(ptr, " distribute");
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_SAMPLE)
+ ptr += sprintf(ptr, " sample");
+ break;
+ case ACTION_REDIRECT_IP:
+ INCREMENT_DISPLAY(ptr, nb_items);
+ char local_buff[INET_ADDRSTRLEN];
+
+ if (inet_ntop(AF_INET,
+ &api->actions[i].u.zr.redirect_ip_v4,
+ local_buff, INET_ADDRSTRLEN) != NULL)
+ ptr += sprintf(ptr,
+ "@redirect ip nh %s", local_buff);
+ break;
+ case ACTION_REDIRECT:
+ INCREMENT_DISPLAY(ptr, nb_items);
+ ptr += sprintf(ptr, "@redirect vrf %u",
+ api->actions[i].u.redirect_vrf);
+ break;
+ case ACTION_MARKING:
+ INCREMENT_DISPLAY(ptr, nb_items);
+ ptr += sprintf(ptr, "@set dscp %u",
+ api->actions[i].u.marking_dscp);
+ break;
+ default:
+ break;
+ }
+ }
+ zlog_info("%s", return_string);
+}
+
+static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
+ struct bgp_pbr_match *bpm,
+ struct bgp_pbr_match_entry *bpme)
+{
+ /* if bpme is null, bpm is also null
+ */
+ if (bpme == NULL)
+ return;
+ /* ipset del entry */
+ if (bpme->installed) {
+ bgp_send_pbr_ipset_entry_match(bpme, false);
+ bpme->installed = false;
+ bpme->backpointer = NULL;
+ }
+ hash_release(bpm->entry_hash, bpme);
+ if (hashcount(bpm->entry_hash) == 0) {
+ /* delete iptable entry first */
+ /* then delete ipset match */
+ if (bpm->installed) {
+ if (bpm->installed_in_iptable) {
+ bgp_send_pbr_iptable(bpm->action,
+ bpm, false);
+ bpm->installed_in_iptable = false;
+ bpm->action->refcnt--;
+ }
+ bgp_send_pbr_ipset_match(bpm, false);
+ bpm->installed = false;
+ bpm->action = NULL;
+ }
+ hash_release(bgp->pbr_match_hash, bpm);
+ /* XXX release pbr_match_action if not used
+ * note that drop does not need to call send_pbr_action
+ */
+ }
+ if (bpa->refcnt == 0) {
+ if (bpa->installed && bpa->table_id != 0) {
+ bgp_send_pbr_rule_action(bpa, false);
+ bgp_zebra_announce_default(bpa->bgp, &(bpa->nh),
+ AFI_IP,
+ bpa->table_id,
+ false);
+ }
+ }
+}
+
+struct bgp_pbr_match_entry_remain {
+ struct bgp_pbr_match_entry *bpme_to_match;
+ struct bgp_pbr_match_entry *bpme_found;
+};
+
+static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data;
+ struct bgp_pbr_match_entry_remain *bpmer =
+ (struct bgp_pbr_match_entry_remain *)arg;
+ struct bgp_pbr_match *bpm_temp;
+ struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
+
+ if (!bpme->backpointer ||
+ bpm == bpme->backpointer ||
+ bpme->backpointer->action == bpm->action)
+ return HASHWALK_CONTINUE;
+ /* ensure bpm other characteristics are equal */
+ bpm_temp = bpme->backpointer;
+ if (bpm_temp->vrf_id != bpm->vrf_id ||
+ bpm_temp->type != bpm->type ||
+ bpm_temp->flags != bpm->flags)
+ return HASHWALK_CONTINUE;
+
+ /* look for remaining bpme */
+ bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
+ if (!bpmer->bpme_found)
+ return HASHWALK_CONTINUE;
+ return HASHWALK_ABORT;
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp,
+ struct bgp_info *binfo,
+ vrf_id_t vrf_id,
+ struct prefix *src,
+ struct prefix *dst)
+{
+ struct bgp_pbr_match temp;
+ struct bgp_pbr_match_entry temp2;
+ struct bgp_pbr_match *bpm;
+ struct bgp_pbr_match_entry *bpme;
+ struct bgp_pbr_match_entry_remain bpmer;
+
+ /* as we don't know information from EC
+ * look for bpm that have the bpm
+ * with vrf_id characteristics
+ */
+ memset(&temp2, 0, sizeof(temp2));
+ memset(&temp, 0, sizeof(temp));
+ if (src) {
+ temp.flags |= MATCH_IP_SRC_SET;
+ prefix_copy(&temp2.src, src);
+ } else
+ temp2.src.family = AF_INET;
+ if (dst) {
+ temp.flags |= MATCH_IP_DST_SET;
+ prefix_copy(&temp2.dst, dst);
+ } else
+ temp2.dst.family = AF_INET;
+
+ if (src == NULL || dst == NULL)
+ temp.type = IPSET_NET;
+ else
+ temp.type = IPSET_NET_NET;
+ if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
+ temp.vrf_id = 0;
+ else
+ temp.vrf_id = vrf_id;
+ bpme = &temp2;
+ bpm = &temp;
+ bpme->backpointer = bpm;
+ /* right now, a previous entry may already exist
+ * flush previous entry if necessary
+ */
+ bpmer.bpme_to_match = bpme;
+ bpmer.bpme_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
+ if (bpmer.bpme_found) {
+ static struct bgp_pbr_match *local_bpm;
+ static struct bgp_pbr_action *local_bpa;
+
+ local_bpm = bpmer.bpme_found->backpointer;
+ local_bpa = local_bpm->action;
+ bgp_pbr_flush_entry(bgp, local_bpa,
+ local_bpm, bpmer.bpme_found);
+ }
+}
+
+static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
+ struct bgp_info *binfo,
+ vrf_id_t vrf_id,
+ struct prefix *src,
+ struct prefix *dst,
+ struct nexthop *nh,
+ float *rate)
+{
+ struct bgp_pbr_match temp;
+ struct bgp_pbr_match_entry temp2;
+ struct bgp_pbr_match *bpm;
+ struct bgp_pbr_match_entry *bpme = NULL;
+ struct bgp_pbr_action temp3;
+ struct bgp_pbr_action *bpa = NULL;
+ struct bgp_pbr_match_entry_remain bpmer;
+
+ /* look for bpa first */
+ memset(&temp3, 0, sizeof(temp3));
+ if (rate)
+ temp3.rate = *rate;
+ if (nh)
+ memcpy(&temp3.nh, nh, sizeof(struct nexthop));
+ temp3.vrf_id = vrf_id;
+ bpa = hash_get(bgp->pbr_action_hash, &temp3,
+ bgp_pbr_action_alloc_intern);
+
+ if (bpa->fwmark == 0) {
+ /* drop is handled by iptable */
+ if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
+ bpa->table_id = 0;
+ bpa->installed = true;
+ } else {
+ bpa->fwmark = bgp_zebra_tm_get_id();
+ bpa->table_id = bpa->fwmark;
+ bpa->installed = false;
+ }
+ bpa->bgp = bgp;
+ bpa->unique = ++bgp_pbr_action_counter_unique;
+ /* 0 value is forbidden */
+ bpa->install_in_progress = false;
+ }
+
+ /* then look for bpm */
+ memset(&temp, 0, sizeof(temp));
+ if (src == NULL || dst == NULL)
+ temp.type = IPSET_NET;
+ else
+ temp.type = IPSET_NET_NET;
+ temp.vrf_id = vrf_id;
+ if (src)
+ temp.flags |= MATCH_IP_SRC_SET;
+ if (dst)
+ temp.flags |= MATCH_IP_DST_SET;
+ temp.action = bpa;
+ bpm = hash_get(bgp->pbr_match_hash, &temp,
+ bgp_pbr_match_alloc_intern);
+
+ /* new, then self allocate ipset_name and unique */
+ if (bpm && bpm->unique == 0) {
+ bpm->unique = ++bgp_pbr_match_counter_unique;
+ /* 0 value is forbidden */
+ sprintf(bpm->ipset_name, "match%p", bpm);
+ bpm->entry_hash = hash_create_size(8,
+ bgp_pbr_match_entry_hash_key,
+ bgp_pbr_match_entry_hash_equal,
+ "Match Entry Hash");
+ bpm->installed = false;
+
+ /* unique2 should be updated too */
+ bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
+ bpm->installed_in_iptable = false;
+ bpm->install_in_progress = false;
+ bpm->install_iptable_in_progress = false;
+ }
+
+ memset(&temp2, 0, sizeof(temp2));
+ if (src)
+ prefix_copy(&temp2.src, src);
+ else
+ temp2.src.family = AF_INET;
+ if (dst)
+ prefix_copy(&temp2.dst, dst);
+ else
+ temp2.dst.family = AF_INET;
+ if (bpm)
+ bpme = hash_get(bpm->entry_hash, &temp2,
+ bgp_pbr_match_entry_alloc_intern);
+ if (bpme && bpme->unique == 0) {
+ bpme->unique = ++bgp_pbr_match_entry_counter_unique;
+ /* 0 value is forbidden */
+ bpme->backpointer = bpm;
+ bpme->installed = false;
+ bpme->install_in_progress = false;
+ }
+
+ /* BGP FS: append entry to zebra
+ * - policies are not routing entries and as such
+ * route replace semantics don't necessarily follow
+ * through to policy entries
+ * - because of that, not all policing information will be stored
+ * into zebra. and non selected policies will be suppressed from zebra
+ * - as consequence, in order to bring consistency
+ * a policy will be added, then ifan ecmp policy exists,
+ * it will be suppressed subsequently
+ */
+ /* ip rule add */
+ if (!bpa->installed) {
+ bgp_send_pbr_rule_action(bpa, true);
+ bgp_zebra_announce_default(bgp, nh,
+ AFI_IP, bpa->table_id, true);
+ }
+
+ /* ipset create */
+ if (bpm && !bpm->installed)
+ bgp_send_pbr_ipset_match(bpm, true);
+ /* ipset add */
+ if (bpme && !bpme->installed)
+ bgp_send_pbr_ipset_entry_match(bpme, true);
+
+ /* iptables */
+ if (bpm && !bpm->installed_in_iptable)
+ bgp_send_pbr_iptable(bpa, bpm, true);
+
+ /* A previous entry may already exist
+ * flush previous entry if necessary
+ */
+ bpmer.bpme_to_match = bpme;
+ bpmer.bpme_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
+ if (bpmer.bpme_found) {
+ static struct bgp_pbr_match *local_bpm;
+ static struct bgp_pbr_action *local_bpa;
+
+ local_bpm = bpmer.bpme_found->backpointer;
+ local_bpa = local_bpm->action;
+ bgp_pbr_flush_entry(bgp, local_bpa,
+ local_bpm, bpmer.bpme_found);
+ }
+
+
+}
+
+static void bgp_pbr_handle_entry(struct bgp *bgp,
+ struct bgp_info *binfo,
+ struct bgp_pbr_entry_main *api,
+ bool add)
+{
+ struct nexthop nh;
+ int i = 0;
+ int continue_loop = 1;
+ float rate = 0;
+ struct prefix *src = NULL, *dst = NULL;
+
+ if (api->match_bitmask & PREFIX_SRC_PRESENT)
+ src = &api->src_prefix;
+ if (api->match_bitmask & PREFIX_DST_PRESENT)
+ dst = &api->dst_prefix;
+ memset(&nh, 0, sizeof(struct nexthop));
+ nh.vrf_id = VRF_UNKNOWN;
+
+ if (!add)
+ return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo,
+ api->vrf_id, src, dst);
+ /* no action for add = true */
+ for (i = 0; i < api->action_num; i++) {
+ switch (api->actions[i].action) {
+ case ACTION_TRAFFICRATE:
+ /* drop packet */
+ if (api->actions[i].u.r.rate == 0) {
+ nh.vrf_id = api->vrf_id;
+ nh.type = NEXTHOP_TYPE_BLACKHOLE;
+ bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+ api->vrf_id, src, dst,
+ &nh, &rate);
+ } else {
+ /* update rate. can be reentrant */
+ rate = api->actions[i].u.r.rate;
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: ignoring Set action rate %f",
+ api->actions[i].u.r.rate);
+ }
+ }
+ break;
+ case ACTION_TRAFFIC_ACTION:
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_SAMPLE) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: Sample action Ignored");
+ }
+ }
+#if 0
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_DISTRIBUTE) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: Distribute action Applies");
+ }
+ continue_loop = 0;
+ /* continue forwarding entry as before
+ * no action
+ */
+ }
+#endif /* XXX to confirm behaviour of traffic action. for now , ignore */
+ /* terminate action: run other filters
+ */
+ break;
+ case ACTION_REDIRECT_IP:
+ nh.type = NEXTHOP_TYPE_IPV4;
+ nh.gate.ipv4.s_addr =
+ api->actions[i].u.zr.redirect_ip_v4.s_addr;
+ nh.vrf_id = api->vrf_id;
+ bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+ api->vrf_id,
+ src, dst,
+ &nh, &rate);
+ /* XXX combination with REDIRECT_VRF
+ * + REDIRECT_NH_IP not done
+ */
+ continue_loop = 0;
+ break;
+ case ACTION_REDIRECT:
+ nh.vrf_id = api->actions[i].u.redirect_vrf;
+ nh.type = NEXTHOP_TYPE_IPV4;
+ bgp_pbr_policyroute_add_to_zebra(bgp, binfo,
+ api->vrf_id,
+ src, dst,
+ &nh, &rate);
+ continue_loop = 0;
+ break;
+ case ACTION_MARKING:
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: Set DSCP %u Ignored",
+ api->actions[i].u.marking_dscp);
+ }
+ break;
+ default:
+ break;
+ }
+ if (continue_loop == 0)
+ break;
+ }
+}
+
+void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
+ struct bgp_info *info, afi_t afi, safi_t safi,
+ bool nlri_update)
+{
+ struct bgp_pbr_entry_main api;
+
+ if (afi == AFI_IP6)
+ return; /* IPv6 not supported */
+ if (safi != SAFI_FLOWSPEC)
+ return; /* not supported */
+ /* Make Zebra API structure. */
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = bgp->vrf_id;
+ api.afi = afi;
+
+ if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ zlog_err("%s: cancel updating entry in bgp pbr",
+ __func__);
+ return;
+ }
+ bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
+}
diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h
new file mode 100644
index 0000000000..5129ada37b
--- /dev/null
+++ b/bgpd/bgp_pbr.h
@@ -0,0 +1,256 @@
+/*
+ * BGP pbr
+ * Copyright (C) 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 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 __BGP_PBR_H__
+#define __BGP_PBR_H__
+
+#include "nexthop.h"
+#include "zclient.h"
+
+/* flowspec case: 0 to 3 actions maximum:
+ * 1 redirect
+ * 1 set dscp
+ * 1 set traffic rate
+ */
+#define ACTIONS_MAX_NUM 4
+enum bgp_pbr_action_enum {
+ ACTION_TRAFFICRATE = 1,
+ ACTION_TRAFFIC_ACTION = 2,
+ ACTION_REDIRECT = 3,
+ ACTION_MARKING = 4,
+ ACTION_REDIRECT_IP = 5
+};
+
+#define TRAFFIC_ACTION_SAMPLE (1 << 0)
+#define TRAFFIC_ACTION_TERMINATE (1 << 1)
+#define TRAFFIC_ACTION_DISTRIBUTE (1 << 2)
+
+#define OPERATOR_COMPARE_LESS_THAN (1<<1)
+#define OPERATOR_COMPARE_GREATER_THAN (1<<2)
+#define OPERATOR_COMPARE_EQUAL_TO (1<<3)
+#define OPERATOR_COMPARE_EXACT_MATCH (1<<4)
+
+#define OPERATOR_UNARY_OR (1<<1)
+#define OPERATOR_UNARY_AND (1<<2)
+
+/* struct used to store values [0;65535]
+ * this can be used for port number of protocol
+ */
+#define BGP_PBR_MATCH_VAL_MAX 5
+
+struct bgp_pbr_match_val {
+ uint16_t value;
+ uint8_t compare_operator;
+ uint8_t unary_operator;
+} bgp_pbr_value_t;
+
+#define FRAGMENT_DONT 1
+#define FRAGMENT_IS 2
+#define FRAGMENT_FIRST 4
+#define FRAGMENT_LAST 8
+
+struct bgp_pbr_fragment_val {
+ uint8_t bitmask;
+};
+
+struct bgp_pbr_entry_action {
+ /* used to store enum bgp_pbr_action_enum enumerate */
+ uint8_t action;
+ union {
+ union {
+ uint8_t rate_info[4]; /* IEEE.754.1985 */
+ float rate;
+ } r __attribute__((aligned(8)));
+ struct _pbr_action {
+ uint8_t do_sample;
+ uint8_t filter;
+ } za;
+ vrf_id_t redirect_vrf;
+ struct _pbr_redirect_ip {
+ struct in_addr redirect_ip_v4;
+ uint8_t duplicate;
+ } zr;
+ uint8_t marking_dscp;
+ } u __attribute__((aligned(8)));
+};
+
+/* BGP Policy Route structure */
+struct bgp_pbr_entry_main {
+ uint8_t type;
+ uint16_t instance;
+
+ uint32_t flags;
+
+ uint8_t message;
+
+ /*
+ * This is an enum but we are going to treat it as a uint8_t
+ * for purpose of encoding/decoding
+ */
+ afi_t afi;
+ safi_t safi;
+
+#define PREFIX_SRC_PRESENT (1 << 0)
+#define PREFIX_DST_PRESENT (1 << 1)
+#define FRAGMENT_PRESENT (1 << 2)
+ uint8_t match_bitmask;
+
+ uint8_t match_src_port_num;
+ uint8_t match_dst_port_num;
+ uint8_t match_port_num;
+ uint8_t match_protocol_num;
+ uint8_t match_icmp_type_num;
+ uint8_t match_icmp_code_num;
+ uint8_t match_packet_length_num;
+ uint8_t match_dscp_num;
+ uint8_t match_tcpflags_num;
+
+ struct prefix src_prefix;
+ struct prefix dst_prefix;
+
+ struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val port[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val icmp_type[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_fragment_val fragment;
+
+ uint16_t action_num;
+ struct bgp_pbr_entry_action actions[ACTIONS_MAX_NUM];
+
+ uint8_t distance;
+
+ uint32_t metric;
+
+ route_tag_t tag;
+
+ uint32_t mtu;
+
+ vrf_id_t vrf_id;
+};
+
+struct bgp_pbr_match {
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+
+ /* mapped on enum ipset_type
+ */
+ uint32_t type;
+
+#define MATCH_IP_SRC_SET (1 << 0)
+#define MATCH_IP_DST_SET (1 << 1)
+ uint32_t flags;
+
+ vrf_id_t vrf_id;
+
+ /* unique identifier for ipset create transaction
+ */
+ uint32_t unique;
+
+ /* unique identifier for iptable add transaction
+ */
+ uint32_t unique2;
+
+ bool installed;
+ bool install_in_progress;
+
+ bool installed_in_iptable;
+ bool install_iptable_in_progress;
+
+ struct hash *entry_hash;
+
+ struct bgp_pbr_action *action;
+
+};
+
+struct bgp_pbr_match_entry {
+ struct bgp_pbr_match *backpointer;
+
+ uint32_t unique;
+
+ struct prefix src;
+ struct prefix dst;
+
+ bool installed;
+ bool install_in_progress;
+};
+
+struct bgp_pbr_action {
+
+ /*
+ * The Unique identifier of this specific pbrms
+ */
+ uint32_t unique;
+
+ uint32_t fwmark;
+
+ uint32_t table_id;
+
+ float rate;
+
+ /*
+ * nexthop information, or drop information
+ * contains src vrf_id and nh contains dest vrf_id
+ */
+ vrf_id_t vrf_id;
+ struct nexthop nh;
+
+ bool installed;
+ bool install_in_progress;
+ uint32_t refcnt;
+ struct bgp *bgp;
+};
+
+extern struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(
+ vrf_id_t vrf_id, char *name,
+ uint32_t unique);
+extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern void bgp_pbr_cleanup(struct bgp *bgp);
+extern void bgp_pbr_init(struct bgp *bgp);
+
+extern uint32_t bgp_pbr_action_hash_key(void *arg);
+extern int bgp_pbr_action_hash_equal(const void *arg1,
+ const void *arg2);
+extern uint32_t bgp_pbr_match_entry_hash_key(void *arg);
+extern int bgp_pbr_match_entry_hash_equal(const void *arg1,
+ const void *arg2);
+extern uint32_t bgp_pbr_match_hash_key(void *arg);
+extern int bgp_pbr_match_hash_equal(const void *arg1,
+ const void *arg2);
+
+void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api);
+
+struct bgp_node;
+struct bgp_info;
+extern void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p,
+ struct bgp_info *new_select,
+ afi_t afi, safi_t safi,
+ bool nlri_update);
+
+#endif /* __BGP_PBR_H__ */
diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c
index 64e083d1ef..3f7ea16045 100644
--- a/bgpd/bgp_rd.c
+++ b/bgpd/bgp_rd.c
@@ -200,3 +200,15 @@ char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size)
snprintf(buf, size, "Unknown Type: %d", type);
return buf;
}
+
+void form_auto_rd(struct in_addr router_id,
+ uint16_t rd_id,
+ struct prefix_rd *prd)
+{
+ char buf[100];
+
+ prd->family = AF_UNSPEC;
+ prd->prefixlen = 64;
+ sprintf(buf, "%s:%hu", inet_ntoa(router_id), rd_id);
+ str2prefix_rd(buf, prd);
+}
diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h
index a8ea83a4a7..c5ea34103f 100644
--- a/bgpd/bgp_rd.h
+++ b/bgpd/bgp_rd.h
@@ -66,5 +66,7 @@ extern void decode_rd_vnc_eth(uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth);
extern int str2prefix_rd(const char *, struct prefix_rd *);
extern char *prefix_rd2str(struct prefix_rd *, char *, size_t);
+extern void form_auto_rd(struct in_addr router_id, uint16_t rd_id,
+ struct prefix_rd *prd);
#endif /* _QUAGGA_BGP_RD_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index a71f5ac956..997d708baf 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -75,6 +75,7 @@
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_pbr.h"
#ifndef VTYSH_EXTRACT_PL
#include "bgpd/bgp_route_clippy.c"
@@ -99,6 +100,8 @@ static const struct message bgp_pmsi_tnltype_str[] = {
{0}
};
+#define VRFID_NONE_STR "-"
+
struct bgp_node *bgp_afi_node_get(struct bgp_table *table, afi_t afi,
safi_t safi, struct prefix *p,
struct prefix_rd *prd)
@@ -222,6 +225,11 @@ struct bgp_info *bgp_info_lock(struct bgp_info *binfo)
struct bgp_info *bgp_info_unlock(struct bgp_info *binfo)
{
+ /* unlink reference to parent, if any. */
+ if (binfo->extra && binfo->extra->parent) {
+ bgp_info_unlock((struct bgp_info *)binfo->extra->parent);
+ binfo->extra->parent = NULL;
+ }
assert(binfo && binfo->lock > 0);
binfo->lock--;
@@ -1333,6 +1341,8 @@ static void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr)
}
if (family == AF_INET6)
memset(&attr->mp_nexthop_global, 0, IPV6_MAX_BYTELEN);
+ if (family == AF_EVPN)
+ memset(&attr->mp_nexthop_global_in, 0, BGP_ATTR_NHLEN_IPV4);
}
int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri,
@@ -1418,6 +1428,16 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri,
return 0;
}
+ /*
+ * If we are doing VRF 2 VRF leaking via the import
+ * statement, we want to prevent the route going
+ * off box as that the RT and RD created are localy
+ * significant and globaly useless.
+ */
+ if (safi == SAFI_MPLS_VPN && ri->extra && ri->extra->num_labels
+ && ri->extra->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK)
+ return 0;
+
/* If it's labeled safi, make sure the route has a valid label. */
if (safi == SAFI_LABELED_UNICAST) {
mpls_label_t label = bgp_adv_label(rn, ri, peer, afi, safi);
@@ -2219,7 +2239,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
/* If best route remains the same and this is not due to user-initiated
* clear, see exactly what needs to be done.
*/
-
if (old_select && old_select == new_select
&& !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED)
@@ -2324,10 +2343,18 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
if (new_select && new_select->type == ZEBRA_ROUTE_BGP
&& (new_select->sub_type == BGP_ROUTE_NORMAL
|| new_select->sub_type == BGP_ROUTE_AGGREGATE
- || new_select->sub_type == BGP_ROUTE_IMPORTED))
+ || new_select->sub_type == BGP_ROUTE_IMPORTED)) {
+
+ /* if this is an evpn imported type-5 prefix,
+ * we need to withdraw the route first to clear
+ * the nh neigh and the RMAC entry.
+ */
+ if (old_select &&
+ is_route_parent_evpn(old_select))
+ bgp_zebra_withdraw(p, old_select, bgp, safi);
bgp_zebra_announce(rn, p, new_select, bgp, afi, safi);
- else {
+ } else {
/* Withdraw the route from the kernel. */
if (old_select && old_select->type == ZEBRA_ROUTE_BGP
&& (old_select->sub_type == BGP_ROUTE_NORMAL
@@ -2341,12 +2368,28 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
/* advertise/withdraw type-5 routes */
if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
if (advertise_type5_routes(bgp, afi) && new_select &&
- (!new_select->extra || !new_select->extra->parent))
- bgp_evpn_advertise_type5_route(bgp, &rn->p,
- new_select->attr,
- afi, safi);
- else if (advertise_type5_routes(bgp, afi) && old_select &&
- (!old_select->extra || !old_select->extra->parent))
+ (!new_select->extra || !new_select->extra->parent)) {
+
+ /* apply the route-map */
+ if (bgp->adv_cmd_rmap[afi][safi].map) {
+ int ret = 0;
+
+ ret = route_map_apply(
+ bgp->adv_cmd_rmap[afi][safi].map,
+ &rn->p, RMAP_BGP, new_select);
+ if (ret == RMAP_MATCH)
+ bgp_evpn_advertise_type5_route(
+ bgp, &rn->p, new_select->attr,
+ afi, safi);
+ } else {
+ bgp_evpn_advertise_type5_route(bgp,
+ &rn->p,
+ new_select->attr,
+ afi, safi);
+
+ }
+ } else if (advertise_type5_routes(bgp, afi) && old_select &&
+ (!old_select->extra || !old_select->extra->parent))
bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi);
}
@@ -4614,7 +4657,7 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p,
if (bgp_static->encap_tunneltype == BGP_ENCAP_TYPE_VXLAN) {
struct bgp_encap_type_vxlan bet;
memset(&bet, 0, sizeof(struct bgp_encap_type_vxlan));
- bet.vnid = p->u.prefix_evpn.eth_tag;
+ bet.vnid = p->u.prefix_evpn.prefix_addr.eth_tag;
bgp_encap_type_vxlan_to_tlv(&bet, &attr);
}
if (bgp_static->router_mac) {
@@ -5089,10 +5132,10 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty,
return CMD_WARNING_CONFIG_FAILED;
}
if ((gw_ip.family == AF_INET
- && IS_EVPN_PREFIX_IPADDR_V6(
+ && is_evpn_prefix_ipaddr_v6(
(struct prefix_evpn *)&p))
|| (gw_ip.family == AF_INET6
- && IS_EVPN_PREFIX_IPADDR_V4(
+ && is_evpn_prefix_ipaddr_v4(
(struct prefix_evpn *)&p))) {
vty_out(vty,
"%% GatewayIp family differs with IP prefix\n");
@@ -6461,6 +6504,13 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
json_object *json_nexthops = NULL;
json_object *json_nexthop_global = NULL;
json_object *json_nexthop_ll = NULL;
+ char vrf_id_str[VRF_NAMSIZ] = {0};
+ bool nexthop_self = CHECK_FLAG(binfo->flags, BGP_INFO_ANNC_NH_SELF)
+ ? true
+ : false;
+ bool nexthop_othervrf = false;
+ vrf_id_t nexthop_vrfid;
+ const char *nexthop_vrfname = "Default";
if (json_paths)
json_path = json_object_new_object();
@@ -6490,6 +6540,39 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
}
/*
+ * If vrf id of nexthop is different from that of prefix,
+ * set up printable string to append
+ */
+ if (binfo->extra && binfo->extra->bgp_orig) {
+ const char *self = "";
+
+ if (nexthop_self)
+ self = "<";
+
+ nexthop_othervrf = true;
+ nexthop_vrfid = binfo->extra->bgp_orig->vrf_id;
+
+ if (binfo->extra->bgp_orig->vrf_id == VRF_UNKNOWN)
+ snprintf(vrf_id_str, sizeof(vrf_id_str),
+ "@%s%s", VRFID_NONE_STR, self);
+ else
+ snprintf(vrf_id_str, sizeof(vrf_id_str), "@%u%s",
+ binfo->extra->bgp_orig->vrf_id, self);
+
+ if (binfo->extra->bgp_orig->inst_type !=
+ BGP_INSTANCE_TYPE_DEFAULT)
+
+ nexthop_vrfname = binfo->extra->bgp_orig->name;
+ } else {
+ const char *self = "";
+
+ if (nexthop_self)
+ self = "<";
+
+ snprintf(vrf_id_str, sizeof(vrf_id_str), "%s", self);
+ }
+
+ /*
* For ENCAP and EVPN routes, nexthop address family is not
* neccessarily the same as the prefix address family.
* Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
@@ -6531,7 +6614,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
json_object_boolean_true_add(json_nexthop_global,
"used");
} else
- vty_out(vty, "%s", nexthop);
+ vty_out(vty, "%s%s", nexthop, vrf_id_str);
} else if (safi == SAFI_EVPN) {
if (json_paths) {
json_nexthop_global = json_object_new_object();
@@ -6543,7 +6626,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
json_object_boolean_true_add(json_nexthop_global,
"used");
} else
- vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
+ vty_out(vty, "%-16s%s", inet_ntoa(attr->nexthop),
+ vrf_id_str);
} else if (safi == SAFI_FLOWSPEC) {
if (attr->nexthop.s_addr != 0) {
if (json_paths) {
@@ -6577,11 +6661,17 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
json_object_boolean_true_add(json_nexthop_global,
"used");
} else {
+ char buf[BUFSIZ];
+
if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
- vty_out(vty, "%-16s",
- inet_ntoa(attr->mp_nexthop_global_in));
+ snprintf(buf, sizeof(buf), "%s%s",
+ inet_ntoa(attr->mp_nexthop_global_in),
+ vrf_id_str);
else
- vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
+ snprintf(buf, sizeof(buf), "%s%s",
+ inet_ntoa(attr->nexthop),
+ vrf_id_str);
+ vty_out(vty, "%-16s", buf);
}
}
@@ -6648,11 +6738,12 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
vty_out(vty, "%*s", len, " ");
} else {
len = vty_out(
- vty, "%s",
+ vty, "%s%s",
inet_ntop(
AF_INET6,
&attr->mp_nexthop_local,
- buf, BUFSIZ));
+ buf, BUFSIZ),
+ vrf_id_str);
len = 16 - len;
if (len < 1)
@@ -6662,10 +6753,11 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
}
} else {
len = vty_out(
- vty, "%s",
+ vty, "%s%s",
inet_ntop(AF_INET6,
&attr->mp_nexthop_global, buf,
- BUFSIZ));
+ BUFSIZ),
+ vrf_id_str);
len = 16 - len;
if (len < 1)
@@ -6724,6 +6816,21 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
if (json_paths) {
+ if (nexthop_self)
+ json_object_boolean_true_add(json_path,
+ "announceNexthopSelf");
+ if (nexthop_othervrf) {
+ json_object_string_add(json_path, "nhVrfName",
+ nexthop_vrfname);
+
+ json_object_int_add(json_path, "nhVrfId",
+ ((nexthop_vrfid == VRF_UNKNOWN)
+ ? -1
+ : (int)nexthop_vrfid));
+ }
+ }
+
+ if (json_paths) {
if (json_nexthop_global || json_nexthop_ll) {
json_nexthops = json_object_new_array();
@@ -7053,10 +7160,10 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p,
vty_out(vty, "%s", str);
XFREE(MTYPE_TMP, str);
- if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) {
+ 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)) {
+ } 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,
@@ -7323,6 +7430,9 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
int addpath_capable;
int has_adj;
unsigned int first_as;
+ bool nexthop_self = CHECK_FLAG(binfo->flags, BGP_INFO_ANNC_NH_SELF)
+ ? true
+ : false;
if (json_paths) {
json_path = json_object_new_object();
@@ -7626,6 +7736,49 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
}
}
+ /*
+ * Note when vrfid of nexthop is different from that of prefix
+ */
+ if (binfo->extra && binfo->extra->bgp_orig) {
+ vrf_id_t nexthop_vrfid = binfo->extra->bgp_orig->vrf_id;
+
+ if (json_paths) {
+ const char *vn;
+
+ if (binfo->extra->bgp_orig->inst_type ==
+ BGP_INSTANCE_TYPE_DEFAULT)
+
+ vn = "Default";
+ else
+ vn = binfo->extra->bgp_orig->name;
+
+ json_object_string_add(json_path, "nhVrfName",
+ vn);
+
+ if (nexthop_vrfid == VRF_UNKNOWN) {
+ json_object_int_add(json_path,
+ "nhVrfId", -1);
+ } else {
+ json_object_int_add(json_path,
+ "nhVrfId", (int)nexthop_vrfid);
+ }
+ } else {
+ if (nexthop_vrfid == VRF_UNKNOWN)
+ vty_out(vty, " vrf ?");
+ else
+ vty_out(vty, " vrf %u", nexthop_vrfid);
+ }
+ }
+
+ if (nexthop_self) {
+ if (json_paths) {
+ json_object_boolean_true_add(json_path,
+ "announceNexthopSelf");
+ } else {
+ vty_out(vty, " announce-nh-self");
+ }
+ }
+
if (!json_paths)
vty_out(vty, "\n");
@@ -8323,10 +8476,16 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
if (!use_json && header) {
vty_out(vty, "BGP table version is %" PRIu64
- ", local router ID is %s\n",
+ ", local router ID is %s, vrf id ",
table->version,
inet_ntoa(bgp->router_id));
+ if (bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s", VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u", bgp->vrf_id);
+ vty_out(vty, "\n");
vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_NCODE_HEADER);
vty_out(vty, BGP_SHOW_OCODE_HEADER);
if (type == bgp_show_type_dampend_paths
|| type == bgp_show_type_damp_neighbor)
@@ -10136,9 +10295,15 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
"0.0.0.0");
} else {
vty_out(vty, "BGP table version is %" PRIu64
- ", local router ID is %s\n",
+ ", local router ID is %s, vrf id ",
table->version, inet_ntoa(bgp->router_id));
+ if (bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s", VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u", bgp->vrf_id);
+ vty_out(vty, "\n");
vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_NCODE_HEADER);
vty_out(vty, BGP_SHOW_OCODE_HEADER);
vty_out(vty, "Originating default network 0.0.0.0\n\n");
@@ -10169,12 +10334,21 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
json_ocode);
} else {
vty_out(vty,
- "BGP table version is 0, local router ID is %s\n",
+ "BGP table version is 0, local router ID is %s, vrf id ",
inet_ntoa(
- bgp->router_id));
+ bgp->router_id));
+ if (bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s",
+ VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u",
+ bgp->vrf_id);
+ vty_out(vty, "\n");
vty_out(vty,
BGP_SHOW_SCODE_HEADER);
vty_out(vty,
+ BGP_SHOW_NCODE_HEADER);
+ vty_out(vty,
BGP_SHOW_OCODE_HEADER);
}
header1 = 0;
@@ -10227,13 +10401,25 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
} else {
vty_out(vty,
"BGP table version is %" PRIu64
- ", local router ID is %s\n",
+ ", local router ID is %s, vrf id ",
table->version,
inet_ntoa(
bgp->router_id));
+ if (bgp->vrf_id ==
+ VRF_UNKNOWN)
+ vty_out(vty,
+ "%s",
+ VRFID_NONE_STR);
+ else
+ vty_out(vty,
+ "%u",
+ bgp->vrf_id);
+ vty_out(vty, "\n");
vty_out(vty,
BGP_SHOW_SCODE_HEADER);
vty_out(vty,
+ BGP_SHOW_NCODE_HEADER);
+ vty_out(vty,
BGP_SHOW_OCODE_HEADER);
}
header1 = 0;
@@ -11243,14 +11429,15 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp,
prefix_rd2str(prd, rdbuf, sizeof(rdbuf));
if (p->u.prefix_evpn.route_type == 5) {
char local_buf[PREFIX_STRLEN];
- uint8_t family = IS_EVPN_PREFIX_IPADDR_V4((
+ uint8_t family = is_evpn_prefix_ipaddr_v4((
struct prefix_evpn *)p)
? AF_INET
: AF_INET6;
- inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr,
+ inet_ntop(family,
+ &p->u.prefix_evpn.prefix_addr.ip.ip.addr,
local_buf, PREFIX_STRLEN);
sprintf(buf, "%s/%u", local_buf,
- p->u.prefix_evpn.ip_prefix_length);
+ p->u.prefix_evpn.prefix_addr.ip_prefix_length);
} else {
prefix2str(p, buf, sizeof(buf));
}
@@ -11262,7 +11449,8 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp,
sizeof(buf2));
vty_out(vty,
" network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n",
- buf, rdbuf, p->u.prefix_evpn.eth_tag,
+ buf, rdbuf,
+ p->u.prefix_evpn.prefix_addr.eth_tag,
decode_label(&bgp_static->label), esi, buf2,
macrouter);
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 1e788b00f1..00e5677fe0 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -54,10 +54,11 @@ enum bgp_show_type {
#define BGP_SHOW_SCODE_HEADER \
- "Status codes: s suppressed, d damped, " \
+ "Status codes: s suppressed, d damped, " \
"h history, * valid, > best, = multipath,\n" \
- " i internal, r RIB-failure, S Stale, R Removed\n"
-#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"
+ " i internal, r RIB-failure, S Stale, R Removed\n"
+#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"
+#define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n"
#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
/* Maximum number of labels we can process or send with a prefix. We
@@ -319,7 +320,8 @@ static inline void bgp_bump_version(struct bgp_node *node)
static inline int bgp_fibupd_safi(safi_t safi)
{
if (safi == SAFI_UNICAST || safi == SAFI_MULTICAST
- || safi == SAFI_LABELED_UNICAST)
+ || safi == SAFI_LABELED_UNICAST
+ || safi == SAFI_FLOWSPEC)
return 1;
return 0;
}
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 4cc889286e..63400f7d31 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -635,7 +635,7 @@ static route_map_result_t route_match_mac_address(void *rule,
p.family = AF_ETHERNET;
p.prefixlen = ETH_ALEN * 8;
- p.u.prefix_eth = prefix->u.prefix_evpn.mac;
+ p.u.prefix_eth = prefix->u.prefix_evpn.macip_addr.mac;
return (access_list_apply(alist, &p) == FILTER_DENY
? RMAP_NOMATCH
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 75ba1609d0..a23d5d03c4 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -25,6 +25,9 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/* If rtrlib compiled with ssh support, don`t fail build */
+#define LIBSSH_LEGACY_0_4
+
#include <zebra.h>
#include <pthread.h>
#include <time.h>
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index 2c7e4e0435..241b23a62d 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -356,20 +356,19 @@ static struct peer *peer_lookup_addr_ipv4(struct in_addr *src)
struct bgp *bgp;
struct peer *peer;
struct listnode *node;
- struct in_addr addr;
- int ret;
bgp = bgp_get_default();
if (!bgp)
return NULL;
for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
- ret = inet_pton(AF_INET, peer->host, &addr);
- if (ret > 0) {
- if (IPV4_ADDR_SAME(&addr, src))
- return peer;
- }
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+
+ if (sockunion2ip(&peer->su) == src->s_addr)
+ return peer;
}
+
return NULL;
}
@@ -377,28 +376,31 @@ static struct peer *bgp_peer_lookup_next(struct in_addr *src)
{
struct bgp *bgp;
struct peer *peer;
+ struct peer *next_peer = NULL;
struct listnode *node;
- struct in_addr *p;
- union sockunion su;
- int ret;
-
- sockunion_init(&su);
bgp = bgp_get_default();
if (!bgp)
return NULL;
for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
- ret = inet_pton(AF_INET, peer->host, &su.sin.sin_addr);
- if (ret > 0) {
- p = &su.sin.sin_addr;
-
- if (ntohl(p->s_addr) > ntohl(src->s_addr)) {
- src->s_addr = p->s_addr;
- return peer;
- }
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+ if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr))
+ continue;
+
+ if (!next_peer
+ || ntohl(sockunion2ip(&next_peer->su))
+ > ntohl(sockunion2ip(&peer->su))) {
+ next_peer = peer;
}
}
+
+ if (next_peer) {
+ src->s_addr = sockunion2ip(&next_peer->su);
+ return next_peer;
+ }
+
return NULL;
}
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 8ba7902a5f..cabd5b5cbd 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -467,13 +467,12 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
nh_modified = 1;
} else if (
peer->sort == BGP_PEER_EBGP
- && paf->safi != SAFI_EVPN
&& (bgp_multiaccess_check_v4(v4nh, peer) == 0)
&& !CHECK_FLAG(
vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
&& !peer_af_flag_check(
- peer, nhafi, paf->safi,
+ peer, paf->afi, paf->safi,
PEER_FLAG_NEXTHOP_UNCHANGED)) {
/* NOTE: not handling case where NH has new AFI
*/
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 6bc50fb77e..b8c81232bb 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -6159,21 +6159,46 @@ static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
return CMD_SUCCESS;
}
-static int vpn_policy_getafi(struct vty *vty, int *doafi)
+/*
+ * v2vimport is true if we are handling a `import vrf ...` command
+ */
+static afi_t vpn_policy_getafi(struct vty *vty, struct bgp *bgp, bool v2vimport)
{
+ afi_t afi;
+
switch (vty->node) {
case BGP_IPV4_NODE:
- doafi[AFI_IP] = 1;
+ afi = AFI_IP;
break;
case BGP_IPV6_NODE:
- doafi[AFI_IP6] = 1;
+ afi = AFI_IP6;
break;
default:
vty_out(vty,
"%% context error: valid only in address-family <ipv4|ipv6> unicast block\n");
- return CMD_WARNING_CONFIG_FAILED;
+ return AFI_MAX;
}
- return CMD_SUCCESS;
+
+ if (!v2vimport) {
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ vty_out(vty,
+ "%% error: Please unconfigure import vrf commands before using vpn commands\n");
+ return AFI_MAX;
+ }
+ } else {
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
+ vty_out(vty,
+ "%% error: Please unconfigure vpn to vrf commands before using import vrf commands\n");
+ return AFI_MAX;
+ }
+ }
+ return afi;
}
DEFPY (af_rd_vpn_export,
@@ -6188,7 +6213,6 @@ DEFPY (af_rd_vpn_export,
VTY_DECLVAR_CONTEXT(bgp, bgp);
struct prefix_rd prd;
int ret;
- int doafi[AFI_MAX] = {0};
afi_t afi;
int idx = 0;
int yes = 1;
@@ -6204,34 +6228,29 @@ DEFPY (af_rd_vpn_export,
}
}
- ret = vpn_policy_getafi(vty, doafi);
- if (ret != CMD_SUCCESS)
- return ret;
-
-
- for (afi = 0; afi < AFI_MAX; ++afi) {
- if (!doafi[afi])
- continue;
-
- /* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
- */
- vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
- bgp_get_default(), bgp);
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
- if (yes) {
- bgp->vpn_policy[afi].tovpn_rd = prd;
- SET_FLAG(bgp->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_RD_SET);
- } else {
- UNSET_FLAG(bgp->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_RD_SET);
- }
+ /*
+ * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+ */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
- /* post-change: re-export vpn routes */
- vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
- bgp_get_default(), bgp);
+ if (yes) {
+ bgp->vpn_policy[afi].tovpn_rd = prd;
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ } else {
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
}
+ /* post-change: re-export vpn routes */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
return CMD_SUCCESS;
}
@@ -6255,9 +6274,7 @@ DEFPY (af_label_vpn_export,
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
mpls_label_t label = MPLS_LABEL_NONE;
- int doafi[AFI_MAX] = {0};
afi_t afi;
- int ret;
int idx = 0;
int yes = 1;
@@ -6269,62 +6286,57 @@ DEFPY (af_label_vpn_export,
label = label_val; /* parser should force unsigned */
}
- ret = vpn_policy_getafi(vty, doafi);
- if (ret != CMD_SUCCESS)
- return ret;
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
- for (afi = 0; afi < AFI_MAX; ++afi) {
- if (!doafi[afi])
- continue;
- if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_LABEL_AUTO))
+ if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO))
+ /* no change */
+ return CMD_SUCCESS;
- 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);
- /*
- * 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);
- }
+ if (!label_auto && CHECK_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);
+ 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);
+ }
- /* post-change: re-export vpn routes */
- vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
- bgp_get_default(), bgp);
+ 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,
+ bgp_get_default(), bgp);
+
return CMD_SUCCESS;
}
@@ -6347,9 +6359,7 @@ DEFPY (af_nexthop_vpn_export,
"IPv6 prefix\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
- int doafi[AFI_MAX] = {0};
afi_t afi;
- int ret;
struct prefix p;
int idx = 0;
int yes = 1;
@@ -6362,34 +6372,29 @@ DEFPY (af_nexthop_vpn_export,
return CMD_WARNING_CONFIG_FAILED;
}
- ret = vpn_policy_getafi(vty, doafi);
- if (ret != CMD_SUCCESS)
- return ret;
-
- for (afi = 0; afi < AFI_MAX; ++afi) {
- if (!doafi[afi])
- continue;
-
- /*
- * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
- */
- vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
- bgp_get_default(), bgp);
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
- if (yes) {
- bgp->vpn_policy[afi].tovpn_nexthop = p;
- SET_FLAG(bgp->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
- } else {
- UNSET_FLAG(bgp->vpn_policy[afi].flags,
- BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
- }
+ /*
+ * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+ */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
- /* post-change: re-export vpn routes */
- vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
- bgp_get_default(), bgp);
+ if (yes) {
+ bgp->vpn_policy[afi].tovpn_nexthop = p;
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
+ } else {
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
}
+ /* post-change: re-export vpn routes */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
return CMD_SUCCESS;
}
@@ -6433,7 +6438,6 @@ DEFPY (af_rt_vpn_imexport,
int ret;
struct ecommunity *ecom = NULL;
int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
- int doafi[AFI_MAX] = {0};
vpn_policy_direction_t dir;
afi_t afi;
int idx = 0;
@@ -6442,9 +6446,9 @@ DEFPY (af_rt_vpn_imexport,
if (argv_find(argv, argc, "no", &idx))
yes = 0;
- ret = vpn_policy_getafi(vty, doafi);
- if (ret != CMD_SUCCESS)
- return ret;
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
ret = vpn_policy_getdirs(vty, direction_str, dodir);
if (ret != CMD_SUCCESS)
@@ -6461,31 +6465,28 @@ DEFPY (af_rt_vpn_imexport,
}
}
- for (afi = 0; afi < AFI_MAX; ++afi) {
- if (!doafi[afi])
+ for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+ if (!dodir[dir])
continue;
- for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
- if (!dodir[dir])
- continue;
- vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
- if (yes) {
- if (bgp->vpn_policy[afi].rtlist[dir])
- ecommunity_free(
- &bgp->vpn_policy[afi].rtlist[dir]);
- bgp->vpn_policy[afi].rtlist[dir] =
- ecommunity_dup(ecom);
- } else {
- if (bgp->vpn_policy[afi].rtlist[dir])
- ecommunity_free(
- &bgp->vpn_policy[afi].rtlist[dir]);
- bgp->vpn_policy[afi].rtlist[dir] = NULL;
- }
-
- vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+ if (yes) {
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(
+ &bgp->vpn_policy[afi].rtlist[dir]);
+ bgp->vpn_policy[afi].rtlist[dir] =
+ ecommunity_dup(ecom);
+ } else {
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(
+ &bgp->vpn_policy[afi].rtlist[dir]);
+ bgp->vpn_policy[afi].rtlist[dir] = NULL;
}
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
}
+
if (ecom)
ecommunity_free(&ecom);
@@ -6517,7 +6518,6 @@ DEFPY (af_route_map_vpn_imexport,
VTY_DECLVAR_CONTEXT(bgp, bgp);
int ret;
int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
- int doafi[AFI_MAX] = {0};
vpn_policy_direction_t dir;
afi_t afi;
int idx = 0;
@@ -6526,41 +6526,39 @@ DEFPY (af_route_map_vpn_imexport,
if (argv_find(argv, argc, "no", &idx))
yes = 0;
- ret = vpn_policy_getafi(vty, doafi);
- if (ret != CMD_SUCCESS)
- return ret;
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
ret = vpn_policy_getdirs(vty, direction_str, dodir);
if (ret != CMD_SUCCESS)
return ret;
- for (afi = 0; afi < AFI_MAX; ++afi) {
- if (!doafi[afi])
+ for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+ if (!dodir[dir])
continue;
- for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
- if (!dodir[dir])
- continue;
-
- vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
- if (yes) {
- if (bgp->vpn_policy[afi].rmap_name[dir])
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp->vpn_policy[afi].rmap_name[dir]);
- bgp->vpn_policy[afi].rmap_name[dir] = XSTRDUP(
- MTYPE_ROUTE_MAP_NAME, rmap_str);
- bgp->vpn_policy[afi].rmap[dir] =
- route_map_lookup_by_name(rmap_str);
- } else {
- if (bgp->vpn_policy[afi].rmap_name[dir])
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp->vpn_policy[afi].rmap_name[dir]);
- bgp->vpn_policy[afi].rmap_name[dir] = NULL;
- bgp->vpn_policy[afi].rmap[dir] = NULL;
- }
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
- vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+ if (yes) {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] = XSTRDUP(
+ MTYPE_ROUTE_MAP_NAME, rmap_str);
+ bgp->vpn_policy[afi].rmap[dir] =
+ route_map_lookup_by_name(rmap_str);
+ if (!bgp->vpn_policy[afi].rmap[dir])
+ return CMD_SUCCESS;
+ } else {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] = NULL;
+ bgp->vpn_policy[afi].rmap[dir] = NULL;
}
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
}
return CMD_SUCCESS;
@@ -6575,6 +6573,158 @@ ALIAS (af_route_map_vpn_imexport,
"For routes leaked from vpn to current address-family\n"
"For routes leaked from current address-family to vpn\n")
+DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd,
+ "[no] import vrf route-map RMAP$rmap_str",
+ NO_STR
+ "Import routes from another VRF\n"
+ "Vrf routes being filtered\n"
+ "Specify route map\n"
+ "name of route-map\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ vpn_policy_direction_t dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ afi_t afi;
+ int idx = 0;
+ int yes = 1;
+ struct bgp *bgp_default;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = 0;
+
+ afi = vpn_policy_getafi(vty, bgp, true);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ bgp_default = bgp_get_default();
+ if (!bgp_default) {
+ int32_t ret;
+ as_t as = bgp->as;
+
+ /* Auto-create assuming the same AS */
+ ret = bgp_get(&bgp_default, &as, NULL,
+ BGP_INSTANCE_TYPE_DEFAULT);
+
+ if (ret) {
+ vty_out(vty,
+ "VRF default is not configured as a bgp instance\n");
+ return CMD_WARNING;
+ }
+ }
+
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+ if (yes) {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_str);
+ bgp->vpn_policy[afi].rmap[dir] =
+ route_map_lookup_by_name(rmap_str);
+ if (!bgp->vpn_policy[afi].rmap[dir])
+ return CMD_SUCCESS;
+ } else {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] = NULL;
+ bgp->vpn_policy[afi].rmap[dir] = NULL;
+ }
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(af_import_vrf_route_map, af_no_import_vrf_route_map_cmd,
+ "no import vrf route-map",
+ NO_STR
+ "Import routes from another VRF\n"
+ "Vrf routes being filtered\n"
+ "Specify route map\n")
+
+DEFPY (bgp_imexport_vrf,
+ bgp_imexport_vrf_cmd,
+ "[no] import vrf NAME$import_name",
+ NO_STR
+ "Import routes from another VRF\n"
+ "VRF to import from\n"
+ "The name of the VRF\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct listnode *node;
+ struct bgp *vrf_bgp, *bgp_default;
+ int32_t ret = 0;
+ as_t as = bgp->as;
+ bool remove = false;
+ int32_t idx = 0;
+ char *vname;
+ enum bgp_instance_type bgp_type = BGP_INSTANCE_TYPE_VRF;
+ safi_t safi;
+ afi_t afi;
+
+ if (argv_find(argv, argc, "no", &idx))
+ remove = true;
+
+ afi = vpn_policy_getafi(vty, bgp, true);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ safi = bgp_node_safi(vty);
+
+ if (((BGP_INSTANCE_TYPE_DEFAULT == bgp->inst_type)
+ && (strcmp(import_name, BGP_DEFAULT_NAME) == 0))
+ || (bgp->name && (strcmp(import_name, bgp->name) == 0))) {
+ vty_out(vty, "%% Cannot %s vrf %s into itself\n",
+ remove ? "unimport" : "import", import_name);
+ return CMD_WARNING;
+ }
+
+ bgp_default = bgp_get_default();
+ if (!bgp_default) {
+ /* Auto-create assuming the same AS */
+ ret = bgp_get(&bgp_default, &as, NULL,
+ BGP_INSTANCE_TYPE_DEFAULT);
+
+ if (ret) {
+ vty_out(vty,
+ "VRF default is not configured as a bgp instance\n");
+ return CMD_WARNING;
+ }
+ }
+
+ vrf_bgp = bgp_lookup_by_name(import_name);
+ if (!vrf_bgp) {
+ if (strcmp(import_name, BGP_DEFAULT_NAME) == 0)
+ vrf_bgp = bgp_default;
+ else
+ /* Auto-create assuming the same AS */
+ ret = bgp_get(&vrf_bgp, &as, import_name, bgp_type);
+
+ if (ret) {
+ vty_out(vty,
+ "VRF %s is not configured as a bgp instance\n",
+ import_name);
+ return CMD_WARNING;
+ }
+ }
+
+ if (remove) {
+ vrf_unimport_from_vrf(bgp, vrf_bgp, afi, safi);
+ } else {
+ /* Already importing from "import_vrf"? */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].import_vrf, node,
+ vname)) {
+ if (strcmp(vname, import_name) == 0)
+ return CMD_WARNING;
+ }
+
+ vrf_import_from_vrf(bgp, vrf_bgp, afi, safi);
+ }
+
+ return CMD_SUCCESS;
+}
+
/* This command is valid only in a bgp vrf instance or the default instance */
DEFPY (bgp_imexport_vpn,
bgp_imexport_vpn_cmd,
@@ -6653,7 +6803,6 @@ DEFPY (af_routetarget_import,
VTY_DECLVAR_CONTEXT(bgp, bgp);
int ret;
struct ecommunity *ecom = NULL;
- int doafi[AFI_MAX] = {0};
afi_t afi;
int idx = 0;
int yes = 1;
@@ -6661,9 +6810,10 @@ DEFPY (af_routetarget_import,
if (argv_find(argv, argc, "no", &idx))
yes = 0;
- ret = vpn_policy_getafi(vty, doafi);
- if (ret != CMD_SUCCESS)
- return ret;
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
if (yes) {
if (!argv_find(argv, argc, "RTLIST", &idx)) {
vty_out(vty, "%% Missing RTLIST\n");
@@ -6673,24 +6823,20 @@ DEFPY (af_routetarget_import,
if (ret != CMD_SUCCESS)
return ret;
}
- for (afi = 0; afi < AFI_MAX; ++afi) {
- if (!doafi[afi])
- continue;
- if (yes) {
- if (bgp->vpn_policy[afi].import_redirect_rtlist)
- ecommunity_free(
- &bgp->vpn_policy[afi]
+
+ if (yes) {
+ if (bgp->vpn_policy[afi].import_redirect_rtlist)
+ ecommunity_free(&bgp->vpn_policy[afi]
.import_redirect_rtlist);
- bgp->vpn_policy[afi].import_redirect_rtlist =
- ecommunity_dup(ecom);
- } else {
- if (bgp->vpn_policy[afi].import_redirect_rtlist)
- ecommunity_free(
- &bgp->vpn_policy[afi]
+ bgp->vpn_policy[afi].import_redirect_rtlist =
+ ecommunity_dup(ecom);
+ } else {
+ if (bgp->vpn_policy[afi].import_redirect_rtlist)
+ ecommunity_free(&bgp->vpn_policy[afi]
.import_redirect_rtlist);
- bgp->vpn_policy[afi].import_redirect_rtlist = NULL;
- }
+ bgp->vpn_policy[afi].import_redirect_rtlist = NULL;
}
+
if (ecom)
ecommunity_free(&ecom);
@@ -10748,6 +10894,125 @@ DEFUN (show_ip_bgp_attr_info,
return CMD_SUCCESS;
}
+static int bgp_show_route_leak_vty(struct vty *vty, const char *name,
+ afi_t afi, safi_t safi)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ char *vname;
+ char buf1[INET6_ADDRSTRLEN];
+ char *ecom_str;
+ vpn_policy_direction_t dir;
+
+ if (name) {
+ bgp = bgp_lookup_by_name(name);
+ if (!bgp) {
+ vty_out(vty, "%% No such BGP instance exist\n");
+ return CMD_WARNING;
+ }
+ } else {
+ bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty,
+ "%% Default BGP instance does not exist\n");
+ return CMD_WARNING;
+ }
+ }
+
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ vty_out(vty,
+ "This VRF is not importing %s routes from any other VRF\n",
+ afi_safi_print(afi, safi));
+ } else {
+ vty_out(vty,
+ "This VRF is importing %s routes from the following VRFs:\n",
+ afi_safi_print(afi, safi));
+ for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].import_vrf, node,
+ vname)) {
+ vty_out(vty, " %s\n", vname);
+ }
+ dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ ecom_str = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[dir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, "Import RT(s): %s\n", ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ vty_out(vty,
+ "This VRF is not exporting %s routes to any other VRF\n",
+ afi_safi_print(afi, safi));
+ } else {
+ vty_out(vty,
+ "This VRF is exporting %s routes to the following VRFs:\n",
+ afi_safi_print(afi, safi));
+ for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].export_vrf, node,
+ vname)) {
+ vty_out(vty, " %s\n", vname);
+ }
+ vty_out(vty, "RD: %s\n",
+ prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd,
+ buf1, RD_ADDRSTRLEN));
+ dir = BGP_VPN_POLICY_DIR_TOVPN;
+ ecom_str = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[dir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, "Emport RT: %s\n", ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "show [ip] bgp route-leak" command. */
+DEFUN (show_ip_bgp_route_leak,
+ show_ip_bgp_route_leak_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] route-leak",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR
+ "Route leaking information\n")
+{
+ char *vrf = NULL;
+ afi_t afi = AFI_MAX;
+ safi_t safi = SAFI_MAX;
+
+ int idx = 0;
+
+ /* show [ip] bgp */
+ if (argv_find(argv, argc, "ip", &idx)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ }
+ /* [vrf VIEWVRFNAME] */
+ if (argv_find(argv, argc, "view", &idx)) {
+ vty_out(vty,
+ "%% This command is not applicable to BGP views\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "vrf", &idx))
+ vrf = argv[++idx]->arg;
+ /* ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] */
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ argv_find_and_parse_safi(argv, argc, &idx, &safi);
+ }
+
+ if (!((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST)) {
+ vty_out(vty,
+ "%% This command is applicable only for unicast ipv4|ipv6\n");
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route_leak_vty(vty, vrf, afi, safi);
+}
+
static void bgp_show_all_instances_updgrps_vty(struct vty *vty, afi_t afi,
safi_t safi)
{
@@ -11744,6 +12009,17 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
{
int indent = 2;
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN])
+ vty_out(vty, "%*simport vrf route-map %s\n", indent, "",
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]);
+
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT))
+ return;
+
if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
@@ -11805,16 +12081,12 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
XFREE(MTYPE_ECOMMUNITY_STR, b);
}
}
- if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) {
- vty_out(vty, "%*sroute-map vpn import %s\n", indent, "",
- bgp->vpn_policy[afi]
- .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]);
- }
- if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]) {
+
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN])
vty_out(vty, "%*sroute-map vpn export %s\n", indent, "",
bgp->vpn_policy[afi]
.rmap_name[BGP_VPN_POLICY_DIR_TOVPN]);
- }
+
if (bgp->vpn_policy[afi].import_redirect_rtlist) {
char *b = ecommunity_ecom2str(
bgp->vpn_policy[afi]
@@ -11881,7 +12153,6 @@ static void bgp_ac_neighbor(vector comps, struct cmd_token *token)
{
struct bgp *bgp;
struct peer *peer;
- struct peer_group *group;
struct listnode *lnbgp, *lnpeer;
for (ALL_LIST_ELEMENTS_RO(bm->bgp, lnbgp, bgp)) {
@@ -11905,11 +12176,6 @@ static void bgp_ac_neighbor(vector comps, struct cmd_token *token)
vector_set(comps, XSTRDUP(MTYPE_COMPLETION, name));
}
-
- if (token->type == VARIABLE_TKN)
- for (ALL_LIST_ELEMENTS_RO(bgp->group, lnpeer, group))
- vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
- group->name));
}
}
@@ -11919,9 +12185,27 @@ static const struct cmd_variable_handler bgp_var_neighbor[] = {
{.varname = "peer", .completions = bgp_ac_neighbor},
{.completions = NULL}};
+static void bgp_ac_peergroup(vector comps, struct cmd_token *token)
+{
+ struct bgp *bgp;
+ struct peer_group *group;
+ struct listnode *lnbgp, *lnpeer;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, lnbgp, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->group, lnpeer, group))
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
+ group->name));
+ }
+}
+
+static const struct cmd_variable_handler bgp_var_peergroup[] = {
+ {.tokenname = "PGNAME", .completions = bgp_ac_peergroup},
+ {.completions = NULL} };
+
void bgp_vty_init(void)
{
cmd_variable_handler_register(bgp_var_neighbor);
+ cmd_variable_handler_register(bgp_var_peergroup);
/* Install bgp top node. */
install_node(&bgp_node, bgp_config_write);
@@ -12303,6 +12587,8 @@ void bgp_vty_init(void)
install_element(BGP_VPNV4_NODE, &no_neighbor_nexthop_self_cmd);
install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_cmd);
/* "neighbor next-hop-self force" commands. */
install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd);
@@ -13019,6 +13305,8 @@ void bgp_vty_init(void)
install_element(VIEW_NODE, &show_ip_bgp_lcommunity_info_cmd);
/* "show [ip] bgp attribute-info" commands. */
install_element(VIEW_NODE, &show_ip_bgp_attr_info_cmd);
+ /* "show [ip] bgp route-leak" command */
+ install_element(VIEW_NODE, &show_ip_bgp_route_leak_cmd);
/* "redistribute" commands. */
install_element(BGP_NODE, &bgp_redistribute_ipv4_hidden_cmd);
@@ -13063,6 +13351,9 @@ void bgp_vty_init(void)
install_element(BGP_IPV4_NODE, &bgp_imexport_vpn_cmd);
install_element(BGP_IPV6_NODE, &bgp_imexport_vpn_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_imexport_vrf_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_imexport_vrf_cmd);
+
/* ttl_security commands */
install_element(BGP_NODE, &neighbor_ttl_security_cmd);
install_element(BGP_NODE, &no_neighbor_ttl_security_cmd);
@@ -13093,6 +13384,8 @@ void bgp_vty_init(void)
install_element(BGP_IPV6_NODE, &af_rt_vpn_imexport_cmd);
install_element(BGP_IPV4_NODE, &af_route_map_vpn_imexport_cmd);
install_element(BGP_IPV6_NODE, &af_route_map_vpn_imexport_cmd);
+ install_element(BGP_IPV4_NODE, &af_import_vrf_route_map_cmd);
+ install_element(BGP_IPV6_NODE, &af_import_vrf_route_map_cmd);
install_element(BGP_IPV4_NODE, &af_routetarget_import_cmd);
install_element(BGP_IPV6_NODE, &af_routetarget_import_cmd);
@@ -13107,6 +13400,8 @@ void bgp_vty_init(void)
install_element(BGP_IPV6_NODE, &af_no_rt_vpn_imexport_cmd);
install_element(BGP_IPV4_NODE, &af_no_route_map_vpn_imexport_cmd);
install_element(BGP_IPV6_NODE, &af_no_route_map_vpn_imexport_cmd);
+ install_element(BGP_IPV4_NODE, &af_no_import_vrf_route_map_cmd);
+ install_element(BGP_IPV6_NODE, &af_no_import_vrf_route_map_cmd);
}
#include "memory.h"
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 9e033c6309..4a909d8cdc 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -56,6 +56,7 @@
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_pbr.h"
/* All information about zebra. */
struct zclient *zclient = NULL;
@@ -583,13 +584,20 @@ static int zebra_read_route(int command, struct zclient *zclient,
char buf[2][PREFIX_STRLEN];
prefix2str(&api.prefix, buf[0], sizeof(buf[0]));
- inet_ntop(api.prefix.family, &nexthop, buf[1], sizeof(buf[1]));
- zlog_debug(
- "Rx route %s VRF %u %s[%d] %s "
- "nexthop %s metric %u tag %" ROUTE_TAG_PRI,
- (add) ? "add" : "delete", vrf_id,
- zebra_route_string(api.type), api.instance, buf[0],
- buf[1], api.metric, api.tag);
+ if (add) {
+ inet_ntop(api.prefix.family, &nexthop, buf[1],
+ sizeof(buf[1]));
+ zlog_debug(
+ "Rx route ADD VRF %u %s[%d] %s nexthop %s (type %d if %u) metric %u tag %" ROUTE_TAG_PRI,
+ vrf_id, zebra_route_string(api.type),
+ api.instance, buf[0], buf[1], nhtype,
+ ifindex, api.metric, api.tag);
+ } else {
+ zlog_debug(
+ "Rx route DEL VRF %u %s[%d] %s",
+ vrf_id, zebra_route_string(api.type),
+ api.instance, buf[0]);
+ }
}
return 0;
@@ -906,28 +914,42 @@ int bgp_nexthop_set(union sockunion *local, union sockunion *remote,
return 0;
}
-static struct in6_addr *bgp_info_to_ipv6_nexthop(struct bgp_info *info)
+static struct in6_addr *bgp_info_to_ipv6_nexthop(struct bgp_info *info,
+ ifindex_t *ifindex)
{
struct in6_addr *nexthop = NULL;
/* Only global address nexthop exists. */
- if (info->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL)
+ if (info->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
nexthop = &info->attr->mp_nexthop_global;
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = info->attr->nh_ifindex;
+
+ }
/* If both global and link-local address present. */
if (info->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
/* Check if route-map is set to prefer global over link-local */
- if (info->attr->mp_nexthop_prefer_global)
+ if (info->attr->mp_nexthop_prefer_global) {
nexthop = &info->attr->mp_nexthop_global;
- else {
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = info->attr->nh_ifindex;
+ } else {
/* Workaround for Cisco's nexthop bug. */
if (IN6_IS_ADDR_UNSPECIFIED(
&info->attr->mp_nexthop_global)
- && info->peer->su_remote->sa.sa_family == AF_INET6)
+ && info->peer->su_remote->sa.sa_family
+ == AF_INET6) {
nexthop =
&info->peer->su_remote->sin6.sin6_addr;
- else
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = info->peer->nexthop.ifp
+ ->ifindex;
+ } else {
nexthop = &info->attr->mp_nexthop_local;
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = info->attr->nh_lla_ifindex;
+ }
}
}
@@ -958,13 +980,16 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p,
}
if (p->family == AF_INET6) {
char buf[2][INET6_ADDRSTRLEN];
+ ifindex_t ifindex;
+ struct in6_addr *nexthop;
+
+ nexthop = bgp_info_to_ipv6_nexthop(info, &ifindex);
zlog_debug(
"Zebra rmap deny: IPv6 route %s/%d nexthop %s",
inet_ntop(AF_INET6, &p->u.prefix6, buf[0],
sizeof(buf[0])),
p->prefixlen,
- inet_ntop(AF_INET6,
- bgp_info_to_ipv6_nexthop(info),
+ inet_ntop(AF_INET6, nexthop,
buf[1], sizeof(buf[1])));
}
}
@@ -973,6 +998,9 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p,
static struct thread *bgp_tm_thread_connect;
static bool bgp_tm_status_connected;
+static bool bgp_tm_chunk_obtained;
+#define BGP_FLOWSPEC_TABLE_CHUNK 100000
+static uint32_t bgp_tm_min, bgp_tm_max, bgp_tm_chunk_size;
static int bgp_zebra_tm_connect(struct thread *t)
{
@@ -993,12 +1021,27 @@ static int bgp_zebra_tm_connect(struct thread *t)
if (!bgp_tm_status_connected)
zlog_debug("Connecting to table manager. Success");
bgp_tm_status_connected = true;
+ if (!bgp_tm_chunk_obtained) {
+ if (bgp_zebra_get_table_range(bgp_tm_chunk_size,
+ &bgp_tm_min,
+ &bgp_tm_max) >= 0)
+ bgp_tm_chunk_obtained = true;
+ }
}
thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay,
&bgp_tm_thread_connect);
return 0;
}
+uint32_t bgp_zebra_tm_get_id(void)
+{
+ static int table_id;
+
+ if (!bgp_tm_chunk_obtained)
+ return ++table_id;
+ return bgp_tm_min++;
+}
+
void bgp_zebra_init_tm_connect(void)
{
int delay = 1;
@@ -1008,6 +1051,9 @@ void bgp_zebra_init_tm_connect(void)
if (bgp_tm_thread_connect != NULL)
return;
bgp_tm_status_connected = false;
+ bgp_tm_chunk_obtained = false;
+ bgp_tm_min = bgp_tm_max = 0;
+ bgp_tm_chunk_size = BGP_FLOWSPEC_TABLE_CHUNK;
thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay,
&bgp_tm_thread_connect);
}
@@ -1029,6 +1075,91 @@ int bgp_zebra_get_table_range(uint32_t chunk_size,
return 0;
}
+static int update_ipv4nh_for_route_install(int nh_othervrf,
+ struct in_addr *nexthop,
+ struct attr *attr,
+ bool is_evpn,
+ struct zapi_nexthop *api_nh)
+{
+ api_nh->gate.ipv4 = *nexthop;
+
+ /* Need to set fields appropriately for EVPN routes imported into
+ * a VRF (which are programmed as onlink on l3-vni SVI) as well as
+ * connected routes leaked into a VRF.
+ */
+ if (is_evpn)
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else if (nh_othervrf &&
+ api_nh->gate.ipv4.s_addr == INADDR_ANY) {
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ api_nh->ifindex = attr->nh_ifindex;
+ } else
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ return 1;
+}
+
+static int update_ipv6nh_for_route_install(int nh_othervrf,
+ struct in6_addr *nexthop,
+ ifindex_t ifindex,
+ struct bgp_info *ri,
+ struct bgp_info *best_ri,
+ bool is_evpn,
+ struct zapi_nexthop *api_nh)
+{
+ struct attr *attr;
+
+ attr = ri->attr;
+
+ if (is_evpn)
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ else if (nh_othervrf) {
+ if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ api_nh->ifindex = attr->nh_ifindex;
+ } else if (IN6_IS_ADDR_LINKLOCAL(nexthop)) {
+ if (ifindex == 0)
+ return 0;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ api_nh->ifindex = ifindex;
+ } else {
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ api_nh->ifindex = 0;
+ }
+ } else {
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop)) {
+ if (ri == best_ri &&
+ attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ if (ri->peer->nexthop.ifp)
+ ifindex = ri->peer->nexthop.ifp
+ ->ifindex;
+ if (!ifindex) {
+ if (ri->peer->conf_if)
+ ifindex = ri->peer->ifp->ifindex;
+ else if (ri->peer->ifname)
+ ifindex = ifname2ifindex(
+ ri->peer->ifname,
+ ri->peer->bgp->vrf_id);
+ else if (ri->peer->nexthop.ifp)
+ ifindex = ri->peer->nexthop.ifp
+ ->ifindex;
+ }
+
+ if (ifindex == 0)
+ return 0;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ api_nh->ifindex = ifindex;
+ } else {
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ api_nh->ifindex = 0;
+ }
+ }
+ api_nh->gate.ipv6 = *nexthop;
+
+ return 1;
+}
+
void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
struct bgp_info *info, struct bgp *bgp, afi_t afi,
safi_t safi)
@@ -1049,6 +1180,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
mpls_label_t label;
int nh_othervrf = 0;
char buf_prefix[PREFIX_STRLEN]; /* filled in if we are debugging */
+ bool is_evpn = false;
+ int nh_updated;
/* Don't try to install if we're not connected to Zebra or Zebra doesn't
* know of this instance.
@@ -1062,6 +1195,10 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (bgp_debug_zebra(p))
prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix));
+ if (safi == SAFI_FLOWSPEC)
+ return bgp_pbr_update_entry(bgp, &rn->p,
+ info, afi, safi, true);
+
/*
* vrf leaking support (will have only one nexthop)
*/
@@ -1070,7 +1207,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
/* Make Zebra API structure. */
memset(&api, 0, sizeof(api));
- memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
@@ -1082,28 +1218,23 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (info->type == ZEBRA_ROUTE_BGP
&& info->sub_type == BGP_ROUTE_IMPORTED) {
- struct bgp_info *bi;
-
- /*
- * Look at parent chain for peer sort
- */
- for (bi = info; bi->extra && bi->extra->parent;
- bi = bi->extra->parent) {
-
- peer = ((struct bgp_info *)(bi->extra->parent))->peer;
- }
+ /* Obtain peer from parent */
+ if (info->extra && info->extra->parent)
+ peer = ((struct bgp_info *)(info->extra->parent))->peer;
}
tag = info->attr->tag;
- /* When we create an aggregate route we must also install a Null0 route
- * in
- * the RIB */
+ /*
+ * When we create an aggregate route we must also install a
+ * Null0 route in the RIB
+ */
if (info->sub_type == BGP_ROUTE_AGGREGATE)
zapi_route_set_blackhole(&api, BLACKHOLE_NULL);
/* If the route's source is EVPN, flag as such. */
- if (is_route_parent_evpn(info))
+ is_evpn = is_route_parent_evpn(info);
+ if (is_evpn)
SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
@@ -1141,8 +1272,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
api_nh->vrf_id = nh_othervrf ? info->extra->bgp_orig->vrf_id
: bgp->vrf_id;
if (nh_family == AF_INET) {
- struct in_addr *nexthop;
-
if (bgp_debug_zebra(&api.prefix)) {
if (mpinfo->extra) {
zlog_debug(
@@ -1158,17 +1287,11 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
}
}
- if (bgp->table_map[afi][safi].name || nh_othervrf) {
+ if (bgp->table_map[afi][safi].name) {
/* Copy info and attributes, so the route-map
apply doesn't modify the BGP route info. */
local_attr = *mpinfo->attr;
mpinfo_cp->attr = &local_attr;
- if (nh_othervrf) {
- /* allow route-map to modify */
- local_attr.nexthop =
- info->extra->nexthop_orig.u
- .prefix4;
- }
}
if (bgp->table_map[afi][safi].name) {
@@ -1185,35 +1308,19 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
}
}
- nexthop = &mpinfo_cp->attr->nexthop;
- api_nh->gate.ipv4 = *nexthop;
-
- /* EVPN type-2 routes are
- programmed as onlink on l3-vni SVI
- */
- if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
- api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- else
- api_nh->type = NEXTHOP_TYPE_IPV4;
+ nh_updated = update_ipv4nh_for_route_install(
+ nh_othervrf,
+ &mpinfo_cp->attr->nexthop,
+ mpinfo_cp->attr, is_evpn, api_nh);
} else {
ifindex_t ifindex;
struct in6_addr *nexthop;
- ifindex = 0;
-
- if (bgp->table_map[afi][safi].name || nh_othervrf) {
+ if (bgp->table_map[afi][safi].name) {
/* Copy info and attributes, so the route-map
apply doesn't modify the BGP route info. */
local_attr = *mpinfo->attr;
mpinfo_cp->attr = &local_attr;
- if (nh_othervrf) {
- /* allow route-map to modify */
- local_attr.mp_nexthop_global =
- info->extra->nexthop_orig.u
- .prefix6;
- local_attr.mp_nexthop_len =
- BGP_ATTR_NHLEN_IPV6_GLOBAL;
- }
}
if (bgp->table_map[afi][safi].name) {
@@ -1234,39 +1341,17 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
tag = mpinfo_cp->attr->tag;
}
}
- nexthop = bgp_info_to_ipv6_nexthop(mpinfo_cp);
-
- if ((mpinfo == info)
- && mpinfo->attr->mp_nexthop_len
- == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
- if (mpinfo->peer->nexthop.ifp)
- ifindex = mpinfo->peer->nexthop.ifp
- ->ifindex;
-
- if (!ifindex) {
- if (mpinfo->peer->conf_if)
- ifindex = mpinfo->peer->ifp->ifindex;
- else if (mpinfo->peer->ifname)
- ifindex = ifname2ifindex(
- mpinfo->peer->ifname,
- bgp->vrf_id);
- else if (mpinfo->peer->nexthop.ifp)
- ifindex = mpinfo->peer->nexthop.ifp
- ->ifindex;
- }
-
- if (IN6_IS_ADDR_LINKLOCAL(nexthop)) {
- if (ifindex == 0)
- continue;
- } else
- ifindex = 0;
-
- api_nh->gate.ipv6 = *nexthop;
- api_nh->ifindex = ifindex;
- api_nh->type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX
- : NEXTHOP_TYPE_IPV6;
+ nexthop = bgp_info_to_ipv6_nexthop(mpinfo_cp,
+ &ifindex);
+ nh_updated = update_ipv6nh_for_route_install(
+ nh_othervrf, nexthop, ifindex,
+ mpinfo, info, is_evpn, api_nh);
}
+ /* Did we get proper nexthop info to update zebra? */
+ if (!nh_updated)
+ continue;
+
if (mpinfo->extra
&& bgp_is_valid_label(&mpinfo->extra->label[0])
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
@@ -1276,9 +1361,12 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
api_nh->label_num = 1;
api_nh->labels[0] = label;
}
+ memcpy(&api_nh->rmac, &(mpinfo->attr->rmac),
+ sizeof(struct ethaddr));
valid_nh_count++;
}
+
/* if this is a evpn route we don't have to include the label */
if (has_valid_label && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)))
SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
@@ -1314,20 +1402,25 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
for (i = 0; i < api.nexthop_num; i++) {
api_nh = &api.nexthops[i];
- if (api_nh->type == NEXTHOP_TYPE_IPV4)
- nh_family = AF_INET;
- else
- nh_family = AF_INET6;
- inet_ntop(nh_family, &api_nh->gate, nh_buf,
- sizeof(nh_buf));
+ if (api_nh->type == NEXTHOP_TYPE_IFINDEX)
+ nh_buf[0] = '\0';
+ else {
+ if (api_nh->type == NEXTHOP_TYPE_IPV4)
+ nh_family = AF_INET;
+ else
+ nh_family = AF_INET6;
+ inet_ntop(nh_family, &api_nh->gate, nh_buf,
+ sizeof(nh_buf));
+ }
label_buf[0] = '\0';
if (has_valid_label
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
sprintf(label_buf, "label %u",
api_nh->labels[0]);
- zlog_debug(" nhop [%d]: %s %s", i + 1, nh_buf,
- label_buf);
+ zlog_debug(" nhop [%d]: %s if %u VRF %u %s",
+ i + 1, nh_buf, api_nh->ifindex,
+ api_nh->vrf_id, label_buf);
}
}
@@ -1379,6 +1472,7 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info,
struct bgp *bgp, safi_t safi)
{
struct zapi_route api;
+ struct peer *peer;
/* Don't try to install if we're not connected to Zebra or Zebra doesn't
* know of this instance.
@@ -1386,8 +1480,13 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info,
if (!bgp_install_info_to_zebra(bgp))
return;
+ if (safi == SAFI_FLOWSPEC) {
+ peer = info->peer;
+ return bgp_pbr_update_entry(peer->bgp, p,
+ info, AFI_IP, safi, false);
+ }
+
memset(&api, 0, sizeof(api));
- memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
@@ -1800,7 +1899,7 @@ int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni)
zclient_create_header(s, ZEBRA_ADVERTISE_DEFAULT_GW, bgp->vrf_id);
stream_putc(s, advertise);
- stream_put3(s, vni);
+ stream_putl(s, vni);
stream_putw_at(s, 0, stream_get_endp(s));
return zclient_send_message(zclient);
@@ -1828,6 +1927,271 @@ int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise)
return zclient_send_message(zclient);
}
+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 bgp_pbr_action *bgp_pbra;
+ ifindex_t ifi;
+
+ if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
+ &ifi, &note))
+ return -1;
+
+ bgp_pbra = bgp_pbr_action_rule_lookup(vrf_id, unique);
+ if (!bgp_pbra) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP rule (%u)",
+ __PRETTY_FUNCTION__, unique);
+ return 0;
+ }
+
+ switch (note) {
+ case ZAPI_RULE_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received RULE_FAIL_INSTALL",
+ __PRETTY_FUNCTION__);
+ bgp_pbra->installed = false;
+ bgp_pbra->install_in_progress = false;
+ break;
+ case ZAPI_RULE_INSTALLED:
+ bgp_pbra->installed = true;
+ bgp_pbra->install_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received RULE_INSTALLED",
+ __PRETTY_FUNCTION__);
+ break;
+ case ZAPI_RULE_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received RULE REMOVED",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+
+ return 0;
+}
+
+static int ipset_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ uint32_t unique;
+ enum zapi_ipset_notify_owner note;
+ struct bgp_pbr_match *bgp_pbim;
+
+ if (!zapi_ipset_notify_decode(zclient->ibuf,
+ &unique,
+ &note))
+ return -1;
+
+ bgp_pbim = bgp_pbr_match_ipset_lookup(vrf_id, unique);
+ if (!bgp_pbim) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP match (%u)",
+ __PRETTY_FUNCTION__, unique);
+ return 0;
+ }
+
+ switch (note) {
+ case ZAPI_IPSET_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_FAIL_INSTALL",
+ __PRETTY_FUNCTION__);
+ bgp_pbim->installed = false;
+ bgp_pbim->install_in_progress = false;
+ break;
+ case ZAPI_IPSET_INSTALLED:
+ bgp_pbim->installed = true;
+ bgp_pbim->install_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_INSTALLED",
+ __PRETTY_FUNCTION__);
+ break;
+ case ZAPI_IPSET_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET REMOVED",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+
+ return 0;
+}
+
+static int ipset_entry_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ uint32_t unique;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+ enum zapi_ipset_entry_notify_owner note;
+ struct bgp_pbr_match_entry *bgp_pbime;
+
+ if (!zapi_ipset_entry_notify_decode(
+ zclient->ibuf,
+ &unique,
+ ipset_name,
+ &note))
+ return -1;
+ bgp_pbime = bgp_pbr_match_ipset_entry_lookup(vrf_id,
+ ipset_name,
+ unique);
+ if (!bgp_pbime) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP match entry (%u)",
+ __PRETTY_FUNCTION__, unique);
+ return 0;
+ }
+
+ switch (note) {
+ case ZAPI_IPSET_ENTRY_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_ENTRY_FAIL_INSTALL",
+ __PRETTY_FUNCTION__);
+ bgp_pbime->installed = false;
+ bgp_pbime->install_in_progress = false;
+ break;
+ case ZAPI_IPSET_ENTRY_INSTALLED:
+ bgp_pbime->installed = true;
+ bgp_pbime->install_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_ENTRY_INSTALLED",
+ __PRETTY_FUNCTION__);
+ break;
+ case ZAPI_IPSET_ENTRY_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_ENTRY_REMOVED",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+ return 0;
+}
+
+static int iptable_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ uint32_t unique;
+ enum zapi_iptable_notify_owner note;
+ struct bgp_pbr_match *bgpm;
+
+ if (!zapi_iptable_notify_decode(
+ zclient->ibuf,
+ &unique,
+ &note))
+ return -1;
+ bgpm = bgp_pbr_match_iptable_lookup(vrf_id, unique);
+ if (!bgpm) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP iptable (%u)",
+ __PRETTY_FUNCTION__, unique);
+ return 0;
+ }
+ switch (note) {
+ case ZAPI_IPTABLE_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPTABLE_FAIL_INSTALL",
+ __PRETTY_FUNCTION__);
+ bgpm->installed_in_iptable = false;
+ bgpm->install_iptable_in_progress = false;
+ break;
+ case ZAPI_IPTABLE_INSTALLED:
+ bgpm->installed_in_iptable = true;
+ bgpm->install_iptable_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPTABLE_INSTALLED",
+ __PRETTY_FUNCTION__);
+ bgpm->action->refcnt++;
+ break;
+ case ZAPI_IPTABLE_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPTABLE REMOVED",
+ __PRETTY_FUNCTION__);
+ break;
+ }
+ return 0;
+}
+
+static void bgp_encode_pbr_rule_action(struct stream *s,
+ struct bgp_pbr_action *pbra)
+{
+ struct prefix any;
+
+ stream_putl(s, 0); /* seqno unused */
+ stream_putl(s, 0); /* ruleno unused */
+
+ stream_putl(s, pbra->unique);
+
+ memset(&any, 0, sizeof(any));
+ any.family = AF_INET;
+ stream_putc(s, any.family);
+ stream_putc(s, any.prefixlen);
+ stream_put(s, &any.u.prefix, prefix_blen(&any));
+
+ stream_putw(s, 0); /* src port */
+
+ stream_putc(s, any.family);
+ stream_putc(s, any.prefixlen);
+ stream_put(s, &any.u.prefix, prefix_blen(&any));
+
+ stream_putw(s, 0); /* dst port */
+
+ stream_putl(s, pbra->fwmark); /* fwmark */
+
+ stream_putl(s, pbra->table_id);
+
+ stream_putl(s, 0); /* ifindex unused */
+}
+
+static void bgp_encode_pbr_ipset_match(struct stream *s,
+ struct bgp_pbr_match *pbim)
+{
+ stream_putl(s, pbim->unique);
+ stream_putl(s, pbim->type);
+
+ stream_put(s, pbim->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+
+
+}
+
+static void bgp_encode_pbr_ipset_entry_match(struct stream *s,
+ struct bgp_pbr_match_entry *pbime)
+{
+ stream_putl(s, pbime->unique);
+ /* check that back pointer is not null */
+ stream_put(s, pbime->backpointer->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ stream_putc(s, pbime->src.family);
+ stream_putc(s, pbime->src.prefixlen);
+ stream_put(s, &pbime->src.u.prefix, prefix_blen(&pbime->src));
+
+ stream_putc(s, pbime->dst.family);
+ stream_putc(s, pbime->dst.prefixlen);
+ stream_put(s, &pbime->dst.u.prefix, prefix_blen(&pbime->dst));
+}
+
+static void bgp_encode_pbr_iptable_match(struct stream *s,
+ struct bgp_pbr_action *bpa,
+ struct bgp_pbr_match *pbm)
+{
+ stream_putl(s, pbm->unique2);
+
+ stream_putl(s, pbm->type);
+
+ stream_putl(s, pbm->flags);
+
+ /* TODO: correlate with what is contained
+ * into bgp_pbr_action.
+ * currently only forward supported
+ */
+ if (bpa->nh.type == NEXTHOP_TYPE_BLACKHOLE)
+ stream_putl(s, ZEBRA_IPTABLES_DROP);
+ else
+ stream_putl(s, ZEBRA_IPTABLES_FORWARD);
+ stream_putl(s, bpa->fwmark);
+ stream_put(s, pbm->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+}
+
/* BGP has established connection with Zebra. */
static void bgp_zebra_connected(struct zclient *zclient)
{
@@ -2100,6 +2464,10 @@ void bgp_zebra_init(struct thread_master *master)
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;
+ zclient->rule_notify_owner = rule_notify_owner;
+ zclient->ipset_notify_owner = ipset_notify_owner;
+ zclient->ipset_entry_notify_owner = ipset_entry_notify_owner;
+ zclient->iptable_notify_owner = iptable_notify_owner;
}
void bgp_zebra_destroy(void)
@@ -2115,3 +2483,176 @@ int bgp_zebra_num_connects(void)
{
return zclient_num_connects;
}
+
+void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, bool install)
+{
+ struct stream *s;
+
+ if (pbra->install_in_progress)
+ return;
+ zlog_debug("%s: table %d fwmark %d %d", __PRETTY_FUNCTION__,
+ pbra->table_id, pbra->fwmark, install);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
+ VRF_DEFAULT);
+ stream_putl(s, 1); /* send one pbr action */
+
+ bgp_encode_pbr_rule_action(s, pbra);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if (!zclient_send_message(zclient) && install)
+ pbra->install_in_progress = true;
+}
+
+void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, bool install)
+{
+ struct stream *s;
+
+ if (pbrim->install_in_progress)
+ return;
+ zlog_debug("%s: name %s type %d %d", __PRETTY_FUNCTION__,
+ pbrim->ipset_name, pbrim->type, install);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_IPSET_CREATE :
+ ZEBRA_IPSET_DESTROY,
+ VRF_DEFAULT);
+
+ stream_putl(s, 1); /* send one pbr action */
+
+ bgp_encode_pbr_ipset_match(s, pbrim);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if (!zclient_send_message(zclient) && install)
+ pbrim->install_in_progress = true;
+}
+
+void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
+ bool install)
+{
+ struct stream *s;
+
+ if (pbrime->install_in_progress)
+ return;
+ zlog_debug("%s: name %s %d %d", __PRETTY_FUNCTION__,
+ pbrime->backpointer->ipset_name,
+ pbrime->unique, install);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_IPSET_ENTRY_ADD :
+ ZEBRA_IPSET_ENTRY_DELETE,
+ VRF_DEFAULT);
+
+ stream_putl(s, 1); /* send one pbr action */
+
+ bgp_encode_pbr_ipset_entry_match(s, pbrime);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if (!zclient_send_message(zclient) && install)
+ pbrime->install_in_progress = true;
+}
+
+void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
+ struct bgp_pbr_match *pbm,
+ bool install)
+{
+ struct stream *s;
+
+ if (pbm->install_iptable_in_progress)
+ return;
+ zlog_debug("%s: name %s type %d mark %d %d", __PRETTY_FUNCTION__,
+ pbm->ipset_name, pbm->type, pba->fwmark, install);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_IPTABLE_ADD :
+ ZEBRA_IPTABLE_DELETE,
+ VRF_DEFAULT);
+
+ bgp_encode_pbr_iptable_match(s, pba, pbm);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if (!zclient_send_message(zclient) && install) {
+ pbm->install_iptable_in_progress = true;
+ pba->refcnt++;
+ }
+}
+
+/* inject in table <table_id> a default route to:
+ * - if nexthop IP is present : to this nexthop
+ * - if vrf is different from local : to the matching VRF
+ */
+void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
+ afi_t afi, uint32_t table_id, bool announce)
+{
+ struct zapi_nexthop *api_nh;
+ struct zapi_route api;
+ struct prefix p;
+
+ if (!nh || nh->type != NEXTHOP_TYPE_IPV4
+ || nh->vrf_id == VRF_UNKNOWN)
+ return;
+ memset(&p, 0, sizeof(struct prefix));
+ /* default route */
+ if (afi != AFI_IP)
+ return;
+ p.family = AF_INET;
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = bgp->vrf_id;
+ api.type = ZEBRA_ROUTE_BGP;
+ api.safi = SAFI_UNICAST;
+ api.prefix = p;
+ api.tableid = table_id;
+ api.nexthop_num = 1;
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ api_nh = &api.nexthops[0];
+
+ /* redirect IP */
+ if (nh->gate.ipv4.s_addr) {
+ char buff[PREFIX_STRLEN];
+
+ api_nh->vrf_id = nh->vrf_id;
+ api_nh->gate.ipv4 = nh->gate.ipv4;
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN);
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_info("BGP: sending default route to %s table %d (redirect IP)",
+ buff, table_id);
+ zclient_route_send(announce ? ZEBRA_ROUTE_ADD
+ : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+ } else if (nh->vrf_id != bgp->vrf_id) {
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ vrf = vrf_lookup_by_id(nh->vrf_id);
+ if (!vrf)
+ return;
+ /* create default route with interface <VRF>
+ * with nexthop-vrf <VRF>
+ */
+ ifp = if_lookup_by_name_all_vrf(vrf->name);
+ if (!ifp)
+ return;
+ api_nh->vrf_id = nh->vrf_id;
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ api_nh->ifindex = ifp->ifindex;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_info("BGP: sending default route to %s table %d (redirect VRF)",
+ vrf->name, table_id);
+ zclient_route_send(announce ? ZEBRA_ROUTE_ADD
+ : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+ return;
+ }
+}
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
index 7263317b6f..7ac40fecff 100644
--- a/bgpd/bgp_zebra.h
+++ b/bgpd/bgp_zebra.h
@@ -25,6 +25,7 @@
extern void bgp_zebra_init(struct thread_master *master);
extern void bgp_zebra_init_tm_connect(void);
+extern uint32_t bgp_zebra_tm_get_id(void);
extern void bgp_zebra_destroy(void);
extern int bgp_zebra_get_table_range(uint32_t chunk_size,
uint32_t *start, uint32_t *end);
@@ -70,4 +71,20 @@ extern int bgp_zebra_advertise_all_vni(struct bgp *, int);
extern int bgp_zebra_num_connects(void);
+struct bgp_pbr_action;
+struct bgp_pbr_match;
+struct bgp_pbr_match_entry;
+extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra,
+ bool install);
+extern void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim,
+ bool install);
+extern void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
+ bool install);
+extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
+ struct bgp_pbr_match *pbm,
+ bool install);
+
+extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
+ afi_t afi, uint32_t table_id, bool announce);
+
#endif /* _QUAGGA_BGP_ZEBRA_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index df0f1bd19c..71707b6afa 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -44,6 +44,7 @@
#include "table.h"
#include "lib/json.h"
#include "frr_pthread.h"
+#include "bitfield.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_table.h"
@@ -82,6 +83,7 @@
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_pbr.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
@@ -1172,6 +1174,11 @@ struct peer *peer_new(struct bgp *bgp)
}
peer->orf_plist[afi][safi] = NULL;
}
+
+ /* set nexthop-unchanged for l2vpn evpn by default */
+ SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+
SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
/* Create buffers. */
@@ -2956,6 +2963,9 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent =
MPLS_LABEL_NONE;
+
+ bgp->vpn_policy[afi].import_vrf = list_new();
+ bgp->vpn_policy[afi].export_vrf = list_new();
}
if (name) {
bgp->name = XSTRDUP(MTYPE_BGP, name);
@@ -2997,7 +3007,12 @@ static struct bgp *bgp_create(as_t *as, const char *name,
QOBJ_REG(bgp, bgp);
update_bgp_group_init(bgp);
+
+ /* assign a unique rd id for auto derivation of vrf's RD */
+ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
+
bgp_evpn_init(bgp);
+ bgp_pbr_init(bgp);
return bgp;
}
@@ -3372,14 +3387,28 @@ void bgp_free(struct bgp *bgp)
rmap = &bgp->table_map[afi][safi];
if (rmap->name)
XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name);
+
+ /*
+ * Yes this is per AFI, but
+ * the list_delete_and_null nulls the pointer
+ * and we'll not leak anything on going down
+ * and the if test will fail on the second safi.
+ */
+ if (bgp->vpn_policy[afi].import_vrf)
+ list_delete_and_null(&bgp->vpn_policy[afi].import_vrf);
+ if (bgp->vpn_policy[afi].export_vrf)
+ list_delete_and_null(&bgp->vpn_policy[afi].export_vrf);
}
bgp_scan_finish(bgp);
bgp_address_destroy(bgp);
bgp_tip_hash_destroy(bgp);
- bgp_evpn_cleanup(bgp);
+ /* release the auto RD id */
+ bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
+ bgp_evpn_cleanup(bgp);
+ bgp_pbr_cleanup(bgp);
if (bgp->name)
XFREE(MTYPE_BGP, bgp->name);
if (bgp->name_pretty)
@@ -4048,6 +4077,35 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
return 0;
}
+ /*
+ * For EVPN we implicitly set the NEXTHOP_UNCHANGED flag,
+ * if we are setting/unsetting flags which conflict with this flag
+ * handle accordingly
+ */
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ if (set) {
+
+ /*
+ * if we are setting NEXTHOP_SELF, we need to unset the
+ * NEXTHOP_UNCHANGED flag
+ */
+ if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) ||
+ CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF))
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ } else {
+
+ /*
+ * if we are unsetting NEXTHOP_SELF, we need to set the
+ * NEXTHOP_UNCHANGED flag to reset the defaults for EVPN
+ */
+ if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) ||
+ CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF))
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ }
+ }
+
if (set)
SET_FLAG(peer->af_flags[afi][safi], flag);
else
@@ -5923,39 +5981,63 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
struct peer_group *group;
struct listnode *node, *nnode;
+ /* apply configuration and set flags */
SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
- peer->pmax[afi][safi] = max;
- peer->pmax_threshold[afi][safi] = threshold;
- peer->pmax_restart[afi][safi] = restart;
if (warning)
SET_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING);
else
UNSET_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING);
+ peer->pmax[afi][safi] = max;
+ peer->pmax_threshold[afi][safi] = threshold;
+ peer->pmax_restart[afi][safi] = restart;
+ /* if handling a peer-group, apply to all children */
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
group = peer->group;
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
- SET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX);
- peer->pmax[afi][safi] = max;
- peer->pmax_threshold[afi][safi] = threshold;
- peer->pmax_restart[afi][safi] = restart;
- if (warning)
+ /*
+ * If peer configuration is user-set, it overrides
+ * peer-group config.
+ */
+ if (!CHECK_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX)) {
SET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX_WARNING);
- else
- UNSET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX_WARNING);
+ PEER_FLAG_MAX_PREFIX);
+ peer->pmax[afi][safi] = max;
+ peer->pmax_threshold[afi][safi] = threshold;
+ peer->pmax_restart[afi][safi] = restart;
+ }
+ if (!CHECK_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING)) {
+ if (warning)
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ else
+ UNSET_FLAG(
+ peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ }
if ((peer->status == Established)
&& (peer->afc[afi][safi]))
bgp_maximum_prefix_overflow(peer, afi, safi, 1);
}
} else {
+ /* if not handling a peer-group, set the override flags */
if ((peer->status == Established) && (peer->afc[afi][safi]))
bgp_maximum_prefix_overflow(peer, afi, safi, 1);
+
+ SET_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX);
+
+ if (warning)
+ SET_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ else
+ UNSET_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
}
return 0;
@@ -5966,49 +6048,49 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
struct peer_group *group;
struct listnode *node, *nnode;
- /* apply peer-group config */
- if (peer_group_active(peer)) {
- if (CHECK_FLAG(peer->group->conf->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX))
- SET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX);
- else
- UNSET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX);
-
- if (CHECK_FLAG(peer->group->conf->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX_WARNING))
- SET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX_WARNING);
- else
- UNSET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX_WARNING);
-
- peer->pmax[afi][safi] = peer->group->conf->pmax[afi][safi];
- peer->pmax_threshold[afi][safi] =
- peer->group->conf->pmax_threshold[afi][safi];
- peer->pmax_restart[afi][safi] =
- peer->group->conf->pmax_restart[afi][safi];
- return 0;
- }
-
UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING);
peer->pmax[afi][safi] = 0;
peer->pmax_threshold[afi][safi] = 0;
peer->pmax_restart[afi][safi] = 0;
- if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ /* if not handling a peer-group, unset override flags */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ UNSET_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX);
+ UNSET_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ /* if peer is part of a peer-group, apply peer-group config */
+ if (peer_group_active(peer)) {
+ peer->pmax[afi][safi] =
+ peer->group->conf->pmax[afi][safi];
+ peer->pmax_threshold[afi][safi] =
+ peer->group->conf->pmax_threshold[afi][safi];
+ peer->pmax_restart[afi][safi] =
+ peer->group->conf->pmax_restart[afi][safi];
+ }
+
return 0;
+ }
+ /*
+ * If this peer is a peer-group, set all peers in the group unless they
+ * have overrides for our config.
+ */
group = peer->group;
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
- UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX);
- UNSET_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_MAX_PREFIX_WARNING);
- peer->pmax[afi][safi] = 0;
- peer->pmax_threshold[afi][safi] = 0;
- peer->pmax_restart[afi][safi] = 0;
+ if (!CHECK_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING))
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ if (!CHECK_FLAG(peer->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX)) {
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX);
+ peer->pmax[afi][safi] = 0;
+ peer->pmax_threshold[afi][safi] = 0;
+ peer->pmax_restart[afi][safi] = 0;
+ }
}
return 0;
}
@@ -7085,7 +7167,9 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
/* atribute-unchanged. */
if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
- || peer_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_UNCHANGED)
+ || (safi != SAFI_EVPN
+ && peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED))
|| peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
if (!peer_group_active(peer)
@@ -7194,6 +7278,16 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
vty_out(vty, " import vpn\n");
}
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ struct listnode *node;
+ char *name;
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].import_vrf, node,
+ name))
+ vty_out(vty, " import vrf %s\n", name);
+ }
}
vty_endframe(vty, " exit-address-family\n");
@@ -7525,6 +7619,7 @@ void bgp_master_init(struct thread_master *master)
bm->start_time = bgp_clock();
bm->t_rmap_update = NULL;
bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER;
+ bm->terminating = false;
bgp_process_queue_init();
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 680bac0214..1ad6a5c9c8 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -144,6 +144,7 @@ struct bgp_master {
/* dynamic mpls label allocation pool */
struct labelpool labelpool;
+ bool terminating; /* global flag that sigint terminate seen */
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp_master)
@@ -188,6 +189,18 @@ struct vpn_policy {
#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)
+
+ /*
+ * If we are importing another vrf into us keep a list of
+ * vrf names that are being imported into us.
+ */
+ struct list *import_vrf;
+
+ /*
+ * if we are being exported to another vrf keep a list of
+ * vrf names that we are being exported to.
+ */
+ struct list *export_vrf;
};
/*
@@ -342,16 +355,19 @@ struct bgp {
/* BGP Per AF flags */
uint16_t af_flags[AFI_MAX][SAFI_MAX];
-#define BGP_CONFIG_DAMPENING (1 << 0)
-#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 1)
-#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 2)
-
+#define BGP_CONFIG_DAMPENING (1 << 0)
/* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */
-#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1)
-#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2)
-#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3)
-#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4)
-
+#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1)
+#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4)
+/* import/export between address families */
+#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5)
+#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6)
+/* vrf-route leaking flags */
+#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7)
+#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8)
+#define BGP_DEFAULT_NAME "default"
/* Route table for next-hop lookup cache. */
struct bgp_table *nexthop_cache_table[AFI_MAX];
@@ -385,6 +401,25 @@ struct bgp {
/* Allocate MPLS labels */
uint8_t allocate_mpls_labels[AFI_MAX][SAFI_MAX];
+ /* Allocate hash entries to store policy routing information
+ * The hash are used to host pbr rules somewhere.
+ * Actually, pbr will only be used by flowspec
+ * those hash elements will have relationship together as
+ * illustrated in below diagram:
+ *
+ * pbr_action a <----- pbr_match i <--- pbr_match_entry 1..n
+ * <----- pbr_match j <--- pbr_match_entry 1..m
+ *
+ * - here in BGP structure, the list of match and actions will
+ * stand for the list of ipset sets, and table_ids in the kernel
+ * - the arrow above between pbr_match and pbr_action indicate
+ * that a backpointer permits match to find the action
+ * - the arrow betwen match_entry and match is a hash list
+ * contained in match, that lists the whole set of entries
+ */
+ struct hash *pbr_match_hash;
+ struct hash *pbr_action_hash;
+
/* timer to re-evaluate neighbor default-originate route-maps */
struct thread *t_rmap_def_originate_eval;
#define RMAP_DEFAULT_ORIGINATE_EVAL_TIMER 5
@@ -473,6 +508,9 @@ struct bgp {
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;
+ /* Automatically derived RD for this VRF */
+ struct prefix_rd vrf_prd_auto;
+
/* RD for this VRF */
struct prefix_rd vrf_prd;
@@ -799,7 +837,28 @@ struct peer {
/* NSF mode (graceful restart) */
uint8_t nsf[AFI_MAX][SAFI_MAX];
- /* Per AF configuration flags. */
+ /* Peer Per AF flags */
+ /*
+ * Parallel array to af_flags that indicates whether each flag
+ * originates from a peer-group or if it is config that is specific to
+ * this individual peer. If a flag is set independent of the
+ * peer-group the same bit should be set here. If this peer is a
+ * peer-group, this memory region should be all zeros. The assumption
+ * is that the default state for all flags is unset.
+ *
+ * Notes:
+ * - if a flag for an individual peer is unset, the corresponding
+ * override flag is unset and the peer is considered to be back in
+ * sync with the peer-group.
+ * - This does *not* contain the flag values, rather it contains
+ * whether the flag at the same position in af_flags is
+ * *peer-specific*.
+ */
+ uint32_t af_flags_override[AFI_MAX][SAFI_MAX];
+ /*
+ * Effective flags, computed by applying peer-group flags and then
+ * overriding with individual flags
+ */
uint32_t af_flags[AFI_MAX][SAFI_MAX];
#define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */
#define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
index 5fb44bb492..ccaa472092 100644
--- a/bgpd/rfapi/rfapi_vty.c
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -46,6 +46,7 @@
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_vnc_types.h"
+#include "bgpd/bgp_label.h"
#include "bgpd/rfapi/rfapi_import.h"
#include "bgpd/rfapi/rfapi_private.h"
@@ -431,8 +432,13 @@ void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p,
XFREE(MTYPE_ECOMMUNITY_STR, s);
}
- if (bi->extra != NULL)
- vty_out(vty, " label=%u", decode_label(&bi->extra->label[0]));
+ if (bi->extra != NULL) {
+ if (bi->extra->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK)
+ vty_out(vty, " label=VRF2VRF");
+ else
+ vty_out(vty, " label=%u",
+ decode_label(&bi->extra->label[0]));
+ }
if (!rfapiGetVncLifetime(bi->attr, &lifetime)) {
vty_out(vty, " life=%d", lifetime);
diff --git a/configure.ac b/configure.ac
index 53a80e790f..8b49295444 100755
--- a/configure.ac
+++ b/configure.ac
@@ -7,7 +7,7 @@
##
AC_PREREQ(2.60)
-AC_INIT(frr, 4.1-dev, [https://github.com/frrouting/frr/issues])
+AC_INIT(frr, 5.1-dev, [https://github.com/frrouting/frr/issues])
PACKAGE_URL="https://frrouting.org/"
AC_SUBST(PACKAGE_URL)
PACKAGE_FULLNAME="FRRouting"
@@ -232,7 +232,6 @@ else
fi
fi
AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"])
-AM_CONDITIONAL([SHARPD], [test "x$enable_dev_build" = "xyes"])
dnl always want these CFLAGS
AC_C_FLAG([-fno-omit-frame-pointer])
@@ -354,6 +353,10 @@ AC_ARG_ENABLE(isisd,
AS_HELP_STRING([--disable-isisd], [do not build isisd]))
AC_ARG_ENABLE(pimd,
AS_HELP_STRING([--disable-pimd], [do not build pimd]))
+AC_ARG_ENABLE(pbrd,
+ AS_HELP_STRING([--disable-pbrd], [do not build pbrd]))
+AC_ARG_ENABLE(sharpd,
+ AS_HELP_STRING([--enable-sharpd], [do not build sharpd]))
AC_ARG_ENABLE(bgp-announce,
AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))
AC_ARG_ENABLE(bgp-vnc,
@@ -1378,6 +1381,7 @@ 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")
+AM_CONDITIONAL(SHARPD, test "${enable_sharpd}" = "yes")
if test "${enable_bgp_announce}" = "no";then
AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra)
@@ -1909,8 +1913,13 @@ AC_MSG_RESULT($ac_cv_htonl_works)
AC_CONFIG_FILES([Makefile
bgpd/Makefile
vtysh/Makefile
- doc/Makefile tests/Makefile
- bgpd/rfp-example/rfptest/Makefile bgpd/rfp-example/librfp/Makefile
+ doc/Makefile
+ doc/user/Makefile
+ doc/manpages/Makefile
+ doc/developer/Makefile
+ tests/Makefile
+ bgpd/rfp-example/rfptest/Makefile
+ bgpd/rfp-example/librfp/Makefile
redhat/frr.spec
debianpkg/Makefile
debianpkg/changelog
diff --git a/debianpkg/README.deb_build.md b/debianpkg/README.deb_build.md
deleted file mode 100644
index c90ca13394..0000000000
--- a/debianpkg/README.deb_build.md
+++ /dev/null
@@ -1,127 +0,0 @@
-Building your own FRRouting Debian Package
-==========================================
-(Tested on Ubuntu 12.04, 14.04, 16.04, 17.10, 18.04, Debian 8 and 9)
-
-**Note:** If you try to build for a different distro, then it will most likely
-fail because of the missing backport. See debianpkg/backports/README about
-adding a new backport.
-
-1. Follow the package installation as outlined in doc/Building_on_XXXX.md
- (XXXX refers your OS Distribution) to install the required build packages
-
-2. Install the following additional packages:
-
- on Ubuntu 12.04, 14.04, 16.04, 17.10, Debian 8 and 9:
-
- apt-get install realpath equivs groff fakeroot debhelper devscripts
-
- on Ubuntu 18.04: (realpath is now part of preinstalled by coreutils)
-
- apt-get install equivs groff fakeroot debhelper devscripts
-
-3. Checkout FRR under a **unpriviledged** user account
-
- git clone https://github.com/frrouting/frr.git frr
- cd frr
- # git checkout <branch> - if different branch than master
-
-4. Run Bootstrap and make distribution tar.gz
-
- ./bootstrap.sh
- ./configure --with-pkg-extra-version=-MyDebPkgVersion
- make dist
-
- Note: configure parameters are not important for the Debian Package
- building - except the `with-pkg-extra-version` if you want to give the
- Debian Package a specific name to mark your own unoffical build
-
-5. Edit `debianpkg/rules` and set the configuration as needed
-
- Look for section `dh_auto_configure` to modify the configure
- options as needed. Options might be different between main `rules` and
- `backports/XXXX/debian/rules`. Please adjust as needed on all files
-
-6. Create backports debian sources
-
- Move the `debianpkg` to `debian` and create the backports
- (Debian requires to not ship a `debian` directory inside the source
- directory to avoid build conflicts with the reserved `debian` subdirectory
- name during the build)
-
- mv debianpkg debian
- make -f debian/rules backports
-
- This will create a `frr_*.orig.tar.gz` with the source (same as dist tar),
- and multiple `frr_*.debian.tar.xz` and `frr_*.dsc` for the debian package
- source on each backport supported distribution
-
-7. Create a new directory to build the package and populate with package src
-
- mkdir frrpkg
- cd frrpkg
- tar xf ~/frr/frr_*.orig.tar.gz
- cd frr*
- . /etc/os-release
- tar xf ~/frr/frr_*${ID}${VERSION_ID}*.debian.tar.xz
-
-8. Build Debian Package Dependencies and install them as needed
-
- sudo mk-build-deps --install debian/control
-
-9. Build Debian Package
-
- Building with standard options:
-
- debuild -b -uc -us
-
- Or change some options:
- (see `rules` file for available options)
-
- debuild --set-envvar=WANT_BGP_VNC=1 --set-envvar=WANT_CUMULUS_MODE=1 -b -uc -us
-
- To build with RPKI, download the librtr packages from
- https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact
- install librtr-dev on the build server and build the packages as
- debuild --set-envvar=WANT_RPKI=1 -b -uc -us
- RPKI packages have an additonal dependency of librtr0 which can be
- found at the same URL
-
-DONE.
-
-If all works correctly, then you should end up with the Debian packages under
-`frrpkg`. If distributed, please make sure you distribute it together with
-the sources (`frr_*.orig.tar.gz`, `frr_*.debian.tar.xz` and `frr_*.dsc`)
-
-
-Enabling daemons after installation of the package:
----------------------------------------------------
-
-1. Edit `/etc/frr/daemons` and enable required routing daemons (Zebra is
-probably needed for most deployments, so make sure to enable it.)
-
-2. Check your firewall / IPtables to make sure the routing protocols are
-allowed.
-
-3. Enable FRR at startup
-
- - On `init.d` based systems (Ubuntu 12.04)
-
- sudo update-rc.d frr defaults
-
- - On `systemd` based systems (Debian 8 and later, Ubuntu 14.04 and later)
-
- sudo systemctl enable frr
-
-4. Start/Restart the daemons (or reboot)
-
- - On `init.d` based systems (Ubuntu 12.04)
-
- sudo invoke-rc.d frr start
-
- - on `systemd` based systems (Debian 8 and later, Ubuntu 14.04 and later)
-
- sudo systemctl start frr
-
-
-Configuration is stored in `/etc/frr/*.conf` files and daemon selection
-is stored in `/etc/frr/daemons`.
diff --git a/debianpkg/backports/ubuntu14.04/debian/frr.postinst b/debianpkg/backports/ubuntu14.04/debian/frr.postinst
index b1d463a33d..5a14e510cd 100644
--- a/debianpkg/backports/ubuntu14.04/debian/frr.postinst
+++ b/debianpkg/backports/ubuntu14.04/debian/frr.postinst
@@ -18,7 +18,7 @@ chgrp ${frrvtygid} /etc/frr/vtysh*
chmod 644 /etc/frr/*
ENVIRONMENTFILE=/etc/environment
-if ! grep --quiet VTYSH_PAGER=/bin/cat ${ENVIRONMENTFILE}; then
+if ! egrep --quiet '^VTYSH_PAGER=' ${ENVIRONMENTFILE}; then
echo "VTYSH_PAGER=/bin/cat" >> ${ENVIRONMENTFILE}
fi
##################################################
diff --git a/debianpkg/frr-doc.docs b/debianpkg/frr-doc.docs
index 4720a3b920..d2218d00f9 100644
--- a/debianpkg/frr-doc.docs
+++ b/debianpkg/frr-doc.docs
@@ -1,6 +1,5 @@
AUTHORS
NEWS
README
-REPORTING-BUGS
doc/user/*.rst
doc/figures/*.png
diff --git a/debianpkg/frr.postinst b/debianpkg/frr.postinst
index 972f8c0500..32af741c98 100644
--- a/debianpkg/frr.postinst
+++ b/debianpkg/frr.postinst
@@ -19,7 +19,7 @@ chgrp ${frrvtygid} /etc/frr/vtysh*
chmod 644 /etc/frr/*
ENVIRONMENTFILE=/etc/environment
-if ! grep --quiet VTYSH_PAGER=/bin/cat ${ENVIRONMENTFILE}; then
+if ! egrep --quiet '^VTYSH_PAGER=' ${ENVIRONMENTFILE}; then
echo "VTYSH_PAGER=/bin/cat" >> ${ENVIRONMENTFILE}
fi
##################################################
diff --git a/doc/Makefile.am b/doc/Makefile.am
index dec6b53e0f..8fa057424a 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -14,7 +14,7 @@
.NOTPARALLEL:
SUBDIRS = manpages user
-AM_MAKEFLAGS = DESTDIR=${DESTDIR} infodir=${infodir} doczdir=${abs_srcdir}
+AM_MAKEFLAGS = DESTDIR=${DESTDIR} infodir=${infodir}
MANPAGE_BUILDDIR = manpages/_build/man
@@ -82,6 +82,10 @@ if EIGRPD
man_MANS += $(MANPAGE_BUILDDIR)/eigrpd.8
endif
+if SHARPD
+man_MANS += $(MANPAGE_BUILDDIR)/sharpd.8
+endif
+
# Automake is particular about manpages. It is aware of them and has some
# special facilities for handling them, but it assumes that manpages are always
# given in groff source and so these facilities are limited to simply
@@ -146,6 +150,7 @@ EXTRA_DIST = frr-sphinx.mk \
manpages/ripd.rst \
manpages/pbrd.rst \
manpages/ripngd.rst \
+ manpages/sharpd.rst \
manpages/vtysh.rst \
manpages/watchfrr.rst \
manpages/zebra.rst \
@@ -212,6 +217,7 @@ EXTRA_DIST = frr-sphinx.mk \
user/routemap.rst \
user/routeserver.rst \
user/rpki.rst \
+ user/sharp.rst \
user/snmp.rst \
user/snmptrap.rst \
user/Useful_Sysctl_Settings.md \
diff --git a/doc/developer/.gitignore b/doc/developer/.gitignore
index 0505537159..2e7d8573f1 100644
--- a/doc/developer/.gitignore
+++ b/doc/developer/.gitignore
@@ -1,3 +1,3 @@
/_templates
/_build
-!/Makefile
+!/Makefile.in
diff --git a/doc/developer/Makefile b/doc/developer/Makefile
deleted file mode 100644
index 9807a750bf..0000000000
--- a/doc/developer/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-include ../frr-sphinx.mk
diff --git a/doc/developer/Makefile.in b/doc/developer/Makefile.in
new file mode 100644
index 0000000000..76758f9242
--- /dev/null
+++ b/doc/developer/Makefile.in
@@ -0,0 +1,8 @@
+# This is necessary to support VPATH builds.
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# This variable is used as the documentation source location in frr-sphinx.mk
+SOURCESDIR = @srcdir@
+
+include @srcdir@/../frr-sphinx.mk
diff --git a/doc/developer/building-frr-on-openbsd6.rst b/doc/developer/building-frr-on-openbsd6.rst
index 1f3aec8d92..46db25a025 100644
--- a/doc/developer/building-frr-on-openbsd6.rst
+++ b/doc/developer/building-frr-on-openbsd6.rst
@@ -14,7 +14,7 @@ Add packages:
::
- pkg_add git autoconf-2.69p2 automake-1.15p0 libtool bison
+ pkg_add git autoconf-2.69p2 automake-1.15.1 libtool bison
pkg_add gmake gawk dejagnu openssl json-c py-test py-sphinx
Select Python2.7 as default (required for pytest)
diff --git a/doc/developer/building.rst b/doc/developer/building.rst
index 92fd1bb63a..051611a65d 100644
--- a/doc/developer/building.rst
+++ b/doc/developer/building.rst
@@ -1,3 +1,5 @@
+.. _building:
+
************
Building FRR
************
diff --git a/doc/developer/conf.py b/doc/developer/conf.py
index a3968b60ff..61253c4b2f 100644
--- a/doc/developer/conf.py
+++ b/doc/developer/conf.py
@@ -342,6 +342,14 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
+# contents of ../extra/frrlexer.py.
+# This is read here to support VPATH build. Since this section is execfile()'d
+# with the file location, we can safely use a relative path here to save the
+# contents of the lexer file for later use even if our relative path changes
+# due to VPATH.
+with open('../extra/frrlexer.py', 'rb') as lex:
+ frrlexerpy = lex.read()
+
# custom extensions here
def setup(app):
# object type for FRR CLI commands, can be extended to document parent CLI
@@ -357,5 +365,5 @@ def setup(app):
#
# frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer")
custom_namespace = {}
- exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace)
+ exec(frrlexerpy, custom_namespace)
lexers['frr'] = custom_namespace['FRRLexer']()
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 17a7e1c0ba..42192db637 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -1,11 +1,13 @@
-Welcome to FRR's documentation!
-===============================
+FRRouting Developer's Guide
+===========================
.. toctree::
:maxdepth: 2
workflow
building
+ packaging
+ process-architecture
library
bgpd
ospf
diff --git a/doc/developer/library.rst b/doc/developer/library.rst
index c5ce1f5982..f6efa33051 100644
--- a/doc/developer/library.rst
+++ b/doc/developer/library.rst
@@ -1,3 +1,5 @@
+.. _libfrr:
+
***************************
Library Facilities (libfrr)
***************************
diff --git a/doc/developer/packaging-debian.rst b/doc/developer/packaging-debian.rst
new file mode 100644
index 0000000000..66339b6d1f
--- /dev/null
+++ b/doc/developer/packaging-debian.rst
@@ -0,0 +1,173 @@
+Debian
+======
+
+(Tested on Ubuntu 12.04, 14.04, 16.04, 17.10, 18.04, Debian 8 and 9)
+
+.. note::
+
+ If you try to build for a different distro, then it will most likely fail
+ because of the missing backport. See :ref:`deb-backports` about adding a new
+ backport.
+
+1. Install build dependencies for your platform as outlined in :ref:`building`.
+
+2. Install the following additional packages:
+
+ - on Ubuntu 12.04, 14.04, 16.04, 17.10, Debian 8 and 9:
+
+ .. code-block:: shell
+
+ apt-get install realpath equivs groff fakeroot debhelper devscripts
+
+ - on Ubuntu 18.04: (realpath is now part of preinstalled by coreutils)
+
+ .. code-block:: shell
+
+ apt-get install equivs groff fakeroot debhelper devscripts
+
+3. Checkout FRR under a **unprivileged** user account:
+
+ .. code-block:: shell
+
+ git clone https://github.com/frrouting/frr.git frr
+ cd frr
+
+ If you wish to build a package for a branch other than master:
+
+ .. code-block:: shell
+
+ git checkout <branch>
+
+4. Run ``bootstrap.sh`` and make a dist tarball:
+
+ .. code-block:: shell
+
+ ./bootstrap.sh
+ ./configure --with-pkg-extra-version=-MyDebPkgVersion
+ make dist
+
+ .. note::
+
+ Configure parameters are not important for the Debian Package building -
+ except the `with-pkg-extra-version` if you want to give the Debian
+ package a specific name to mark your own unoffical build.
+
+5. Edit :file:`debianpkg/rules` and set the configuration as needed.
+
+ Look for section ``dh_auto_configure`` to modify the configure options as
+ needed. Options might be different between the top-level ``rules``` and
+ :file:`backports/XXXX/debian/rules`. Please adjust as needed on all files.
+
+6. Create backports debian sources
+
+ Rename the :file:`debianpkg` directory to :file:`debian` and create the
+ backports (Debian requires to not ship a :file:`debian` directory inside the
+ source directory to avoid build conflicts with the reserved ``debian``
+ subdirectory name during the build):
+
+ .. code-block:: shell
+
+ mv debianpkg debian
+ make -f debian/rules backports
+
+ This will create a :file:`frr_*.orig.tar.gz` with the source (same as the
+ dist tarball), as well as multiple :file:`frr_*.debian.tar.xz` and
+ :file:`frr_*.dsc` corresponding to each distribution for which a backport is
+ available.
+
+7. Create a new directory to build the package and populate with package
+ source.
+
+ .. code-block:: shell
+
+ mkdir frrpkg
+ cd frrpkg
+ tar xf ~/frr/frr_*.orig.tar.gz
+ cd frr*
+ . /etc/os-release
+ tar xf ~/frr/frr_*${ID}${VERSION_ID}*.debian.tar.xz
+
+8. Build Debian package dependencies and install them as needed.
+
+ .. code-block:: shell
+
+ sudo mk-build-deps --install debian/control
+
+9. Build Debian Package
+
+ Building with standard options:
+
+ .. code-block:: shell
+
+ debuild -b -uc -us
+
+ Or change some options (see `rules` file for available options):
+
+ .. code-block:: shell
+
+ debuild --set-envvar=WANT_BGP_VNC=1 --set-envvar=WANT_CUMULUS_MODE=1 -b -uc -us
+
+ To build with RPKI:
+
+ - Download the librtr packages from
+ https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact
+
+ - install librtr-dev on the build server
+
+ Then build with:
+
+ .. code-block:: shell
+
+ debuild --set-envvar=WANT_RPKI=1 -b -uc -us
+
+ RPKI packages have an additonal dependency of ``librtr0`` which can be found
+ at the same URL.
+
+10. Done!
+
+If all worked correctly, then you should end up with the Debian packages under
+:file:`frrpkg`. If distributed, please make sure you distribute it together
+with the sources (``frr_*.orig.tar.gz``, ``frr_*.debian.tar.xz`` and
+``frr_*.dsc``)
+
+.. _deb-backports:
+
+Debian Backports
+----------------
+
+The :file:`debianpkg/backports` directory contains the Debian directories for
+backports to other Debian platforms. These are built via the ``3.0 (custom)``
+source format, which allows one to build a source package directly out of
+tarballs (e.g. an orig.tar.gz tarball and a debian.tar.gz file), at which point
+the format can be changed to a real format (e.g. ``3.0 (quilt)``).
+
+Source packages are assembled via targets of the same name as the system to
+which the backport is done (e.g. ``precise``), included in :file:`debian/rules`.
+
+To create a new Debian backport:
+
+- Add its name to ``KNOWN_BACKPORTS``, defined in :file:`debian/rules`.
+- Create a directory of the same name in :file:`debian/backports`.
+- Add the files ``exclude``, ``versionext``, and ``debian/source/format`` under
+ this directory.
+
+For the last point, these files should contain the following:
+
+``exclude``
+ Contains whitespace-separated paths (relative to the root of the source dir)
+ that should be excluded from the source package (e.g.
+ :file:`debian/patches`).
+
+``versionext``
+ Contains the suffix added to the version number for this backport's build.
+ Distributions often have guidelines for what this should be. If left empty,
+ no new :file:`debian/changelog` entry is created.
+
+``debian/source/format``
+ Contains the source format of the resulting source package. As of of the
+ writing of this document the only supported format is ``3.0 (quilt)``.
+
+- Add appropriate files under the :file:`debian/` subdirectory. These will be
+ included in the source package, overriding any top-level :file:`debian/`
+ files with equivalent paths.
+
diff --git a/doc/developer/packaging.rst b/doc/developer/packaging.rst
new file mode 100644
index 0000000000..e9bb3a5409
--- /dev/null
+++ b/doc/developer/packaging.rst
@@ -0,0 +1,8 @@
+*********
+Packaging
+*********
+
+.. toctree::
+ :maxdepth: 2
+
+ packaging-debian
diff --git a/doc/developer/process-architecture.rst b/doc/developer/process-architecture.rst
new file mode 100644
index 0000000000..806afa644c
--- /dev/null
+++ b/doc/developer/process-architecture.rst
@@ -0,0 +1,320 @@
+.. _process-architecture:
+
+Process Architecture
+====================
+
+FRR inherited its overall design architecture from Quagga. The chosen model for
+Quagga is that of a suite of independent daemons that do IPC via Unix domain
+sockets. Within each daemon, the architecture follows the event-driven model.
+FRR has inherited this model as well. As FRR is deployed at larger scales and
+gains ever more features, each adding to the overall processing workload, we
+are approaching the saturation point for a single thread per daemon. In light
+of this, there are ongoing efforts to introduce multithreading to various
+components of FRR. This document aims to describe the current design choices
+and overall model for integrating the event-driven and multithreaded
+architectures into a cohesive whole.
+
+Terminology
+-----------
+Because this document describes the architecture for true kernel threads as
+well as the event system, a digression on terminology is in order here.
+
+Historically Quagga's event system was viewed as an implementation of userspace
+threading. Because of this design choice, the names for various datastructures
+within the event system are variations on the term "thread". The primary
+context datastructure in this system is called a "threadmaster". What would
+today be called an 'event' or 'task' in systems such as libevent are called
+"threads" and the datastructure for them is ``struct thread``. To add to the
+confusion, these "threads" have various types, one of which is "event". To
+hopefully avoid some of this confusion, this document refers to these "threads"
+as a 'task' except where the datastructures are explicitly named. When they are
+explicitly named, they will be formatted ``like this`` to differentiate from
+the conceptual names. When speaking of kernel threads, the term used will be
+"pthread" since FRR's kernel threading implementation is POSIX threads.
+
+.. This should be broken into its document under :ref:`libfrr`
+.. _event-architecture:
+
+Event Architecture
+------------------
+This section presents a brief overview of the event model as currently
+implemented in FRR. This doc should be expanded and broken off into its own
+section. For now it provides basic information necessary to understand the
+interplay between the event system and kernel threads.
+
+The core event system is implemented in :file:`lib/thread.[ch]`. The primary
+structure is ``struct thread_master``, hereafter referred to as a
+``threadmaster``. A ``threadmaster`` is a global state object, or context, that
+holds all the tasks currently pending execution as well as statistics on tasks
+that have already executed. The event system is driven by adding tasks to this
+data structure and then calling a function to retrieve the next task to
+execute. At initialization, a daemon will typically create one
+``threadmaster``, add a small set of initial tasks, and then run a loop to
+fetch each task and execute it.
+
+These tasks have various types corresponding to their general action. The types
+are given by integer macros in :file:`thread.h` and are:
+
+``THREAD_READ``
+ Task which waits for a file descriptor to become ready for reading and then
+ executes.
+
+``THREAD_WRITE``
+ Task which waits for a file descriptor to become ready for writing and then
+ executes.
+
+``THREAD_TIMER``
+ Task which executes after a certain amount of time has passed since it was
+ scheduled.
+
+``THREAD_EVENT``
+ Generic task that executes with high priority and carries an arbitrary
+ integer indicating the event type to its handler. These are commonly used to
+ implement the finite state machines typically found in routing protocols.
+
+``THREAD_READY``
+ Type used internally for tasks on the ready queue.
+
+``THREAD_UNUSED``
+ Type used internally for ``struct thread`` objects that aren't being used.
+ The event system pools ``struct thread`` to avoid heap allocations; this is
+ the type they have when they're in the pool.
+
+``THREAD_EXECUTE``
+ Just before a task is run its type is changed to this. This is used to show
+ ``X`` as the type in the output of :clicmd:`show thread cpu`.
+
+The programmer never has to work with these types explicitly. Each type of task
+is created and queued via special-purpose functions (actually macros, but
+irrelevant for the time being) for the specific type. For example, to add a
+``THREAD_READ`` task, you would call
+
+::
+
+ thread_add_read(struct thread_master *master, int (*handler)(struct thread *), void *arg, int fd, struct thread **ref);
+
+The ``struct thread`` is then created and added to the appropriate internal
+datastructure within the ``threadmaster``.
+
+The Event Loop
+^^^^^^^^^^^^^^
+To use the event system, after creating a ``threadmaster`` the program adds an
+initial set of tasks. As these tasks execute, they add more tasks that execute
+at some point in the future. This sequence of tasks drives the lifecycle of the
+program. When no more tasks are available, the program dies. Typically at
+startup the first task added is an I/O task for VTYSH as well as any network
+sockets needed for peerings or IPC.
+
+To retrieve the next task to run the program calls ``thread_fetch()``.
+``thread_fetch()`` internally computes which task to execute next based on
+rudimentary priority logic. Events (type ``THREAD_EVENT``) execute with the
+highest priority, followed by expired timers and finally I/O tasks (type
+``THREAD_READ`` and ``THREAD_WRITE``). When scheduling a task a function and an
+arbitrary argument are provided. The task returned from ``thread_fetch()`` is
+then executed with ``thread_call()``.
+
+The following diagram illustrates a simplified version of this infrastructure.
+
+.. todo: replace these with SVG
+.. figure:: ../figures/threadmaster-single.png
+ :align: center
+
+ Lifecycle of a program using a single threadmaster.
+
+The series of "task" boxes represents the current ready task queue. The various
+other queues for other types are not shown. The fetch-execute loop is
+illustrated at the bottom.
+
+Mapping the general names used in the figure to specific FRR functions:
+
+- ``task`` is ``struct thread *``
+- ``fetch`` is ``thread_fetch()``
+- ``exec()`` is ``thread_call``
+- ``cancel()`` is ``thread_cancel()``
+- ``schedule()`` is any of the various task-specific ``thread_add_*`` functions
+
+Adding tasks is done with various task-specific function-like macros. These
+macros wrap underlying functions in :file:`thread.c` to provide additional
+information added at compile time, such as the line number the task was
+scheduled from, that can be accessed at runtime for debugging, logging and
+informational purposes. Each task type has its own specific scheduling function
+that follow the naming convention ``thread_add_<type>``; see :file:`thread.h`
+for details.
+
+There are some gotchas to keep in mind:
+
+- I/O tasks are keyed off the file descriptor associated with the I/O
+ operation. This means that for any given file descriptor, only one of each
+ type of I/O task (``THREAD_READ`` and ``THREAD_WRITE``) can be scheduled. For
+ example, scheduling two write tasks one after the other will overwrite the
+ first task with the second, resulting in total loss of the first task and
+ difficult bugs.
+
+- Timer tasks are only as accurate as the monotonic clock provided by the
+ underlying operating system.
+
+- Memory management of the arbitrary handler argument passed in the schedule
+ call is the responsibility of the caller.
+
+
+Kernel Thread Architecture
+--------------------------
+Efforts have begun to introduce kernel threads into FRR to improve performance
+and stability. Naturally a kernel thread architecture has long been seen as
+orthogonal to an event-driven architecture, and the two do have significant
+overlap in terms of design choices. Since the event model is tightly integrated
+into FRR, careful thought has been put into how pthreads are introduced, what
+role they fill, and how they will interoperate with the event model.
+
+Design Overview
+^^^^^^^^^^^^^^^
+Each kernel thread behaves as a lightweight process within FRR, sharing the
+same process memory space. On the other hand, the event system is designed to
+run in a single process and drive serial execution of a set of tasks. With this
+consideration, a natural choice is to implement the event system within each
+kernel thread. This allows us to leverage the event-driven execution model with
+the currently existing task and context primitives. In this way the familiar
+execution model of FRR gains the ability to execute tasks simultaneously while
+preserving the existing model for concurrency.
+
+The following figure illustrates the architecture with multiple pthreads, each
+running their own ``threadmaster``-based event loop.
+
+.. todo: replace these with SVG
+.. figure:: ../figures/threadmaster-multiple.png
+ :align: center
+
+ Lifecycle of a program using multiple pthreads, each running their own
+ ``threadmaster``
+
+Each roundrect represents a single pthread running the same event loop
+described under :ref:`event-architecture`. Note the arrow from the ``exec()``
+box on the right to the ``schedule()`` box in the middle pthread. This
+illustrates code running in one pthread scheduling a task onto another
+pthread's threadmaster. A global lock for each ``threadmaster`` is used to
+synchronize these operations. The pthread names are examples.
+
+
+.. This should be broken into its document under :ref:`libfrr`
+.. _kernel-thread-wrapper:
+
+Kernel Thread Wrapper
+^^^^^^^^^^^^^^^^^^^^^
+The basis for the integration of pthreads and the event system is a lightweight
+wrapper for both systems implemented in :file:`lib/frr_pthread.[ch]`. The
+header provides a core datastructure, ``struct frr_pthread``, that encapsulates
+structures from both POSIX threads and :file:`thread.[ch]`. In particular, this
+datastructure has a pointer to a ``threadmaster`` that runs within the pthread.
+It also has fields for a name as well as start and stop functions that have
+signatures similar to the POSIX arguments for ``pthread_create()``.
+
+Calling ``frr_pthread_new()`` creates and registers a new ``frr_pthread``. The
+returned structure has a pre-initialized ``threadmaster``, and its ``start``
+and ``stop`` functions are initialized to defaults that will run a basic event
+loop with the given threadmaster. Calling ``frr_pthread_run`` starts the thread
+with the ``start`` function. From there, the model is the same as the regular
+event model. To schedule tasks on a particular pthread, simply use the regular
+:file:`thread.c` functions as usual and provide the ``threadmaster`` pointed to
+from the ``frr_pthread``. As part of implementing the wrapper, the
+:file:`thread.c` functions were made thread-safe. Consequently, it is safe to
+schedule events on a ``threadmaster`` belonging both to the calling thread as
+well as *any other pthread*. This serves as the basis for inter-thread
+communication and boils down to a slightly more complicated method of message
+passing, where the messages are the regular task events as used in the
+event-driven model. The only difference is thread cancellation, which requires
+calling ``thread_cancel_async()`` instead of ``thread_cancel`` to cancel a task
+currently scheduled on a ``threadmaster`` belonging to a different pthread.
+This is necessary to avoid race conditions in the specific case where one
+pthread wants to guarantee that a task on another pthread is cancelled before
+proceeding.
+
+In addition, the existing commands to show statistics and other information for
+tasks within the event driven model have been expanded to handle multiple
+pthreads; running :clicmd:`show thread cpu` will display the usual event
+breakdown, but it will do so for each pthread running in the program. For
+example, :ref:`bgpd` runs a dedicated I/O pthread and shows the following
+output for :clicmd:`show thread cpu`:
+
+::
+
+ frr# show thread cpu
+
+ Thread statistics for bgpd:
+
+ Showing statistics for pthread main
+ ------------------------------------
+ CPU (user+system): Real (wall-clock):
+ Active Runtime(ms) Invoked Avg uSec Max uSecs Avg uSec Max uSecs Type Thread
+ 0 1389.000 10 138900 248000 135549 255349 T subgroup_coalesce_timer
+ 0 0.000 1 0 0 18 18 T bgp_startup_timer_expire
+ 0 850.000 18 47222 222000 47795 233814 T work_queue_run
+ 0 0.000 10 0 0 6 14 T update_subgroup_merge_check_thread_cb
+ 0 0.000 8 0 0 117 160 W zclient_flush_data
+ 2 2.000 1 2000 2000 831 831 R bgp_accept
+ 0 1.000 1 1000 1000 2832 2832 E zclient_connect
+ 1 42082.000 240574 174 37000 178 72810 R vtysh_read
+ 1 152.000 1885 80 2000 96 6292 R zclient_read
+ 0 549346.000 2997298 183 7000 153 20242 E bgp_event
+ 0 2120.000 300 7066 14000 6813 22046 T (bgp_holdtime_timer)
+ 0 0.000 2 0 0 57 59 T update_group_refresh_default_originate_route_map
+ 0 90.000 1 90000 90000 73729 73729 T bgp_route_map_update_timer
+ 0 1417.000 9147 154 48000 132 61998 T bgp_process_packet
+ 300 71807.000 2995200 23 3000 24 11066 T (bgp_connect_timer)
+ 0 1894.000 12713 148 45000 112 33606 T (bgp_generate_updgrp_packets)
+ 0 0.000 1 0 0 105 105 W vtysh_write
+ 0 52.000 599 86 2000 138 6992 T (bgp_start_timer)
+ 1 1.000 8 125 1000 164 593 R vtysh_accept
+ 0 15.000 600 25 2000 15 153 T (bgp_routeadv_timer)
+ 0 11.000 299 36 3000 53 3128 RW bgp_connect_check
+
+
+ Showing statistics for pthread BGP I/O thread
+ ----------------------------------------------
+ CPU (user+system): Real (wall-clock):
+ Active Runtime(ms) Invoked Avg uSec Max uSecs Avg uSec Max uSecs Type Thread
+ 0 1611.000 9296 173 13000 188 13685 R bgp_process_reads
+ 0 2995.000 11753 254 26000 182 29355 W bgp_process_writes
+
+
+ Showing statistics for pthread BGP Keepalives thread
+ -----------------------------------------------------
+ CPU (user+system): Real (wall-clock):
+ Active Runtime(ms) Invoked Avg uSec Max uSecs Avg uSec Max uSecs Type Thread
+ No data to display yet.
+
+Attentive readers will notice that there is a third thread, the Keepalives
+thread. This thread is responsible for -- surprise -- generating keepalives for
+peers. However, there are no statistics showing for that thread. Although the
+pthread uses the ``frr_pthread`` wrapper, it opts not to use the embedded
+``threadmaster`` facilities. Instead it replaces the ``start`` and ``stop``
+functions with custom functions. This was done because the ``threadmaster``
+facilities introduce a small but significant amount of overhead relative to the
+pthread's task. In this case since the pthread does not need the event-driven
+model and does not need to receive tasks from other pthreads, it is simpler and
+more efficient to implement it outside of the provided event facilities. The
+point to take away from this example is that while the facilities to make using
+pthreads within FRR easy are already implemented, the wrapper is flexible and
+allows usage of other models while still integrating with the rest of the FRR
+core infrastructure. Starting and stopping this pthread works the same as it
+does for any other ``frr_pthread``; the only difference is that event
+statistics are not collected for it, because there are no events.
+
+Notes on Design and Documentation
+---------------------------------
+Because of the choice to embed the existing event system into each pthread
+within FRR, at this time there is not integrated support for other models of
+pthread use such as divide and conquer. Similarly, there is no explicit support
+for thread pooling or similar higher level constructs. The currently existing
+infrastructure is designed around the concept of long-running worker threads
+responsible for specific jobs within each daemon. This is not to say that
+divide and conquer, thread pooling, etc. could not be implemented in the
+future. However, designs in this direction must be very careful to take into
+account the existing codebase. Introducing kernel threads into programs that
+have been written under the assumption of a single thread of execution must be
+done very carefully to avoid insidious errors and to ensure the program remains
+understandable and maintainable.
+
+In keeping with these goals, future work on kernel threading should be
+extensively documented here and FRR developers should be very careful with
+their design choices, as poor choices tightly integrated can prove to be
+catastrophic for development efforts in the future.
diff --git a/doc/figures/threadmaster-multiple.png b/doc/figures/threadmaster-multiple.png
new file mode 100644
index 0000000000..2ded50c4cb
--- /dev/null
+++ b/doc/figures/threadmaster-multiple.png
Binary files differ
diff --git a/doc/figures/threadmaster-single.png b/doc/figures/threadmaster-single.png
new file mode 100644
index 0000000000..a068389b2a
--- /dev/null
+++ b/doc/figures/threadmaster-single.png
Binary files differ
diff --git a/doc/figures/threadmaster.svg b/doc/figures/threadmaster.svg
new file mode 100644
index 0000000000..a8d2c6adfe
--- /dev/null
+++ b/doc/figures/threadmaster.svg
@@ -0,0 +1,42 @@
+<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+ <title>Layer 1</title>
+ <rect stroke="#000000" id="svg_14" height="209.999998" width="78.999998" y="42.000002" x="44" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/>
+ <rect stroke="#000000" id="svg_7" height="391.000012" width="278.000011" y="20" x="24.999992" fill="#e5e5e5"/>
+ <rect stroke="#000000" id="svg_8" height="371.999978" width="259.000006" y="27.5" x="34.5" fill="#aad4ff"/>
+ <text stroke="#000000" transform="matrix(0.6990958452224731,0,0,0.7454545497894287,7.038336245343089,56.89090812206268) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_19" y="259.756097" x="80.760476" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">I/O</text>
+ <text id="svg_35" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="139.834812" x="60.305235" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task3</text>
+ <text id="svg_36" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="143.062739" x="62.867657" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task3</text>
+ <rect stroke="#000000" id="svg_9" height="196.000002" width="53" y="35" x="44" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/>
+ <text id="svg_40" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="144.681173" x="161.373439" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <rect id="svg_15" stroke="#000000" height="196.000002" width="53" y="35" x="104" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/>
+ <rect id="svg_16" stroke="#000000" height="196.000002" width="53" y="35" x="164" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/>
+ <rect id="svg_17" stroke="#000000" height="196.000002" width="53" y="35" x="225" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#a2f9a2"/>
+ <text id="svg_20" stroke="#000000" transform="matrix(0.5625633135745076,0,0,0.5774844344081629,24.86726517334988,96.24168390657653) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="264.951043" x="181.542341" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">Timer</text>
+ <text id="svg_21" stroke="#000000" transform="matrix(0.5749753656942643,0,0,0.6334746061662456,38.254388934566045,86.97425370192815) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="256.836039" x="263.041264" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">Event</text>
+ <text id="svg_22" stroke="#000000" transform="matrix(0.5985540174236015,0,0,0.6031454293478589,42.9067138136931,90.27170546109113) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="263.072047" x="346.818415" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">Ready</text>
+ <line id="svg_24" y2="62" x2="278.034186" y1="62" x1="44" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
+ <line id="svg_25" y2="90" x2="278.034186" y1="90" x1="44" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
+ <line id="svg_26" y2="118.5" x2="278.034186" y1="118.5" x1="44" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
+ <rect id="svg_27" height="45" width="182" y="287" x="103" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="#cccccc"/>
+ <rect id="svg_28" height="45" width="182" y="342" x="103" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="#cccccc"/>
+ <text stroke="#000000" transform="matrix(0.791700541973114,0,0,0.7857142686843872,21.471117571927607,58.28571891784668) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_31" y="325.818183" x="222.3407" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">thread_fetch()</text>
+ <text stroke="#000000" transform="matrix(0.8357433386864528,0,0,0.7633649135312126,6.654719490831912,79.40761438745744) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_32" y="380.181209" x="228.949154" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">thread_call()</text>
+ <text stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_33" y="47" x="56" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task1</text>
+ <text style="cursor: move;" id="svg_34" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="94.51867" x="58.592092" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task2</text>
+ <text style="cursor: move;" id="svg_37" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="141.444306" x="58.584825" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task9</text>
+ <text style="cursor: move;" id="svg_38" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="50.812022" x="163.086583" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task4</text>
+ <text style="cursor: move;" id="svg_39" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="92.891296" x="164.799726" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task7</text>
+ <text id="svg_41" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="138.207438" x="158.803698" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <text id="svg_42" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="54.046518" x="265.881445" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <text id="svg_43" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="96.125793" x="264.168301" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <text id="svg_44" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="143.060369" x="259.02887" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <text id="svg_45" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="50.809651" x="370.383203" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <text id="svg_46" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="99.36266" x="372.096346" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <text id="svg_47" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="141.441935" x="372.096346" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text>
+ <line stroke="#000000" id="svg_54" y2="286.009435" x2="275" y1="230.500001" x1="275" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="none"/>
+ <line stroke="#000000" id="svg_57" y2="278.49643" x2="267.246429" y1="286" x1="275.5" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="none"/>
+ <line id="svg_58" y2="285.754309" x2="274.995691" y1="278.5" x1="282.25" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/>
+ </g>
+</svg>
diff --git a/doc/frr-sphinx.mk b/doc/frr-sphinx.mk
index df4056760d..3e4c67d374 100644
--- a/doc/frr-sphinx.mk
+++ b/doc/frr-sphinx.mk
@@ -10,6 +10,11 @@ SPHINXBUILD ?= sphinx-build
PAPER ?=
BUILDDIR = _build
+# This is a custom FRR variable just for this docs subdirectory used to support
+# VPATH builds. Makefiles which include this file should override it to point
+# to the correct sources path.
+SOURCESDIR ?= .
+
# User-friendly check for sphinx-build
ifneq ($(MAKECMDGOALS), clean)
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
@@ -23,9 +28,9 @@ endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCESDIR)
# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCESDIR)
.PHONY: help
help:
diff --git a/doc/manpages/.gitignore b/doc/manpages/.gitignore
index 0505537159..2e7d8573f1 100644
--- a/doc/manpages/.gitignore
+++ b/doc/manpages/.gitignore
@@ -1,3 +1,3 @@
/_templates
/_build
-!/Makefile
+!/Makefile.in
diff --git a/doc/manpages/Makefile b/doc/manpages/Makefile.in
index ebbbc31009..f28746cee6 100644
--- a/doc/manpages/Makefile
+++ b/doc/manpages/Makefile.in
@@ -1,4 +1,11 @@
-include ../frr-sphinx.mk
+# This is necessary to support VPATH builds.
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# This variable is used as the documentation source location in frr-sphinx.mk
+SOURCESDIR = @srcdir@
+
+include @srcdir@/../frr-sphinx.mk
# -----------------------------------------------------------------------------
# Automake requires that 3rd-party Makefiles recognize these targets.
diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py
index 41683ed678..2b0f7e3893 100644
--- a/doc/manpages/conf.py
+++ b/doc/manpages/conf.py
@@ -323,6 +323,7 @@ man_pages = [
('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8),
+ ('sharpd', 'sharpd', fwfrr.format("a SHARP "), [], 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/index.rst b/doc/manpages/index.rst
index 964cc07d71..6d3f3aae55 100644
--- a/doc/manpages/index.rst
+++ b/doc/manpages/index.rst
@@ -3,9 +3,6 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-Welcome to FRR's documentation!
-===============================
-
.. toctree::
:maxdepth: 2
@@ -22,6 +19,7 @@ Welcome to FRR's documentation!
mtracebis
ripd
ripngd
+ sharpd
watchfrr
zebra
vtysh
diff --git a/doc/manpages/sharpd.rst b/doc/manpages/sharpd.rst
new file mode 100644
index 0000000000..016f3f9254
--- /dev/null
+++ b/doc/manpages/sharpd.rst
@@ -0,0 +1,38 @@
+******
+SHARPD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: sharpd
+
+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/.gitignore b/doc/user/.gitignore
index 0505537159..2e7d8573f1 100644
--- a/doc/user/.gitignore
+++ b/doc/user/.gitignore
@@ -1,3 +1,3 @@
/_templates
/_build
-!/Makefile
+!/Makefile.in
diff --git a/doc/user/Makefile b/doc/user/Makefile.in
index 223f8a64a3..77c6abf917 100644
--- a/doc/user/Makefile
+++ b/doc/user/Makefile.in
@@ -1,4 +1,11 @@
-include ../frr-sphinx.mk
+# This is necessary to support VPATH builds.
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# This variable is used as the documentation source location in frr-sphinx.mk
+SOURCESDIR = @srcdir@
+
+include @srcdir@/../frr-sphinx.mk
# -----------------------------------------------------------------------------
# Automake requires that 3rd-party Makefiles recognize these targets.
diff --git a/doc/user/basic.rst b/doc/user/basic.rst
index f134133da4..b861444e88 100644
--- a/doc/user/basic.rst
+++ b/doc/user/basic.rst
@@ -55,18 +55,23 @@ Basic Config Commands
Set hostname of the router.
-.. index:: password PASSWORD
+.. index::
+ single: no password PASSWORD
+ single: password PASSWORD
-.. clicmd:: password PASSWORD
+.. clicmd:: [no] password PASSWORD
- Set password for vty interface. If there is no password, a vty won't
- accept connections.
+ Set password for vty interface. The ``no`` form of the command deletes the
+ password. If there is no password, a vty won't accept connections.
-.. index:: enable password PASSWORD
+.. index::
+ single: no enable password PASSWORD
+ single: enable password PASSWORD
-.. clicmd:: enable password PASSWORD
+.. clicmd:: [no] enable password PASSWORD
- Set enable password.
+ Set enable password. The ``no`` form of the command deletes the enable
+ password.
.. index::
single: no log trap [LEVEL]
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 3298460a58..861e3d0d6d 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1549,7 +1549,9 @@ is specified, the BGP protocol process belongs to the default VRF.
BGP routes may be leaked (i.e., copied) between a unicast VRF RIB and the VPN
safi RIB of the default VRF (leaking is also permitted between the unicast RIB
-of the default VRF and VPN). A common application of this feature is to
+of the default VRF and VPN). A shortcut syntax is also available for
+specifying leaking from one vrf to another vrf using the VPN RIB as
+the intemediary. A common application of the VPN-VRF feature is to
connect a customer's private routing domain to a provider's VPN service.
Leaking is configured from the point of view of an individual VRF: ``import``
refers to routes leaked from VPN to a unicast VRF, whereas ``export`` refers to
@@ -1586,6 +1588,9 @@ routing domain which is shared across all its sites. More complex routing
topologies are possible through use of additional route-targets to augment the
leaking of sets of routes in various ways.
+When using the shortcut syntax for vrf-to-vrf leaking, the RD and RT are
+auto-derived.
+
Configuration
-------------
@@ -1667,6 +1672,24 @@ address-family:
Disables import or export of routes between the current unicast VRF and VPN.
+.. index:: import vrf VRFNAME
+.. clicmd:: import vrf VRFNAME
+
+ Shortcut syntax for specifying automatic leaking from vrf VRFNAME to
+ the current VRF using the VPN RIB as intermediary. The RD and RT
+ are auto derived and should not be specified explicitly for either the
+ source or destination VRF's.
+
+ This shortcut syntax mode is not compatible with the explicit
+ `import vpn` and `export vpn` statements for the two VRF's involved.
+ The CLI will disallow attempts to configure incompatible leaking
+ modes.
+
+.. index:: no import vrf VRFNAME
+.. clicmd:: no import vrf VRFNAME
+
+ Disables automatic leaking from vrf VRFNAME to the current VRF using
+ the VPN RIB as intermediary.
.. _displaying-bgp-information:
diff --git a/doc/user/bugs.rst b/doc/user/bugs.rst
new file mode 100644
index 0000000000..60ddf612db
--- /dev/null
+++ b/doc/user/bugs.rst
@@ -0,0 +1,67 @@
+.. _bugs:
+
+**************
+Reporting Bugs
+**************
+
+This file describes the procedure for reporting FRRouting bugs. You are asked
+to follow this format when submitting bug reports.
+
+Bugs submitted with woefully incomplete information will receive little
+attention and are likely to be closed. If you hit a suspected bug in an older
+version, you may be asked to test with a later version in your environment.
+
+Often you may be asked for additional information to help solve the bug. Bugs
+may be closed after 30 days of non-response to requests to reconfirm or supply
+additional information.
+
+Please report bugs on the project GitHub issue tracker at
+https://github.com/frrouting/frr/issues
+
+Report Format & Requested Information
+=====================================
+
+When reporting a bug, please provide the following information.
+
+#. Your FRR version if it is a release build, or the commit hash if you built
+ from source.
+
+#. If you compiled from source, please provide your ``./configure`` line,
+ including all option flags.
+
+#. A full list of the FRR daemons you run.
+
+#. Your platform name and version, e.g. ``Ubuntu 18.04``.
+
+#. Problem description.
+
+ - Provide as much information as possible.
+ - Copy and paste relevant commands and their output to describe your network
+ setup.
+ - Topology diagrams are helpful when reporting bugs involving more than one
+ box.
+ - Platform routing tables and interface configurations are useful if you are
+ reporting a routing issue.
+
+ *Please be sure to review the provided information and censor any sensitive
+ material.*
+
+#. All FRR configuration files you use. Again, please be sure to censor any
+ sensitive information. For sensitive v4 / v6 addresses, we ask that you
+ censor the inner octets; e.g., ``192.XXX.XXX.32/24``.
+
+#. If you are reporting a crash and have a core file, please supply a stack
+ trace using GDB:
+
+ ::
+
+ $ gdb exec_file core_file
+ (gdb) bt .
+
+#. Run all FRR daemons with full debugging on and send *only* the portion of
+ logs which are relevant to your problem.
+
+#. Patches, workarounds, and fixes are always welcome.
+
+.. seealso:: :ref:`basic-config-commands`
+
diff --git a/doc/user/conf.py b/doc/user/conf.py
index 886403b69d..3fced11024 100644
--- a/doc/user/conf.py
+++ b/doc/user/conf.py
@@ -342,6 +342,14 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
+# contents of ../extra/frrlexer.py.
+# This is read here to support VPATH build. Since this section is execfile()'d
+# with the file location, we can safely use a relative path here to save the
+# contents of the lexer file for later use even if our relative path changes
+# due to VPATH.
+with open('../extra/frrlexer.py', 'rb') as lex:
+ frrlexerpy = lex.read()
+
# custom extensions here
def setup(app):
# object type for FRR CLI commands, can be extended to document parent CLI
@@ -357,5 +365,5 @@ def setup(app):
#
# frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer")
custom_namespace = {}
- exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace)
+ exec(frrlexerpy, custom_namespace)
lexers['frr'] = custom_namespace['FRRLexer']()
diff --git a/doc/user/index.rst b/doc/user/index.rst
index c264b4c83e..f20ff8ec2b 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -1,5 +1,5 @@
-Welcome to FRR's documentation!
-===============================
+FRRouting User Guide
+====================
.. toctree::
:maxdepth: 2
@@ -25,7 +25,9 @@ Welcome to FRR's documentation!
pbr
ripd
ripngd
+ sharp
vnc
+ bugs
glossary
appendix
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index 8501054fdb..700a727d8f 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -117,11 +117,6 @@ customize the build to include or exclude specific features and dependencies.
Enable Traffic Engineering Extension for ISIS (RFC5305)
-.. option:: --enable-multipath <ARG>
-
- Enable support for Equal Cost Multipath. `ARG` is the maximum number
- of ECMP paths to allow, set to 0 to allow unlimited number of paths.
-
.. option:: --enable-realms
Enable the support of Linux Realms. Convert tag values from 1-255 into a
@@ -176,6 +171,14 @@ customize the build to include or exclude specific features and dependencies.
With this option, we provide a way to strip out these characters for APK dev
package builds.
+.. option:: --enable-multipath=X
+
+ Compile FRR with up to X way ECMP supported. This number can be from 0-999.
+ For backwards compatability with older configure options when setting X = 0,
+ we will build FRR with 64 way ECMP. This is needed because there are
+ hardcoded arrays that FRR builds towards, so we need to know how big to
+ make these arrays at build time.
+
You may specify any combination of the above options to the configure
script. By default, the executables are placed in :file:`/usr/local/sbin`
and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/`
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 56f95e64b0..71bc047720 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -101,7 +101,7 @@ OSPF6 interface
.. index:: ipv6 ospf6 hello-interval HELLOINTERVAL
.. clicmd:: ipv6 ospf6 hello-interval HELLOINTERVAL
- Sets interface's Hello Interval. Default 40
+ Sets interface's Hello Interval. Default 10
.. index:: ipv6 ospf6 dead-interval DEADINTERVAL
.. clicmd:: ipv6 ospf6 dead-interval DEADINTERVAL
diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst
index eec575cf3f..aa48a3cd4f 100644
--- a/doc/user/pbr.rst
+++ b/doc/user/pbr.rst
@@ -57,7 +57,7 @@ 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)
+.. clicmd:: pbr-map NAME seq (1-700)
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
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst
new file mode 100644
index 0000000000..090628044c
--- /dev/null
+++ b/doc/user/sharp.rst
@@ -0,0 +1,67 @@
+.. _sharp:
+
+***
+SHARP
+***
+
+:abbr:`SHARP` Super Happy Advanced Routing Process. This daemon is useful
+for the testing of FRR itself as well as useful for creation of Proof of
+Concept labs.
+
+.. _starting-sharp:
+
+Starting SHARP
+==============
+
+Default configuration file for *sharpd* is :file:`sharpd.conf`. The typical
+location of :file:`sharpd.conf` is |INSTALL_PREFIX_ETC|/sharpd.conf.
+
+If the user is using integrated config, then :file:`sharpd.conf` need not be
+present and the :file:`frr.conf` is read instead.
+
+.. program:: sharpd
+
+:abbr:`SHARP` supports all the common FRR daemon start options which are
+documented elsewhere.
+
+.. _using-sharp:
+
+USING SHARP
+===========
+
+All sharp commands are under the enable node and proceeded by the
+:abbr:`sharp` keyword. There are currently no permenent sharp
+commands for configuration.
+
+..index:: sharp install
+..clicmd:: sharp install routes A.B.C.D nexthop E.F.G.H (1-1000000)
+
+Install up to a million /32 routes starting at A.B.C.D with specified nexthop
+E.F.G.H. The nexthop is a NEXTHOP_TYPE_IPV4 and must be reachable to be
+installed into the kernel. The routes are installed into zebra as
+ZEBRA_ROUTE_SHARP and can be used as part of a normal route redistribution.
+Route installation time is noted in the debug log and upon zebra successful
+installation into the kernel and sharp receiving the notification of all
+route installs the success will be noted in the debug log as well.
+
+..index:: sharp remove
+..clicmd:: sharp remove routes A.B.C.D (1-1000000)
+
+Remove up 1000000 million /32 routes starting at A.B.C.D. The routes are
+removed from zebra. Route deletion start is noted in the debug log
+and when all routes have been successfully deleted the debug log will
+be updated with this information as well.
+
+..index:: sharp label
+..clicmd:: sharp label <ipv4|ipv6> vrf NAME label (0-1000000)
+
+Install a label into the kernel that causes the specified vrf NAME table to be
+used for pop and forward operations when the specified label is seen.
+
+..index:: sharp watch
+..clicmd: sharp watch nexthop <A.B.C.D|X:X::X:X>
+
+Instruct zebra to monitor and notify sharp when the specified nexthop is
+changed. The notification from zebra is written into the debug log.
+
+
diff --git a/doc/user/snmp.rst b/doc/user/snmp.rst
index 1a24d56cb7..38918ff250 100644
--- a/doc/user/snmp.rst
+++ b/doc/user/snmp.rst
@@ -43,6 +43,9 @@ master SNMP agent (snmpd) and each of the FRR daemons must be configured. In
In each of the FRR daemons, ``agentx`` command will enable AgentX support.
:file:`/etc/snmp/snmpd.conf`:
+
+::
+
#
# example access restrictions setup
#
@@ -113,6 +116,9 @@ 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.
:file:`/etc/snmp/snmpd.conf`:
+
+::
+
#
# example access restrictions setup
#
@@ -126,6 +132,9 @@ the below snmpd.conf as wrong access restrictions can be hard to debug.
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
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index 0927a4dbe9..9d927359ba 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -31,7 +31,21 @@ Besides the common invocation options (:ref:`common-invocation-options`), the
When program terminates, retain routes added by zebra.
-.. program:: configure
+.. option:: -e X, --ecmp X
+
+ Run zebra with a limited ecmp ability compared to what it is compiled to.
+ If you are running zebra on hardware limited functionality you can
+ force zebra to limit the maximum ecmp allowed to X. This number
+ is bounded by what you compiled FRR with as the maximum number.
+
+.. option:: -n, --vrfwnetns
+
+ When *Zebra* starts with this option, the VRF backend is based on Linux
+ network namespaces. That implies that all network namespaces discovered by
+ ZEBRA will create an associated VRF. The other daemons will operate on the VRF
+ VRF defined by *Zebra*, as usual.
+
+ .. seealso:: :ref:`vrf`
.. _interface-commands:
@@ -47,6 +61,10 @@ Standard Commands
.. clicmd:: interface IFNAME
+.. index:: interface IFNAME vrf VRF
+
+.. clicmd:: interface IFNAME vrf VRF
+
.. index:: shutdown
.. clicmd:: shutdown
@@ -227,8 +245,8 @@ Link Parameters Commands
Static Route Commands
=====================
-Static routing is a very fundamental feature of routing technology. It
-defines static prefix and gateway.
+Static routing is a very fundamental feature of routing technology. It defines
+static prefix and gateway.
.. index:: ip route NETWORK GATEWAY
.. clicmd:: ip route NETWORK GATEWAY
@@ -246,16 +264,16 @@ defines static prefix and gateway.
ip route 10.0.0.0/8 ppp0
ip route 10.0.0.0/8 null0
- First example defines 10.0.0.0/8 static route with gateway 10.0.0.2.
- Second one defines the same prefix but with gateway to interface ppp0. The
- third install a blackhole route.
+ First example defines 10.0.0.0/8 static route with gateway 10.0.0.2. Second
+ one defines the same prefix but with gateway to interface ppp0. The third
+ install a blackhole route.
.. index:: ip route NETWORK NETMASK GATEWAY
.. clicmd:: ip route NETWORK NETMASK GATEWAY
- This is alternate version of above command. When NETWORK is
- A.B.C.D format, user must define NETMASK value with A.B.C.D
- format. GATEWAY is same option as above command.
+ This is alternate version of above command. When NETWORK is 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
@@ -338,16 +356,139 @@ default) should the specified gateways not be reachable. E.g.:
on Linux operating systems only, and perform AND matching on packet's
destination and source addresses in the kernel's forwarding path. Note that
destination longest-prefix match is "more important" than source LPM, e.g.
- *"2001:db8:1::/64 from 2001:db8::/48"* will win over
- *"2001:db8::/48 from 2001:db8:1::/64"* if both match.
+ ``2001:db8:1::/64 from 2001:db8::/48`` will win over
+ ``2001:db8::/48 from 2001:db8:1::/64`` if both match.
.. index:: table TABLENO
.. clicmd:: table TABLENO
- Select the primary kernel routing table to be used. This only works
- for kernels supporting multiple routing tables (like GNU/Linux 2.2.x
- and later). After setting TABLENO with this command,
- static routes defined after this are added to the specified table.
+ Select the primary kernel routing table to be used. This only works for
+ kernels supporting multiple routing tables (like GNU/Linux 2.2.x and later).
+ After setting TABLENO with this command, static routes defined after this
+ are added to the specified table.
+
+.. _vrf:
+
+Virtual Routing and Forwarding
+==============================
+
+FRR supports :abbr:`VRF (Virtual Routing and Forwarding)`. VRF is a way to
+separate networking contexts on the same machine. Those networking contexts are
+associated with separate interfaces, thus making it possible to associate one
+interface with a specific VRF.
+
+VRF can be used, for example, when instantiating per enterprise networking
+services, without having to instantiate the physical host machine or the
+routing management daemons for each enterprise. As a result, interfaces are
+separate for each set of VRF, and routing daemons can have their own context
+for each VRF.
+
+This conceptual view introduces the *Default VRF* case. If the user does not
+configure any specific VRF, then by default, FRR uses the *Default VRF*.
+
+In the context of *Zebra*, this is done automatically when configuring a static
+route with, for example, :clicmd:`ip route NETWORK GATEWAY`:
+
+.. code-block:: frr
+
+ # case without VRF
+ configure terminal
+ ip route 10.0.0.0 255.255.255.0 10.0.0.2
+ exit
+
+Configuring VRF networking contexts can be done in various ways on FRR. The VRF
+interfaces can be configured by entering in interface configuration mode
+:clicmd:`interface IFNAME vrf VRF`. Also, if the user wants to configure a
+static route for a specific VRF, then a specific VRF configuration mode is
+available. After entering into that mode with :clicmd:`vrf VRF` the user can
+enter the same route command as before, but this time, the route command will
+apply to the VRF.
+
+.. code-block:: frr
+
+ # case with VRF
+ configure terminal
+ vrf r1-cust1
+ ip route 10.0.0.0 255.255.255.0 10.0.0.2
+ exit-vrf
+
+A VRF backend mode is chosen when running *Zebra*.
+
+If no option is chosen, then the *Linux VRF* implementation as references in
+https://www.kernel.org/doc/Documentation/networking/vrf.txt will be mapped over
+the *Zebra* VRF. The routing table associated to that VRF is a Linux table
+identifier located in the same *Linux network namespace* where *Zebra* started.
+
+If the :option:`-n` option is chosen, then the *Linux network namespace* will
+be mapped over the *Zebra* VRF. That implies that *Zebra* is able to configure
+several *Linux network namespaces*. The routing table associated to that VRF
+is the whole routing tables located in that namespace. For instance, this mode
+matches OpenStack Network Namespaces. It matches also OpenFastPath. The default
+behavior remains Linux VRF which is supported by the Linux kernel community,
+see https://www.kernel.org/doc/Documentation/networking/vrf.txt.
+
+Because of that difference, there are some subtle differences when running some
+commands in relationship to VRF. Here is an extract of some of those commands:
+
+.. index:: vrf VRF
+.. clicmd:: vrf VRF
+
+ This command is available on configuration mode. By default, above command
+ permits accessing the vrf configuration mode. This mode is available for
+ both VRFs. It is to be noted that *Zebra* does not create *Linux VRF*.
+ Provisioning this command is used to keep the configuration intact.
+
+.. index:: netns NAMESPACE
+.. clicmd:: netns NAMESPACE
+
+ This command is based on VRF configuration mode. This command is available
+ when *Zebra* is run in :option:`-n` mode. This command reflects which *Linux
+ network namespace* is to be mapped with *Zebra* VRF. It is to be noted that
+ *Zebra* creates and detects added/suppressed VRFs from the Linux environment
+ (in fact, those managed with iproute2). Provisioning this command is used to
+ keep the configuration intact.
+
+.. index:: ip route NETWORK NETMASK GATEWAY NEXTHOPVRF
+.. clicmd:: ip route NETWORK NETMASK GATEWAY NEXTHOPVRF
+
+ This command is based on VRF configuration mode or in configuration mode. If
+ on configuration mode, this applies to default VRF. Otherwise, this command
+ applies to the VRF of the vrf configuration mode. This command is used to
+ configure a vrf route leak across 2 VRFs. This command is only available
+ when *Zebra* is launched without :option:`-n` option.
+
+.. index:: ip route NETWORK NETMASK GATEWAY table TABLENO
+.. clicmd:: ip route NETWORK NETMASK GATEWAY table TABLENO
+
+ This command is based on configuration mode. There, for default VRF, this
+ command is available for all modes. The ``TABLENO`` configured is one of the
+ tables from Default *Linux network namespace*.
+
+.. index:: show ip route vrf VRF
+.. clicmd:: show ip route vrf VRF
+
+ The show command permits dumping the routing table associated to the VRF. If
+ *Zebra* is launched with default settings, this will be the ``TABLENO`` of
+ the VRF configured on the kernel, thanks to information provided in
+ https://www.kernel.org/doc/Documentation/networking/vrf.txt. If *Zebra* is
+ launched with :option:`-n` option, this will be the default routing table of
+ the *Linux network namespace* ``VRF``.
+
+.. index:: show ip route vrf VRF table TABLENO
+.. clicmd:: show ip route vrf VRF table TABLENO
+
+ The show command is only available with :option:`-n` option. This command
+ will dump the routing table ``TABLENO`` of the *Linux network namespace*
+ ``VRF``.
+
+The usual static route commands are also available in the VRF context. When
+entered within the VRF context the static routes are created in the VRF.
+
+.. code-block:: frr
+
+ ip route 10.0.0.0 255.255.255.0 10.0.0.2 vrf r1-cust1 table 43
+ show ip table vrf r1-cust1 table 43
+
.. _multicast-rib-commands:
@@ -519,6 +660,8 @@ The FIB push interface comprises of a TCP connection between zebra and
the FPM. The connection is initiated by zebra -- that is, the FPM acts
as the TCP server.
+.. program:: configure
+
The relevant zebra code kicks in when zebra is configured with the
:option:`--enable-fpm` flag. Zebra periodically attempts to connect to
the well-known FPM port. Once the connection is up, zebra starts
diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c
index 6d74670514..d9e89357ca 100644
--- a/eigrpd/eigrp_hello.c
+++ b/eigrpd/eigrp_hello.c
@@ -630,7 +630,7 @@ static struct eigrp_packet *eigrp_hello_encode(struct eigrp_interface *ei,
uint16_t length = EIGRP_HEADER_LEN;
// allocate a new packet to be sent
- ep = eigrp_packet_new(ei->ifp->mtu, NULL);
+ ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), NULL);
if (ep) {
// encode common header feilds
diff --git a/eigrpd/eigrp_macros.h b/eigrpd/eigrp_macros.h
index eea7a26425..b30e19a867 100644
--- a/eigrpd/eigrp_macros.h
+++ b/eigrpd/eigrp_macros.h
@@ -35,6 +35,8 @@
//--------------------------------------------------------------------------
+#define EIGRP_PACKET_MTU(mtu) ((mtu) - (sizeof(struct ip)))
+
/* Topology Macros */
diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c
index 990d1dc08e..59864532cf 100644
--- a/eigrpd/eigrp_packet.c
+++ b/eigrpd/eigrp_packet.c
@@ -51,6 +51,7 @@
#include "eigrpd/eigrp_zebra.h"
#include "eigrpd/eigrp_vty.h"
#include "eigrpd/eigrp_dump.h"
+#include "eigrpd/eigrp_macros.h"
#include "eigrpd/eigrp_network.h"
#include "eigrpd/eigrp_topology.h"
#include "eigrpd/eigrp_fsm.h"
@@ -1088,7 +1089,7 @@ struct eigrp_packet *eigrp_packet_duplicate(struct eigrp_packet *old,
{
struct eigrp_packet *new;
- new = eigrp_packet_new(nbr->ei->ifp->mtu, nbr);
+ new = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr);
new->length = old->length;
new->retrans_counter = old->retrans_counter;
new->dst = old->dst;
diff --git a/eigrpd/eigrp_query.c b/eigrpd/eigrp_query.c
index 00234bb35c..dd4231fa00 100644
--- a/eigrpd/eigrp_query.c
+++ b/eigrpd/eigrp_query.c
@@ -167,6 +167,7 @@ void eigrp_send_query(struct eigrp_interface *ei)
struct eigrp_prefix_entry *pe;
bool has_tlv = false;
bool new_packet = true;
+ uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu);
for (ALL_LIST_ELEMENTS(ei->eigrp->topology_changes_internalIPV4, node,
nnode, pe)) {
@@ -174,7 +175,7 @@ void eigrp_send_query(struct eigrp_interface *ei)
continue;
if (new_packet) {
- ep = eigrp_packet_new(ei->ifp->mtu, NULL);
+ ep = eigrp_packet_new(eigrp_mtu, NULL);
/* Prepare EIGRP INIT UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_QUERY, ei->eigrp,
@@ -197,7 +198,7 @@ void eigrp_send_query(struct eigrp_interface *ei)
listnode_add(pe->rij, nbr);
}
- if (length + EIGRP_TLV_MAX_IPV4_BYTE > (uint16_t)ei->ifp->mtu) {
+ if (length + EIGRP_TLV_MAX_IPV4_BYTE > eigrp_mtu) {
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
&& ei->params.auth_keychain != NULL) {
eigrp_make_md5_digest(ei, ep->s,
diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c
index a702c1fbd1..b7490cd492 100644
--- a/eigrpd/eigrp_reply.c
+++ b/eigrpd/eigrp_reply.c
@@ -85,7 +85,7 @@ void eigrp_send_reply(struct eigrp_neighbor *nbr, struct eigrp_prefix_entry *pe)
* End of filtering
*/
- ep = eigrp_packet_new(ei->ifp->mtu, nbr);
+ ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr);
/* Prepare EIGRP INIT UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_REPLY, eigrp, ep->s, 0,
diff --git a/eigrpd/eigrp_siaquery.c b/eigrpd/eigrp_siaquery.c
index d398d75724..ff38325465 100644
--- a/eigrpd/eigrp_siaquery.c
+++ b/eigrpd/eigrp_siaquery.c
@@ -119,7 +119,7 @@ void eigrp_send_siaquery(struct eigrp_neighbor *nbr,
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
- ep = eigrp_packet_new(nbr->ei->ifp->mtu, nbr);
+ ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr);
/* Prepare EIGRP INIT UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_SIAQUERY, nbr->ei->eigrp, ep->s, 0,
diff --git a/eigrpd/eigrp_siareply.c b/eigrpd/eigrp_siareply.c
index 3b7a82b665..d3dd123f90 100644
--- a/eigrpd/eigrp_siareply.c
+++ b/eigrpd/eigrp_siareply.c
@@ -118,7 +118,7 @@ void eigrp_send_siareply(struct eigrp_neighbor *nbr,
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
- ep = eigrp_packet_new(nbr->ei->ifp->mtu, nbr);
+ ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr);
/* Prepare EIGRP INIT UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_SIAREPLY, nbr->ei->eigrp, ep->s, 0,
diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c
index 2d1bc46e6b..becb29a95f 100644
--- a/eigrpd/eigrp_topology.c
+++ b/eigrpd/eigrp_topology.c
@@ -443,17 +443,24 @@ void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest)
struct eigrp *eigrp = eigrp_lookup();
for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) {
- if (((uint64_t)entry->distance
- <= (uint64_t)dest->distance * (uint64_t)eigrp->variance)
- && entry->distance != EIGRP_MAX_METRIC) // is successor
- {
- entry->flags |= EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
- entry->flags &= ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
- } else if (entry->reported_distance
- < dest->fdistance) // is feasible successor
- {
- entry->flags |= EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
- entry->flags &= ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ if (entry->reported_distance < dest->fdistance) {
+ // is feasible successor, can be successor
+ if (((uint64_t)entry->distance
+ <= (uint64_t)dest->distance
+ * (uint64_t)eigrp->variance)
+ && entry->distance != EIGRP_MAX_METRIC) {
+ // is successor
+ entry->flags |=
+ EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ entry->flags &=
+ ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
+ } else {
+ // is feasible successor only
+ entry->flags |=
+ EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
+ entry->flags &=
+ ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ }
} else {
entry->flags &= ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
entry->flags &= ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c
index bd80ea366f..a3080136b5 100644
--- a/eigrpd/eigrp_update.c
+++ b/eigrpd/eigrp_update.c
@@ -420,7 +420,7 @@ void eigrp_update_send_init(struct eigrp_neighbor *nbr)
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
- ep = eigrp_packet_new(nbr->ei->ifp->mtu, nbr);
+ ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr);
/* Prepare EIGRP INIT UPDATE header */
if (IS_DEBUG_EIGRP_PACKET(0, RECV))
@@ -533,10 +533,10 @@ void eigrp_update_send_EOT(struct eigrp_neighbor *nbr)
struct eigrp *eigrp = ei->eigrp;
struct prefix *dest_addr;
uint32_t seq_no = eigrp->sequence_number;
- uint16_t mtu = ei->ifp->mtu;
+ uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu);
struct route_node *rn;
- ep = eigrp_packet_new(mtu, nbr);
+ ep = eigrp_packet_new(eigrp_mtu, nbr);
/* Prepare EIGRP EOT UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, EIGRP_EOT_FLAG,
@@ -557,13 +557,13 @@ void eigrp_update_send_EOT(struct eigrp_neighbor *nbr)
if (eigrp_nbr_split_horizon_check(te, ei))
continue;
- if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > mtu) {
+ if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) {
eigrp_update_place_on_nbr_queue(nbr, ep, seq_no,
length);
seq_no++;
length = EIGRP_HEADER_LEN;
- ep = eigrp_packet_new(mtu, nbr);
+ ep = eigrp_packet_new(eigrp_mtu, nbr);
eigrp_packet_header_init(
EIGRP_OPC_UPDATE, nbr->ei->eigrp, ep->s,
EIGRP_EOT_FLAG, seq_no,
@@ -604,13 +604,14 @@ void eigrp_update_send(struct eigrp_interface *ei)
struct eigrp *eigrp = ei->eigrp;
struct prefix *dest_addr;
uint32_t seq_no = eigrp->sequence_number;
+ uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu);
if (ei->nbrs->count == 0)
return;
uint16_t length = EIGRP_HEADER_LEN;
- ep = eigrp_packet_new(ei->ifp->mtu, NULL);
+ ep = eigrp_packet_new(eigrp_mtu, NULL);
/* Prepare EIGRP INIT UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, 0, seq_no, 0);
@@ -633,8 +634,7 @@ void eigrp_update_send(struct eigrp_interface *ei)
if (eigrp_nbr_split_horizon_check(ne, ei))
continue;
- if ((length + EIGRP_TLV_MAX_IPV4_BYTE)
- > (uint16_t)ei->ifp->mtu) {
+ if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) {
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
&& (ei->params.auth_keychain != NULL)) {
eigrp_make_md5_digest(ei, ep->s,
@@ -651,7 +651,7 @@ void eigrp_update_send(struct eigrp_interface *ei)
eigrp_update_send_to_all_nbrs(ei, ep);
length = EIGRP_HEADER_LEN;
- ep = eigrp_packet_new(ei->ifp->mtu, NULL);
+ ep = eigrp_packet_new(eigrp_mtu, NULL);
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s,
0, seq_no, 0);
if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5)
@@ -790,7 +790,7 @@ static void eigrp_update_send_GR_part(struct eigrp_neighbor *nbr)
}
}
- ep = eigrp_packet_new(ei->ifp->mtu, nbr);
+ ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr);
/* Prepare EIGRP Graceful restart UPDATE header */
eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, flags,
diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c
index 9c61512df4..e903dc8c7f 100644
--- a/isisd/isis_redist.c
+++ b/isisd/isis_redist.c
@@ -184,8 +184,7 @@ static void isis_redist_update_ext_reach(struct isis_area *area, int level,
route_map_result_t map_ret;
memcpy(&area_info, info, sizeof(area_info));
- if (redist->metric != 0xffffffff)
- area_info.metric = redist->metric;
+ area_info.metric = redist->metric;
if (redist->map_name) {
map_ret =
@@ -540,7 +539,7 @@ DEFUN (isis_redistribute,
int afi;
int type;
int level;
- unsigned long metric;
+ unsigned long metric = 0;
const char *routemap = NULL;
family = str2family(argv[idx_afi]->text);
@@ -567,9 +566,6 @@ DEFUN (isis_redistribute,
return CMD_WARNING_CONFIG_FAILED;
}
- metric = 0xffffffff;
- routemap = NULL;
-
if (argc > idx_metric_rmap + 1) {
if (argv[idx_metric_rmap + 1]->arg[0] == '\0')
return CMD_WARNING_CONFIG_FAILED;
@@ -651,7 +647,7 @@ DEFUN (isis_default_originate,
int family;
int originate_type = DEFAULT_ORIGINATE;
int level;
- unsigned long metric = 0xffffffff;
+ unsigned long metric = 0;
const char *routemap = NULL;
family = str2family(argv[idx_afi]->text);
@@ -748,7 +744,7 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area,
continue;
vty_out(vty, " redistribute %s %s level-%d", family_str,
zebra_route_string(type), level);
- if (redist->metric != 0xffffffff)
+ if (redist->metric)
vty_out(vty, " metric %u", redist->metric);
if (redist->map_name)
vty_out(vty, " route-map %s", redist->map_name);
@@ -766,7 +762,7 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area,
family_str, level);
if (redist->redist == DEFAULT_ORIGINATE_ALWAYS)
vty_out(vty, " always");
- if (redist->metric != 0xffffffff)
+ if (redist->metric)
vty_out(vty, " metric %u", redist->metric);
if (redist->map_name)
vty_out(vty, " route-map %s", redist->map_name);
diff --git a/lib/command.c b/lib/command.c
index 0720762da0..0fa6bde334 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -85,6 +85,7 @@ const char *node_names[] = {
"keychain", // KEYCHAIN_NODE,
"keychain key", // KEYCHAIN_KEY_NODE,
"logical-router", // LOGICALROUTER_NODE,
+ "static ip", // IP_NODE,
"vrf", // VRF_NODE,
"interface", // INTERFACE_NODE,
"nexthop-group", // NH_GROUP_NODE,
@@ -119,7 +120,6 @@ const char *node_names[] = {
"ldp l2vpn", // LDP_L2VPN_NODE,
"ldp", // LDP_PSEUDOWIRE_NODE,
"isis", // ISIS_NODE,
- "static ip", // IP_NODE,
"ipv4 access list", // ACCESS_NODE,
"ipv4 prefix list", // PREFIX_NODE,
"ipv6 access list", // ACCESS_IPV6_NODE,
@@ -529,87 +529,103 @@ static int config_write_host(struct vty *vty)
if (cmd_domainname_get())
vty_out(vty, "domainname %s\n", cmd_domainname_get());
- if (host.encrypt) {
- if (host.password_encrypt)
- vty_out(vty, "password 8 %s\n", host.password_encrypt);
- if (host.enable_encrypt)
- vty_out(vty, "enable password 8 %s\n",
- host.enable_encrypt);
- } else {
- if (host.password)
- vty_out(vty, "password %s\n", host.password);
- if (host.enable)
- vty_out(vty, "enable password %s\n", host.enable);
- }
+ /* The following are all configuration commands that are not sent to
+ * watchfrr. For instance watchfrr is hardcoded to log to syslog so
+ * we would always display 'log syslog informational' in the config
+ * which would cause other daemons to then switch to syslog when they
+ * parse frr.conf.
+ */
+ if (strcmp(zlog_default->protoname, "WATCHFRR")) {
+ if (host.encrypt) {
+ if (host.password_encrypt)
+ vty_out(vty, "password 8 %s\n",
+ host.password_encrypt);
+ if (host.enable_encrypt)
+ vty_out(vty, "enable password 8 %s\n",
+ host.enable_encrypt);
+ } else {
+ if (host.password)
+ vty_out(vty, "password %s\n", host.password);
+ if (host.enable)
+ vty_out(vty, "enable password %s\n",
+ host.enable);
+ }
- if (zlog_default->default_lvl != LOG_DEBUG) {
- vty_out(vty, "! N.B. The 'log trap' command is deprecated.\n");
- vty_out(vty, "log trap %s\n",
- zlog_priority[zlog_default->default_lvl]);
- }
+ if (zlog_default->default_lvl != LOG_DEBUG) {
+ vty_out(vty,
+ "! N.B. The 'log trap' command is deprecated.\n");
+ vty_out(vty, "log trap %s\n",
+ zlog_priority[zlog_default->default_lvl]);
+ }
- if (host.logfile
- && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) {
- vty_out(vty, "log file %s", host.logfile);
- if (zlog_default->maxlvl[ZLOG_DEST_FILE]
- != zlog_default->default_lvl)
- vty_out(vty, " %s",
- zlog_priority
- [zlog_default->maxlvl[ZLOG_DEST_FILE]]);
- vty_out(vty, "\n");
- }
+ if (host.logfile
+ && (zlog_default->maxlvl[ZLOG_DEST_FILE]
+ != ZLOG_DISABLED)) {
+ vty_out(vty, "log file %s", host.logfile);
+ if (zlog_default->maxlvl[ZLOG_DEST_FILE]
+ != zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority
+ [zlog_default->maxlvl
+ [ZLOG_DEST_FILE]]);
+ vty_out(vty, "\n");
+ }
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
- vty_out(vty, "log stdout");
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
- != zlog_default->default_lvl)
- vty_out(vty, " %s",
- zlog_priority[zlog_default->maxlvl
- [ZLOG_DEST_STDOUT]]);
- vty_out(vty, "\n");
- }
+ if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
+ vty_out(vty, "log stdout");
+ if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
+ != zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority
+ [zlog_default->maxlvl
+ [ZLOG_DEST_STDOUT]]);
+ vty_out(vty, "\n");
+ }
- if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
- vty_out(vty, "no log monitor\n");
- else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
- != zlog_default->default_lvl)
- vty_out(vty, "log monitor %s\n",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]]);
-
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
- vty_out(vty, "log syslog");
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
- != zlog_default->default_lvl)
- vty_out(vty, " %s",
+ if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ vty_out(vty, "no log monitor\n");
+ else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
+ != zlog_default->default_lvl)
+ vty_out(vty, "log monitor %s\n",
zlog_priority[zlog_default->maxlvl
- [ZLOG_DEST_SYSLOG]]);
- vty_out(vty, "\n");
- }
+ [ZLOG_DEST_MONITOR]]);
+
+ if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
+ vty_out(vty, "log syslog");
+ if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
+ != zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->maxlvl
+ [ZLOG_DEST_SYSLOG]]);
+ vty_out(vty, "\n");
+ }
- if (zlog_default->facility != LOG_DAEMON)
- vty_out(vty, "log facility %s\n",
- facility_name(zlog_default->facility));
+ if (zlog_default->facility != LOG_DAEMON)
+ vty_out(vty, "log facility %s\n",
+ facility_name(zlog_default->facility));
- if (zlog_default->record_priority == 1)
- vty_out(vty, "log record-priority\n");
+ if (zlog_default->record_priority == 1)
+ vty_out(vty, "log record-priority\n");
- if (zlog_default->timestamp_precision > 0)
- vty_out(vty, "log timestamp precision %d\n",
- zlog_default->timestamp_precision);
+ if (zlog_default->timestamp_precision > 0)
+ vty_out(vty, "log timestamp precision %d\n",
+ zlog_default->timestamp_precision);
- if (host.advanced)
- vty_out(vty, "service advanced-vty\n");
+ if (host.advanced)
+ vty_out(vty, "service advanced-vty\n");
- if (host.encrypt)
- vty_out(vty, "service password-encryption\n");
+ if (host.encrypt)
+ vty_out(vty, "service password-encryption\n");
- if (host.lines >= 0)
- vty_out(vty, "service terminal-length %d\n", host.lines);
+ if (host.lines >= 0)
+ vty_out(vty, "service terminal-length %d\n",
+ host.lines);
- if (host.motdfile)
- vty_out(vty, "banner motd file %s\n", host.motdfile);
- else if (!host.motd)
- vty_out(vty, "no banner motd\n");
+ if (host.motdfile)
+ vty_out(vty, "banner motd file %s\n", host.motdfile);
+ else if (!host.motd)
+ vty_out(vty, "no banner motd\n");
+ }
if (debug_memstats_at_exit)
vty_out(vty, "!\ndebug memstats-at-exit\n");
@@ -1873,7 +1889,7 @@ DEFUN (config_hostname,
{
struct cmd_token *word = argv[1];
- if (!isalpha((int)word->arg[0])) {
+ if (!isalnum((int)word->arg[0])) {
vty_out(vty, "Please specify string starting with alphabet\n");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -1895,7 +1911,7 @@ DEFUN (config_no_hostname,
DEFUN (config_password,
password_cmd,
"password [(8-8)] WORD",
- "Assign the terminal connection password\n"
+ "Modify the terminal connection password\n"
"Specifies a HIDDEN password will follow\n"
"The password string\n")
{
@@ -1934,6 +1950,34 @@ DEFUN (config_password,
return CMD_SUCCESS;
}
+/* VTY interface password delete. */
+DEFUN (no_config_password,
+ no_password_cmd,
+ "no password",
+ NO_STR
+ "Modify the terminal connection password\n")
+{
+ bool warned = false;
+
+ if (host.password) {
+ if (!vty_shell_serv(vty)) {
+ vty_out(vty, NO_PASSWD_CMD_WARNING);
+ warned = true;
+ }
+ XFREE(MTYPE_HOST, host.password);
+ }
+ host.password = NULL;
+
+ if (host.password_encrypt) {
+ if (!warned && !vty_shell_serv(vty))
+ vty_out(vty, NO_PASSWD_CMD_WARNING);
+ XFREE(MTYPE_HOST, host.password_encrypt);
+ }
+ host.password_encrypt = NULL;
+
+ return CMD_SUCCESS;
+}
+
/* VTY enable password set. */
DEFUN (config_enable_password,
enable_password_cmd,
@@ -1995,12 +2039,22 @@ DEFUN (no_config_enable_password,
"Modify enable password parameters\n"
"Assign the privileged level password\n")
{
- if (host.enable)
+ bool warned = false;
+
+ if (host.enable) {
+ if (!vty_shell_serv(vty)) {
+ vty_out(vty, NO_PASSWD_CMD_WARNING);
+ warned = true;
+ }
XFREE(MTYPE_HOST, host.enable);
+ }
host.enable = NULL;
- if (host.enable_encrypt)
+ if (host.enable_encrypt) {
+ if (!warned && !vty_shell_serv(vty))
+ vty_out(vty, NO_PASSWD_CMD_WARNING);
XFREE(MTYPE_HOST, host.enable_encrypt);
+ }
host.enable_encrypt = NULL;
return CMD_SUCCESS;
@@ -2304,7 +2358,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel)
#if defined(HAVE_CUMULUS)
if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
- zlog_default->maxlvl[ZLOG_DEST_SYSLOG] = ZLOG_DISABLED;
+ zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
#endif
return CMD_SUCCESS;
}
@@ -2330,6 +2384,16 @@ DEFUN (config_log_file,
zlog_default->default_lvl);
}
+static void disable_log_file(void)
+{
+ zlog_reset_file();
+
+ if (host.logfile)
+ XFREE(MTYPE_HOST, host.logfile);
+
+ host.logfile = NULL;
+}
+
DEFUN (no_config_log_file,
no_config_log_file_cmd,
"no log file [FILENAME [LEVEL]]",
@@ -2339,13 +2403,7 @@ DEFUN (no_config_log_file,
"Logging file name\n"
"Logging level\n")
{
- zlog_reset_file();
-
- if (host.logfile)
- XFREE(MTYPE_HOST, host.logfile);
-
- host.logfile = NULL;
-
+ disable_log_file();
return CMD_SUCCESS;
}
@@ -2357,6 +2415,9 @@ DEFUN (config_log_syslog,
LOG_LEVEL_DESC)
{
int idx_log_levels = 2;
+
+ disable_log_file();
+
if (argc == 3) {
int level;
if ((level = level_match(argv[idx_log_levels]->arg))
@@ -2710,6 +2771,7 @@ void cmd_init(int terminal)
if (terminal > 0) {
install_element(CONFIG_NODE, &password_cmd);
+ install_element(CONFIG_NODE, &no_password_cmd);
install_element(CONFIG_NODE, &enable_password_cmd);
install_element(CONFIG_NODE, &no_enable_password_cmd);
diff --git a/lib/command.h b/lib/command.h
index f18de3417c..8d9c39b0ea 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -85,6 +85,7 @@ enum node_type {
KEYCHAIN_NODE, /* Key-chain node. */
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
LOGICALROUTER_NODE, /* Logical-Router node. */
+ IP_NODE, /* Static ip route node. */
VRF_NODE, /* VRF mode node. */
INTERFACE_NODE, /* Interface mode node. */
NH_GROUP_NODE, /* Nexthop-Group mode node. */
@@ -119,7 +120,6 @@ enum node_type {
LDP_L2VPN_NODE, /* LDP L2VPN node */
LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */
ISIS_NODE, /* ISIS protocol mode */
- IP_NODE, /* Static ip route node. */
ACCESS_NODE, /* Access list node. */
PREFIX_NODE, /* Prefix list node. */
ACCESS_IPV6_NODE, /* Access list node. */
@@ -376,6 +376,10 @@ struct cmd_node {
#define CONF_BACKUP_EXT ".sav"
+/* Command warnings. */
+#define NO_PASSWD_CMD_WARNING \
+ "Please be aware that removing the password is a security risk and you should think twice about this command.\n"
+
/* IPv4 only machine should not accept IPv6 address for peer's IP
address. So we replace VTY command string like below. */
#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n"
diff --git a/lib/command_graph.c b/lib/command_graph.c
index 4555d6eaeb..0e8669c4b5 100644
--- a/lib/command_graph.c
+++ b/lib/command_graph.c
@@ -25,8 +25,6 @@
#include <zebra.h>
#include "command_graph.h"
-#include "command.h"
-#include "log.h"
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text")
@@ -462,6 +460,9 @@ void cmd_graph_names(struct graph *graph)
#ifndef BUILDING_CLIPPY
+#include "command.h"
+#include "log.h"
+
void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
{
static bool wasend;
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index 33591cb4e7..7f2d06548b 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -102,4 +102,14 @@ static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6,
memcpy((char *)in6 + 12, &in, sizeof(struct in_addr));
}
+/*
+ * convert an ipv4 mapped ipv6 address back to ipv4 address
+ */
+static inline void ipv4_mapped_ipv6_to_ipv4(struct in6_addr *in6,
+ struct in_addr *in)
+{
+ memset(in, 0, sizeof(struct in_addr));
+ memcpy(in, (char *)in6 + 12, sizeof(struct in_addr));
+}
+
#endif /* __IPADDR_H__ */
diff --git a/lib/plist.c b/lib/plist.c
index 01b55f9f1d..e1dac46a90 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -73,7 +73,7 @@ struct prefix_master {
struct prefix_list_list str;
/* Whether sequential number is used. */
- int seqnum;
+ bool seqnum;
/* The latest update. */
struct prefix_list *recent;
@@ -348,7 +348,7 @@ static void prefix_list_delete(struct prefix_list *plist)
static struct prefix_list_entry *
prefix_list_entry_make(struct prefix *prefix, enum prefix_list_type type,
- int seq, int le, int ge, int any)
+ int64_t seq, int le, int ge, int any)
{
struct prefix_list_entry *pentry;
@@ -381,10 +381,10 @@ void prefix_list_delete_hook(void (*func)(struct prefix_list *plist))
}
/* Calculate new sequential number. */
-static int prefix_new_seq_get(struct prefix_list *plist)
+static int64_t prefix_new_seq_get(struct prefix_list *plist)
{
- int maxseq;
- int newseq;
+ int64_t maxseq;
+ int64_t newseq;
struct prefix_list_entry *pentry;
maxseq = newseq = 0;
@@ -401,7 +401,7 @@ static int prefix_new_seq_get(struct prefix_list *plist)
/* Return prefix list entry which has same seq number. */
static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist,
- int seq)
+ int64_t seq)
{
struct prefix_list_entry *pentry;
@@ -413,7 +413,8 @@ static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist,
static struct prefix_list_entry *
prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix,
- enum prefix_list_type type, int seq, int le, int ge)
+ enum prefix_list_type type, int64_t seq,
+ int le, int ge)
{
struct prefix_list_entry *pentry;
@@ -771,7 +772,7 @@ static void __attribute__((unused)) prefix_list_print(struct prefix_list *plist)
p = &pentry->prefix;
- printf(" seq %u %s %s/%d", pentry->seq,
+ printf(" seq %" PRId64 " %s %s/%d", pentry->seq,
prefix_list_type_str(pentry),
inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
@@ -793,7 +794,7 @@ prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new)
size_t validbits = new->prefix.prefixlen;
struct pltrie_table *table;
struct prefix_list_entry *pentry;
- int seq = 0;
+ int64_t seq = 0;
if (new->seq == -1)
seq = prefix_new_seq_get(plist);
@@ -845,13 +846,13 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name,
struct prefix_list_entry *dup;
struct prefix p, p_tmp;
int any = 0;
- int seqnum = -1;
+ int64_t seqnum = -1;
int lenum = 0;
int genum = 0;
/* Sequential number. */
if (seq)
- seqnum = atoi(seq);
+ seqnum = (int64_t)atol(seq);
/* ge and le number */
if (ge)
@@ -972,7 +973,7 @@ static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi,
struct prefix_list *plist;
struct prefix_list_entry *pentry;
struct prefix p;
- int seqnum = -1;
+ int64_t seqnum = -1;
int lenum = 0;
int genum = 0;
@@ -998,7 +999,7 @@ static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi,
/* Check sequence number. */
if (seq)
- seqnum = atoi(seq);
+ seqnum = (int64_t)atol(seq);
/* ge and le number */
if (ge)
@@ -1113,7 +1114,7 @@ static void vty_show_prefix_entry(struct vty *vty, afi_t afi,
vty_out(vty, " Description: %s\n", plist->desc);
vty_out(vty,
- " count: %d, range entries: %d, sequences: %u - %u\n",
+ " count: %d, range entries: %d, sequences: %" PRId64 " - %" PRId64 "\n",
plist->count, plist->rangecount,
plist->head ? plist->head->seq : 0,
plist->tail ? plist->tail->seq : 0);
@@ -1128,7 +1129,7 @@ static void vty_show_prefix_entry(struct vty *vty, afi_t afi,
vty_out(vty, " ");
if (master->seqnum)
- vty_out(vty, "seq %u ", pentry->seq);
+ vty_out(vty, "seq %" PRId64 " ", pentry->seq);
vty_out(vty, "%s ", prefix_list_type_str(pentry));
@@ -1164,14 +1165,14 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name,
{
struct prefix_list *plist;
struct prefix_master *master;
- int seqnum = 0;
+ int64_t seqnum = 0;
master = prefix_master_get(afi, 0);
if (master == NULL)
return CMD_WARNING;
if (seq)
- seqnum = atoi(seq);
+ seqnum = (int64_t)atol(seq);
if (name) {
plist = prefix_list_lookup(afi, name);
@@ -1236,7 +1237,7 @@ static int vty_show_prefix_list_prefix(struct vty *vty, afi_t afi,
}
if (match) {
- vty_out(vty, " seq %u %s ", pentry->seq,
+ vty_out(vty, " seq %" PRId64 " %s ", pentry->seq,
prefix_list_type_str(pentry));
if (pentry->any)
@@ -1387,7 +1388,7 @@ DEFPY (ip_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv4.seqnum = no ? 0 : 1;
+ prefix_master_ipv4.seqnum = no ? false : true;
return CMD_SUCCESS;
}
@@ -1581,7 +1582,7 @@ DEFPY (ipv6_prefix_list_sequence_number,
PREFIX_LIST_STR
"Include/exclude sequence numbers in NVGEN\n")
{
- prefix_master_ipv6.seqnum = no ? 0 : 1;
+ prefix_master_ipv6.seqnum = no ? false : true;
return CMD_SUCCESS;
}
@@ -1744,7 +1745,7 @@ static int config_write_prefix_afi(afi_t afi, struct vty *vty)
afi == AFI_IP ? "" : "v6", plist->name);
if (master->seqnum)
- vty_out(vty, "seq %u ", pentry->seq);
+ vty_out(vty, "seq %" PRId64 " ", pentry->seq);
vty_out(vty, "%s ", prefix_list_type_str(pentry));
@@ -1783,7 +1784,7 @@ static int config_write_prefix_afi(afi_t afi, struct vty *vty)
afi == AFI_IP ? "" : "v6", plist->name);
if (master->seqnum)
- vty_out(vty, "seq %u ", pentry->seq);
+ vty_out(vty, "seq %" PRId64 " ", pentry->seq);
vty_out(vty, "%s", prefix_list_type_str(pentry));
@@ -1959,7 +1960,8 @@ int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name,
struct prefix *p = &pentry->prefix;
char buf[BUFSIZ];
- vty_out(vty, " seq %u %s %s/%d", pentry->seq,
+ vty_out(vty, " seq %" PRId64 " %s %s/%d",
+ pentry->seq,
prefix_list_type_str(pentry),
inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
diff --git a/lib/plist_int.h b/lib/plist_int.h
index aa81a3bce2..6bc2d034d6 100644
--- a/lib/plist_int.h
+++ b/lib/plist_int.h
@@ -48,7 +48,7 @@ struct prefix_list {
/* Each prefix-list's entry. */
struct prefix_list_entry {
- int seq;
+ int64_t seq;
int le;
int ge;
diff --git a/lib/prefix.c b/lib/prefix.c
index b38dd94589..05af190e9d 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1206,54 +1206,104 @@ int str2prefix(const char *str, struct prefix *p)
return 0;
}
-static const char *prefixevpn2str(const struct prefix *p, char *str, int size)
+static const char *prefixevpn_ead2str(const struct prefix_evpn *p, char *str,
+ int size)
+{
+ snprintf(str, size, "Unsupported EVPN prefix");
+ return str;
+}
+
+static const char *prefixevpn_macip2str(const struct prefix_evpn *p, char *str,
+ int size)
{
uint8_t family;
char buf[PREFIX2STR_BUFFER];
char buf2[ETHER_ADDR_STRLEN];
- if (p->u.prefix_evpn.route_type == 2) {
- if (IS_EVPN_PREFIX_IPADDR_NONE((struct prefix_evpn *)p))
- snprintf(str, size, "[%d]:[%s]/%d",
- p->u.prefix_evpn.route_type,
- prefix_mac2str(&p->u.prefix_evpn.mac, buf2,
- sizeof(buf2)),
- p->prefixlen);
- else {
- family = IS_EVPN_PREFIX_IPADDR_V4(
- (struct prefix_evpn *)p)
- ? AF_INET
- : AF_INET6;
- snprintf(str, size, "[%d]:[%s]:[%s]/%d",
- p->u.prefix_evpn.route_type,
- prefix_mac2str(&p->u.prefix_evpn.mac, buf2,
- sizeof(buf2)),
- inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr,
- buf, PREFIX2STR_BUFFER),
- p->prefixlen);
- }
- } else if (p->u.prefix_evpn.route_type == 3) {
- family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)
- ? AF_INET
- : AF_INET6;
- snprintf(str, size, "[%d]:[%s]/%d", p->u.prefix_evpn.route_type,
- inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf,
- PREFIX2STR_BUFFER),
+ if (is_evpn_prefix_ipaddr_none(p))
+ snprintf(str, size, "[%d]:[%s]/%d",
+ p->prefix.route_type,
+ prefix_mac2str(&p->prefix.macip_addr.mac,
+ buf2, sizeof(buf2)),
p->prefixlen);
- } else if (p->u.prefix_evpn.route_type == 5) {
- family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)
+ else {
+ family = is_evpn_prefix_ipaddr_v4(p)
? AF_INET
: AF_INET6;
- snprintf(str, size, "[%d]:[%u][%s/%d]/%d",
- p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag,
- inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf,
- PREFIX2STR_BUFFER),
- p->u.prefix_evpn.ip_prefix_length, p->prefixlen);
- } else {
- sprintf(str, "Unsupported EVPN route type %d",
- p->u.prefix_evpn.route_type);
+ snprintf(str, size, "[%d]:[%s]:[%s]/%d",
+ p->prefix.route_type,
+ prefix_mac2str(&p->prefix.macip_addr.mac,
+ buf2, sizeof(buf2)),
+ inet_ntop(family,
+ &p->prefix.macip_addr.ip.ip.addr,
+ buf, PREFIX2STR_BUFFER),
+ p->prefixlen);
}
+ return str;
+}
+
+static const char *prefixevpn_imet2str(const struct prefix_evpn *p, char *str,
+ int size)
+{
+ uint8_t family;
+ char buf[PREFIX2STR_BUFFER];
+
+ family = is_evpn_prefix_ipaddr_v4(p)
+ ? AF_INET
+ : AF_INET6;
+ snprintf(str, size, "[%d]:[%s]/%d", p->prefix.route_type,
+ inet_ntop(family,
+ &p->prefix.imet_addr.ip.ip.addr, buf,
+ PREFIX2STR_BUFFER),
+ p->prefixlen);
+ return str;
+}
+static const char *prefixevpn_es2str(const struct prefix_evpn *p, char *str,
+ int size)
+{
+ snprintf(str, size, "Unsupported EVPN prefix");
+ return str;
+}
+
+static const char *prefixevpn_prefix2str(const struct prefix_evpn *p, char *str,
+ int size)
+{
+ uint8_t family;
+ char buf[PREFIX2STR_BUFFER];
+
+ family = is_evpn_prefix_ipaddr_v4(p)
+ ? AF_INET
+ : AF_INET6;
+ snprintf(str, size, "[%d]:[%u][%s/%d]/%d",
+ p->prefix.route_type,
+ p->prefix.prefix_addr.eth_tag,
+ inet_ntop(family,
+ &p->prefix.prefix_addr.ip.ip.addr, buf,
+ PREFIX2STR_BUFFER),
+ p->prefix.prefix_addr.ip_prefix_length,
+ p->prefixlen);
+ return str;
+}
+
+static const char *prefixevpn2str(const struct prefix_evpn *p, char *str,
+ int size)
+{
+ switch (p->prefix.route_type) {
+ case 1:
+ return prefixevpn_ead2str(p, str, size);
+ case 2:
+ return prefixevpn_macip2str(p, str, size);
+ case 3:
+ return prefixevpn_imet2str(p, str, size);
+ case 4:
+ return prefixevpn_es2str(p, str, size);
+ case 5:
+ return prefixevpn_prefix2str(p, str, size);
+ default:
+ snprintf(str, size, "Unsupported EVPN prefix");
+ break;
+ }
return str;
}
@@ -1277,7 +1327,7 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
break;
case AF_EVPN:
- prefixevpn2str(p, str, size);
+ prefixevpn2str((const struct prefix_evpn *)p, str, size);
break;
case AF_FLOWSPEC:
diff --git a/lib/prefix.h b/lib/prefix.h
index f01c85b811..ab3c05ae74 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -56,26 +56,56 @@ struct ethaddr {
#define PREFIX_LEN_ROUTE_TYPE_5_IPV4 (18*8)
#define PREFIX_LEN_ROUTE_TYPE_5_IPV6 (30*8)
-/* EVPN address (RFC 7432) */
-struct evpn_addr {
- uint8_t route_type;
+typedef struct esi_t_ {
+ uint8_t val[10];
+} esi_t;
+
+struct evpn_ead_addr {
+ esi_t esi;
+ uint32_t eth_tag;
+};
+
+struct evpn_macip_addr {
+ uint32_t eth_tag;
uint8_t ip_prefix_length;
struct ethaddr mac;
+ struct ipaddr ip;
+};
+
+struct evpn_imet_addr {
uint32_t eth_tag;
+ uint8_t ip_prefix_length;
struct ipaddr ip;
-#if 0
- union
- {
- uint8_t addr;
- struct in_addr v4_addr;
- struct in6_addr v6_addr;
- } ip;
-#endif
};
-#define IS_EVPN_PREFIX_IPADDR_NONE(evp) IS_IPADDR_NONE(&(evp)->prefix.ip)
-#define IS_EVPN_PREFIX_IPADDR_V4(evp) IS_IPADDR_V4(&(evp)->prefix.ip)
-#define IS_EVPN_PREFIX_IPADDR_V6(evp) IS_IPADDR_V6(&(evp)->prefix.ip)
+struct evpn_es_addr {
+ esi_t esi;
+ uint8_t ip_prefix_length;
+ struct ipaddr ip;
+};
+
+struct evpn_prefix_addr {
+ uint32_t eth_tag;
+ uint8_t ip_prefix_length;
+ struct ipaddr ip;
+};
+
+/* EVPN address (RFC 7432) */
+struct evpn_addr {
+ uint8_t route_type;
+ union {
+ struct evpn_ead_addr _ead_addr;
+ struct evpn_macip_addr _macip_addr;
+ struct evpn_imet_addr _imet_addr;
+ struct evpn_es_addr _es_addr;
+ struct evpn_prefix_addr _prefix_addr;
+ } u;
+#define ead_addr u._ead_addr
+#define macip_addr u._macip_addr
+#define imet_addr u._imet_addr
+#define es_addr u._es_addr
+#define prefix_addr u._prefix_addr
+};
/*
* A struct prefix contains an address family, a prefix length, and an
@@ -177,6 +207,39 @@ struct prefix_evpn {
struct evpn_addr prefix __attribute__((aligned(8)));
};
+static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp)
+{
+ if (evp->prefix.route_type == 2)
+ return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip);
+ if (evp->prefix.route_type == 3)
+ return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip);
+ if (evp->prefix.route_type == 5)
+ return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip);
+ return 0;
+}
+
+static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp)
+{
+ if (evp->prefix.route_type == 2)
+ return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip);
+ if (evp->prefix.route_type == 3)
+ return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip);
+ if (evp->prefix.route_type == 5)
+ return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip);
+ return 0;
+}
+
+static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp)
+{
+ if (evp->prefix.route_type == 2)
+ return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip);
+ if (evp->prefix.route_type == 3)
+ return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip);
+ if (evp->prefix.route_type == 5)
+ return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip);
+ return 0;
+}
+
/* Prefix for a generic pointer */
struct prefix_ptr {
uint8_t family;
diff --git a/lib/routemap.c b/lib/routemap.c
index ea61043a8d..892b19dac5 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -722,7 +722,7 @@ static void route_map_delete(struct route_map *map)
/* Clear all dependencies */
route_map_clear_all_references(name);
- map->deleted = 1;
+ map->deleted = true;
/* Execute deletion hook. */
if (route_map_master.delete_hook) {
(*route_map_master.delete_hook)(name);
@@ -762,19 +762,19 @@ int route_map_mark_updated(const char *name, int del_later)
map = route_map_lookup_by_name(name);
- /* If we did not find the routemap with deleted=0 try again
- * with deleted=1
+ /* If we did not find the routemap with deleted=false try again
+ * with deleted=true
*/
if (!map) {
memset(&tmp_map, 0, sizeof(struct route_map));
tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
- tmp_map.deleted = 1;
+ tmp_map.deleted = true;
map = hash_lookup(route_map_master_hash, &tmp_map);
XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);
}
if (map) {
- map->to_be_processed = 1;
+ map->to_be_processed = true;
ret = 0;
}
@@ -786,7 +786,7 @@ int route_map_clear_updated(struct route_map *map)
int ret = -1;
if (map) {
- map->to_be_processed = 0;
+ map->to_be_processed = false;
if (map->deleted)
route_map_free_map(map);
}
@@ -2743,7 +2743,7 @@ void route_map_finish(void)
/* cleanup route_map */
while (route_map_master.head) {
struct route_map *map = route_map_master.head;
- map->to_be_processed = 0;
+ map->to_be_processed = false;
route_map_delete(map);
}
diff --git a/lib/routemap.h b/lib/routemap.h
index 0046b77c46..990c7fa72f 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -158,8 +158,8 @@ struct route_map {
struct route_map *prev;
/* Maintain update info */
- int to_be_processed; /* True if modification isn't acted on yet */
- int deleted; /* If 1, then this node will be deleted */
+ bool to_be_processed; /* True if modification isn't acted on yet */
+ bool deleted; /* If 1, then this node will be deleted */
QOBJ_FIELDS
};
diff --git a/lib/stream.c b/lib/stream.c
index 927a3d3d55..c4edd3d5bf 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -1113,6 +1113,7 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s)
fifo->head = s;
fifo->tail = s;
+ fifo->tail->next = NULL;
fifo->count++;
}
@@ -1131,6 +1132,9 @@ struct stream *stream_fifo_pop(struct stream_fifo *fifo)
fifo->tail = NULL;
fifo->count--;
+
+ /* ensure stream is scrubbed of references to this fifo */
+ s->next = NULL;
}
return s;
diff --git a/lib/zclient.c b/lib/zclient.c
index dac3af5821..0f7cf350db 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -975,8 +975,6 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
stream_putl(s, api->flags);
stream_putc(s, api->message);
stream_putc(s, api->safi);
- if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
- stream_put(s, &(api->rmac), sizeof(struct ethaddr));
/* Put prefix information. */
stream_putc(s, api->prefix.family);
@@ -1061,6 +1059,11 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
api_nh->label_num
* sizeof(mpls_label_t));
}
+
+ /* Router MAC for EVPN routes. */
+ if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_put(s, &(api_nh->rmac),
+ sizeof(struct ethaddr));
}
}
@@ -1101,8 +1104,6 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETL(s, api->flags);
STREAM_GETC(s, api->message);
STREAM_GETC(s, api->safi);
- if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
- STREAM_GET(&(api->rmac), s, sizeof(struct ethaddr));
/* Prefix. */
STREAM_GETC(s, api->prefix.family);
@@ -1212,6 +1213,11 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
api_nh->label_num
* sizeof(mpls_label_t));
}
+
+ /* Router MAC for EVPN routes. */
+ if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_get(&(api_nh->rmac), s,
+ sizeof(struct ethaddr));
}
}
@@ -1369,6 +1375,26 @@ stream_failure:
return false;
}
+bool zapi_iptable_notify_decode(struct stream *s,
+ uint32_t *unique,
+ enum zapi_iptable_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;
+}
+
struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
{
struct nexthop *n = nexthop_new();
@@ -2794,6 +2820,22 @@ static int zclient_read(struct thread *thread)
(*zclient->label_chunk)(command, zclient, length,
vrf_id);
break;
+ case ZEBRA_IPSET_NOTIFY_OWNER:
+ if (zclient->ipset_notify_owner)
+ (*zclient->ipset_notify_owner)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_IPSET_ENTRY_NOTIFY_OWNER:
+ if (zclient->ipset_entry_notify_owner)
+ (*zclient->ipset_entry_notify_owner)(command,
+ zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_IPTABLE_NOTIFY_OWNER:
+ if (zclient->iptable_notify_owner)
+ (*zclient->iptable_notify_owner)(command,
+ zclient, length,
+ vrf_id);
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index 71f5b38384..c5eaf9c0fd 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -258,6 +258,10 @@ struct zclient {
struct zclient *zclient,
uint16_t length,
vrf_id_t vrf_id);
+ int (*iptable_notify_owner)(int command,
+ struct zclient *zclient,
+ uint16_t length,
+ vrf_id_t vrf_id);
};
/* Zebra API message flag. */
@@ -298,6 +302,8 @@ struct zapi_nexthop {
/* MPLS labels for BGP-LU or Segment Routing */
uint8_t label_num;
mpls_label_t labels[MPLS_MAX_LABELS];
+
+ struct ethaddr rmac;
};
/*
@@ -338,8 +344,6 @@ struct zapi_route {
vrf_id_t vrf_id;
uint32_t tableid;
-
- struct ethaddr rmac;
};
/* Zebra IPv4 route message API. */
@@ -680,6 +684,9 @@ bool zapi_ipset_entry_notify_decode(struct stream *s,
uint32_t *unique,
char *ipset_name,
enum zapi_ipset_entry_notify_owner *note);
+bool zapi_iptable_notify_decode(struct stream *s,
+ uint32_t *unique,
+ enum zapi_iptable_notify_owner *note);
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
extern bool zapi_nexthop_update_decode(struct stream *s,
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index b895b5ad8b..bc1ce621ae 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -684,7 +684,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
{
struct prefix prefix, abr_prefix;
struct ospf6_route_table *table = NULL;
- struct ospf6_route *range, *route, *old = NULL;
+ struct ospf6_route *range, *route, *old = NULL, *old_route;
struct ospf6_route *abr_entry;
uint8_t type = 0;
char options[3] = {0, 0, 0};
@@ -695,14 +695,15 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
int is_debug = 0;
struct ospf6_inter_prefix_lsa *prefix_lsa = NULL;
struct ospf6_inter_router_lsa *router_lsa = NULL;
- struct ospf6_path *path;
+ bool old_entry_updated = false;
memset(&prefix, 0, sizeof(prefix));
if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) {
if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) {
is_debug++;
- zlog_debug("Examin %s in area %s", lsa->name, oa->name);
+ zlog_debug("%s: Examin %s in area %s",
+ __PRETTY_FUNCTION__, lsa->name, oa->name);
}
prefix_lsa =
@@ -720,7 +721,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
} else if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_ROUTER)) {
if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER)) {
is_debug++;
- zlog_debug("Examin %s in area %s", lsa->name, oa->name);
+ zlog_debug("%s: Examin %s in area %s",
+ __PRETTY_FUNCTION__, lsa->name, oa->name);
}
router_lsa =
@@ -768,7 +770,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
}
if (OSPF6_LSA_IS_MAXAGE(lsa)) {
if (is_debug)
- zlog_debug("LSA is MaxAge, ignore");
+ zlog_debug("%s: LSA %s is MaxAge, ignore",
+ __PRETTY_FUNCTION__, lsa->name);
if (old)
ospf6_route_remove(old, table);
return;
@@ -845,9 +848,24 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
|| CHECK_FLAG(abr_entry->flag, OSPF6_ROUTE_REMOVE)
|| !CHECK_FLAG(abr_entry->path.router_bits, OSPF6_ROUTER_BIT_B)) {
if (is_debug)
- zlog_debug("ABR router entry does not exist, ignore");
- if (old)
- ospf6_route_remove(old, table);
+ zlog_debug("%s: ABR router entry does not exist, ignore",
+ __PRETTY_FUNCTION__);
+ if (old) {
+ if (old->type == OSPF6_DEST_TYPE_ROUTER &&
+ oa->intra_brouter_calc) {
+ if (is_debug)
+ zlog_debug(
+ "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)",
+ __PRETTY_FUNCTION__, buf,
+ (void *)old);
+ } else {
+ if (is_debug)
+ zlog_debug("%s: remove old entry: %s %p ",
+ __PRETTY_FUNCTION__, buf,
+ (void *)old);
+ ospf6_route_remove(old, table);
+ }
+ }
return;
}
@@ -902,11 +920,11 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
route->path.type = OSPF6_PATH_TYPE_INTER;
route->path.cost = abr_entry->path.cost + cost;
- ospf6_route_copy_nexthops(route, abr_entry);
-
- path = ospf6_path_dup(&route->path);
- ospf6_copy_nexthops(path->nh_list, abr_entry->nh_list);
- listnode_add_sort(route->paths, path);
+ /* Inter abr_entry is same as brouter.
+ * Avoid duplicate nexthops to brouter and its
+ * learnt route. i.e. use merge nexthops.
+ */
+ ospf6_route_merge_nexthops(route, abr_entry);
/* (7) If the routes are identical, copy the next hops over to existing
route. ospf6's route table implementation will otherwise string both
@@ -915,11 +933,28 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
*/
old = ospf6_route_lookup(&prefix, table);
- if (old && (ospf6_route_cmp(route, old) == 0)) {
- ospf6_route_merge_nexthops(old, route);
+ for (old_route = old; old_route; old_route = old_route->next) {
+ if (!ospf6_route_is_same(old_route, route) ||
+ (old_route->type != route->type) ||
+ (old_route->path.type != route->path.type))
+ continue;
+
+ if ((ospf6_route_cmp(route, old_route) != 0)) {
+ if (is_debug) {
+ prefix2str(&prefix, buf, sizeof(buf));
+ zlog_debug("%s: old %p %s cost %u new route cost %u are not same",
+ __PRETTY_FUNCTION__,
+ (void *)old_route, buf,
+ old_route->path.cost,
+ route->path.cost);
+ }
+ continue;
+ }
+ old_entry_updated = true;
+ ospf6_route_merge_nexthops(old, route);
if (is_debug)
- zlog_debug("%s: Update route: %s old cost %u new cost %u nh count %u",
+ zlog_debug("%s: Update route: %s old cost %u new cost %u nh %u",
__PRETTY_FUNCTION__,
buf, old->path.cost, route->path.cost,
listcount(route->nh_list));
@@ -930,9 +965,12 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
/* Delete new route */
ospf6_route_delete(route);
- } else {
+ break;
+ }
+
+ if (old_entry_updated == false) {
if (is_debug)
- zlog_debug("%s: Install route: %s cost %u nh count %u",
+ zlog_debug("%s: Install route: %s cost %u nh %u",
__PRETTY_FUNCTION__, buf, route->path.cost,
listcount(route->nh_list));
/* ospf6_ia_add_nw_route (table, &prefix, route); */
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
index eaf3e5c6de..ba497a168e 100644
--- a/ospf6d/ospf6_area.h
+++ b/ospf6d/ospf6_area.h
@@ -50,6 +50,9 @@ struct ospf6_area {
/* Area type */
int no_summary;
+ /* Brouter traversal protection */
+ int intra_brouter_calc;
+
/* OSPF interface list */
struct list *if_list;
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index de4ee2e1ac..26e6deadae 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -1314,17 +1314,60 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
return 0;
}
+static void ospf6_intra_prefix_update_route_origin(struct ospf6_route *oa_route)
+{
+ struct ospf6_path *h_path;
+ struct ospf6_route *g_route, *nroute;
+
+ /* Update Global ospf6 route path */
+ g_route = ospf6_route_lookup(&oa_route->prefix,
+ ospf6->route_table);
+
+ for (ospf6_route_lock(g_route); g_route &&
+ ospf6_route_is_prefix(&oa_route->prefix, g_route);
+ g_route = nroute) {
+ nroute = ospf6_route_next(g_route);
+ if (g_route->type != oa_route->type)
+ continue;
+ if (g_route->path.area_id != oa_route->path.area_id)
+ continue;
+ if (g_route->path.type != OSPF6_PATH_TYPE_INTRA)
+ continue;
+ if (g_route->path.cost != oa_route->path.cost)
+ continue;
+
+ if (ospf6_route_is_same_origin(g_route, oa_route)) {
+ h_path = (struct ospf6_path *)listgetdata(
+ listhead(g_route->paths));
+ g_route->path.origin.type = h_path->origin.type;
+ g_route->path.origin.id = h_path->origin.id;
+ g_route->path.origin.adv_router =
+ h_path->origin.adv_router;
+ break;
+ }
+ }
+
+ h_path = (struct ospf6_path *)listgetdata(
+ listhead(oa_route->paths));
+ oa_route->path.origin.type = h_path->origin.type;
+ oa_route->path.origin.id = h_path->origin.id;
+ oa_route->path.origin.adv_router = h_path->origin.adv_router;
+}
+
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_route *old_route, *ls_entry;
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;
+ struct interface *ifp;
+ struct ospf6_lsa *lsa;
+ struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
/* check for old entry match with new route origin,
* delete old entry.
@@ -1361,7 +1404,7 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
o_path->cost, route->path.cost);
}
- /* Remove selected current rout path's nh from
+ /* Remove selected current path's nh from
* effective nh list.
*/
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
@@ -1385,22 +1428,6 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
* 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)) {
@@ -1415,6 +1442,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
if (oa->route_table->hook_add)
(*oa->route_table->hook_add)
(old_route);
+
+ if (old_route->path.origin.id ==
+ route->path.origin.id &&
+ old_route->path.origin.adv_router ==
+ route->path.origin.adv_router) {
+ ospf6_intra_prefix_update_route_origin(
+ old_route);
+ }
break;
}
} else {
@@ -1426,8 +1461,12 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
old_route->path.cost,
route->path.cost);
}
- ospf6_route_remove(old_route,
+ if (oa->route_table->hook_remove)
+ ospf6_route_remove(old_route,
oa->route_table);
+ else
+ SET_FLAG(old_route->flag,
+ OSPF6_ROUTE_REMOVE);
break;
}
}
@@ -1467,72 +1506,101 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa,
/* 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",
+ 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->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);
- }
}
+ } else {
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);
+ }
+
+ list_delete_all_node(old_route->nh_list);
- 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);
+ for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
+ o_path)) {
+ ls_entry = ospf6_route_lookup(
+ &o_path->ls_prefix,
+ oa->spf_table);
+ if (ls_entry == NULL) {
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
+ zlog_debug("%s: ls_prfix %s ls_entry not found.",
+ __PRETTY_FUNCTION__,
+ buf);
+ continue;
}
+ lsa = ospf6_lsdb_lookup(o_path->origin.type,
+ o_path->origin.id,
+ o_path->origin.adv_router,
+ oa->lsdb);
+ if (lsa == NULL) {
+ if (IS_OSPF6_DEBUG_EXAMIN(
+ INTRA_PREFIX)) {
+ struct prefix adv_prefix;
- 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);
+ ospf6_linkstate_prefix(
+ o_path->origin.adv_router,
+ o_path->origin.id, &adv_prefix);
+ prefix2str(&adv_prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: adv_router %s lsa not found",
+ __PRETTY_FUNCTION__,
+ buf);
+ }
+ continue;
+ }
+ intra_prefix_lsa =
+ (struct ospf6_intra_prefix_lsa *)
+ OSPF6_LSA_HEADER_END(lsa->header);
+
+ if (intra_prefix_lsa->ref_adv_router
+ == oa->ospf6->router_id) {
+ ifp = if_lookup_prefix(
+ &old_route->prefix,
+ VRF_DEFAULT);
+ if (ifp)
+ ospf6_route_add_nexthop(
+ old_route,
+ ifp->ifindex,
+ NULL);
+ } else {
+ ospf6_route_merge_nexthops(old_route,
+ ls_entry);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("%s: route %s %p with final effective paths %u nh%u",
+ __PRETTY_FUNCTION__, buf,
+ (void *)old_route,
+ old_route->paths ?
+ listcount(old_route->paths) : 0,
+ listcount(old_route->nh_list));
}
+
+ /* used in intra_route_calculation() to add to
+ * global ospf6 route table.
+ */
+ UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE);
+ SET_FLAG(old_route->flag, OSPF6_ROUTE_ADD);
+ /* 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.
*/
@@ -1642,7 +1710,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
route->path.metric_type = 1;
route->path.cost =
ls_entry->path.cost + ntohs(op->prefix_metric);
-
+ memcpy(&route->path.ls_prefix, &ls_prefix,
+ sizeof(struct prefix));
if (direct_connect) {
ifp = if_lookup_prefix(&route->prefix, VRF_DEFAULT);
if (ifp)
@@ -1660,20 +1729,21 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
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,
+ zlog_debug("%s Update route: %s old cost %u new cost %u paths %u nh %u",
+ __PRETTY_FUNCTION__, buf,
old->path.cost, route->path.cost,
- listcount(route->nh_list),
- listcount(route->paths));
+ listcount(route->paths),
+ listcount(route->nh_list));
}
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));
+ zlog_debug("%s route %s add with cost %u paths %u nh %u",
+ __PRETTY_FUNCTION__, buf,
+ route->path.cost,
+ listcount(route->paths),
+ listcount(route->nh_list));
}
ospf6_route_add(route, oa->route_table);
}
@@ -1684,12 +1754,102 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
zlog_debug("Trailing garbage ignored");
}
+static void ospf6_intra_prefix_lsa_remove_update_route(struct ospf6_lsa *lsa,
+ struct ospf6_area *oa,
+ struct ospf6_route *route)
+{
+ struct listnode *anode, *anext;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ struct ospf6_path *o_path;
+ bool nh_updated = false;
+ char buf[PREFIX2STR_BUFFER];
+
+ /* 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(&route->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 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));
+ zlog_debug("%s: route %s update paths %u nh %u",
+ __PRETTY_FUNCTION__, buf,
+ route->paths ? listcount(route->paths) : 0,
+ route->nh_list ? listcount(route->nh_list)
+ : 0);
+ }
+
+ /* Update Global Route table and
+ * RIB/FIB with effective
+ * nh_list
+ */
+ if (oa->route_table->hook_add)
+ (*oa->route_table->hook_add)(route);
+
+ /* 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)) {
+ ospf6_intra_prefix_update_route_origin(route);
+ }
+ }
+
+}
+
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, *route_to_del;
+ struct ospf6_route *route, *nroute;
int prefix_num;
struct ospf6_prefix *op;
char *start, *current, *end;
@@ -1717,22 +1877,6 @@ 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;
@@ -1757,134 +1901,8 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
* 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;
-
+ ospf6_intra_prefix_lsa_remove_update_route(
+ lsa, oa, route);
} else {
if (route->path.origin.type != lsa->header->type
@@ -1896,9 +1914,11 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
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,
+ zlog_debug("%s: route remove %s with path type %u cost %u paths %u nh %u",
+ __PRETTY_FUNCTION__, buf,
+ route->path.type,
route->path.cost,
+ listcount(route->paths),
listcount(route->nh_list));
}
ospf6_route_remove(route, oa->route_table);
@@ -1906,8 +1926,6 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa)
}
if (route)
ospf6_route_unlock(route);
-
- ospf6_route_delete(route_to_del);
}
if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
@@ -2029,8 +2047,10 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
uint32_t brouter_id;
char brouter_name[16];
- if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id))
- zlog_info("border-router calculation for area %s", oa->name);
+ if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
+ IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ zlog_info("%s: border-router calculation for area %s",
+ __PRETTY_FUNCTION__, oa->name);
hook_add = oa->ospf6->brouter_table->hook_add;
hook_remove = oa->ospf6->brouter_table->hook_remove;
@@ -2096,6 +2116,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
for (brouter = ospf6_route_head(oa->ospf6->brouter_table); brouter;
brouter = nbrouter) {
+
/*
* brouter may have been "deleted" in the last loop iteration.
* If this is the case there is still 1 final refcount lock
@@ -2104,6 +2125,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
* skip processing the deleted route.
*/
if (brouter->lock == 1) {
+ if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ ospf6_brouter_debug_print(brouter);
nbrouter = ospf6_route_next(brouter);
continue;
} else {
@@ -2155,8 +2178,14 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
brouter_id)
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
oa->area_id))
- zlog_info("brouter %s disappears via area %s",
- brouter_name, oa->name);
+ zlog_info("%s: brouter %s disappears via area %s",
+ __PRETTY_FUNCTION__, brouter_name,
+ oa->name);
+ /* This is used to protect nbrouter from removed from
+ * the table. For an example, ospf6_abr_examin_summary,
+ * removes brouters which are marked for remove.
+ */
+ oa->intra_brouter_calc = 1;
ospf6_route_remove(brouter, oa->ospf6->brouter_table);
brouter = NULL;
} else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)
@@ -2166,8 +2195,9 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
brouter_id)
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
oa->area_id))
- zlog_info("brouter %s appears via area %s",
- brouter_name, oa->name);
+ zlog_info("%s: brouter %s appears via area %s",
+ __PRETTY_FUNCTION__, brouter_name,
+ oa->name);
/* newly added */
if (hook_add)
@@ -2187,11 +2217,14 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD);
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE);
}
+ /* Reset for nbrouter */
+ oa->intra_brouter_calc = 0;
}
- if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id))
- zlog_info("border-router calculation for area %s: done",
- oa->name);
+ if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
+ IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ zlog_info("%s: border-router calculation for area %s: done",
+ __PRETTY_FUNCTION__, oa->name);
}
struct ospf6_lsa_handler router_handler = {.lh_type = OSPF6_LSTYPE_ROUTER,
diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c
index 8be00d9b41..15d8eb6cf2 100644
--- a/ospf6d/ospf6_route.c
+++ b/ospf6d/ospf6_route.c
@@ -611,9 +611,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
prefix2str(&route->prefix, buf, sizeof(buf));
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_debug("%s %p: route add %p: %s",
+ zlog_debug("%s %p: route add %p: %s paths %u nh %u",
ospf6_route_table_name(table), (void *)table,
- (void *)route, buf);
+ (void *)route, buf, listcount(route->paths),
+ listcount(route->nh_list));
else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: %s", ospf6_route_table_name(table),
buf);
@@ -664,11 +665,13 @@ 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 nh %u: update of %p old cost %u nh %u",
+ "%s %p: route add %p cost %u paths %u nh %u: update of %p cost %u paths %u nh %u",
ospf6_route_table_name(table), (void *)table,
(void *)route, route->path.cost,
+ listcount(route->paths),
listcount(route->nh_list), (void *)old,
- old->path.cost, listcount(old->nh_list));
+ old->path.cost, listcount(old->paths),
+ listcount(old->nh_list));
else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: update",
ospf6_route_table_name(table));
@@ -922,10 +925,11 @@ struct ospf6_route *ospf6_route_next(struct ospf6_route *route)
struct ospf6_route *next = route->next;
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info("%s %p: route next: %p<-[%p]->%p",
+ zlog_info("%s %p: route next: %p<-[%p]->%p , route ref count %u",
ospf6_route_table_name(route->table),
(void *)route->table, (void *)route->prev,
- (void *)route, (void *)route->next);
+ (void *)route, (void *)route->next,
+ route->lock);
ospf6_route_unlock(route);
if (next)
diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h
index a69e9a920f..02002533e6 100644
--- a/ospf6d/ospf6_route.h
+++ b/ospf6d/ospf6_route.h
@@ -91,6 +91,9 @@ struct ospf6_path {
/* Cost */
uint8_t metric_type;
uint32_t cost;
+
+ struct prefix ls_prefix;
+
union {
uint32_t cost_e2;
uint32_t cost_config;
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index e4a4891526..7bf099fbbf 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -97,7 +97,8 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route)
static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
{
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) ||
+ IS_OSPF6_DEBUG_BROUTER) {
uint32_t brouter_id;
char brouter_name[16];
@@ -116,15 +117,17 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
static void ospf6_top_brouter_hook_remove(struct ospf6_route *route)
{
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) ||
+ IS_OSPF6_DEBUG_BROUTER) {
uint32_t brouter_id;
char brouter_name[16];
brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
inet_ntop(AF_INET, &brouter_id, brouter_name,
sizeof(brouter_name));
- zlog_debug("%s: brouter %s del with nh count %u",
- __PRETTY_FUNCTION__, brouter_name,
+ zlog_debug("%s: brouter %p %s del with adv router %x nh %u",
+ __PRETTY_FUNCTION__, (void *)route, brouter_name,
+ route->path.origin.adv_router,
listcount(route->nh_list));
}
route->flag |= OSPF6_ROUTE_REMOVE;
diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c
index 8d6d5b4a26..db61fe087b 100644
--- a/ospf6d/ospf6d.c
+++ b/ospf6d/ospf6d.c
@@ -97,7 +97,7 @@ DEFUN_NOSH (show_debugging_ospf6,
DEBUG_STR
OSPF6_STR)
{
- vty_out(vty, "OSPF6 debugging status:");
+ vty_out(vty, "OSPF6 debugging status:\n");
config_write_ospf6_debug(vty);
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 52b954d6a1..ea31d8c2ca 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -263,6 +263,9 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp,
ospf_opaque_type9_lsa_init(oi);
oi->ospf = ospf;
+
+ ospf_if_stream_set(oi);
+
QOBJ_REG(oi, ospf_interface);
if (IS_DEBUG_OSPF_EVENT)
@@ -322,6 +325,9 @@ void ospf_if_free(struct ospf_interface *oi)
{
ospf_if_down(oi);
+ if (oi->obuf)
+ ospf_fifo_free(oi->obuf);
+
assert(oi->state == ISM_Down);
ospf_opaque_type9_lsa_term(oi);
@@ -496,9 +502,8 @@ void ospf_if_stream_unset(struct ospf_interface *oi)
struct ospf *ospf = oi->ospf;
if (oi->obuf) {
- ospf_fifo_free(oi->obuf);
- oi->obuf = NULL;
-
+ /* flush the interface packet queue */
+ ospf_fifo_flush(oi->obuf);
/*reset protocol stats */
ospf_if_reset_stats(oi);
@@ -781,7 +786,6 @@ int ospf_if_up(struct ospf_interface *oi)
if (oi->type == OSPF_IFTYPE_LOOPBACK)
OSPF_ISM_EVENT_SCHEDULE(oi, ISM_LoopInd);
else {
- ospf_if_stream_set(oi);
OSPF_ISM_EVENT_SCHEDULE(oi, ISM_InterfaceUp);
}
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index 1ccf3ebffa..5be96e86d0 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -470,6 +470,18 @@ void pbr_nht_change_group(const char *name)
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
}
+/*
+ * Since we are writing into the name field which is PBR_MAP_NAMELEN
+ * size, we are expecting this to field to be at max 100 bytes.
+ * Newer compilers understand that the %s portion may be up to
+ * 100 bytes( because of the size of the string. The %u portion
+ * is expected to be 10 bytes. So in `theory` there are situations
+ * where we might truncate. The reality this is never going to
+ * happen( who is going to create a nexthop group name that is
+ * over say 30 characters? ). As such we are expecting the
+ * calling function to subtract 10 from the size_t l before
+ * we pass it in to get around this new gcc fun.
+ */
char *pbr_nht_nexthop_make_name(char *name, size_t l,
uint32_t seqno, char *buffer)
{
@@ -485,7 +497,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
struct pbr_nexthop_cache lookup;
memset(&find, 0, sizeof(find));
- pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_MAP_NAMELEN,
+ pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_MAP_NAMELEN - 10,
pbrms->seqno, find.name);
if (!pbrms->internal_nhg_name)
pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index 03f2104835..b7d2b1a928 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -38,7 +38,7 @@
#include "pbrd/pbr_vty_clippy.c"
#endif
-DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)",
+DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)",
"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"
@@ -54,7 +54,7 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)",
return CMD_SUCCESS;
}
-DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
+DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]",
NO_STR
"Delete pbr-map\n"
"The name of the PBR MAP\n"
@@ -269,7 +269,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
if (pbrms->nhg)
nh = nexthop_exists(pbrms->nhg, &nhop);
else {
- char buf[100];
+ char buf[PBR_MAP_NAMELEN];
if (no) {
vty_out(vty, "No nexthops to delete");
@@ -280,7 +280,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
pbrms->internal_nhg_name =
XSTRDUP(MTYPE_TMP,
pbr_nht_nexthop_make_name(pbrms->parent->name,
- PBR_MAP_NAMELEN,
+ PBR_MAP_NAMELEN - 10,
pbrms->seqno,
buf));
nh = NULL;
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index bc7dd20832..cdacfad4b4 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -479,6 +479,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
stream_putw(s, 0); /* src port */
pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
stream_putw(s, 0); /* dst port */
+ stream_putl(s, 0); /* fwmark */
if (pbrms->nhgrp_name)
stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name));
else if (pbrms->nhg)
diff --git a/pimd/COMMANDS b/pimd/COMMANDS
index 6f2e020bd8..141ec62aeb 100644
--- a/pimd/COMMANDS
+++ b/pimd/COMMANDS
@@ -24,6 +24,7 @@ verification commands:
show ip igmp groups retransmissions IGMP group retransmission
show ip igmp sources IGMP sources information
show ip igmp sources retransmissions IGMP source retransmission
+ show ip igmp statistics IGMP statistics information
show ip pim address PIM interface address
show ip pim assert PIM interface assert
show ip pim assert-internal PIM interface internal assert state
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 35514a85da..81191eb96c 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1294,6 +1294,76 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
}
}
+static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
+ const char *ifname, uint8_t uj)
+{
+ struct interface *ifp;
+ struct igmp_stats rx_stats;
+
+ igmp_stats_init(&rx_stats);
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp;
+ struct listnode *sock_node;
+ struct igmp_sock *igmp;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (ifname && strcmp(ifname, ifp->name))
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
+ igmp)) {
+ igmp_stats_add(&rx_stats, &igmp->rx_stats);
+ }
+ }
+ if (uj) {
+ json_object *json = NULL;
+ json_object *json_row = NULL;
+
+ json = json_object_new_object();
+ json_row = json_object_new_object();
+
+ json_object_string_add(json_row, "name", ifname ? ifname :
+ "global");
+ json_object_int_add(json_row, "queryV1", rx_stats.query_v1);
+ json_object_int_add(json_row, "queryV2", rx_stats.query_v2);
+ json_object_int_add(json_row, "queryV3", rx_stats.query_v3);
+ json_object_int_add(json_row, "leaveV3", rx_stats.leave_v2);
+ json_object_int_add(json_row, "reportV1", rx_stats.report_v1);
+ json_object_int_add(json_row, "reportV2", rx_stats.report_v2);
+ json_object_int_add(json_row, "reportV3", rx_stats.report_v3);
+ json_object_int_add(json_row, "mtraceResponse",
+ rx_stats.mtrace_rsp);
+ json_object_int_add(json_row, "mtraceRequest",
+ rx_stats.mtrace_req);
+ json_object_int_add(json_row, "unsupported",
+ rx_stats.unsupported);
+ json_object_object_add(json, ifname ? ifname : "global",
+ json_row);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ } else {
+ vty_out(vty, "IGMP RX statistics\n");
+ vty_out(vty, "Interface : %s\n",
+ ifname ? ifname : "global");
+ vty_out(vty, "V1 query : %u\n", rx_stats.query_v1);
+ vty_out(vty, "V2 query : %u\n", rx_stats.query_v2);
+ vty_out(vty, "V3 query : %u\n", rx_stats.query_v3);
+ vty_out(vty, "V2 leave : %u\n", rx_stats.leave_v2);
+ vty_out(vty, "V1 report : %u\n", rx_stats.report_v1);
+ vty_out(vty, "V2 report : %u\n", rx_stats.report_v2);
+ vty_out(vty, "V3 report : %u\n", rx_stats.report_v3);
+ vty_out(vty, "mtrace response : %u\n", rx_stats.mtrace_rsp);
+ vty_out(vty, "mtrace request : %u\n", rx_stats.mtrace_req);
+ vty_out(vty, "unsupported : %u\n", rx_stats.unsupported);
+ }
+}
+
static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty,
uint8_t uj)
{
@@ -3527,6 +3597,33 @@ DEFUN (show_ip_igmp_sources_retransmissions,
return CMD_SUCCESS;
}
+DEFUN (show_ip_igmp_statistics,
+ show_ip_igmp_statistics_cmd,
+ "show ip igmp [vrf NAME] statistics [interface WORD] [json]",
+ SHOW_STR
+ IP_STR
+ IGMP_STR
+ VRF_CMD_HELP_STR
+ "IGMP statistics\n"
+ "interface\n"
+ "IGMP interface\n"
+ JSON_STR)
+{
+ int idx = 2;
+ struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+ uint8_t uj = use_json(argc, argv);
+
+ if (!vrf)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "WORD", &idx))
+ igmp_show_statistics(vrf->info, vty, argv[idx]->arg, uj);
+ else
+ igmp_show_statistics(vrf->info, vty, NULL, uj);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_pim_assert,
show_ip_pim_assert_cmd,
"show ip pim [vrf NAME] assert",
@@ -8644,6 +8741,7 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd);
install_element(VIEW_NODE, &show_ip_igmp_sources_cmd);
install_element(VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd);
+ install_element(VIEW_NODE, &show_ip_igmp_statistics_cmd);
install_element(VIEW_NODE, &show_ip_pim_assert_cmd);
install_element(VIEW_NODE, &show_ip_pim_assert_internal_cmd);
install_element(VIEW_NODE, &show_ip_pim_assert_metric_cmd);
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index ff7238ae97..5c4d99ad4d 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -62,7 +62,16 @@ void pim_if_init(struct pim_instance *pim)
void pim_if_terminate(struct pim_instance *pim)
{
- // Nothing to do at this moment
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ pim_if_delete(ifp);
+ }
return;
}
@@ -1547,27 +1556,20 @@ int pim_if_connected_to_source(struct interface *ifp, struct in_addr src)
return 0;
}
-int pim_if_is_loopback(struct pim_instance *pim, struct interface *ifp)
+bool pim_if_is_loopback(struct interface *ifp)
{
- if (if_is_loopback(ifp))
- return 1;
+ if (if_is_loopback(ifp) || if_is_vrf(ifp))
+ return true;
- if (strcmp(ifp->name, pim->vrf->name) == 0)
- return 1;
-
- return 0;
+ return false;
}
-int pim_if_is_vrf_device(struct interface *ifp)
+bool pim_if_is_vrf_device(struct interface *ifp)
{
- struct vrf *vrf;
+ if (if_is_vrf(ifp))
+ return true;
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- if (strncmp(ifp->name, vrf->name, strlen(ifp->name)) == 0)
- return 1;
- }
-
- return 0;
+ return false;
}
int pim_if_ifchannel_count(struct pim_interface *pim_ifp)
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
index 5ecd07d227..cf025cbd4a 100644
--- a/pimd/pim_iface.h
+++ b/pimd/pim_iface.h
@@ -207,9 +207,9 @@ void pim_if_create_pimreg(struct pim_instance *pim);
int pim_if_connected_to_source(struct interface *ifp, struct in_addr src);
int pim_update_source_set(struct interface *ifp, struct in_addr source);
-int pim_if_is_loopback(struct pim_instance *pim, struct interface *ifp);
+bool pim_if_is_loopback(struct interface *ifp);
-int pim_if_is_vrf_device(struct interface *ifp);
+bool pim_if_is_vrf_device(struct interface *ifp);
int pim_if_ifchannel_count(struct pim_interface *pim_ifp);
#endif /* PIM_IFACE_H */
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index bb56379c14..5e6b0f10cf 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -124,11 +124,6 @@ static void pim_ifchannel_find_new_children(struct pim_ifchannel *ch)
}
}
-void pim_ifchannel_free(struct pim_ifchannel *ch)
-{
- XFREE(MTYPE_PIM_IFCHANNEL, ch);
-}
-
void pim_ifchannel_delete(struct pim_ifchannel *ch)
{
struct pim_interface *pim_ifp;
@@ -198,7 +193,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch)
zlog_debug("%s: ifchannel entry %s is deleted ",
__PRETTY_FUNCTION__, ch->sg_str);
- pim_ifchannel_free(ch);
+ XFREE(MTYPE_PIM_IFCHANNEL, ch);
}
void pim_ifchannel_delete_all(struct interface *ifp)
diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h
index 0af9ebf0be..b9d4d291d8 100644
--- a/pimd/pim_ifchannel.h
+++ b/pimd/pim_ifchannel.h
@@ -114,7 +114,6 @@ RB_HEAD(pim_ifchannel_rb, pim_ifchannel);
RB_PROTOTYPE(pim_ifchannel_rb, pim_ifchannel, pim_ifp_rb,
pim_ifchannel_compare);
-void pim_ifchannel_free(struct pim_ifchannel *ch);
void pim_ifchannel_delete(struct pim_ifchannel *ch);
void pim_ifchannel_delete_all(struct interface *ifp);
void pim_ifchannel_membership_clear(struct interface *ifp);
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 5e1aecc3a3..c980f5fcba 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -303,6 +303,21 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version,
return -1;
}
+ /* Collecting IGMP Rx stats */
+ switch (query_version) {
+ case 1:
+ igmp->rx_stats.query_v1++;
+ break;
+ case 2:
+ igmp->rx_stats.query_v2++;
+ break;
+ case 3:
+ igmp->rx_stats.query_v3++;
+ break;
+ default:
+ igmp->rx_stats.unsupported++;
+ }
+
/*
* RFC 3376 defines some guidelines on operating in backwards
* compatibility with older versions of IGMP but there are some gaps in
@@ -400,6 +415,9 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from,
return -1;
}
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.report_v1++;
+
if (PIM_DEBUG_IGMP_TRACE) {
zlog_warn("%s %s: FIXME WRITEME", __FILE__,
__PRETTY_FUNCTION__);
@@ -524,6 +542,9 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.unsupported++;
+
return -1;
}
@@ -867,6 +888,8 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
pim_ifp->igmp_default_robustness_variable;
igmp->sock_creation = pim_time_monotonic_sec();
+ igmp_stats_init(&igmp->rx_stats);
+
if (mtrace_only) {
igmp->mtrace_only = mtrace_only;
return igmp;
diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h
index 561a127d0f..c8b880ddd7 100644
--- a/pimd/pim_igmp.h
+++ b/pimd/pim_igmp.h
@@ -25,6 +25,7 @@
#include <zebra.h>
#include "vty.h"
#include "linklist.h"
+#include "pim_igmp_stats.h"
/*
The following sizes are likely to support
@@ -94,6 +95,8 @@ struct igmp_sock {
struct list *igmp_group_list; /* list of struct igmp_group */
struct hash *igmp_group_hash;
+
+ struct igmp_stats rx_stats;
};
struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c
index d3ae185709..673e2ca5b8 100644
--- a/pimd/pim_igmp_mtrace.c
+++ b/pimd/pim_igmp_mtrace.c
@@ -671,6 +671,9 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
return -1;
}
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.mtrace_req++;
+
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
@@ -881,6 +884,9 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
mtracep->checksum = checksum;
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.mtrace_rsp++;
+
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c
new file mode 100644
index 0000000000..428816e1f0
--- /dev/null
+++ b/pimd/pim_igmp_stats.c
@@ -0,0 +1,42 @@
+/*
+ * PIM for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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 "pim_igmp_stats.h"
+
+void igmp_stats_init(struct igmp_stats *stats)
+{
+ memset(stats, 0, sizeof(struct igmp_stats));
+}
+
+void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b)
+{
+ if (!a || !b)
+ return;
+
+ a->query_v1 += b->query_v1;
+ a->query_v2 += b->query_v2;
+ a->query_v3 += b->query_v3;
+ a->report_v1 += b->report_v1;
+ a->report_v2 += b->report_v2;
+ a->report_v3 += b->report_v3;
+ a->leave_v2 += b->leave_v2;
+ a->mtrace_rsp += b->mtrace_rsp;
+ a->mtrace_req += b->mtrace_req;
+ a->unsupported += b->unsupported;
+}
diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h
new file mode 100644
index 0000000000..57b5cc62f4
--- /dev/null
+++ b/pimd/pim_igmp_stats.h
@@ -0,0 +1,41 @@
+/*
+ * PIM for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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 PIM_IGMP_STATS_H
+#define PIM_IGMP_STATS_H
+
+#include <zebra.h>
+
+struct igmp_stats {
+ uint32_t query_v1;
+ uint32_t query_v2;
+ uint32_t query_v3;
+ uint32_t report_v1;
+ uint32_t report_v2;
+ uint32_t report_v3;
+ uint32_t leave_v2;
+ uint32_t mtrace_rsp;
+ uint32_t mtrace_req;
+ uint32_t unsupported;
+};
+
+void igmp_stats_init(struct igmp_stats *stats);
+void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b);
+
+#endif /* PIM_IGMP_STATS_H */
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
index dbbe83a965..19c3768813 100644
--- a/pimd/pim_igmpv2.c
+++ b/pimd/pim_igmpv2.c
@@ -121,6 +121,9 @@ int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from,
return -1;
}
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.report_v2++;
+
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
if (PIM_DEBUG_IGMP_PACKETS) {
@@ -167,6 +170,9 @@ int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from,
return -1;
}
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.leave_v2++;
+
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
if (PIM_DEBUG_IGMP_PACKETS) {
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
index 3360e36b4a..5ccad39b33 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -1900,6 +1900,9 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from,
return -1;
}
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.report_v3++;
+
num_groups = ntohs(
*(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
if (num_groups < 1) {
diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c
index ab4ffc26ae..7e5bb34e31 100644
--- a/pimd/pim_instance.c
+++ b/pimd/pim_instance.c
@@ -35,13 +35,6 @@
static void pim_instance_terminate(struct pim_instance *pim)
{
- /* Traverse and cleanup rpf_hash */
- if (pim->rpf_hash) {
- hash_clean(pim->rpf_hash, (void *)pim_rp_list_hash_clean);
- hash_free(pim->rpf_hash);
- pim->rpf_hash = NULL;
- }
-
if (pim->ssm_info) {
pim_ssm_terminate(pim->ssm_info);
pim->ssm_info = NULL;
@@ -54,6 +47,13 @@ static void pim_instance_terminate(struct pim_instance *pim)
pim_upstream_terminate(pim);
+ /* Traverse and cleanup rpf_hash */
+ if (pim->rpf_hash) {
+ hash_clean(pim->rpf_hash, (void *)pim_rp_list_hash_clean);
+ hash_free(pim->rpf_hash);
+ pim->rpf_hash = NULL;
+ }
+
pim_oil_terminate(pim);
pim_if_terminate(pim);
diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c
index 53a3382987..7afc37a28e 100644
--- a/pimd/pim_msdp.c
+++ b/pimd/pim_msdp.c
@@ -228,6 +228,8 @@ static void pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa,
/* release all mem associated with a sa */
static void pim_msdp_sa_free(struct pim_msdp_sa *sa)
{
+ pim_msdp_sa_state_timer_setup(sa, false);
+
XFREE(MTYPE_PIM_MSDP_SA, sa);
}
@@ -1170,6 +1172,12 @@ enum pim_msdp_err pim_msdp_peer_add(struct pim_instance *pim,
/* release all mem associated with a peer */
static void pim_msdp_peer_free(struct pim_msdp_peer *mp)
{
+ /*
+ * Let's make sure we are not running when we delete
+ * the underlying data structure
+ */
+ pim_msdp_peer_stop_tcp_conn(mp, false);
+
if (mp->ibuf) {
stream_free(mp->ibuf);
}
@@ -1181,6 +1189,8 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp)
if (mp->mesh_group_name) {
XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name);
}
+
+ mp->pim = NULL;
XFREE(MTYPE_PIM_MSDP_PEER, mp);
}
@@ -1611,6 +1621,8 @@ void pim_msdp_init(struct pim_instance *pim, struct thread_master *master)
/* counterpart to MSDP init; XXX: unused currently */
void pim_msdp_exit(struct pim_instance *pim)
{
+ pim_msdp_sa_adv_timer_setup(pim, false);
+
/* XXX: stop listener and delete all peer sessions */
if (pim->msdp.peer_hash) {
diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c
index ffe5d52a15..de09b070f4 100644
--- a/pimd/pim_pim.c
+++ b/pimd/pim_pim.c
@@ -653,7 +653,7 @@ static int pim_hello_send(struct interface *ifp, uint16_t holdtime)
{
struct pim_interface *pim_ifp = ifp->info;
- if (pim_if_is_loopback(pim_ifp->pim, ifp))
+ if (pim_if_is_loopback(ifp))
return 0;
if (hello_send(ifp, holdtime)) {
@@ -755,7 +755,7 @@ void pim_hello_restart_triggered(struct interface *ifp)
/*
* No need to ever start loopback or vrf device hello's
*/
- if (pim_if_is_loopback(pim_ifp->pim, ifp))
+ if (pim_if_is_loopback(ifp))
return;
/*
diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c
index b5f5f646d4..9329d72ce0 100644
--- a/pimd/pim_upstream.c
+++ b/pimd/pim_upstream.c
@@ -140,12 +140,6 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim,
return NULL;
}
-void pim_upstream_free(struct pim_upstream *up)
-{
- XFREE(MTYPE_PIM_UPSTREAM, up);
- up = NULL;
-}
-
static void upstream_channel_oil_detach(struct pim_upstream *up)
{
if (up->channel_oil) {
@@ -161,6 +155,8 @@ static void upstream_channel_oil_detach(struct pim_upstream *up)
struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
struct pim_upstream *up, const char *name)
{
+ struct listnode *node, *nnode;
+ struct pim_ifchannel *ch;
bool notify_msdp = false;
struct prefix nht_p;
@@ -200,20 +196,17 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
notify_msdp = true;
}
- pim_upstream_remove_children(pim, up);
- if (up->sources)
- list_delete_and_null(&up->sources);
-
pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
upstream_channel_oil_detach(up);
+ for (ALL_LIST_ELEMENTS(up->ifchannels, node, nnode, ch))
+ pim_ifchannel_delete(ch);
list_delete_and_null(&up->ifchannels);
- /*
- notice that listnode_delete() can't be moved
- into pim_upstream_free() because the later is
- called by list_delete_all_node()
- */
+ pim_upstream_remove_children(pim, up);
+ if (up->sources)
+ list_delete_and_null(&up->sources);
+
if (up->parent && up->parent->sources)
listnode_delete(up->parent->sources, up);
up->parent = NULL;
@@ -237,7 +230,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
}
pim_delete_tracked_nexthop(pim, &nht_p, up, NULL);
- pim_upstream_free(up);
+ XFREE(MTYPE_PIM_UPSTREAM, up);
return NULL;
}
@@ -1545,8 +1538,15 @@ unsigned int pim_upstream_hash_key(void *arg)
void pim_upstream_terminate(struct pim_instance *pim)
{
- if (pim->upstream_list)
+ struct listnode *node, *nnode;
+ struct pim_upstream *up;
+
+ if (pim->upstream_list) {
+ for (ALL_LIST_ELEMENTS(pim->upstream_list, node, nnode, up))
+ pim_upstream_del(pim, up, __PRETTY_FUNCTION__);
+
list_delete_and_null(&pim->upstream_list);
+ }
if (pim->upstream_hash)
hash_free(pim->upstream_hash);
@@ -1773,6 +1773,5 @@ void pim_upstream_init(struct pim_instance *pim)
pim_upstream_equal, hash_name);
pim->upstream_list = list_new();
- pim->upstream_list->del = (void (*)(void *))pim_upstream_free;
pim->upstream_list->cmp = pim_upstream_compare;
}
diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h
index b75a5b9df3..ba133b39dd 100644
--- a/pimd/pim_upstream.h
+++ b/pimd/pim_upstream.h
@@ -137,7 +137,6 @@ struct pim_upstream {
int64_t state_transition; /* Record current state uptime */
};
-void pim_upstream_free(struct pim_upstream *up);
struct pim_upstream *pim_upstream_find(struct pim_instance *pim,
struct prefix_sg *sg);
struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
index 6cce697202..ae7f61c8eb 100644
--- a/pimd/pim_zebra.c
+++ b/pimd/pim_zebra.c
@@ -69,6 +69,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct interface *ifp;
+ struct pim_instance *pim;
/*
zebra api adds/dels interfaces using the same call
@@ -78,6 +79,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient,
if (!ifp)
return 0;
+ pim = pim_get_pim_instance(vrf_id);
if (PIM_DEBUG_ZEBRA) {
zlog_debug(
"%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
@@ -86,8 +88,19 @@ static int pim_zebra_if_add(int command, struct zclient *zclient,
if_is_operative(ifp));
}
- if (if_is_operative(ifp))
+ if (if_is_operative(ifp)) {
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ /*
+ * If we have a pim_ifp already and this is an if_add
+ * that means that we probably have a vrf move event
+ * If that is the case, set the proper vrfness.
+ */
+ if (pim_ifp)
+ pim_ifp->pim = pim;
pim_if_addr_add_all(ifp);
+ }
/*
* If we are a vrf device that is up, open up the pim_socket for
@@ -145,6 +158,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient,
static int pim_zebra_if_state_up(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
+ struct pim_instance *pim;
struct interface *ifp;
uint32_t table_id;
@@ -164,7 +178,19 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient,
if_is_operative(ifp));
}
+ pim = pim_get_pim_instance(vrf_id);
if (if_is_operative(ifp)) {
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ /*
+ * If we have a pim_ifp already and this is an if_add
+ * that means that we probably have a vrf move event
+ * If that is the case, set the proper vrfness.
+ */
+ if (pim_ifp)
+ pim_ifp->pim = pim;
+
/*
pim_if_addr_add_all() suffices for bringing up both IGMP and
PIM
@@ -274,6 +300,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
struct connected *c;
struct prefix *p;
struct pim_interface *pim_ifp;
+ struct pim_instance *pim;
/*
zebra api notifies address adds/dels events by using the same call
@@ -327,8 +354,12 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient,
}
pim_if_addr_add(c);
- if (pim_ifp)
+ if (pim_ifp) {
+ pim = pim_get_pim_instance(vrf_id);
+ pim_ifp->pim = pim;
+
pim_rp_check_on_if_add(pim_ifp);
+ }
if (if_is_loopback(c->ifp)) {
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 2254362221..0696d9b1e8 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -20,6 +20,7 @@ pimd_libpim_a_SOURCES = \
pimd/pim_ifchannel.c \
pimd/pim_igmp.c \
pimd/pim_igmp_mtrace.c \
+ pimd/pim_igmp_stats.c \
pimd/pim_igmpv2.c \
pimd/pim_igmpv3.c \
pimd/pim_instance.c \
@@ -69,6 +70,7 @@ noinst_HEADERS += \
pimd/pim_igmp.h \
pimd/pim_igmp_join.h \
pimd/pim_igmp_mtrace.h \
+ pimd/pim_igmp_stats.h \
pimd/pim_igmpv2.h \
pimd/pim_igmpv3.h \
pimd/pim_instance.h \
diff --git a/redhat/frr.service b/redhat/frr.service
index cc6ec429a3..3ae0aabfe2 100644
--- a/redhat/frr.service
+++ b/redhat/frr.service
@@ -9,7 +9,7 @@ Type=forking
NotifyAccess=all
StartLimitInterval=3m
StartLimitBurst=3
-TimeoutSec=1m
+TimeoutSec=2m
WatchdogSec=60s
RestartSec=5
Restart=on-abnormal
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index f83c08e611..8632a4fb2f 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -534,7 +534,7 @@ rm -rf %{buildroot}
%defattr(-,root,root)
%doc */*.sample* AUTHORS COPYING
%doc doc/mpls
-%doc ChangeLog INSTALL NEWS README REPORTING-BUGS SERVICES
+%doc ChangeLog NEWS README SERVICES
%if 0%{?frr_user:1}
%dir %attr(751,%frr_user,%frr_user) %{_sysconfdir}
%dir %attr(750,%frr_user,%frr_user) /var/log/frr
diff --git a/snapcraft/defaults/babeld.conf.default b/snapcraft/defaults/babeld.conf.default
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/snapcraft/defaults/babeld.conf.default
diff --git a/snapcraft/defaults/eigrpd.conf.default b/snapcraft/defaults/eigrpd.conf.default
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/snapcraft/defaults/eigrpd.conf.default
diff --git a/snapcraft/defaults/pbrd.conf.default b/snapcraft/defaults/pbrd.conf.default
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/snapcraft/defaults/pbrd.conf.default
diff --git a/snapcraft/scripts/Makefile b/snapcraft/scripts/Makefile
index 966623d11d..110f4b2878 100644
--- a/snapcraft/scripts/Makefile
+++ b/snapcraft/scripts/Makefile
@@ -12,6 +12,9 @@ install:
install -D -m 0755 pimd-service $(DESTDIR)/bin/
install -D -m 0755 ldpd-service $(DESTDIR)/bin/
install -D -m 0755 nhrpd-service $(DESTDIR)/bin/
+ install -D -m 0755 babeld-service $(DESTDIR)/bin/
+ install -D -m 0755 eigrpd-service $(DESTDIR)/bin/
+ install -D -m 0755 pbrd-service $(DESTDIR)/bin/
install -D -m 0755 set-options $(DESTDIR)/bin/
install -D -m 0755 show_version $(DESTDIR)/bin/
diff --git a/snapcraft/scripts/babeld-service b/snapcraft/scripts/babeld-service
new file mode 100644
index 0000000000..9e022f8569
--- /dev/null
+++ b/snapcraft/scripts/babeld-service
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e -x
+
+if ! [ -e $SNAP_DATA/babeld.conf ]; then
+ cp $SNAP/etc/frr/babeld.conf.default $SNAP_DATA/babeld.conf
+fi
+exec $SNAP/sbin/babeld \
+ -f $SNAP_DATA/babeld.conf \
+ --pid_file $SNAP_DATA/babeld.pid \
+ --socket $SNAP_DATA/zsock \
+ --vty_socket $SNAP_DATA
+
diff --git a/snapcraft/scripts/eigrpd-service b/snapcraft/scripts/eigrpd-service
new file mode 100644
index 0000000000..fe945e5f7d
--- /dev/null
+++ b/snapcraft/scripts/eigrpd-service
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e -x
+
+if ! [ -e $SNAP_DATA/eigrpd.conf ]; then
+ cp $SNAP/etc/frr/eigrpd.conf.default $SNAP_DATA/eigrpd.conf
+fi
+exec $SNAP/sbin/eigrpd \
+ -f $SNAP_DATA/eigrpd.conf \
+ --pid_file $SNAP_DATA/eigrpd.pid \
+ --socket $SNAP_DATA/zsock \
+ --vty_socket $SNAP_DATA
+
diff --git a/snapcraft/scripts/pbrd-service b/snapcraft/scripts/pbrd-service
new file mode 100644
index 0000000000..a9265a1ae6
--- /dev/null
+++ b/snapcraft/scripts/pbrd-service
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e -x
+
+if ! [ -e $SNAP_DATA/pbrd.conf ]; then
+ cp $SNAP/etc/frr/pbrd.conf.default $SNAP_DATA/pbrd.conf
+fi
+exec $SNAP/sbin/pbrd \
+ -f $SNAP_DATA/pbrd.conf \
+ --pid_file $SNAP_DATA/pbrd.pid \
+ --socket $SNAP_DATA/zsock \
+ --vty_socket $SNAP_DATA
+
diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in
index 17fabb16f8..b4e1812c86 100644
--- a/snapcraft/snapcraft.yaml.in
+++ b/snapcraft/snapcraft.yaml.in
@@ -4,7 +4,8 @@ summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP routing daemon
description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM routing daemon
FRRouting (FRR) is free software which manages TCP/IP based routing
protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2,
- RIPng, PIM and LDP as well as the IPv6 versions of these.
+ RIPng, PIM, LDP, Babel, EIGRP and PBR (Policy-based routing) as well as
+ the IPv6 versions of these.
FRRouting (frr) is a fork of Quagga.
confinement: strict
grade: devel
@@ -91,6 +92,27 @@ apps:
- network
- network-bind
- network-control
+ babeld:
+ command: bin/babeld-service
+ daemon: simple
+ plugs:
+ - network
+ - network-bind
+ - network-control
+ eigrpd:
+ command: bin/eigrpd-service
+ daemon: simple
+ plugs:
+ - network
+ - network-bind
+ - network-control
+ pbrd:
+ command: bin/pbrd-service
+ daemon: simple
+ plugs:
+ - network
+ - network-bind
+ - network-control
set:
command: bin/set-options
zebra-debug:
@@ -153,7 +175,25 @@ apps:
- network
- network-bind
- network-control
-
+ babeld-debug:
+ command: sbin/babeld -f $SNAP_DATA/babeld.conf --pid_file $SNAP_DATA/babeld.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA
+ plugs:
+ - network
+ - network-bind
+ - network-control
+ eigrpd-debug:
+ command: sbin/eigrpd -f $SNAP_DATA/eigrpd.conf --pid_file $SNAP_DATA/eigrpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA
+ plugs:
+ - network
+ - network-bind
+ - network-control
+ pbrd-debug:
+ command: sbin/pbrd -f $SNAP_DATA/pbrd.conf --pid_file $SNAP_DATA/pbrd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA
+ plugs:
+ - network
+ - network-bind
+ - network-control
+
parts:
frr:
build-packages:
@@ -190,6 +230,9 @@ parts:
- libtinfo5
- libreadline6
- libjson-c2
+ - libc-ares2
+ - libatm1
+ - libprotobuf-c1
plugin: autotools
source: ../frr-@PACKAGE_VERSION@.tar.gz
configflags:
@@ -228,6 +271,9 @@ parts:
ripngd.conf.default: etc/frr/ripngd.conf.default
ldpd.conf.default: etc/frr/ldpd.conf.default
nhrpd.conf.default: etc/frr/nhrpd.conf.default
+ babeld.conf.default: etc/frr/babeld.conf.default
+ eigrpd.conf.default: etc/frr/eigrpd.conf.default
+ pbrd.conf.default: etc/frr/pbrd.conf.default
vtysh.conf.default: etc/frr/vtysh.conf.default
frr-scripts:
plugin: make
diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c
index 34c35cfcc5..8db1cb2ca1 100644
--- a/tests/bgpd/test_mp_attr.c
+++ b/tests/bgpd/test_mp_attr.c
@@ -945,6 +945,24 @@ static struct test_segment mp_unreach_segments[] = {
},
{NULL, NULL, {0}, 0, 0}};
+static struct test_segment mp_prefix_sid[] = {
+ {
+ "PREFIX-SID",
+ "PREFIX-SID Test 1",
+ {
+ 0x01, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02,
+ 0x03, 0x00, 0x08, 0x00,
+ 0x00, 0x0a, 0x1b, 0xfe,
+ 0x00, 0x00, 0x0a
+ },
+ .len = 21,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, { 0 }, 0, 0},
+};
+
/* nlri_parse indicates 0 on successful parse, and -1 otherwise.
* attr_parse indicates BGP_ATTR_PARSE_PROCEED/0 on success,
* and BGP_ATTR_PARSE_ERROR/-1 or lower negative ret on err.
@@ -1000,10 +1018,20 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type)
printf("%s: %s\n", t->name, t->desc);
- if (type == BGP_ATTR_MP_REACH_NLRI)
+ switch (type) {
+ case BGP_ATTR_MP_REACH_NLRI:
parse_ret = bgp_mp_reach_parse(&attr_args, &nlri);
- else
+ break;
+ case BGP_ATTR_MP_UNREACH_NLRI:
parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri);
+ break;
+ case BGP_ATTR_PREFIX_SID:
+ parse_ret = bgp_attr_prefix_sid(t->len, &attr_args, &nlri);
+ break;
+ default:
+ printf("unknown type");
+ return;
+ }
if (!parse_ret) {
iana_afi_t pkt_afi;
iana_safi_t pkt_safi;
@@ -1022,7 +1050,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type)
if (!parse_ret) {
if (type == BGP_ATTR_MP_REACH_NLRI)
nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 0);
- else
+ else if (type == BGP_ATTR_MP_UNREACH_NLRI)
nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 1);
}
handle_result(peer, t, parse_ret, nlri_ret);
@@ -1033,6 +1061,7 @@ static as_t asn = 100;
int main(void)
{
+ struct interface ifp;
struct peer *peer;
int i, j;
@@ -1065,6 +1094,9 @@ int main(void)
peer->status = Established;
peer->curr = stream_new(BGP_MAX_PACKET_SIZE);
+ ifp.ifindex = 0;
+ peer->nexthop.ifp = &ifp;
+
for (i = AFI_IP; i < AFI_MAX; i++)
for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
peer->afc[i][j] = 1;
@@ -1081,6 +1113,10 @@ int main(void)
parse_test(peer, &mp_unreach_segments[i++],
BGP_ATTR_MP_UNREACH_NLRI);
+ i = 0;
+ while (mp_prefix_sid[i].name)
+ parse_test(peer, &mp_prefix_sid[i++],
+ BGP_ATTR_PREFIX_SID);
printf("failures: %d\n", failed);
return failed;
}
diff --git a/tools/etc/iproute2/rt_protos.d/frr.conf b/tools/etc/iproute2/rt_protos.d/frr.conf
index b8d4c1c03b..cac75bdfba 100644
--- a/tools/etc/iproute2/rt_protos.d/frr.conf
+++ b/tools/etc/iproute2/rt_protos.d/frr.conf
@@ -8,4 +8,5 @@
191 nhrp
192 eigrp
193 ldp
-194 sharp \ No newline at end of file
+194 sharp
+195 pbr
diff --git a/tools/frr b/tools/frr
index 27136bb762..fec94af689 100755
--- a/tools/frr
+++ b/tools/frr
@@ -552,16 +552,19 @@ case "$1" in
# Additionally if a new protocol is added
# we need to add it here as well as
# in rt_netlink.h( follow the directions! )
+ ip route flush proto 4
+ ip route flush proto 11
+ ip route flush proto 42
ip route flush proto 186
+ ip route flush proto 187
ip route flush proto 188
- ip route flush proto 4
ip route flush proto 189
ip route flush proto 190
- ip route flush proto 11
- ip route flush proto 187
- ip route flush proto 192
- ip route flush proto 42
ip route flush proto 191
+ ip route flush proto 192
+ ip route flush proto 193
+ ip route flush proto 194
+ ip route flush proto 195
else
[ -n "$dmn" ] && eval "${dmn/-/_}=0"
start_watchfrr
diff --git a/tools/frr.service b/tools/frr.service
index 8800bf6b0f..5f44274ec3 100644
--- a/tools/frr.service
+++ b/tools/frr.service
@@ -10,7 +10,7 @@ Type=forking
NotifyAccess=all
StartLimitInterval=3m
StartLimitBurst=3
-TimeoutSec=1m
+TimeoutSec=2m
WatchdogSec=60s
RestartSec=5
Restart=on-abnormal
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 52641de72c..d82f9fd1b8 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -54,6 +54,7 @@ vtysh_scan += $(top_srcdir)/bgpd/bgp_nexthop.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_route.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_routemap.c
vtysh_scan += $(top_srcdir)/bgpd/bgp_vty.c
+vtysh_scan += $(top_srcdir)/bgpd/bgp_flowspec_vty.c
endif
if RPKI
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index cdc047e5ae..9fff2ee58c 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -1537,7 +1537,7 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd,
}
DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd,
- "pbr-map NAME seq (1-1000)",
+ "pbr-map NAME seq (1-700)",
"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"
@@ -1547,7 +1547,7 @@ DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd,
return CMD_SUCCESS;
}
-DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
+DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]",
NO_STR
"Delete pbr-map\n"
"The name of the PBR MAP\n"
@@ -2361,13 +2361,22 @@ DEFUNSH(VTYSH_ALL, no_vtysh_service_password_encrypt,
DEFUNSH(VTYSH_ALL, vtysh_config_password, vtysh_password_cmd,
"password [(8-8)] LINE",
- "Assign the terminal connection password\n"
+ "Modify the terminal connection password\n"
"Specifies a HIDDEN password will follow\n"
"The password string\n")
{
return CMD_SUCCESS;
}
+DEFUNSH(VTYSH_ALL, no_vtysh_config_password, no_vtysh_password_cmd,
+ "no password", NO_STR
+ "Modify the terminal connection password\n")
+{
+ vty_out(vty, NO_PASSWD_CMD_WARNING);
+
+ return CMD_SUCCESS;
+}
+
DEFUNSH(VTYSH_ALL, vtysh_config_enable_password, vtysh_enable_password_cmd,
"enable password [(8-8)] LINE",
"Modify enable password parameters\n"
@@ -2383,6 +2392,8 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password,
"Modify enable password parameters\n"
"Assign the privileged level password\n")
{
+ vty_out(vty, NO_PASSWD_CMD_WARNING);
+
return CMD_SUCCESS;
}
@@ -2611,20 +2622,25 @@ DEFUN (vtysh_write_memory,
/* If integrated frr.conf explicitely set. */
if (want_config_integrated()) {
ret = CMD_WARNING_CONFIG_FAILED;
+
+ /* first attempt to use watchfrr if it's available */
+ bool used_watchfrr = false;
+
for (i = 0; i < array_size(vtysh_client); i++)
if (vtysh_client[i].flag == VTYSH_WATCHFRR)
break;
- if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1)
+ if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1) {
+ used_watchfrr = true;
ret = vtysh_client_execute(&vtysh_client[i],
"do write integrated",
outputfile);
+ }
/*
- * If watchfrr returns CMD_WARNING_CONFIG_FAILED this means
- * that it could not write the config, but additionally
- * indicates that we should not try either
+ * If we didn't use watchfrr, fallback to writing the config
+ * ourselves
*/
- if (ret != CMD_SUCCESS && ret != CMD_WARNING_CONFIG_FAILED) {
+ if (!used_watchfrr) {
printf("\nWarning: attempting direct configuration write without "
"watchfrr.\nFile permissions and ownership may be "
"incorrect, or write may fail.\n\n");
@@ -3600,6 +3616,7 @@ void vtysh_init_vty(void)
install_element(CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd);
install_element(CONFIG_NODE, &vtysh_password_cmd);
+ install_element(CONFIG_NODE, &no_vtysh_password_cmd);
install_element(CONFIG_NODE, &vtysh_enable_password_cmd);
install_element(CONFIG_NODE, &no_vtysh_enable_password_cmd);
}
diff --git a/zebra/connected.c b/zebra/connected.c
index 23f2f666a0..a9a4dfe08f 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -403,10 +403,10 @@ void connected_down(struct interface *ifp, struct connected *ifc)
* head.
*/
rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, 0, 0, false, NULL);
+ &p, NULL, &nh, 0, 0, false);
rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, 0, 0, false, NULL);
+ &p, NULL, &nh, 0, 0, false);
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[PREFIX_STRLEN];
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index e28c189f86..7423e9237f 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -1025,7 +1025,7 @@ static void if_netlink_check_ifp_instance_consistency(uint16_t cmd,
struct interface *ifp,
ns_id_t ns_id)
{
- struct interface *old_ifp;
+ struct interface *other_ifp;
/*
* look if interface name is also found on other netns
@@ -1037,29 +1037,42 @@ static void if_netlink_check_ifp_instance_consistency(uint16_t cmd,
if (!vrf_is_backend_netns() ||
!strcmp(ifp->name, "lo"))
return;
- old_ifp = if_lookup_by_name_not_ns(ns_id, ifp->name);
- if (!old_ifp)
+ other_ifp = if_lookup_by_name_not_ns(ns_id, ifp->name);
+ if (!other_ifp)
+ return;
+ /* because previous interface may be inactive,
+ * interface is moved back to default vrf
+ * then one may find the same pointer; ignore
+ */
+ if (other_ifp == ifp)
return;
if ((cmd == RTM_NEWLINK)
- && (CHECK_FLAG(old_ifp->status, ZEBRA_INTERFACE_ACTIVE)))
+ && (CHECK_FLAG(other_ifp->status, ZEBRA_INTERFACE_ACTIVE)))
return;
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("%s %s(%u) %s VRF %u",
- cmd == RTM_DELLINK ?
- "RTM_DELLINK replaced by" :
- "RTM_NEWLINK replaces",
+ if (IS_ZEBRA_DEBUG_KERNEL && cmd == RTM_NEWLINK) {
+ zlog_debug("RTM_NEWLINK %s(%u, VRF %u) replaces %s(%u, VRF %u)\n",
ifp->name,
- old_ifp->ifindex,
- cmd == RTM_DELLINK ?
- "in" : "from",
- old_ifp->vrf_id);
+ ifp->ifindex,
+ ifp->vrf_id,
+ other_ifp->name,
+ other_ifp->ifindex,
+ other_ifp->vrf_id);
+ } else if (IS_ZEBRA_DEBUG_KERNEL && cmd == RTM_DELLINK) {
+ zlog_debug("RTM_DELLINK %s(%u, VRF %u) is replaced by %s(%u, VRF %u)\n",
+ ifp->name,
+ ifp->ifindex,
+ ifp->vrf_id,
+ other_ifp->name,
+ other_ifp->ifindex,
+ other_ifp->vrf_id);
+ }
/* the found interface replaces the current one
* remove it
*/
if (cmd == RTM_DELLINK)
if_delete(ifp);
else
- if_delete(old_ifp);
+ if_delete(other_ifp);
/* the found interface is replaced by the current one
* suppress it
*/
diff --git a/zebra/interface.c b/zebra/interface.c
index 6f59a2d399..7de18d683c 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -202,6 +202,7 @@ struct interface *if_link_per_ns(struct zebra_ns *ns, struct interface *ifp)
if (rn->info) {
ifp = (struct interface *)rn->info;
route_unlock_node(rn); /* get */
+ ifp->node = rn;
return ifp;
}
@@ -725,6 +726,9 @@ void if_delete_update(struct interface *ifp)
return;
}
+ if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE))
+ return;
+
/* Mark interface as inactive */
UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE);
@@ -1224,8 +1228,13 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
br_slave->bridge_ifindex);
}
- if (zebra_if->link_ifindex != IFINDEX_INTERNAL)
- vty_out(vty, " Link ifindex %u\n", zebra_if->link_ifindex);
+ if (zebra_if->link_ifindex != IFINDEX_INTERNAL) {
+ vty_out(vty, " Link ifindex %u", zebra_if->link_ifindex);
+ if (zebra_if->link)
+ vty_out(vty, "(%s)\n", zebra_if->link->name);
+ else
+ vty_out(vty, "(Unknown)\n");
+ }
if (HAS_LINK_PARAMS(ifp)) {
int i;
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index db4f19460a..6b587dab38 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -463,7 +463,7 @@ int netlink_parse_info(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *,
int read_in = 0;
while (1) {
- char buf[NL_PKT_BUF_SIZE];
+ char buf[NL_RCV_PKT_BUF_SIZE];
struct iovec iov = {.iov_base = buf, .iov_len = sizeof buf};
struct sockaddr_nl snl;
struct msghdr msg = {.msg_name = (void *)&snl,
diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h
index 8441eeac76..dc075b9aff 100644
--- a/zebra/kernel_netlink.h
+++ b/zebra/kernel_netlink.h
@@ -23,6 +23,7 @@
#ifdef HAVE_NETLINK
+#define NL_RCV_PKT_BUF_SIZE 32768
#define NL_PKT_BUF_SIZE 8192
extern void netlink_parse_rtattr(struct rtattr **tb, int max,
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 1a94807317..b85c4748c4 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1043,7 +1043,7 @@ void rtm_read(struct rt_msghdr *rtm)
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- NULL, 0, 0, true, NULL);
+ NULL, 0, 0, true);
if (!nh.type) {
nh.type = NEXTHOP_TYPE_IPV4;
@@ -1058,7 +1058,7 @@ void rtm_read(struct rt_msghdr *rtm)
else
rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, true, NULL);
+ &nh, 0, 0, true);
}
if (dest.sa.sa_family == AF_INET6) {
/* One day we might have a debug section here like one in the
@@ -1089,7 +1089,7 @@ void rtm_read(struct rt_msghdr *rtm)
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- NULL, 0, 0, true, NULL);
+ NULL, 0, 0, true);
if (!nh.type) {
nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX
@@ -1106,7 +1106,7 @@ void rtm_read(struct rt_msghdr *rtm)
else
rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, true, NULL);
+ &nh, 0, 0, true);
}
}
@@ -1386,7 +1386,7 @@ static void routing_socket(struct zebra_ns *zns)
zlog_err("routing_socket: Can't raise privileges");
routing_sock =
- ns_socket(AF_ROUTE, SOCK_RAW, 0, (ns_id_t)zns->ns->ns_id);
+ ns_socket(AF_ROUTE, SOCK_RAW, 0, zns->ns_id);
if (routing_sock < 0) {
if (zserv_privs.change(ZPRIVS_LOWER))
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 810ee33839..5a6565aec9 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -592,7 +592,7 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re)
rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table,
re->flags, &p, NULL, re->ng.nexthop,
- zebrad.rtm_table_default, re->metric, false, NULL);
+ zebrad.rtm_table_default, re->metric, false);
return 0;
}
diff --git a/zebra/rib.h b/zebra/rib.h
index d68bf787c0..7b9e6d56a7 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -313,8 +313,7 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, bool fromkernel,
- struct ethaddr *rmac);
+ uint32_t table_id, uint32_t metric, bool fromkernel);
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
union g_addr *addr,
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index cdc52211cc..9510a0e12c 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -98,7 +98,8 @@ static inline int is_selfroute(int proto)
|| (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG)
|| (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP)
|| (proto == RTPROT_LDP) || (proto == RTPROT_BABEL)
- || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)) {
+ || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)
+ || (proto == RTPROT_PBR)) {
return 1;
}
@@ -142,7 +143,18 @@ static inline int zebra2proto(int proto)
case ZEBRA_ROUTE_SHARP:
proto = RTPROT_SHARP;
break;
+ case ZEBRA_ROUTE_PBR:
+ proto = RTPROT_PBR;
+ break;
default:
+ /*
+ * When a user adds a new protocol this will show up
+ * to let them know to do something about it. This
+ * is intentionally a warn because we should see
+ * this as part of development of a new protocol
+ */
+ zlog_warn("%s: Please add this protocol(%d) to proper rt_netlink.c handling",
+ __PRETTY_FUNCTION__, proto);
proto = RTPROT_ZEBRA;
break;
}
@@ -184,7 +196,22 @@ static inline int proto2zebra(int proto, int family)
case RTPROT_STATIC:
proto = ZEBRA_ROUTE_STATIC;
break;
+ case RTPROT_SHARP:
+ proto = ZEBRA_ROUTE_SHARP;
+ break;
+ case RTPROT_PBR:
+ proto = ZEBRA_ROUTE_PBR;
+ break;
default:
+ /*
+ * When a user adds a new protocol this will show up
+ * to let them know to do something about it. This
+ * is intentionally a warn because we should see
+ * this as part of development of a new protocol
+ */
+ zlog_warn("%s: Please add this protocol(%d) to proper rt_netlink.c handling",
+ __PRETTY_FUNCTION__,
+ proto);
proto = ZEBRA_ROUTE_KERNEL;
break;
}
@@ -586,12 +613,12 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
if (gate)
memcpy(&nh.gate, gate, sz);
rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
- &p, NULL, &nh, table, metric, true, NULL);
+ &p, NULL, &nh, table, metric, true);
} else {
/* XXX: need to compare the entire list of nexthops
* here for NLM_F_APPEND stupidity */
rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
- &p, NULL, NULL, table, metric, true, NULL);
+ &p, NULL, NULL, table, metric, true);
}
}
@@ -1206,14 +1233,6 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,
"netlink_route_multipath() (%s): "
"nexthop via if %u",
routedesc, nexthop->ifindex);
- } else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): "
- "nexthop via if %u",
- routedesc, nexthop->ifindex);
- } else {
- rtnh->rtnh_ifindex = 0;
}
}
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 51350fd6fb..78888f48ca 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -52,6 +52,7 @@
#define RTPROT_EIGRP 192
#define RTPROT_LDP 193
#define RTPROT_SHARP 194
+#define RTPROT_PBR 195
void rt_netlink_init(void);
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index e03caa61b7..9a18cc22f0 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -1392,7 +1392,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
&(api_nh->gate.ipv4),
sizeof(struct in_addr));
zebra_vxlan_evpn_vrf_route_add(
- vrf_id, &api.rmac, &vtep_ip,
+ vrf_id, &api_nh->rmac, &vtep_ip,
&api.prefix);
}
break;
@@ -1425,7 +1425,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
&(api_nh->gate.ipv6),
sizeof(struct in6_addr));
zebra_vxlan_evpn_vrf_route_add(
- vrf_id, &api.rmac, &vtep_ip,
+ vrf_id, &api_nh->rmac, &vtep_ip,
&api.prefix);
}
break;
@@ -1532,7 +1532,7 @@ static void zread_route_del(ZAPI_HANDLER_ARGS)
rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance,
api.flags, &api.prefix, src_p, NULL, table_id, api.metric,
- false, &api.rmac);
+ false);
/* Stats */
switch (api.prefix.family) {
@@ -1734,7 +1734,7 @@ static void zread_ipv4_delete(ZAPI_HANDLER_ARGS)
table_id = zvrf->table_id;
rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &p, NULL, NULL, table_id, 0, false, NULL);
+ api.flags, &p, NULL, NULL, table_id, 0, false);
client->v4_route_del_cnt++;
stream_failure:
@@ -2158,8 +2158,7 @@ static void zread_ipv6_delete(ZAPI_HANDLER_ARGS)
src_pp = NULL;
rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &p, src_pp, NULL, client->rtm_table, 0, false,
- NULL);
+ api.flags, &p, src_pp, NULL, client->rtm_table, 0, false);
client->v6_route_del_cnt++;
diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c
index 07e81aa020..d20f93f521 100644
--- a/zebra/zebra_ptm.c
+++ b/zebra/zebra_ptm.c
@@ -298,6 +298,9 @@ DEFUN (zebra_ptm_enable_if,
int old_ptm_enable;
int send_linkdown = 0;
+ if_data = ifp->info;
+ if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC;
+
if (ifp->ifindex == IFINDEX_INTERNAL) {
return CMD_SUCCESS;
}
@@ -317,9 +320,6 @@ DEFUN (zebra_ptm_enable_if,
}
}
- if_data = ifp->info;
- if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC;
-
return CMD_SUCCESS;
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 67832f2d3f..7ec640164a 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -2394,8 +2394,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, bool fromkernel,
- struct ethaddr *rmac)
+ uint32_t table_id, uint32_t metric, bool fromkernel)
{
struct route_table *table;
struct route_node *rn;
@@ -2569,7 +2568,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
&(tmp_nh->gate.ipv6),
sizeof(struct in6_addr));
}
- zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac,
+ zebra_vxlan_evpn_vrf_route_del(re->vrf_id,
&vtep_ip, p);
}
}
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 16aece9747..e6f80f92a7 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -1215,6 +1215,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
json_object_int_add(json_route, "metric", re->metric);
}
+ json_object_int_add(json_route, "internalStatus",
+ re->status);
+ json_object_int_add(json_route, "internalFlags",
+ re->flags);
if (uptime < ONE_DAY_SECOND)
sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
tm->tm_sec);
@@ -1231,6 +1235,9 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
for (ALL_NEXTHOPS(re->ng, nexthop)) {
json_nexthop = json_object_new_object();
+ json_object_int_add(json_nexthop, "flags",
+ nexthop->flags);
+
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
json_object_boolean_true_add(json_nexthop,
"duplicate");
@@ -2791,12 +2798,14 @@ DEFUN (vrf_vni_mapping,
DEFUN (no_vrf_vni_mapping,
no_vrf_vni_mapping_cmd,
- "no vni " CMD_VNI_RANGE,
+ "no vni " CMD_VNI_RANGE "[prefix-routes-only]",
NO_STR
"VNI corresponding to tenant VRF\n"
- "VNI-ID")
+ "VNI-ID\n"
+ "prefix-routes-only\n")
{
int ret = 0;
+ int filter = 0;
char err[ERR_STR_SZ];
vni_t vni = strtoul(argv[2]->arg, NULL, 10);
@@ -2805,7 +2814,11 @@ DEFUN (no_vrf_vni_mapping,
assert(vrf);
assert(zvrf);
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
+ if (argc == 4)
+ filter = 1;
+
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err,
+ ERR_STR_SZ, filter, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 6e901a0457..3278c86b99 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -3238,15 +3238,9 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac,
/* handle rmac delete */
-static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, struct ethaddr *rmac,
+static void zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac,
struct prefix *host_prefix)
{
- zebra_mac_t *zrmac = NULL;
-
- zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
- if (!zrmac)
- return -1;
-
host_list_delete_host(zrmac->host_list, host_prefix);
if (list_isempty(zrmac->host_list)) {
@@ -3256,7 +3250,6 @@ static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, struct ethaddr *rmac,
/* del the rmac entry */
zl3vni_rmac_del(zl3vni, zrmac);
}
- return 0;
}
/*
@@ -3394,15 +3387,9 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip,
}
/* handle nh neigh delete */
-static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip,
- struct prefix *host_prefix)
+static void zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *nh,
+ struct prefix *host_prefix)
{
- zebra_neigh_t *nh = NULL;
-
- nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
- if (!nh)
- return -1;
-
host_list_delete_host(nh->host_list, host_prefix);
if (list_isempty(nh->host_list)) {
@@ -3412,8 +3399,6 @@ static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip,
/* delete the nh entry */
zl3vni_nh_del(zl3vni, nh);
}
-
- return 0;
}
/* handle neigh update from kernel - the only thing of interest is to
@@ -3958,34 +3943,65 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac,
struct prefix *host_prefix)
{
zebra_l3vni_t *zl3vni = NULL;
+ struct ipaddr ipv4_vtep;
zl3vni = zl3vni_from_vrf(vrf_id);
if (!zl3vni || !is_l3vni_oper_up(zl3vni))
return;
- /* add the next hop neighbor */
+ /*
+ * add the next hop neighbor -
+ * neigh to be installed is the ipv6 nexthop neigh
+ */
zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
- /* add the rmac */
- zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix);
+ /*
+ * if the remote vtep is a ipv4 mapped ipv6 address convert it to ipv4
+ * address. Rmac is programmed against the ipv4 vtep because we only
+ * support ipv4 tunnels in the h/w right now
+ */
+ memset(&ipv4_vtep, 0, sizeof(struct ipaddr));
+ ipv4_vtep.ipa_type = IPADDR_V4;
+ if (vtep_ip->ipa_type == IPADDR_V6)
+ ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6,
+ &(ipv4_vtep.ipaddr_v4));
+ else
+ memcpy(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4,
+ sizeof(struct in_addr));
+
+ /*
+ * add the rmac - remote rmac to be installed is against the ipv4
+ * nexthop address
+ */
+ zl3vni_remote_rmac_add(zl3vni, rmac, &ipv4_vtep, host_prefix);
}
/* handle evpn vrf route delete */
-void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, struct ethaddr *rmac,
+void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
struct ipaddr *vtep_ip,
struct prefix *host_prefix)
{
zebra_l3vni_t *zl3vni = NULL;
+ zebra_neigh_t *nh = NULL;
+ zebra_mac_t *zrmac = NULL;
zl3vni = zl3vni_from_vrf(vrf_id);
if (!zl3vni)
return;
+ /* find the next hop entry and rmac entry */
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh)
+ return;
+ zrmac = zl3vni_rmac_lookup(zl3vni, &nh->emac);
+
/* delete the next hop entry */
- zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix);
+ zl3vni_remote_nh_del(zl3vni, nh, host_prefix);
/* delete the rmac entry */
- zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix);
+ if (zrmac)
+ zl3vni_remote_rmac_del(zl3vni, zrmac, host_prefix);
+
}
void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
@@ -5143,7 +5159,7 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS)
l += IPV4_MAX_BYTELEN;
/* Get flags - sticky mac and/or gateway mac */
- flags = stream_getc(s);
+ STREAM_GETC(s, flags);
sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
l++;
@@ -6516,6 +6532,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
return -1;
}
+ if (filter && !CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)) {
+ snprintf(err, ERR_STR_SZ,
+ "prefix-routes-only is not set for the vni");
+ return -1;
+ }
+
zebra_vxlan_process_l3vni_oper_down(zl3vni);
/* delete and uninstall all rmacs */
@@ -6602,7 +6624,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
}
s = msg;
- advertise = stream_getc(s);
+ STREAM_GETC(s, advertise);
vni = stream_get3(s);
zvni = zvni_lookup(vni);
@@ -6641,6 +6663,9 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS)
zvni_advertise_subnet(zvni, vlan_if, 1);
else
zvni_advertise_subnet(zvni, vlan_if, 0);
+
+stream_failure:
+ return;
}
/*
@@ -6663,7 +6688,7 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS)
s = msg;
STREAM_GETC(s, advertise);
- STREAM_GET(&vni, s, 3);
+ STREAM_GETL(s, vni);
if (!vni) {
if (IS_ZEBRA_DEBUG_VXLAN)
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 6153c7d7e3..34d1152751 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -160,7 +160,6 @@ extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
struct ipaddr *ip,
struct prefix *host_prefix);
extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
- struct ethaddr *rmac,
struct ipaddr *vtep_ip,
struct prefix *host_prefix);