diff options
147 files changed, 4230 insertions, 1535 deletions
diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 2b211ccafd..c3e7e939e4 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -4,9 +4,9 @@ pkgver=@VERSION@ pkgrel=0 pkgdesc="Free Range Routing is a fork of quagga" url="https://frrouting.org/" -arch="all" +arch="x86_64" license="GPL-2.0" -depends="iproute2 json-c c-ares ipsec-tools iproute2" +depends="json-c c-ares ipsec-tools iproute2 python py-ipaddr bash" makedepends="ncurses-dev net-snmp-dev gawk texinfo perl acct autoconf automake bash binutils binutils-libs bison bsd-compat-headers build-base @@ -20,18 +20,42 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl patch pax-utils pcre perl pkgconf python2 python2-dev readline readline-dev sqlite-libs squashfs-tools sudo tar texinfo xorriso xz-libs py-sphinx" +install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall" subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg" -source="$pkgname-$pkgver.tar.gz" +source="$pkgname-$pkgver.tar.gz docker-start daemons daemons.conf" builddir="$srcdir"/$pkgname-$pkgver +_sbindir=/usr/lib/frr +_sysconfdir=/etc/frr +_libdir=/usr/lib +_localstatedir=/var/run/frr +_user=frr + build() { cd "$builddir" - ./configure --prefix=/usr || return 1 + ./configure \ + --prefix=/usr \ + --sbindir=$_sbindir \ + --sysconfdir=$_sysconfdir \ + --libdir=$_libdir \ + --localstatedir=$_localstatedir \ + --enable-systemd=no \ + --enable-vtysh \ + --enable-multipath=64 \ + --enable-vty-group=frrvty \ + --enable-user=$_user \ + --enable-group=$_user || return 1 make || return 1 } package() { cd "$builddir" make DESTDIR="$pkgdir" install || return 1 + + install -Dm755 "$srcdir"/docker-start "$pkgdir"$_sbindir + install -Dm644 "$srcdir"/daemons "$pkgdir"$_sysconfdir + install -Dm644 "$srcdir"/daemons.conf "$pkgdir"$_sysconfdir + install -d "$pkgdir"/etc/init.d + ln -s ${_sbindir}/frr "$pkgdir"/etc/init.d/frr } diff --git a/alpine/docker-start b/alpine/docker-start new file mode 100755 index 0000000000..43854ab142 --- /dev/null +++ b/alpine/docker-start @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +## +# For volume mounts... +## +chown -R frr:frr /etc/frr +/etc/init.d/frr start +exec sleep 10000d diff --git a/alpine/frr.post-deinstall b/alpine/frr.post-deinstall new file mode 100755 index 0000000000..8f5d3dc40c --- /dev/null +++ b/alpine/frr.post-deinstall @@ -0,0 +1,6 @@ +#!/bin/sh + +getent passwd frr > /dev/null && deluser frr +getent group frrvty > /dev/null && delgroup frrvty +getent group frr > /dev/null && delgroup frr +exit 0 diff --git a/alpine/frr.pre-deinstall b/alpine/frr.pre-deinstall new file mode 100755 index 0000000000..72cf73bc1c --- /dev/null +++ b/alpine/frr.pre-deinstall @@ -0,0 +1,4 @@ +#!/bin/sh + +/etc/init.d/frr stop +exit 0 diff --git a/alpine/frr.pre-install b/alpine/frr.pre-install new file mode 100755 index 0000000000..da608cdcbd --- /dev/null +++ b/alpine/frr.pre-install @@ -0,0 +1,10 @@ +#!/bin/sh + +for g in frr frrvty; do + ! getent group $g > /dev/null && addgroup -S $g +done + +! getent passwd frr > /dev/null && \ + adduser -S -D -h /var/run/frr -s /sbin/nologin -G frr -g frr frr + +adduser frr frrvty diff --git a/babeld/route.c b/babeld/route.c index 501dd1f4df..bc7590fb39 100644 --- a/babeld/route.c +++ b/babeld/route.c @@ -176,6 +176,7 @@ insert_route(struct babel_route *route) resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots); if(route_slots >= max_route_slots) return NULL; + assert(routes); route->next = NULL; if(n < route_slots) memmove(routes + n + 1, routes + n, diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 61d46dfcb9..a2880b7b94 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -87,7 +87,7 @@ libbgp_a_SOURCES = \ bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \ bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \ bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \ - bgp_flowspec_vty.c + bgp_flowspec_vty.c bgp_labelpool.c noinst_HEADERS = \ bgp_memory.h \ @@ -100,7 +100,8 @@ noinst_HEADERS = \ bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \ $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \ bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \ - bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h + bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \ + bgp_labelpool.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index cb1c131ef7..7454b81b96 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -37,6 +37,7 @@ unsigned long eth_tag_id; struct attr; +/* EVPN ESI */ struct eth_segment_id { uint8_t val[ESI_LEN]; }; diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 8f38f5765f..d1bc7f6e53 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -852,7 +852,7 @@ int community_list_set(struct community_list_handler *ch, const char *name, /* Unset community-list */ int community_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style, int delete_all) + const char *str, int direct, int style) { struct community_entry *entry = NULL; struct community_list *list; @@ -864,16 +864,14 @@ int community_list_unset(struct community_list_handler *ch, const char *name, return COMMUNITY_LIST_ERR_CANT_FIND_LIST; /* Delete all of entry belongs to this community-list. */ - if (delete_all) { + if (!str) { community_list_delete(list); route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED); return 0; } - if (style == COMMUNITY_LIST_STANDARD) { - if (str) - com = community_str2com(str); - } + if (style == COMMUNITY_LIST_STANDARD) + com = community_str2com(str); if (com) { entry = community_list_entry_lookup(list, com, direct); @@ -1117,11 +1115,13 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, return 0; } -/* Unset extcommunity-list. When str is NULL, delete all of - extcommunity-list entry belongs to the specified name. */ +/* Unset extcommunity-list. + * + * When str is NULL, delete all extcommunity-list entries belonging to the + * specified name. + */ int extcommunity_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style, - int delete_all) + const char *str, int direct, int style) { struct community_entry *entry = NULL; struct community_list *list; @@ -1133,16 +1133,14 @@ int extcommunity_list_unset(struct community_list_handler *ch, const char *name, return COMMUNITY_LIST_ERR_CANT_FIND_LIST; /* Delete all of entry belongs to this extcommunity-list. */ - if (delete_all) { + if (!str) { community_list_delete(list); route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED); return 0; } - if (style == EXTCOMMUNITY_LIST_STANDARD) { - if (str) - ecom = ecommunity_str2com(str, 0, 1); - } + if (style == EXTCOMMUNITY_LIST_STANDARD) + ecom = ecommunity_str2com(str, 0, 1); if (ecom) { entry = community_list_entry_lookup(list, ecom, direct); diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 0dbde2a453..9efb34d7b9 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -133,13 +133,13 @@ extern int community_list_set(struct community_list_handler *ch, int style); extern int community_list_unset(struct community_list_handler *ch, const char *name, const char *str, int direct, - int style, int delete_all); + int style); extern int extcommunity_list_set(struct community_list_handler *ch, const char *name, const char *str, int direct, int style); extern int extcommunity_list_unset(struct community_list_handler *ch, const char *name, const char *str, - int direct, int style, int delete_all); + int direct, int style); extern int lcommunity_list_set(struct community_list_handler *ch, const char *name, const char *str, int direct, int style); diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 1ed557e074..bce6056ded 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -233,7 +233,7 @@ int bgp_damp_withdraw(struct bgp_info *binfo, struct bgp_node *rn, afi_t afi, /* Remove the route from a reuse list if it is on one. */ if (CHECK_FLAG(bdi->binfo->flags, BGP_INFO_DAMPED)) { /* If decay rate isn't equal to 0, reinsert brn. */ - if (bdi->penalty != last_penalty) { + if (bdi->penalty != last_penalty && bdi->index >= 0) { bgp_reuse_list_delete(bdi); bgp_reuse_list_add(bdi); } diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index ae4ff5d67e..29ac5f520d 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -58,6 +58,7 @@ unsigned long conf_bgp_debug_nht; unsigned long conf_bgp_debug_update_groups; unsigned long conf_bgp_debug_vpn; unsigned long conf_bgp_debug_flowspec; +unsigned long conf_bgp_debug_labelpool; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -73,6 +74,7 @@ unsigned long term_bgp_debug_nht; unsigned long term_bgp_debug_update_groups; unsigned long term_bgp_debug_vpn; unsigned long term_bgp_debug_flowspec; +unsigned long term_bgp_debug_labelpool; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -1655,6 +1657,44 @@ DEFUN (no_debug_bgp_vpn, return CMD_SUCCESS; } +DEFUN (debug_bgp_labelpool, + debug_bgp_labelpool_cmd, + "debug bgp labelpool", + DEBUG_STR + BGP_STR + "label pool\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON(labelpool, LABELPOOL); + else + TERM_DEBUG_ON(labelpool, LABELPOOL); + + if (vty->node != CONFIG_NODE) + vty_out(vty, "enabled debug bgp labelpool\n"); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_labelpool, + no_debug_bgp_labelpool_cmd, + "no debug bgp labelpool", + NO_STR + DEBUG_STR + BGP_STR + "label pool\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF(labelpool, LABELPOOL); + else + TERM_DEBUG_OFF(labelpool, LABELPOOL); + + + if (vty->node != CONFIG_NODE) + vty_out(vty, "disabled debug bgp labelpool\n"); + + return CMD_SUCCESS; +} + DEFUN (no_debug_bgp, no_debug_bgp_cmd, "no debug bgp", @@ -1692,6 +1732,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL); TERM_DEBUG_OFF(flowspec, FLOWSPEC); + TERM_DEBUG_OFF(labelpool, LABELPOOL); vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -1764,6 +1805,8 @@ DEFUN_NOSH (show_debugging_bgp, vty_out(vty, " BGP vpn label event debugging is on\n"); if (BGP_DEBUG(flowspec, FLOWSPEC)) vty_out(vty, " BGP flowspec debugging is on\n"); + if (BGP_DEBUG(labelpool, LABELPOOL)) + vty_out(vty, " BGP labelpool debugging is on\n"); vty_out(vty, "\n"); return CMD_SUCCESS; @@ -1819,6 +1862,8 @@ int bgp_debug_count(void) ret++; if (BGP_DEBUG(flowspec, FLOWSPEC)) ret++; + if (BGP_DEBUG(labelpool, LABELPOOL)) + ret++; return ret; } @@ -1916,6 +1961,10 @@ static int bgp_config_write_debug(struct vty *vty) vty_out(vty, "debug bgp flowspec\n"); write++; } + if (CONF_BGP_DEBUG(labelpool, LABELPOOL)) { + vty_out(vty, "debug bgp labelpool\n"); + write++; + } return write; } @@ -2015,6 +2064,11 @@ void bgp_debug_init(void) install_element(CONFIG_NODE, &debug_bgp_vpn_cmd); install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd); install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd); + + install_element(ENABLE_NODE, &debug_bgp_labelpool_cmd); + install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd); } /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index a0b179e213..ad476ee918 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -74,6 +74,7 @@ extern unsigned long conf_bgp_debug_nht; extern unsigned long conf_bgp_debug_update_groups; extern unsigned long conf_bgp_debug_vpn; extern unsigned long conf_bgp_debug_flowspec; +extern unsigned long conf_bgp_debug_labelpool; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -87,6 +88,7 @@ extern unsigned long term_bgp_debug_nht; extern unsigned long term_bgp_debug_update_groups; extern unsigned long term_bgp_debug_vpn; extern unsigned long term_bgp_debug_flowspec; +extern unsigned long term_bgp_debug_labelpool; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -120,6 +122,7 @@ struct bgp_debug_filter { #define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04 #define BGP_DEBUG_VPN_LEAK_LABEL 0x08 #define BGP_DEBUG_FLOWSPEC 0x01 +#define BGP_DEBUG_LABELPOOL 0x01 #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 54ec7d392b..80166dd32b 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -736,6 +736,14 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) "MM:%u", seqnum); } else unk_ecom = 1; + } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) { + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) { + len = sprintf( + str_buf + str_pnt, + "FS:redirect IP 0x%x", *(pnt+5)); + } else + unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP) { sub_type = *pnt++; @@ -785,10 +793,6 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) len = sprintf( str_buf + str_pnt, "FS:marking %u", *(pnt+5)); - } else if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) { - len = sprintf( - str_buf + str_pnt, - "FS:redirect IP 0x%x", *(pnt+5)); } else unk_ecom = 1; } else diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 31ff1481ba..0c22c5a149 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -28,6 +28,7 @@ #define ECOMMUNITY_ENCODE_OPAQUE 0x03 #define ECOMMUNITY_ENCODE_EVPN 0x06 #define ECOMMUNITY_ENCODE_TRANS_EXP 0x80 /* Flow Spec */ +#define ECOMMUNITY_ENCODE_REDIRECT_IP_NH 0x08 /* Flow Spec */ /* RFC7674 */ #define ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 0x81 #define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82 diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index e5e5f72699..483d65be71 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2810,10 +2810,12 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, { struct prefix_rd prd; struct prefix_evpn p; + struct bgp_route_evpn evpn; uint8_t ipaddr_len; uint8_t macaddr_len; mpls_label_t label[BGP_MAX_LABELS]; /* holds the VNI(s) as in packet */ uint32_t num_labels = 0; + uint32_t eth_tag; int ret; /* Type-2 route should be either 33, 37 or 49 bytes or an @@ -2829,6 +2831,8 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, return -1; } + memset(&evpn, 0, sizeof(evpn)); + /* Make prefix_rd */ prd.family = AF_UNSPEC; prd.prefixlen = 64; @@ -2841,10 +2845,13 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, p.prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; - /* Skip over Ethernet Seg Identifier for now. */ - pfx += 10; + /* Copy Ethernet Seg Identifier */ + memcpy(&evpn.eth_s_id.val, pfx, ESI_LEN); + pfx += ESI_LEN; - /* Skip over Ethernet Tag for now. */ + /* Copy Ethernet Tag */ + memcpy(ð_tag, pfx, 4); + p.prefix.eth_tag = ntohl(eth_tag); pfx += 4; /* Get the MAC Addr len */ @@ -2902,11 +2909,11 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, if (attr) ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, - &prd, &label[0], num_labels, 0, NULL); + &prd, &label[0], num_labels, 0, &evpn); else ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, - &prd, &label[0], num_labels, NULL); + &prd, &label[0], num_labels, &evpn); return ret; } @@ -2920,6 +2927,7 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, struct prefix_rd prd; struct prefix_evpn p; uint8_t ipaddr_len; + uint32_t eth_tag; int ret; /* Type-3 route should be either 17 or 29 bytes: RD (8), Eth Tag (4), @@ -2956,7 +2964,9 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, p.prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_IMET_ROUTE; - /* Skip over Ethernet Tag for now. */ + /* Copy Ethernet Tag */ + memcpy(ð_tag, pfx, 4); + p.prefix.eth_tag = ntohl(eth_tag); pfx += 4; /* Get the IP. */ @@ -3574,7 +3584,7 @@ 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", 0); + json_object_int_add(json, "ethTag", p->prefix.eth_tag); json_object_int_add(json, "ipLen", IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BITLEN @@ -3585,10 +3595,7 @@ void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json) if (IS_EVPN_PREFIX_IPADDR_NONE(p)) { json_object_int_add(json, "routeType", p->prefix.route_type); - json_object_int_add( - json, "esi", - 0); /* TODO: we don't support esi yet */ - json_object_int_add(json, "ethTag", 0); + json_object_int_add(json, "ethTag", p->prefix.eth_tag); json_object_int_add(json, "macLen", 8 * ETH_ALEN); json_object_string_add(json, "mac", prefix_mac2str(&p->prefix.mac, @@ -3602,10 +3609,7 @@ void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json) json_object_int_add(json, "routeType", p->prefix.route_type); - json_object_int_add( - json, "esi", - 0); /* TODO: we don't support esi yet */ - json_object_int_add(json, "ethTag", 0); + json_object_int_add(json, "ethTag", p->prefix.eth_tag); json_object_int_add(json, "macLen", 8 * ETH_ALEN); json_object_string_add(json, "mac", prefix_mac2str(&p->prefix.mac, @@ -3635,14 +3639,17 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) char buf2[PREFIX2STR_BUFFER]; if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) { - snprintf(buf, len, "[%d]:[0]:[%d]:[%s]", p->prefix.route_type, + snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type, + p->prefix.eth_tag, IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, inet_ntoa(p->prefix.ip.ipaddr_v4)); } else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { if (IS_EVPN_PREFIX_IPADDR_NONE(p)) - snprintf(buf, len, "[%d]:[0]:[0]:[%d]:[%s]", - p->prefix.route_type, 8 * ETH_ALEN, + snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", + p->prefix.route_type, + p->prefix.eth_tag, + 8 * ETH_ALEN, prefix_mac2str(&p->prefix.mac, buf1, sizeof(buf1))); else { @@ -3650,8 +3657,10 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) family = IS_EVPN_PREFIX_IPADDR_V4(p) ? AF_INET : AF_INET6; - snprintf(buf, len, "[%d]:[0]:[0]:[%d]:[%s]:[%d]:[%s]", - p->prefix.route_type, 8 * ETH_ALEN, + snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]:[%d]:[%s]", + p->prefix.route_type, + p->prefix.eth_tag, + 8 * ETH_ALEN, prefix_mac2str(&p->prefix.mac, buf1, sizeof(buf1)), family == AF_INET ? IPV4_MAX_BITLEN @@ -3660,8 +3669,10 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) PREFIX2STR_BUFFER)); } } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { - snprintf(buf, len, "[%d]:[0]:[0]:[%d]:[%s]", - p->prefix.route_type, p->prefix.ip_prefix_length, + 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)); @@ -3703,8 +3714,11 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, len += 3; stream_putc(s, len); stream_put(s, prd->val, 8); /* RD */ - stream_put(s, 0, 10); /* ESI */ - stream_putl(s, 0); /* Ethernet Tag ID */ + if (attr) + 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_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 */ @@ -3720,7 +3734,7 @@ 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, 0); /* Ethernet Tag ID */ + stream_putl(s, evp->prefix.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); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 6cb5c9d8da..c74a1bfb7c 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -327,9 +327,9 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, "* valid, > best, i - internal\n"); vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n"); vty_out(vty, - "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); + "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); - vty_out(vty, "EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]\n\n"); + vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n"); vty_out(vty, "%s", ri_header); } @@ -2110,11 +2110,11 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* RD header and legend - once overall. */ if (rd_header && !json) { vty_out(vty, - "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n"); + "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); vty_out(vty, - "EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]\n\n"); + "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n"); rd_header = 0; } diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 247da5d183..7bf11f12aa 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -312,6 +312,9 @@ void route_vty_out_flowspec(struct vty *vty, struct prefix *p, json_object_array_add(json_paths, json_ecom_path); } + if (attr->nexthop.s_addr != 0 && + display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "\tNH %-16s\n", inet_ntoa(attr->nexthop)); XFREE(MTYPE_ECOMMUNITY_STR, s); } peer_uptime(binfo->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c new file mode 100644 index 0000000000..2c98cd9ef9 --- /dev/null +++ b/bgpd/bgp_labelpool.c @@ -0,0 +1,592 @@ +/* + * BGP Label Pool - Manage label chunk allocations from zebra asynchronously + * + * Copyright (C) 2018 LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "stream.h" +#include "mpls.h" +#include "vty.h" +#include "fifo.h" +#include "linklist.h" +#include "skiplist.h" +#include "workqueue.h" +#include "zclient.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_labelpool.h" +#include "bgpd/bgp_debug.h" + +/* + * Definitions and external declarations. + */ +extern struct zclient *zclient; + +/* + * Remember where pool data are kept + */ +static struct labelpool *lp; + +/* request this many labels at a time from zebra */ +#define LP_CHUNK_SIZE 50 + +DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk") +DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO") +DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment") +DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback") + +#define LABEL_FIFO_ADD(F, N) \ + do { \ + FIFO_ADD((F), (N)); \ + (F)->count++; \ + } while (0) + +#define LABEL_FIFO_DEL(F, N) \ + do { \ + FIFO_DEL((N)); \ + (F)->count--; \ + } while (0) + +#define LABEL_FIFO_INIT(F) \ + do { \ + FIFO_INIT((F)); \ + (F)->count = 0; \ + } while (0) + +#define LABEL_FIFO_COUNT(F) ((F)->count) + +#define LABEL_FIFO_EMPTY(F) FIFO_EMPTY(F) + +#define LABEL_FIFO_HEAD(F) ((F)->next == (F) ? NULL : (F)->next) + +struct lp_chunk { + uint32_t first; + uint32_t last; +}; + +/* + * label control block + */ +struct lp_lcb { + mpls_label_t label; /* MPLS_LABEL_NONE = not allocated */ + int type; + void *labelid; /* unique ID */ + /* + * callback for label allocation and loss + * + * allocated: false = lost + */ + int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); +}; + +/* XXX same first elements as "struct fifo" */ +struct lp_fifo { + struct lp_fifo *next; + struct lp_fifo *prev; + + uint32_t count; + struct lp_lcb lcb; +}; + +struct lp_cbq_item { + int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); + int type; + mpls_label_t label; + void *labelid; + bool allocated; /* false = lost */ +}; + +static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data) +{ + struct lp_cbq_item *lcbq = data; + int rc; + int debug = BGP_DEBUG(labelpool, LABELPOOL); + + if (debug) + zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d", + __func__, lcbq->labelid, lcbq->label, lcbq->allocated); + + if (lcbq->label == MPLS_LABEL_NONE) { + /* shouldn't happen */ + zlog_err("%s: error: label==MPLS_LABEL_NONE", __func__); + return WQ_SUCCESS; + } + + rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated); + + if (lcbq->allocated && rc) { + /* + * Callback rejected allocation. This situation could arise + * if there was a label request followed by the requestor + * deciding it didn't need the assignment (e.g., config + * change) while the reply to the original request (with + * label) was in the work queue. + */ + if (debug) + zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u", + __func__, lcbq->labelid, lcbq->label); + + uintptr_t lbl = lcbq->label; + void *labelid; + struct lp_lcb *lcb; + + /* + * If the rejected label was marked inuse by this labelid, + * release the label back to the pool. + * + * Further, if the rejected label was still assigned to + * this labelid in the LCB, delete the LCB. + */ + if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) { + if (labelid == lcbq->labelid) { + if (!skiplist_search(lp->ledger, labelid, + (void **)&lcb)) { + if (lcbq->label == lcb->label) + skiplist_delete(lp->ledger, + labelid, NULL); + } + skiplist_delete(lp->inuse, (void *)lbl, NULL); + } + } + } + + return WQ_SUCCESS; +} + +static void lp_cbq_item_free(struct work_queue *wq, void *data) +{ + XFREE(MTYPE_BGP_LABEL_CBQ, data); +} + +static void lp_lcb_free(void *goner) +{ + if (goner) + XFREE(MTYPE_BGP_LABEL_CB, goner); +} + +static void lp_chunk_free(void *goner) +{ + if (goner) + XFREE(MTYPE_BGP_LABEL_CHUNK, goner); +} + +void bgp_lp_init(struct thread_master *master, struct labelpool *pool) +{ + if (BGP_DEBUG(labelpool, LABELPOOL)) + zlog_debug("%s: entry", __func__); + + lp = pool; /* Set module pointer to pool data */ + + lp->ledger = skiplist_new(0, NULL, lp_lcb_free); + lp->inuse = skiplist_new(0, NULL, NULL); + lp->chunks = list_new(); + lp->chunks->del = lp_chunk_free; + lp->requests = XCALLOC(MTYPE_BGP_LABEL_FIFO, sizeof(struct lp_fifo)); + LABEL_FIFO_INIT(lp->requests); + lp->callback_q = work_queue_new(master, "label callbacks"); + if (!lp->callback_q) { + zlog_err("%s: Failed to allocate work queue", __func__); + exit(1); + } + + lp->callback_q->spec.workfunc = lp_cbq_docallback; + lp->callback_q->spec.del_item_data = lp_cbq_item_free; + lp->callback_q->spec.max_retries = 0; +} + +void bgp_lp_finish(void) +{ + struct lp_fifo *lf; + + if (!lp) + return; + + skiplist_free(lp->ledger); + lp->ledger = NULL; + + skiplist_free(lp->inuse); + lp->inuse = NULL; + + list_delete_and_null(&lp->chunks); + + while ((lf = LABEL_FIFO_HEAD(lp->requests))) { + + LABEL_FIFO_DEL(lp->requests, lf); + XFREE(MTYPE_BGP_LABEL_FIFO, lf); + } + XFREE(MTYPE_BGP_LABEL_FIFO, lp->requests); + lp->requests = NULL; + + work_queue_free_and_null(&lp->callback_q); + + lp = NULL; +} + +static mpls_label_t get_label_from_pool(void *labelid) +{ + struct listnode *node; + struct lp_chunk *chunk; + int debug = BGP_DEBUG(labelpool, LABELPOOL); + + /* + * Find a free label + * Linear search is not efficient but should be executed infrequently. + */ + for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { + uintptr_t lbl; + + if (debug) + zlog_debug("%s: chunk first=%u last=%u", + __func__, chunk->first, chunk->last); + + for (lbl = chunk->first; lbl <= chunk->last; ++lbl) { + /* labelid is key to all-request "ledger" list */ + if (!skiplist_insert(lp->inuse, (void *)lbl, labelid)) { + /* + * Success + */ + return lbl; + } + } + } + return MPLS_LABEL_NONE; +} + +/* + * Success indicated by value of "label" field in returned LCB + */ +static struct lp_lcb *lcb_alloc( + int type, + void *labelid, + int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)) +{ + /* + * Set up label control block + */ + struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB, + sizeof(struct lp_lcb)); + + new->label = get_label_from_pool(labelid); + new->type = type; + new->labelid = labelid; + new->cbfunc = cbfunc; + + return new; +} + +/* + * Callers who need labels must supply a type, labelid, and callback. + * The type is a value defined in bgp_labelpool.h (add types as needed). + * The callback is for asynchronous notification of label allocation. + * The labelid is passed as an argument to the callback. It should be unique + * to the requested label instance. + * + * If zebra is not connected, callbacks with labels will be delayed + * until connection is established. If zebra connection is lost after + * labels have been assigned, existing assignments via this labelpool + * module will continue until reconnection. + * + * When connection to zebra is reestablished, previous label assignments + * will be invalidated (via callbacks having the "allocated" parameter unset) + * and new labels will be automatically reassigned by this labelpool module + * (that is, a requestor does not need to call lp_get() again if it is + * notified via callback that its label has been lost: it will eventually + * get another callback with a new label assignment). + * + * Prior requests for a given labelid are detected so that requests and + * assignments are not duplicated. + */ +void bgp_lp_get( + int type, + void *labelid, + int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)) +{ + struct lp_lcb *lcb; + int requested = 0; + int debug = BGP_DEBUG(labelpool, LABELPOOL); + + if (debug) + zlog_debug("%s: labelid=%p", __func__, labelid); + + /* + * Have we seen this request before? + */ + if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { + requested = 1; + } else { + lcb = lcb_alloc(type, labelid, cbfunc); + if (debug) + zlog_debug("%s: inserting lcb=%p label=%u", + __func__, lcb, lcb->label); + int rc = skiplist_insert(lp->ledger, labelid, lcb); + + if (rc) { + /* shouldn't happen */ + zlog_err("%s: can't insert new LCB into ledger list", + __func__); + XFREE(MTYPE_BGP_LABEL_CB, lcb); + return; + } + } + + if (lcb->label != MPLS_LABEL_NONE) { + /* + * Fast path: we filled the request from local pool (or + * this is a duplicate request that we filled already). + * Enqueue response work item with new label. + */ + struct lp_cbq_item *q; + + q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item)); + + q->cbfunc = lcb->cbfunc; + q->type = lcb->type; + q->label = lcb->label; + q->labelid = lcb->labelid; + q->allocated = true; + + work_queue_add(lp->callback_q, q); + + return; + } + + if (requested) + return; + + if (debug) + zlog_debug("%s: slow path. lcb=%p label=%u", + __func__, lcb, lcb->label); + + /* + * Slow path: we are out of labels in the local pool, + * so remember the request and also get another chunk from + * the label manager. + * + * We track number of outstanding label requests: don't + * need to get a chunk for each one. + */ + + struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO, + sizeof(struct lp_fifo)); + + lf->lcb = *lcb; + LABEL_FIFO_ADD(lp->requests, lf); + + if (LABEL_FIFO_COUNT(lp->requests) > lp->pending_count) { + if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) { + lp->pending_count += LP_CHUNK_SIZE; + return; + } + } +} + +void bgp_lp_release( + int type, + void *labelid, + mpls_label_t label) +{ + struct lp_lcb *lcb; + + if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { + if (label == lcb->label && type == lcb->type) { + uintptr_t lbl = label; + + /* no longer in use */ + skiplist_delete(lp->inuse, (void *)lbl, NULL); + + /* no longer requested */ + skiplist_delete(lp->ledger, labelid, NULL); + } + } +} + +/* + * zebra response giving us a chunk of labels + */ +void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) +{ + struct lp_chunk *chunk; + int debug = BGP_DEBUG(labelpool, LABELPOOL); + struct lp_fifo *lf; + + if (last < first) { + zlog_err("%s: zebra label chunk invalid: first=%u, last=%u", + __func__, first, last); + return; + } + + chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk)); + + chunk->first = first; + chunk->last = last; + + listnode_add(lp->chunks, chunk); + + lp->pending_count -= (last - first + 1); + + if (debug) { + zlog_debug("%s: %u pending requests", __func__, + LABEL_FIFO_COUNT(lp->requests)); + } + + while ((lf = LABEL_FIFO_HEAD(lp->requests))) { + + struct lp_lcb *lcb; + void *labelid = lf->lcb.labelid; + + if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) { + /* request no longer in effect */ + + if (debug) { + zlog_debug("%s: labelid %p: request no longer in effect", + __func__, labelid); + } + goto finishedrequest; + } + + /* have LCB */ + if (lcb->label != MPLS_LABEL_NONE) { + /* request already has a label */ + if (debug) { + zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p", + __func__, labelid, + lcb->label, lcb->label, lcb); + } + goto finishedrequest; + } + + lcb->label = get_label_from_pool(lcb->labelid); + + if (lcb->label == MPLS_LABEL_NONE) { + /* + * Out of labels in local pool, await next chunk + */ + if (debug) { + zlog_debug("%s: out of labels, await more", + __func__); + } + break; + } + + /* + * we filled the request from local pool. + * Enqueue response work item with new label. + */ + struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ, + sizeof(struct lp_cbq_item)); + + q->cbfunc = lcb->cbfunc; + q->type = lcb->type; + q->label = lcb->label; + q->labelid = lcb->labelid; + q->allocated = true; + + if (debug) + zlog_debug("%s: assigning label %u to labelid %p", + __func__, q->label, q->labelid); + + work_queue_add(lp->callback_q, q); + +finishedrequest: + LABEL_FIFO_DEL(lp->requests, lf); + XFREE(MTYPE_BGP_LABEL_FIFO, lf); + } +} + +/* + * continue using allocated labels until zebra returns + */ +void bgp_lp_event_zebra_down(void) +{ + /* rats. */ +} + +/* + * Inform owners of previously-allocated labels that their labels + * are not valid. Request chunk from zebra large enough to satisfy + * previously-allocated labels plus any outstanding requests. + */ +void bgp_lp_event_zebra_up(void) +{ + int labels_needed; + int chunks_needed; + void *labelid; + struct lp_lcb *lcb; + + /* + * Get label chunk allocation request dispatched to zebra + */ + labels_needed = LABEL_FIFO_COUNT(lp->requests) + + skiplist_count(lp->inuse); + + /* round up */ + chunks_needed = (labels_needed / LP_CHUNK_SIZE) + 1; + labels_needed = chunks_needed * LP_CHUNK_SIZE; + + zclient_send_get_label_chunk(zclient, 0, labels_needed); + lp->pending_count = labels_needed; + + /* + * Invalidate current list of chunks + */ + list_delete_all_node(lp->chunks); + + /* + * Invalidate any existing labels and requeue them as requests + */ + while (!skiplist_first(lp->inuse, NULL, &labelid)) { + + /* + * Get LCB + */ + if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { + + if (lcb->label != MPLS_LABEL_NONE) { + /* + * invalidate + */ + struct lp_cbq_item *q; + + q = XCALLOC(MTYPE_BGP_LABEL_CBQ, + sizeof(struct lp_cbq_item)); + q->cbfunc = lcb->cbfunc; + q->type = lcb->type; + q->label = lcb->label; + q->labelid = lcb->labelid; + q->allocated = false; + work_queue_add(lp->callback_q, q); + + lcb->label = MPLS_LABEL_NONE; + } + + /* + * request queue + */ + struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO, + sizeof(struct lp_fifo)); + + lf->lcb = *lcb; + LABEL_FIFO_ADD(lp->requests, lf); + } + + skiplist_delete_first(lp->inuse); + } +} diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h new file mode 100644 index 0000000000..fa35cde0e1 --- /dev/null +++ b/bgpd/bgp_labelpool.h @@ -0,0 +1,51 @@ +/* + * BGP Label Pool - Manage label chunk allocations from zebra asynchronously + * + * Copyright (C) 2018 LabN Consulting, L.L.C. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_BGP_LABELPOOL_H +#define _FRR_BGP_LABELPOOL_H + +#include <zebra.h> + +#include "mpls.h" + +/* + * Types used in bgp_lp_get for debug tracking; add more as needed + */ +#define LP_TYPE_VRF 0x00000001 + +struct labelpool { + struct skiplist *ledger; /* all requests */ + struct skiplist *inuse; /* individual labels */ + struct list *chunks; /* granted by zebra */ + struct lp_fifo *requests; /* blocked on zebra */ + struct work_queue *callback_q; + uint32_t pending_count; /* requested from zebra */ +}; + +extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool); +extern void bgp_lp_finish(void); +extern void bgp_lp_get(int type, void *labelid, + int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)); +extern void bgp_lp_release(int type, void *labelid, mpls_label_t label); +extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last); +extern void bgp_lp_event_zebra_down(void); +extern void bgp_lp_event_zebra_up(void); + +#endif /* _FRR_BGP_LABELPOOL_H */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index e33f3d3477..915387ca2d 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -592,6 +592,8 @@ void bgp_info_mpath_update(struct bgp_node *rn, struct bgp_info *new_best, */ new_mpath = listgetdata(mp_node); list_delete_node(mp_list, mp_node); + assert(new_mpath); + assert(prev_mpath); if ((mpath_count < maxpaths) && (new_mpath != new_best) && bgp_info_nexthop_cmp(prev_mpath, new_mpath)) { if (new_mpath == next_mpath) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 13a283a058..08aaed6577 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -310,6 +310,65 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; } +int vpn_leak_label_callback( + mpls_label_t label, + void *labelid, + bool allocated) +{ + struct vpn_policy *vp = (struct vpn_policy *)labelid; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (debug) + zlog_debug("%s: label=%u, allocated=%d", + __func__, label, allocated); + + if (!allocated) { + /* + * previously-allocated label is now invalid + */ + if (CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO) && + (vp->tovpn_label != MPLS_LABEL_NONE)) { + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + vp->tovpn_label = MPLS_LABEL_NONE; + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + } + return 0; + } + + /* + * New label allocation + */ + if (!CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + + /* + * not currently configured for auto label, reject allocation + */ + return -1; + } + + if (vp->tovpn_label != MPLS_LABEL_NONE) { + if (label == vp->tovpn_label) { + /* already have same label, accept but do nothing */ + return 0; + } + /* Shouldn't happen: different label allocation */ + zlog_err("%s: %s had label %u but got new assignment %u", + __func__, vp->bgp->name_pretty, vp->tovpn_label, label); + /* use new one */ + } + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + vp->tovpn_label = label; + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, + vp->afi, bgp_get_default(), vp->bgp); + + return 0; +} + static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) { int i; @@ -331,6 +390,57 @@ static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) return 0; } +static bool labels_same(struct bgp_info *bi, mpls_label_t *label, uint32_t n) +{ + uint32_t i; + + if (!bi->extra) { + if (!n) + return true; + else + return false; + } + + if (n != bi->extra->num_labels) + return false; + + for (i = 0; i < n; ++i) { + if (label[i] != bi->extra->label[i]) + return false; + } + return true; +} + +/* + * make encoded route labels match specified encoded label set + */ +static void setlabels( + struct bgp_info *bi, + mpls_label_t *label, /* array of labels */ + uint32_t num_labels) +{ + if (num_labels) + assert(label); + assert(num_labels <= BGP_MAX_LABELS); + + if (!num_labels) { + if (bi->extra) + bi->extra->num_labels = 0; + return; + } + + struct bgp_info_extra *extra = bgp_info_extra_get(bi); + uint32_t i; + + for (i = 0; i < num_labels; ++i) { + extra->label[i] = label[i]; + if (!bgp_is_valid_label(&label[i])) { + bgp_set_valid_label(&extra->label[i]); + } + } + extra->num_labels = num_labels; +} + /* * returns pointer to new bgp_info upon success */ @@ -343,7 +453,7 @@ leak_update( safi_t safi, struct bgp_info *source_bi, mpls_label_t *label, - int num_labels, + uint32_t num_labels, void *parent, struct bgp *bgp_orig, struct prefix *nexthop_orig, @@ -371,7 +481,10 @@ leak_update( } if (bi) { + bool labelssame = labels_same(bi, label, num_labels); + if (attrhash_cmp(bi->attr, new_attr) + && labelssame && !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) { bgp_attr_unintern(&new_attr); @@ -395,6 +508,12 @@ leak_update( bi->attr = new_attr; bi->uptime = bgp_clock(); + /* + * rewrite labels + */ + if (!labelssame) + setlabels(bi, label, num_labels); + if (nexthop_self_flag) bgp_info_set_flag(bn, bi, BGP_INFO_ANNC_NH_SELF); @@ -442,23 +561,10 @@ leak_update( if (nexthop_self_flag) bgp_info_set_flag(bn, new, BGP_INFO_ANNC_NH_SELF); + if (num_labels) + setlabels(new, label, num_labels); + bgp_info_extra_get(new); - if (label) { - int i; - - for (i = 0; i < num_labels; ++i) { - new->extra->label[i] = label[i]; - if (!bgp_is_valid_label(&label[i])) { - if (debug) { - zlog_debug( - "%s: %s: marking label %d valid", - __func__, buf_prefix, i); - } - bgp_set_valid_label(&new->extra->label[i]); - } - } - new->extra->num_labels = num_labels; - } new->extra->parent = parent; if (bgp_orig) @@ -889,8 +995,10 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ const char *debugmsg; struct prefix nexthop_orig; mpls_label_t *pLabels = NULL; - int num_labels = 0; + uint32_t num_labels = 0; int nexthop_self_flag = 1; + struct bgp_info *bi_ultimate = NULL; + int origin_local = 0; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); @@ -982,13 +1090,40 @@ static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ /* * ensure labels are copied + * + * However, there is a special case: if the route originated in + * another local VRF (as opposed to arriving via VPN), then the + * nexthop is reached by hairpinning through this router (me) + * using IP forwarding only (no LSP). Therefore, the route + * imported to the VRF should not have labels attached. Note + * that nexthop tracking is also involved: eliminating the + * labels for these routes enables the non-labeled nexthops + * from the originating VRF to be considered valid for this route. */ - if (info_vpn->extra && info_vpn->extra->num_labels) { + + /* work back to original route */ + for (bi_ultimate = info_vpn; + bi_ultimate->extra && bi_ultimate->extra->parent; + bi_ultimate = bi_ultimate->extra->parent) + ; + + /* if original route was unicast, then it did not arrive over vpn */ + if (bi_ultimate->net) { + struct bgp_table *table; + + table = bgp_node_table(bi_ultimate->net); + if (table && (table->safi == SAFI_UNICAST)) + origin_local = 1; + } + + /* copy labels */ + if (!origin_local && info_vpn->extra && info_vpn->extra->num_labels) { num_labels = info_vpn->extra->num_labels; if (num_labels > BGP_MAX_LABELS) num_labels = BGP_MAX_LABELS; pLabels = info_vpn->extra->label; } + if (debug) { char buf_prefix[PREFIX_STRLEN]; prefix2str(p, buf_prefix, sizeof(buf_prefix)); diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 64303325ee..7398559498 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -77,6 +77,7 @@ extern void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi); extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi); +extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc); static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, const char **pmsg) @@ -111,6 +112,17 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, *pmsg = "rd not defined"; return 0; } + + /* Is there an "auto" export label that isn't allocated yet? */ + if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO) && + (bgp_vrf->vpn_policy[afi].tovpn_label == MPLS_LABEL_NONE)) { + + if (pmsg) + *pmsg = "auto label not allocated"; + return 0; + } + return 1; } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index f0b30f0186..9ea2f22cec 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -649,7 +649,6 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, uint8_t sub_code, uint8_t *data, size_t datalen) { struct stream *s; - int length; /* Lock I/O mutex to prevent other threads from pushing packets */ pthread_mutex_lock(&peer->io_mtx); @@ -670,7 +669,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, stream_write(s, data, datalen); /* Set BGP packet length. */ - length = bgp_packet_set_size(s); + bgp_packet_set_size(s); /* wipe output buffer */ stream_fifo_clean(peer->obuf); @@ -697,13 +696,13 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, bgp_notify.code = code; bgp_notify.subcode = sub_code; bgp_notify.data = NULL; - bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE; + bgp_notify.length = datalen; bgp_notify.raw_data = data; peer->notify.code = bgp_notify.code; peer->notify.subcode = bgp_notify.subcode; - if (bgp_notify.length) { + if (bgp_notify.length && data) { bgp_notify.data = XMALLOC(MTYPE_TMP, bgp_notify.length * 3); for (i = 0; i < bgp_notify.length; i++) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 944ae5b5dc..a71f5ac956 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1874,7 +1874,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, if (debug) { bgp_info_path_with_addpath_rx_str(new_select, path_buf); - zlog_debug("%s: %s is the bestpath from AS %d", + zlog_debug("%s: %s is the bestpath from AS %u", pfx_buf, path_buf, aspath_get_first_as( new_select->attr->aspath)); @@ -6545,8 +6545,20 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, } else vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); } else if (safi == SAFI_FLOWSPEC) { - /* already done */ - /* IPv4 Next Hop */ + if (attr->nexthop.s_addr != 0) { + if (json_paths) { + json_nexthop_global = json_object_new_object(); + json_object_string_add( + json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + json_object_string_add(json_nexthop_global, + "afi", "ipv4"); + json_object_boolean_true_add(json_nexthop_global, + "used"); + } else { + vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); + } + } } else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { if (json_paths) { json_nexthop_global = json_object_new_object(); @@ -7035,32 +7047,36 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, default: vty_out(vty, "?"); } + + char *str = esi2str(&(attr->evpn_overlay.eth_s_id)); + + vty_out(vty, "%s", str); + XFREE(MTYPE_TMP, str); + + if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) { + vty_out(vty, "/%s", + inet_ntoa(attr->evpn_overlay.gw_ip.ipv4)); + } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) { + vty_out(vty, "/%s", + inet_ntop(AF_INET6, + &(attr->evpn_overlay.gw_ip.ipv6), buf, + BUFSIZ)); + } + if (attr->ecommunity) { + char *mac = NULL; + struct ecommunity_val *routermac = ecommunity_lookup( + attr->ecommunity, ECOMMUNITY_ENCODE_EVPN, + ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC); + if (routermac) + mac = ecom_mac2str((char *)routermac->val); + if (mac) { + vty_out(vty, "/%s", (char *)mac); + XFREE(MTYPE_TMP, mac); + } + } + vty_out(vty, "\n"); } - struct eth_segment_id *id = &(attr->evpn_overlay.eth_s_id); - char *str = esi2str(id); - vty_out(vty, "%s", str); - XFREE(MTYPE_TMP, str); - if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) { - vty_out(vty, "/%s", inet_ntoa(attr->evpn_overlay.gw_ip.ipv4)); - } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) { - vty_out(vty, "/%s", - inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6), - buf, BUFSIZ)); - } - if (attr->ecommunity) { - char *mac = NULL; - struct ecommunity_val *routermac = ecommunity_lookup( - attr->ecommunity, ECOMMUNITY_ENCODE_EVPN, - ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC); - if (routermac) - mac = ecom_mac2str((char *)routermac->val); - if (mac) { - vty_out(vty, "/%s", (char *)mac); - XFREE(MTYPE_TMP, mac); - } - } - vty_out(vty, "\n"); } /* dampening route */ diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 3e577317ad..6bc50fb77e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6245,12 +6245,13 @@ ALIAS (af_rd_vpn_export, DEFPY (af_label_vpn_export, af_label_vpn_export_cmd, - "[no] label vpn export (0-1048575)$label_val", + "[no] label vpn export <(0-1048575)$label_val|auto$label_auto>", NO_STR "label value for VRF\n" "Between current address-family and vpn\n" "For routes leaked from current address-family to vpn\n" - "Label Value <0-1048575>\n") + "Label Value <0-1048575>\n" + "Automatically assign a label\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); mpls_label_t label = MPLS_LABEL_NONE; @@ -6263,8 +6264,10 @@ DEFPY (af_label_vpn_export, if (argv_find(argv, argc, "no", &idx)) yes = 0; - if (yes) - label = label_val; /* rely on parser to force unsigned */ + if (yes) { + if (!label_auto) + label = label_val; /* parser should force unsigned */ + } ret = vpn_policy_getafi(vty, doafi); if (ret != CMD_SUCCESS) @@ -6274,13 +6277,48 @@ DEFPY (af_label_vpn_export, if (!doafi[afi]) continue; + if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) + + continue; /* no change */ + /* * pre-change: un-export vpn routes (vpn->vrf routes unaffected) */ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_get_default(), bgp); + if (!label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + + if (bgp->vpn_policy[afi].tovpn_label != + MPLS_LABEL_NONE) { + + /* + * label has previously been automatically + * assigned by labelpool: release it + * + * NB if tovpn_label == MPLS_LABEL_NONE it + * means the automatic assignment is in flight + * and therefore the labelpool callback must + * detect that the auto label is not needed. + */ + + bgp_lp_release(LP_TYPE_VRF, + &bgp->vpn_policy[afi], + bgp->vpn_policy[afi].tovpn_label); + } + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO); + } + bgp->vpn_policy[afi].tovpn_label = label; + if (label_auto) { + SET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO); + bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi], + vpn_leak_label_callback); + } /* post-change: re-export vpn routes */ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, @@ -11706,9 +11744,16 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, { int indent = 2; - if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { - vty_out(vty, "%*slabel vpn export %u\n", indent, "", - bgp->vpn_policy[afi].tovpn_label); + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + + vty_out(vty, "%*slabel vpn export %s\n", indent, "", "auto"); + + } else { + if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { + vty_out(vty, "%*slabel vpn export %u\n", indent, "", + bgp->vpn_policy[afi].tovpn_label); + } } if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) { @@ -13161,8 +13206,6 @@ DEFUN (no_ip_community_list_standard_all, "Specify community to accept\n" COMMUNITY_VAL_STR) { - int delete_all = 0; - char *cl_name_or_number = NULL; int direct = 0; int style = COMMUNITY_LIST_STANDARD; @@ -13177,7 +13220,7 @@ DEFUN (no_ip_community_list_standard_all, char *str = argv_concat(argv, argc, idx); int ret = community_list_unset(bgp_clist, cl_name_or_number, str, - direct, style, delete_all); + direct, style); XFREE(MTYPE_TMP, str); @@ -13242,8 +13285,6 @@ DEFUN (no_ip_community_list_expanded_all, "Specify community to accept\n" COMMUNITY_VAL_STR) { - int delete_all = 0; - char *cl_name_or_number = NULL; int direct = 0; int style = COMMUNITY_LIST_EXPANDED; @@ -13258,7 +13299,7 @@ DEFUN (no_ip_community_list_expanded_all, char *str = argv_concat(argv, argc, idx); int ret = community_list_unset(bgp_clist, cl_name_or_number, str, - direct, style, delete_all); + direct, style); XFREE(MTYPE_TMP, str); @@ -13795,8 +13836,6 @@ DEFUN (no_ip_extcommunity_list_standard_all, "Specify community to accept\n" EXTCOMMUNITY_VAL_STR) { - int deleteall = 0; - int style = EXTCOMMUNITY_LIST_STANDARD; int direct = 0; char *cl_number_or_name = NULL; @@ -13811,7 +13850,7 @@ DEFUN (no_ip_extcommunity_list_standard_all, char *str = argv_concat(argv, argc, idx); int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str, - direct, style, deleteall); + direct, style); XFREE(MTYPE_TMP, str); @@ -13836,8 +13875,6 @@ DEFUN (no_ip_extcommunity_list_expanded_all, "Specify community to accept\n" "An ordered list as a regular-expression\n") { - int deleteall = 0; - int style = EXTCOMMUNITY_LIST_EXPANDED; int direct = 0; char *cl_number_or_name = NULL; @@ -13852,7 +13889,7 @@ DEFUN (no_ip_extcommunity_list_expanded_all, char *str = argv_concat(argv, argc, idx); int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str, - direct, style, deleteall); + direct, style); XFREE(MTYPE_TMP, str); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 023a866315..b564fccf43 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -55,6 +55,7 @@ #endif #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_labelpool.h" /* All information about zebra. */ struct zclient *zclient = NULL; @@ -1876,6 +1877,9 @@ static void bgp_zebra_connected(struct zclient *zclient) /* Send the client registration */ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + /* tell label pool that zebra is connected */ + bgp_lp_event_zebra_up(); + /* TODO - What if we have peers and networks configured, do we have to * kick-start them? */ @@ -2042,6 +2046,41 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, } } +static void bgp_zebra_process_label_chunk( + int cmd, + struct zclient *zclient, + zebra_size_t length, + vrf_id_t vrf_id) +{ + struct stream *s = NULL; + uint8_t response_keep; + uint32_t first; + uint32_t last; + + s = zclient->ibuf; + STREAM_GETC(s, response_keep); + STREAM_GETL(s, first); + STREAM_GETL(s, last); + + if (first > last || + first < MPLS_LABEL_UNRESERVED_MIN || + last > MPLS_LABEL_UNRESERVED_MAX) { + + zlog_err("%s: Invalid Label chunk: %u - %u", + __func__, first, last); + return; + } + if (BGP_DEBUG(zebra, ZEBRA)) { + zlog_debug("Label Chunk assign: %u - %u (%u) ", + first, last, response_keep); + } + + bgp_lp_event_chunk(response_keep, first, last); + +stream_failure: /* for STREAM_GETX */ + return; +} + extern struct zebra_privs_t bgpd_privs; void bgp_zebra_init(struct thread_master *master) @@ -2076,6 +2115,7 @@ void bgp_zebra_init(struct thread_master *master) zclient->local_l3vni_del = bgp_zebra_process_local_l3vni; zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix; zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix; + zclient->label_chunk = bgp_zebra_process_label_chunk; } void bgp_zebra_destroy(void) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index c46111e1fb..df0f1bd19c 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -81,7 +81,7 @@ #include "bgpd/bgp_io.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_flowspec.h" - +#include "bgpd/bgp_labelpool.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_QOBJ_TYPE(bgp_master) @@ -2951,6 +2951,8 @@ static struct bgp *bgp_create(as_t *as, const char *name, #endif /* ENABLE_BGP_VNC */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { + bgp->vpn_policy[afi].bgp = bgp; + bgp->vpn_policy[afi].afi = afi; bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = MPLS_LABEL_NONE; @@ -7536,6 +7538,9 @@ void bgp_master_init(struct thread_master *master) /* Enable multiple instances by default. */ bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); + /* mpls label dynamic allocation pool */ + bgp_lp_init(bm->master, &bm->labelpool); + QOBJ_REG(bm, bgp_master); } @@ -7714,4 +7719,6 @@ void bgp_terminate(void) if (bm->t_rmap_update) BGP_TIMER_OFF(bm->t_rmap_update); + + bgp_lp_finish(); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 0c5f72662c..680bac0214 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -37,6 +37,7 @@ #include "bgp_memory.h" #include "bitfield.h" #include "vxlan.h" +#include "bgp_labelpool.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -140,6 +141,9 @@ struct bgp_master { /* Id space for automatic RD derivation for an EVI/VRF */ bitfield_t rd_idspace; + /* dynamic mpls label allocation pool */ + struct labelpool labelpool; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp_master) @@ -167,6 +171,25 @@ typedef enum { BGP_VPN_POLICY_DIR_MAX = 2 } vpn_policy_direction_t; +struct vpn_policy { + struct bgp *bgp; /* parent */ + afi_t afi; + struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX]; + struct ecommunity *import_redirect_rtlist; + char *rmap_name[BGP_VPN_POLICY_DIR_MAX]; + struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX]; + + /* should be mpls_label_t? */ + uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */ + uint32_t tovpn_zebra_vrf_label_last_sent; + struct prefix_rd tovpn_rd; + struct prefix tovpn_nexthop; /* unset => set to 0 */ + uint32_t flags; +#define BGP_VPN_POLICY_TOVPN_LABEL_AUTO (1 << 0) +#define BGP_VPN_POLICY_TOVPN_RD_SET (1 << 1) +#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2) +}; + /* * Type of 'struct bgp'. * - Default: The default instance @@ -465,22 +488,7 @@ struct bgp { /* route map for advertise ipv4/ipv6 unicast (type-5 routes) */ struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX]; - /* vpn-policy */ - struct { - struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX]; - struct ecommunity *import_redirect_rtlist; - char *rmap_name[BGP_VPN_POLICY_DIR_MAX]; - struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX]; - - /* should be mpls_label_t? */ - uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */ - uint32_t tovpn_zebra_vrf_label_last_sent; - struct prefix_rd tovpn_rd; - struct prefix tovpn_nexthop; /* unset => set to 0 */ - uint32_t flags; -#define BGP_VPN_POLICY_TOVPN_RD_SET 0x00000004 -#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET 0x00000008 - } vpn_policy[AFI_MAX]; + struct vpn_policy vpn_policy[AFI_MAX]; QOBJ_FIELDS }; diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 8c4d5ab043..3e9722d920 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -548,13 +548,12 @@ rfapi_group_new(struct bgp *bgp, rfapi_group_cfg_type_t type, const char *name) rfg = XCALLOC(MTYPE_RFAPI_GROUP_CFG, sizeof(struct rfapi_nve_group_cfg)); - if (rfg) { - rfg->type = type; - rfg->name = strdup(name); - /* add to tail of list */ - listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg); - } + rfg->type = type; + rfg->name = strdup(name); + /* add to tail of list */ + listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg); rfg->label = MPLS_LABEL_NONE; + QOBJ_REG(rfg, rfapi_nve_group_cfg); return rfg; diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index d5208f6966..c1af269d3f 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -1543,10 +1543,15 @@ static int rfapiNhlAddNodeRoutes( int count = 0; int is_l2 = (rn->p.family == AF_ETHERNET); - if (rfapiRibFTDFilterRecentPrefix( - (struct rfapi_descriptor *)(rfd_rib_node->table->info), rn, - pfx_target_original)) { - return 0; + if (rfd_rib_node && rfd_rib_node->table && rfd_rib_node->table->info) { + struct rfapi_descriptor *rfd; + + rfd = (struct rfapi_descriptor *)(rfd_rib_node->table->info); + + if (rfapiRibFTDFilterRecentPrefix( + rfd, rn, pfx_target_original)) + + return 0; } seen_nexthops = @@ -2797,19 +2802,16 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, uint32_t lifetime; struct rfapi_withdraw *wcb; - if - CHECK_FLAG(bi->flags, BGP_INFO_REMOVED) - { - /* - * Already on the path to being withdrawn, - * should already have a timer set up to - * delete it. - */ - vnc_zlog_debug_verbose( - "%s: already being withdrawn, do nothing", - __func__); - return; - } + if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) { + /* + * Already on the path to being withdrawn, + * should already have a timer set up to + * delete it. + */ + vnc_zlog_debug_verbose( + "%s: already being withdrawn, do nothing", __func__); + return; + } rfapiGetVncLifetime(bi->attr, &lifetime); vnc_zlog_debug_verbose("%s: VNC lifetime is %u", __func__, lifetime); diff --git a/doc/Makefile.am b/doc/Makefile.am index a6e693c0c9..dec6b53e0f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -264,4 +264,5 @@ EXTRA_DIST = frr-sphinx.mk \ figures/ospf_api_architecture.png \ figures/ospf_api_msghdr.png \ figures/ospf_api_msgs1.png \ - figures/ospf_api_msgs2.png + figures/ospf_api_msgs2.png \ + extra/frrlexer.py diff --git a/doc/developer/_static/overrides.css b/doc/developer/_static/overrides.css index 1e0de66c55..0d871c961a 100644 --- a/doc/developer/_static/overrides.css +++ b/doc/developer/_static/overrides.css @@ -2,3 +2,7 @@ div.body { max-width: none; } + +pre { + background-color: #e2e2e2; +} diff --git a/doc/developer/building-frr-on-alpine.rst b/doc/developer/building-frr-on-alpine.rst index f0f2aee138..d303784d4e 100644 --- a/doc/developer/building-frr-on-alpine.rst +++ b/doc/developer/building-frr-on-alpine.rst @@ -10,6 +10,28 @@ Depending on your host, there are different ways of installing docker. Refer to the documentation here for instructions on how to install a free version of docker: https://www.docker.com/community-edition +Pre-built packages and docker images +------------------------------------ + +The master branch of https://github.com/frrouting/frr.git has a +continuous delivery of docker images to docker hub at: +https://hub.docker.com/r/ajones17/frr/. These images have the frr packages +in /pkgs/apk and have the frr package pre-installed. To copy Alpine +packages out of these images: + +:: + + id=`docker create ajones17/frr:latest` + docker cp ${id}:/pkgs _some_directory_ + docker rm $id + +To run the frr daemons (see below for how to configure them): + +:: + + docker run -it --rm --name frr ajones17/frr:latest + docker exec -it frr /bin/sh + Work with sources ----------------- @@ -52,13 +74,32 @@ And to run the image: :: - docker run -it --rm frr:latest /bin/sh + docker run -it --rm --name frr frr:latest + +In the default configuration, none of the frr daemons will be running. +To configure the daemons, exec into the container and edit the configuration +files or mount a volume with configuration files into the container on +startup. To configure by hand: + +:: + + docker exec -it frr /bin/sh + vi /etc/frr/daemons + vi /etc/frr/daemons.conf + cp /etc/frr/zebra.conf.sample /etc/frr/zebra.conf + vi /etc/frr/zebra.conf + /etc/init.d/frr start + +Or, to configure the daemons using /etc/frr from a host volume, put the +config files in, say, ./docker/etc and bind mount that into the +container: + +:: -Currently, we only package the raw daemons and example files, so, you'll -need to run the daemons by hand (or, better, orchestrate in the Dockerfile). + docker run -it --rm -v `pwd`/docker/etc:/etc/frr frr:latest -We can also build directly from docker-compose, with a docker-compose.yml file -like this one: +We can also build the base image directly from docker-compose, with a +docker-compose.yml file like this one: :: diff --git a/doc/developer/building-frr-on-centos6.rst b/doc/developer/building-frr-on-centos6.rst index 1c53ea6ba3..d50376548f 100644 --- a/doc/developer/building-frr-on-centos6.rst +++ b/doc/developer/building-frr-on-centos6.rst @@ -125,7 +125,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -135,7 +135,7 @@ Add frr groups and user -c "FRR FRRouting suite" -d /var/run/frr frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -176,7 +176,7 @@ an example.) sudo make SPHINXBUILD=sphinx-build2.7 install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -198,7 +198,7 @@ Create empty FRR configuration files sudo chmod 640 /etc/frr/*.conf Install daemon config file -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -206,13 +206,13 @@ Install daemon config file sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons as required by changing the value to ``yes`` Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and set the following values (ignore the other settings) @@ -233,7 +233,7 @@ Load the modifed sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf Add init.d startup files -~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -241,14 +241,14 @@ Add init.d startup files sudo chkconfig --add frr Enable frr daemon at startup -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: sudo chkconfig frr on Start FRR manually (or reboot) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: diff --git a/doc/developer/building-frr-on-centos7.rst b/doc/developer/building-frr-on-centos7.rst index 9807543e17..31cd4dcc49 100644 --- a/doc/developer/building-frr-on-centos7.rst +++ b/doc/developer/building-frr-on-centos7.rst @@ -31,7 +31,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -41,7 +41,7 @@ Add frr groups and user -c "FRR FRRouting suite" -d /var/run/frr frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -83,7 +83,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -106,7 +106,7 @@ Create empty FRR configuration files sudo chmod 640 /etc/frr/*.conf Install daemon config file -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -114,13 +114,13 @@ Install daemon config file sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons as required by changing the value to ``yes`` Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the following content: @@ -140,7 +140,7 @@ Load the modifed sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf Install frr Service and redhat init files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -148,21 +148,21 @@ Install frr Service and redhat init files sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr Register the systemd files -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ :: sudo systemctl preset frr.service Enable required frr at startup -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: sudo systemctl enable frr Reboot or start FRR manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: diff --git a/doc/developer/building-frr-on-debian8.rst b/doc/developer/building-frr-on-debian8.rst index 36ed0c4e66..d1e65a472d 100644 --- a/doc/developer/building-frr-on-debian8.rst +++ b/doc/developer/building-frr-on-debian8.rst @@ -32,7 +32,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -43,7 +43,7 @@ Add frr groups and user sudo usermod -a -G frrvty frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -80,7 +80,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -99,7 +99,7 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the other settings) @@ -118,7 +118,7 @@ other settings) system Troubleshooting -~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^ **Local state directory** diff --git a/doc/developer/building-frr-on-debian9.rst b/doc/developer/building-frr-on-debian9.rst index a1aa63236c..7dad9a7bd4 100644 --- a/doc/developer/building-frr-on-debian9.rst +++ b/doc/developer/building-frr-on-debian9.rst @@ -19,7 +19,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -30,7 +30,7 @@ Add frr groups and user sudo usermod -a -G frrvty frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -68,7 +68,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -88,7 +88,7 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the other settings) @@ -110,7 +110,7 @@ Troubleshooting --------------- Shared library error -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ If you try and start any of the frrouting daemons you may see the below error due to the frrouting shared library directory not being found: diff --git a/doc/developer/building-frr-on-fedora24.rst b/doc/developer/building-frr-on-fedora24.rst index ed81d3f59c..208c580b63 100644 --- a/doc/developer/building-frr-on-fedora24.rst +++ b/doc/developer/building-frr-on-fedora24.rst @@ -24,7 +24,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -34,7 +34,7 @@ Add frr groups and user -c "FRR FRRouting suite" -d /var/run/frr frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -75,7 +75,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -99,7 +99,7 @@ Create empty FRR configuration files sudo chmod 640 /etc/frr/*.conf Install daemon config file -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -107,13 +107,13 @@ Install daemon config file sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons as required by changing the value to ``yes`` Enable IP & IPv6 forwarding (and MPLS) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the following content: (Please make sure to list all interfaces with @@ -155,7 +155,7 @@ And load the kernel modules on the running system: sudo modprobe mpls-router mpls-iptunnel Install frr Service and redhat init files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -163,14 +163,14 @@ Install frr Service and redhat init files sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr Enable required frr at startup -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: sudo systemctl enable frr Reboot or start FRR manually -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: diff --git a/doc/developer/building-frr-on-freebsd10.rst b/doc/developer/building-frr-on-freebsd10.rst index ccbe8c55c3..5d14db5fa7 100644 --- a/doc/developer/building-frr-on-freebsd10.rst +++ b/doc/developer/building-frr-on-freebsd10.rst @@ -34,7 +34,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr group and user -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ :: @@ -76,7 +76,7 @@ an example) sudo gmake install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -95,7 +95,7 @@ Create empty FRR configuration files sudo chmod 640 /usr/local/etc/frr/*.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to the end of ``/etc/sysctl.conf``: diff --git a/doc/developer/building-frr-on-freebsd11.rst b/doc/developer/building-frr-on-freebsd11.rst index 214fdbf9c8..87fb30226d 100644 --- a/doc/developer/building-frr-on-freebsd11.rst +++ b/doc/developer/building-frr-on-freebsd11.rst @@ -34,7 +34,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr group and user -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ :: @@ -76,7 +76,7 @@ an example) sudo gmake install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -95,7 +95,7 @@ Create empty FRR configuration files sudo chmod 640 /usr/local/etc/frr/*.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to the end of ``/etc/sysctl.conf``: diff --git a/doc/developer/building-frr-on-freebsd9.rst b/doc/developer/building-frr-on-freebsd9.rst index 909b3a8d64..02279debea 100644 --- a/doc/developer/building-frr-on-freebsd9.rst +++ b/doc/developer/building-frr-on-freebsd9.rst @@ -47,7 +47,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr group and user -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ :: @@ -89,7 +89,7 @@ an example) sudo gmake install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -108,7 +108,7 @@ Create empty FRR configuration files sudo chmod 640 /usr/local/etc/frr/*.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to the end of ``/etc/sysctl.conf``: diff --git a/doc/developer/building-frr-on-lede-openwrt.rst b/doc/developer/building-frr-on-lede-openwrt.rst index b9ee9c51e1..d14754b37f 100644 --- a/doc/developer/building-frr-on-lede-openwrt.rst +++ b/doc/developer/building-frr-on-lede-openwrt.rst @@ -98,11 +98,11 @@ DAEMONS= or don't install unneded packages For example: zebra bgpd ldpd isisd nhrpd ospfd ospf6d pimd ripd ripngd Enable the serivce -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ - service frr enable Start the service -~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^ - service frr start diff --git a/doc/developer/building-frr-on-netbsd6.rst b/doc/developer/building-frr-on-netbsd6.rst index ce5c58045f..ca0845d0d0 100644 --- a/doc/developer/building-frr-on-netbsd6.rst +++ b/doc/developer/building-frr-on-netbsd6.rst @@ -44,7 +44,7 @@ Get FRR, compile it and install it (from Git) --------------------------------------------- Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -54,7 +54,7 @@ Add frr groups and user -d /nonexistent -s /sbin/nologin frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example) @@ -89,7 +89,7 @@ an example) sudo gmake install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -109,7 +109,7 @@ Create empty FRR configuration files sudo chmod 640 /usr/pkg/etc/frr/*.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to the end of ``/etc/sysctl.conf``: @@ -123,7 +123,7 @@ Add the following lines to the end of ``/etc/sysctl.conf``: system Install rc.d init files -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -131,7 +131,7 @@ Install rc.d init files chmod 555 /etc/rc.d/*.sh Enable FRR processes -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ (Enable the required processes only) diff --git a/doc/developer/building-frr-on-netbsd7.rst b/doc/developer/building-frr-on-netbsd7.rst index eaaf87fd19..86242ef965 100644 --- a/doc/developer/building-frr-on-netbsd7.rst +++ b/doc/developer/building-frr-on-netbsd7.rst @@ -35,7 +35,7 @@ Get FRR, compile it and install it (from Git) --------------------------------------------- Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -45,7 +45,7 @@ Add frr groups and user -d /nonexistent -s /sbin/nologin frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example) @@ -80,7 +80,7 @@ an example) sudo gmake install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -99,7 +99,7 @@ Create empty FRR configuration files sudo chmod 640 /usr/pkg/etc/frr/*.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to the end of ``/etc/sysctl.conf``: @@ -113,7 +113,7 @@ Add the following lines to the end of ``/etc/sysctl.conf``: system Install rc.d init files -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -121,7 +121,7 @@ Install rc.d init files chmod 555 /etc/rc.d/*.sh Enable FRR processes -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ (Enable the required processes only) diff --git a/doc/developer/building-frr-on-omnios.rst b/doc/developer/building-frr-on-omnios.rst index 0eb2b9fec8..03f3845de8 100644 --- a/doc/developer/building-frr-on-omnios.rst +++ b/doc/developer/building-frr-on-omnios.rst @@ -9,7 +9,7 @@ OmniOS restrictions: use without MPLS Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -93,7 +93,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr group and user -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ :: @@ -138,7 +138,7 @@ an example) sudo gmake install Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: diff --git a/doc/developer/building-frr-on-openbsd6.rst b/doc/developer/building-frr-on-openbsd6.rst index 895048886e..1f3aec8d92 100644 --- a/doc/developer/building-frr-on-openbsd6.rst +++ b/doc/developer/building-frr-on-openbsd6.rst @@ -30,7 +30,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr group and user -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ :: @@ -40,7 +40,7 @@ Add frr group and user -d /nonexistent -s /sbin/nologin _frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example) @@ -75,7 +75,7 @@ an example) doas gmake install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -100,7 +100,7 @@ Create empty FRR configuration files doas chmod 640 /etc/frr/*.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to the end of ``/etc/rc.conf``: @@ -113,7 +113,7 @@ Add the following lines to the end of ``/etc/rc.conf``: **Reboot** to apply the config to the system Enable MPLS Forwarding -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ To enable MPLS forwarding on a given interface, use the following command: @@ -132,7 +132,7 @@ Example: inet 10.0.1.1 255.255.255.0 mpls Install rc.d init files -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ (create them in /etc/rc.d - no example are included at this time with FRR source) @@ -152,7 +152,7 @@ Example (for zebra - store as ``/etc/rc.d/frr_zebra.sh``) rc_cmd $1 Enable FRR processes -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ (Enable the required processes only) diff --git a/doc/developer/building-frr-on-ubuntu1204.rst b/doc/developer/building-frr-on-ubuntu1204.rst index 68e476fec2..bba49c1ce7 100644 --- a/doc/developer/building-frr-on-ubuntu1204.rst +++ b/doc/developer/building-frr-on-ubuntu1204.rst @@ -70,7 +70,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -81,7 +81,7 @@ Add frr groups and user sudo usermod -a -G frrvty frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -116,7 +116,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -135,7 +135,7 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the other settings) @@ -154,7 +154,7 @@ other settings) system Install the init.d service -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -164,7 +164,7 @@ Install the init.d service sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf Enable daemons -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ | Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for those daemons you want to start by systemd. @@ -181,7 +181,7 @@ Enable daemons isisd=yes Start the init.d service -~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^ - /etc/init.d/frr start - use ``/etc/init.d/frr status`` to check its status. diff --git a/doc/developer/building-frr-on-ubuntu1404.rst b/doc/developer/building-frr-on-ubuntu1404.rst index 10944cb8e1..c86f1124a7 100644 --- a/doc/developer/building-frr-on-ubuntu1404.rst +++ b/doc/developer/building-frr-on-ubuntu1404.rst @@ -24,7 +24,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -35,7 +35,7 @@ Add frr groups and user sudo usermod -a -G frrvty frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -71,7 +71,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -90,7 +90,7 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf Enable IP & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the other settings) @@ -106,7 +106,10 @@ other settings) net.ipv6.conf.all.forwarding=1 **Reboot** or use ``sysctl -p`` to apply the same config to the running -system ### Install the init.d service +system + +Install the init.d service +^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -116,7 +119,7 @@ system ### Install the init.d service sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf Enable daemons -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ | Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for those daemons you want to start by systemd. @@ -133,7 +136,7 @@ Enable daemons isisd=yes Start the init.d service -~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^ - /etc/init.d/frr start - use ``/etc/init.d/frr status`` to check its status. diff --git a/doc/developer/building-frr-on-ubuntu1604.rst b/doc/developer/building-frr-on-ubuntu1604.rst index 9c296f8edf..1b371893e2 100644 --- a/doc/developer/building-frr-on-ubuntu1604.rst +++ b/doc/developer/building-frr-on-ubuntu1604.rst @@ -25,7 +25,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -36,7 +36,7 @@ Add frr groups and user sudo usermod -a -G frrvty frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -72,7 +72,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -91,7 +91,7 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf Enable IPv4 & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the other settings) @@ -107,7 +107,7 @@ other settings) net.ipv6.conf.all.forwarding=1 Enable MPLS Forwarding (with Linux Kernel >= 4.5) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a line equal to ``net.mpls.conf.eth0.input`` or each interface used with @@ -122,7 +122,7 @@ MPLS net.mpls.platform_labels=100000 Add MPLS kernel modules -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to ``/etc/modules-load.d/modules.conf``: @@ -136,7 +136,7 @@ Add the following lines to ``/etc/modules-load.d/modules.conf``: system Install the systemd service (if rebooted from last step, change directory back to frr directory) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -148,7 +148,7 @@ Install the systemd service (if rebooted from last step, change directory back t sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf Enable daemons -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ | Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for those daemons you want to start by systemd. @@ -165,12 +165,12 @@ Enable daemons isisd=yes Enable the systemd service -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ - systemctl enable frr Start the systemd service -~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^ - systemctl start frr - use ``systemctl status frr`` to check its status. diff --git a/doc/developer/building-frr-on-ubuntu1804.rst b/doc/developer/building-frr-on-ubuntu1804.rst index 6c79ed87d6..17edb7ef4f 100644 --- a/doc/developer/building-frr-on-ubuntu1804.rst +++ b/doc/developer/building-frr-on-ubuntu1804.rst @@ -20,7 +20,7 @@ Get FRR, compile it and install it (from Git) using any packages** Add frr groups and user -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -31,7 +31,7 @@ Add frr groups and user sudo usermod -a -G frrvty frr Download Source, configure and compile it -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (You may prefer different options on configure statement. These are just an example.) @@ -67,7 +67,7 @@ an example.) sudo make install Create empty FRR configuration files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -86,7 +86,7 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf Enable IPv4 & IPv6 forwarding -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the other settings) @@ -102,7 +102,7 @@ other settings) net.ipv6.conf.all.forwarding=1 Enable MPLS Forwarding (with Linux Kernel >= 4.5) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a line equal to ``net.mpls.conf.eth0.input`` or each interface used with @@ -117,7 +117,7 @@ MPLS net.mpls.platform_labels=100000 Add MPLS kernel modules -~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^ Add the following lines to ``/etc/modules-load.d/modules.conf``: @@ -131,7 +131,7 @@ Add the following lines to ``/etc/modules-load.d/modules.conf``: system Install the systemd service (if rebooted from last step, change directory back to frr directory) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -143,7 +143,7 @@ Install the systemd service (if rebooted from last step, change directory back t sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf Enable daemons -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ | Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for those daemons you want to start by systemd. @@ -160,12 +160,12 @@ Enable daemons isisd=yes Enable the systemd service -~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^ - systemctl enable frr Start the systemd service -~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^ - systemctl start frr - use ``systemctl status frr`` to check its status. diff --git a/doc/developer/building.rst b/doc/developer/building.rst index c7a53734d9..92fd1bb63a 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -1,5 +1,6 @@ +************ Building FRR -========================= +************ .. toctree:: :maxdepth: 2 diff --git a/doc/developer/conf.py b/doc/developer/conf.py index 9ae1991817..a3968b60ff 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -15,6 +15,8 @@ import sys import os import re +import pygments +from sphinx.highlighting import lexers # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -51,7 +53,7 @@ master_doc = 'index' # General information about the project. project = u'FRR' copyright = u'2017, FRR' -author = u'FRR' +author = u'FRR authors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -67,34 +69,40 @@ release = u'?.?-?' # Extract values from codebase for substitution into docs. # ----------------------------------------------------------------------------- -# Various installation prefixes. Reasonable defaults are set where possible. -# Values are overridden by logic below. +# Various installation prefixes. Values are extracted from config.status. +# Reasonable defaults are set in case that file does not exist. replace_vars = { - 'AUTHORS': 'Kunihiro Ishiguro, et al.', + 'AUTHORS': author, 'COPYRIGHT_YEAR': '1999-2005', - 'COPYRIGHT_STR': None, + 'COPYRIGHT_STR': 'Copyright (c) 1999-2005', 'PACKAGE_NAME': project.lower(), 'PACKAGE_TARNAME': project.lower(), - 'PACKAGE_STRING': None, + 'PACKAGE_STRING': project.lower() + ' latest', 'PACKAGE_URL': 'https://frrouting.org/', - 'PACKAGE_VERSION': None, - 'INSTALL_PREFIX_ETC': None, - 'INSTALL_PREFIX_SBIN': None, - 'INSTALL_PREFIX_STATE': None, - 'INSTALL_PREFIX_MODULES': None, - 'INSTALL_USER': None, - 'INSTALL_GROUP': None, - 'INSTALL_VTY_GROUP': None, + 'PACKAGE_VERSION': 'latest', + 'INSTALL_PREFIX_ETC': '/etc/frr', + 'INSTALL_PREFIX_SBIN': '/usr/lib/frr', + 'INSTALL_PREFIX_STATE': '/var/run/frr', + 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules', + 'INSTALL_USER': 'frr', + 'INSTALL_GROUP': 'frr', + 'INSTALL_VTY_GROUP': 'frrvty', + 'GROUP': 'frr', + 'USER': 'frr', } # extract version information, installation location, other stuff we need to # use when building final documents val = re.compile('^S\["([^"]+)"\]="(.*)"$') -with open('../../config.status', 'r') as cfgstatus: - for ln in cfgstatus.readlines(): - m = val.match(ln) - if not m or m.group(1) not in replace_vars.keys(): continue - replace_vars[m.group(1)] = m.group(2) +try: + with open('../../config.status', 'r') as cfgstatus: + for ln in cfgstatus.readlines(): + m = val.match(ln) + if not m or m.group(1) not in replace_vars.keys(): continue + replace_vars[m.group(1)] = m.group(2) +except IOError: + # if config.status doesn't exist, just ignore it + pass # manually fill out some of these we can't get from config.status replace_vars['COPYRIGHT_STR'] = "Copyright (c)" @@ -341,3 +349,13 @@ def setup(app): app.add_object_type('clicmd', 'clicmd') # css overrides for HTML theme app.add_stylesheet('overrides.css') + # load Pygments lexer for FRR config syntax + # + # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we + # do it manually since not all of our supported build platforms have 2.2 + # yet. + # + # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer") + custom_namespace = {} + exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace) + lexers['frr'] = custom_namespace['FRRLexer']() diff --git a/doc/developer/hooks.rst b/doc/developer/hooks.rst index 0afa297aa7..4140a0d171 100644 --- a/doc/developer/hooks.rst +++ b/doc/developer/hooks.rst @@ -12,26 +12,26 @@ the appropriate function signature (parameters) for the hook. Example: .. code-block:: c - :caption: mydaemon.h + :caption: mydaemon.h - #include "hook.h" - DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info)) + #include "hook.h" + DECLARE_HOOK(some_update_event, (struct eventinfo *info), (info)) .. code-block:: c - :caption: mydaemon.c + :caption: mydaemon.c - #include "mydaemon.h" - DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info)) - ... - hook_call(some_update_event, info); + #include "mydaemon.h" + DEFINE_HOOK(some_update_event, (struct eventinfo *info), (info)) + ... + hook_call(some_update_event, info); .. code-block:: c - :caption: mymodule.c + :caption: mymodule.c - #include "mydaemon.h" - static int event_handler(struct eventinfo *info); - ... - hook_register(some_update_event, event_handler); + #include "mydaemon.h" + static int event_handler(struct eventinfo *info); + ... + hook_register(some_update_event, event_handler); Do not use parameter names starting with "hook", these can collide with names used by the hook code itself. diff --git a/doc/developer/library.rst b/doc/developer/library.rst index 791aedb624..c5ce1f5982 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -1,5 +1,6 @@ -libfrr library facilities -========================= +*************************** +Library Facilities (libfrr) +*************************** .. toctree:: :maxdepth: 2 diff --git a/doc/developer/memtypes.rst b/doc/developer/memtypes.rst index d40ebe31cd..d43bc2555e 100644 --- a/doc/developer/memtypes.rst +++ b/doc/developer/memtypes.rst @@ -6,37 +6,37 @@ Memtypes FRR includes wrappers arround ``malloc()`` and ``free()`` that count the number of objects currently allocated, for each of a defined ``MTYPE``. -To this extent, there are `memory groups` and `memory types`. Each memory +To this extent, there are *memory groups* and *memory types*. Each memory type must belong to a memory group, this is used just to provide some basic structure. Example: .. code-block:: c - :caption: mydaemon.h + :caption: mydaemon.h - DECLARE_MGROUP(MYDAEMON) - DECLARE_MTYPE(MYNEIGHBOR) + DECLARE_MGROUP(MYDAEMON) + DECLARE_MTYPE(MYNEIGHBOR) .. code-block:: c - :caption: mydaemon.c - - DEFINE_MGROUP( MYDAEMON, "My daemon's memory") - DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry") - DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name") - - struct neigh *neighbor_new(const char *name) - { - struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n)); - n->name = XSTRDUP(MYNEIGHBORNAME, name); - return n; - } - - void neighbor_free(struct neigh *n) - { - XFREE(MYNEIGHBORNAME, n->name); - XFREE(MYNEIGHBOR, n); - } + :caption: mydaemon.c + + DEFINE_MGROUP( MYDAEMON, "My daemon's memory") + DEFINE_MTYPE( MYDAEMON, MYNEIGHBOR, "Neighbor entry") + DEFINE_MTYPE_STATIC(MYDAEMON, MYNEIGHBORNAME, "Neighbor name") + + struct neigh *neighbor_new(const char *name) + { + struct neigh *n = XMALLOC(MYNEIGHBOR, sizeof(*n)); + n->name = XSTRDUP(MYNEIGHBORNAME, name); + return n; + } + + void neighbor_free(struct neigh *n) + { + XFREE(MYNEIGHBORNAME, n->name); + XFREE(MYNEIGHBOR, n); + } Definition diff --git a/doc/developer/next-hop-tracking.rst b/doc/developer/next-hop-tracking.rst index 25e2d1fe1a..8a6a4ccb33 100644 --- a/doc/developer/next-hop-tracking.rst +++ b/doc/developer/next-hop-tracking.rst @@ -100,7 +100,7 @@ Design ------ Modules -~~~~~~~ +^^^^^^^ The core design introduces an "nht" (next hop tracking) module in BGP and "rnh" (recursive nexthop) module in Zebra. The "nht" module @@ -178,7 +178,7 @@ The next hop notification control flow is the following: zclient message format -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER messages are encoded in the following way: @@ -233,7 +233,7 @@ encoded in the following way: BGP data structure -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ Legend: :: @@ -260,7 +260,7 @@ Legend: Zebra data structure -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ RNH table:: @@ -279,7 +279,7 @@ RNH table:: }; User interface changes -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ :: @@ -338,7 +338,7 @@ User interface changes + no shut all links to r4 Future work -~~~~~~~~~~~ +^^^^^^^^^^^ - route-policy for next hop validation (e.g. ignore default route) - damping for rapid next hop changes diff --git a/doc/developer/ospf-sr.rst b/doc/developer/ospf-sr.rst index 3f8b549df3..4a673b155b 100644 --- a/doc/developer/ospf-sr.rst +++ b/doc/developer/ospf-sr.rst @@ -29,7 +29,7 @@ Implementation details ---------------------- Concepts -~~~~~~~~ +^^^^^^^^ Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various information: @@ -47,7 +47,7 @@ Segment Routing functions (see below) when an Extended Link / Prefix or Router Information LSA s are received. Overview -~~~~~~~~ +^^^^^^^^ Following files where modified or added: @@ -113,7 +113,7 @@ The figure below shows the relation between the various files: Figure 1: Overview of Segment Routing interaction Module interactions -~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^ To process incoming LSA, the code is based on the capability to call `hook()` functions when LSA are inserted or delete to / from the LSDB and the @@ -186,7 +186,7 @@ Configuration ------------- Linux Kernel -~~~~~~~~~~~~ +^^^^^^^^^^^^ In order to use OSPF Segment Routing, you must setup MPLS data plane. Up to know, only Linux Kernel version >= 4.5 is supported. @@ -231,7 +231,7 @@ especially the `lo` one. For that purpose, disable RP filtering with: sysctl -w net.ipv4.conf.lo.rp_filter=0 OSPFd -~~~~~ +^^^^^ Here it is a simple example of configuration to enable Segment Routing. Note that `opaque capability` and `router information` must be set to activate diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index f6519ea8f6..f025c47bb4 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -668,7 +668,7 @@ AddressSanitizer instrumentation and run through a series of tests to look for any results. Testing your own code with this tool before submission is encouraged. You can enable it by passing:: - + --enable-address-sanitizer to ``configure``. @@ -678,7 +678,7 @@ ThreadSanitizer detecting data races. If you are working on or around multithreaded code, extensive testing with this instrumtation enabled is *highly* recommended. You can enable it by passing:: - + --enable-thread-sanitizer to ``configure``. @@ -687,7 +687,7 @@ MemorySanitizer Similar to AddressSanitizer, this tool provides runtime instrumentation for detecting use of uninitialized heap memory. Testing your own code with this tool before submission is encouraged. You can enable it by passing:: - + --enable-memory-sanitizer to ``configure``. @@ -775,27 +775,33 @@ FRR uses Sphinx+RST as its documentation system. The document you are currently reading was generated by Sphinx from RST source in :file:`doc/developer/workflow.rst`. The documentation is structured as follows: -+-----------------------+--------------------------------------------------------------+ -| Directory | Contents | -+=======================+==============================================================+ -| :file:`doc/user` | User documentation; configuration guides; protocol overviews | -+-----------------------+--------------------------------------------------------------+ -| :file:`doc/developer` | Developer's documentation; API specs; datastructures; | -| | architecture overviews; project management procedure | -+-----------------------+--------------------------------------------------------------+ -| :file:`doc/manpages` | Source for manpages | -+-----------------------+--------------------------------------------------------------+ -| :file:`doc/figures` | Images and diagrams | -+-----------------------+--------------------------------------------------------------+ - -Each of these directories, with the exception of :file:`doc/figures`, contains -a Sphinx-generated Makefile and configuration script :file:`conf.py` used to -set various document parameters. The makefile can be used for a variety of -targets; invoke `make help` in any of these directories for a listing of -available output formats. For convenience, there is a top-level -:file:`Makefile.am` that has targets for PDF and HTML documentation for both -developer and user documentation, respectively. That makefile is also -responsible for building manual pages packed with distribution builds. ++-----------------------+-------------------------------------------+ +| Directory | Contents | ++=======================+===========================================+ +| :file:`doc/user` | User documentation; configuration guides; | +| | protocol overviews | ++-----------------------+-------------------------------------------+ +| :file:`doc/developer` | Developer's documentation; API specs; | +| | datastructures; architecture overviews; | +| | project management procedure | ++-----------------------+-------------------------------------------+ +| :file:`doc/manpages` | Source for manpages | ++-----------------------+-------------------------------------------+ +| :file:`doc/figures` | Images and diagrams | ++-----------------------+-------------------------------------------+ +| :file:`doc/extra` | Miscellaneous Sphinx extensions, scripts, | +| | customizations, etc. | ++-----------------------+-------------------------------------------+ + +Each of these directories, with the exception of :file:`doc/figures` and +:file:`doc/extra`, contains a Sphinx-generated Makefile and configuration +script :file:`conf.py` used to set various document parameters. The makefile +can be used for a variety of targets; invoke `make help` in any of these +directories for a listing of available output formats. For convenience, there +is a top-level :file:`Makefile.am` that has targets for PDF and HTML +documentation for both developer and user documentation, respectively. That +makefile is also responsible for building manual pages packed with distribution +builds. Indent and styling should follow existing conventions: @@ -890,6 +896,15 @@ your implementation of a new BGP draft should go in the BGP chapter instead of being its own chapter. If you are adding a new protocol daemon, please create a new chapter. +FRR Specific Markup +------------------- + +FRR has some customizations applied to the Sphinx markup that go a long way +towards making documentation easier to use, write and maintain. + +CLI Commands +^^^^^^^^^^^^ + When documenting CLI please use a combination of the ``.. index::`` and ``.. clicmd::`` directives. For example, the command :clicmd:`show pony` would be documented as follows: @@ -923,6 +938,29 @@ When documented this way, CLI commands can be cross referenced with the This is very helpful for users who want to quickly remind themselves what a particular command does. +Configuration Snippets +^^^^^^^^^^^^^^^^^^^^^^ + +When putting blocks of example configuration please use the +``.. code-block::`` directive and specify ``frr`` as the highlighting language, +as in the following example. This will tell Sphinx to use a custom Pygments +lexer to highlight FRR configuration syntax. + +.. code-block:: rest + + .. code-block:: frr + + ! + ! Example configuration file. + ! + log file /tmp/log.log + service integrated-vtysh-config + ! + ip route 1.2.3.0/24 reject + ipv6 route de:ea:db:ee:ff::/64 reject + ! + + .. _GitHub: https://github.com/frrouting/frr .. _GitHub issues: https://github.com/frrouting/frr/issues diff --git a/doc/extra/frrlexer.py b/doc/extra/frrlexer.py new file mode 100644 index 0000000000..528bec985b --- /dev/null +++ b/doc/extra/frrlexer.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017 Vincent Bernat <bernat@luffy.cx> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Text, Comment, Keyword +from pygments.token import String, Number, Name + + +class FRRLexer(RegexLexer): + name = "frr" + aliases = ["frr"] + tokens = { + 'root': [ + (r'^[ \t]*!.*?\n', Comment.Singleline), + (r'"(\\\\|\\"|[^"])*"', String.Double), + (r'[a-f0-9]*:[a-f0-9]*:[a-f0-9:]*(:\d+\.\d+\.\d+\.\d+)?(/\d+)?', + Number), # IPv6 + (r'\d+\.\d+\.\d+\.\d+(/\d+)?', Number), # IPv4 + (r'^([ \t]*)(no[ \t]+)?([-\w]+)', + bygroups(Text, Keyword, Name.Function)), + (r'[ \t]+', Text), + (r'\n', Text), + (r'\d+', Number), + (r'\S+', Text), + ], + } diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index b5ea537faa..41683ed678 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -51,7 +51,7 @@ master_doc = 'index' # General information about the project. project = u'FRR' copyright = u'2017, FRR' -author = u'Kunihiro Ishiguro, et al.' +author = u'FRR authors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -67,24 +67,24 @@ release = u'?.?-?' # Extract values from codebase for substitution into docs. # ----------------------------------------------------------------------------- -# Various installation prefixes. Reasonable defaults are set where possible. -# Values are overridden by logic below. +# Various installation prefixes. Values are extracted from config.status. +# Reasonable defaults are set in case that file does not exist. replace_vars = { 'AUTHORS': author, 'COPYRIGHT_YEAR': '1999-2005', - 'COPYRIGHT_STR': None, + 'COPYRIGHT_STR': 'Copyright (c) 1999-2005', 'PACKAGE_NAME': project.lower(), 'PACKAGE_TARNAME': project.lower(), - 'PACKAGE_STRING': None, + 'PACKAGE_STRING': project.lower() + ' latest', 'PACKAGE_URL': 'https://frrouting.org/', - 'PACKAGE_VERSION': None, - 'INSTALL_PREFIX_ETC': None, - 'INSTALL_PREFIX_SBIN': None, - 'INSTALL_PREFIX_STATE': None, - 'INSTALL_PREFIX_MODULES': None, - 'INSTALL_USER': None, - 'INSTALL_GROUP': None, - 'INSTALL_VTY_GROUP': None, + 'PACKAGE_VERSION': 'latest', + 'INSTALL_PREFIX_ETC': '/etc/frr', + 'INSTALL_PREFIX_SBIN': '/usr/lib/frr', + 'INSTALL_PREFIX_STATE': '/var/run/frr', + 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules', + 'INSTALL_USER': 'frr', + 'INSTALL_GROUP': 'frr', + 'INSTALL_VTY_GROUP': 'frrvty', 'GROUP': 'frr', 'USER': 'frr', } @@ -92,11 +92,15 @@ replace_vars = { # extract version information, installation location, other stuff we need to # use when building final documents val = re.compile('^S\["([^"]+)"\]="(.*)"$') -with open('../../config.status', 'r') as cfgstatus: - for ln in cfgstatus.readlines(): - m = val.match(ln) - if not m or m.group(1) not in replace_vars.keys(): continue - replace_vars[m.group(1)] = m.group(2) +try: + with open('../../config.status', 'r') as cfgstatus: + for ln in cfgstatus.readlines(): + m = val.match(ln) + if not m or m.group(1) not in replace_vars.keys(): continue + replace_vars[m.group(1)] = m.group(2) +except IOError: + # if config.status doesn't exist, just ignore it + pass # manually fill out some of these we can't get from config.status replace_vars['COPYRIGHT_STR'] = "Copyright (c)" diff --git a/doc/user/_static/overrides.css b/doc/user/_static/overrides.css index 1e0de66c55..0d871c961a 100644 --- a/doc/user/_static/overrides.css +++ b/doc/user/_static/overrides.css @@ -2,3 +2,7 @@ div.body { max-width: none; } + +pre { + background-color: #e2e2e2; +} diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 29d05abba7..4a5056e233 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -42,7 +42,7 @@ Config files are generally found in |INSTALL_PREFIX_ETC|. Each of the daemons has its own config file. The daemon name plus ``.conf`` is the default config file name. For example, zebra's default config file name is :file:`zebra.conf`. You can specify a config file using the :option:`-f` or -:option:`--config-file` options when starting the daemon. +:option:`--config_file` options when starting the daemon. .. _basic-config-commands: @@ -261,27 +261,27 @@ Sample Config File Below is a sample configuration file for the zebra daemon. -:: +.. code-block:: frr - ! - ! Zebra configuration file - ! - hostname Router - password zebra - enable password zebra - ! - log stdout - ! - ! + ! + ! Zebra configuration file + ! + hostname Router + password zebra + enable password zebra + ! + log stdout + ! + ! '!' and '#' are comment characters. If the first character of the word is one of the comment characters then from the rest of the line forward will be ignored as a comment. -:: +.. code-block:: frr - password zebra!password + password zebra!password If a comment character is not the first character of the word, it's a normal character. So in the above example '!' will not be regarded as a @@ -358,13 +358,11 @@ Common Invocation Options These options apply to all |PACKAGE_NAME| daemons. -.. option:: -d -.. option:: --daemon +.. option:: -d, --daemon Run in daemon mode. -.. option:: -f <file> -.. option:: --config-file <file> +.. option:: -f, --config_file <file> Set configuration file name. @@ -372,8 +370,7 @@ These options apply to all |PACKAGE_NAME| daemons. Display this help and exit. -.. option:: -i <file> -.. option:: --pid-file <file> +.. option:: -i, --pid_file <file> Upon startup the process identifier of the daemon is written to a file, typically in :file:`/var/run`. This file can be used by the init system @@ -386,25 +383,21 @@ These options apply to all |PACKAGE_NAME| daemons. machine can be used to collect differing routing views from differing points in the network. -.. option:: -A <address> -.. option:: --vty-addr <address> +.. option:: -A, --vty_addr <address> Set the VTY local address to bind to. If set, the VTY socket will only be bound to this address. -.. option:: -P <port> -.. option:: --vty-port <port> +.. option:: -P, --vty_port <port> Set the VTY TCP port number. If set to 0 then the TCP VTY sockets will not be opened. .. option:: -u <user> -.. option:: --vty_addr <user> Set the user and group to run as. -.. option:: -v -.. option:: --version +.. option:: -v, --version Print program version. @@ -418,8 +411,7 @@ unloading modules at runtime is not supported (yet). To load a module, use the following command line option at daemon startup: -.. option:: -M <module:options> -.. option:: --module <module:options> +.. option:: -M, --module <module:options> Load the specified module, optionally passing options to it. If the module name contains a slash (/), it is assumed to be a full pathname to a file to @@ -474,32 +466,32 @@ is no VTY password, one cannot connect to the VTY interface at all. :: - % telnet localhost 2601 - Trying 127.0.0.1... - Connected to localhost. - Escape character is '^]'. + % telnet localhost 2601 + Trying 127.0.0.1... + Connected to localhost. + Escape character is '^]'. - Hello, this is |PACKAGE_NAME| (version |PACKAGE_VERSION|) - |COPYRIGHT_STR| + Hello, this is |PACKAGE_NAME| (version |PACKAGE_VERSION|) + |COPYRIGHT_STR| - User Access Verification + User Access Verification - Password: XXXXX - Router> ? - enable . . . Turn on privileged commands - exit . . . Exit current mode and down to previous mode - help . . . Description of the interactive help system - list . . . Print command list - show . . . Show system inform + Password: XXXXX + Router> ? + enable . . . Turn on privileged commands + exit . . . Exit current mode and down to previous mode + help . . . Description of the interactive help system + list . . . Print command list + show . . . Show system inform - wh. . . Display who is on a vty - Router> enable - Password: XXXXX - Router# configure terminal - Router(config)# interface eth0 - Router(config-if)# ip address 10.0.0.1/8 - Router(config-if)# ^Z - Router# + wh. . . Display who is on a vty + Router> enable + Password: XXXXX + Router# configure terminal + Router(config)# interface eth0 + Router(config-if)# ip address 10.0.0.1/8 + Router(config-if)# ^Z + Router# :kbd:`?` and the ``find`` command are very useful for looking up commands. @@ -553,22 +545,22 @@ These commands are used for moving the CLI cursor. The :kbd:`C` character means press the Control Key. :kbd:`C-f` / :kbd:`LEFT` - Move forward one character. + Move forward one character. :kbd:`C-b` / :kbd:`RIGHT` - Move backward one character. + Move backward one character. :kbd:`M-f` - Move forward one word. + Move forward one word. :kbd:`M-b` - Move backward one word. + Move backward one word. :kbd:`C-a` - Move to the beginning of the line. + Move to the beginning of the line. :kbd:`C-e` - Move to the end of the line. + Move to the end of the line. .. _cli-editing-commands: @@ -581,31 +573,31 @@ character means press the Control Key. :kbd:`C-h` / :kbd:`DEL` - Delete the character before point. + Delete the character before point. :kbd:`C-d` - Delete the character after point. + Delete the character after point. :kbd:`M-d` - Forward kill word. + Forward kill word. :kbd:`C-w` - Backward kill word. + Backward kill word. :kbd:`C-k` - Kill to the end of the line. + Kill to the end of the line. :kbd:`C-u` - Kill line from the beginning, erasing input. + Kill line from the beginning, erasing input. :kbd:`C-t` - Transpose character. + Transpose character. CLI Advanced Commands @@ -616,27 +608,27 @@ insta-help, and VTY session management. :kbd:`C-c` - Interrupt current input and moves to the next line. + Interrupt current input and moves to the next line. :kbd:`C-z` - End current configuration session and move to top node. + End current configuration session and move to top node. :kbd:`C-n` / :kbd:`DOWN` - Move down to next line in the history buffer. + Move down to next line in the history buffer. :kbd:`C-p` / :kbd:`UP` - Move up to previous line in the history buffer. + Move up to previous line in the history buffer. :kbd:`TAB` - Use command line completion by typing :kbd:`TAB`. + Use command line completion by typing :kbd:`TAB`. :kbd:`?` - You can use command line help by typing `help` at the beginning of - the line. Typing :kbd:`?` at any point in the line will show possible - completions. + You can use command line help by typing ``help`` at the beginning of the + line. Typing :kbd:`?` at any point in the line will show possible + completions. diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 7a508b0df6..899977e830 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -26,19 +26,16 @@ be specified (:ref:`common-invocation-options`). .. program:: bgpd -.. option:: -p <port> -.. option:: --bgp_port <port> +.. option:: -p, --bgp_port <port> Set the bgp protocol's port number. When port number is 0, that means do not listen bgp port. -.. option:: -r -.. option:: --retain +.. option:: -r, --retain When program terminates, retain BGP routes added by zebra. -.. option:: -l -.. option:: --listenon +.. option:: -l, --listenon Specify a specific IP address for bgpd to listen on, rather than its default of INADDR_ANY / IN6ADDR_ANY. This can be useful to constrain bgpd @@ -473,12 +470,14 @@ BGP route .. index:: network A.B.C.D/M .. clicmd:: network A.B.C.D/M - This command adds the announcement network.:: + This command adds the announcement network. - router bgp 1 - address-family ipv4 unicast - network 10.0.0.0/8 - exit-address-family + .. code-block:: frr + + router bgp 1 + address-family ipv4 unicast + network 10.0.0.0/8 + exit-address-family This configuration example says that network 10.0.0.0/8 will be announced to all neighbors. Some vendors' routers don't advertise @@ -606,15 +605,17 @@ Defining Peer .. clicmd:: neighbor PEER remote-as ASN Creates a new neighbor whose remote-as is ASN. PEER can be an IPv4 address - or an IPv6 address or an interface to use for the connection.:: + or an IPv6 address or an interface to use for the connection. - router bgp 1 - neighbor 10.0.0.1 remote-as 2 + .. code-block:: frr + + router bgp 1 + neighbor 10.0.0.1 remote-as 2 In this case my router, in AS-1, is trying to peer with AS-2 at 10.0.0.1. This command must be the first command used when configuring a neighbor. If - the remote-as is not specified, *bgpd* will complain like this::: + the remote-as is not specified, *bgpd* will complain like this: :: can't find neighbor 10.0.0.1 @@ -714,7 +715,9 @@ required. Specify the IPv4 source address to use for the :abbr:`BGP` session to this neighbour, may be specified as either an IPv4 address directly or as an interface name (in which case the *zebra* daemon MUST be running in order - for *bgpd* to be able to retrieve interface state).:: + for *bgpd* to be able to retrieve interface state). + + .. code-block:: frr router bgp 64555 neighbor foo update-source 192.168.0.1 @@ -1190,7 +1193,10 @@ Following configuration is the most typical usage of BGP communities attribute. AS 7675 provides upstream Internet connection to AS 100. When following configuration exists in AS 7675, AS 100 networks operator can set local preference in AS 7675 network by setting BGP -communities attribute to the updates.:: +communities attribute to the updates. + + +.. code-block:: frr router bgp 7675 neighbor 192.168.0.1 remote-as 100 @@ -1221,7 +1227,9 @@ communities attribute to the updates.:: Following configuration announce 10.0.0.0/8 from AS 100 to AS 7675. The route has communities value 7675:80 so when above configuration exists in AS 7675, announced route's local preference will be set to -value 80.:: +value 80. + +.. code-block:: frr router bgp 100 network 10.0.0.0/8 @@ -1241,7 +1249,9 @@ Following configuration is an example of BGP route filtering using communities attribute. This configuration only permit BGP routes which has BGP communities value 0:80 or 0:90. Network operator can put special internal communities value at BGP border router, then -limit the BGP routes announcement into the internal network.:: +limit the BGP routes announcement into the internal network. + +.. code-block:: frr router bgp 7675 neighbor 192.168.0.1 remote-as 100 @@ -1257,7 +1267,9 @@ limit the BGP routes announcement into the internal network.:: Following exmaple filter BGP routes which has communities value 1:1. When there is no match community-list returns deny. To avoid -filtering all of routes, we need to define permit any at last.:: +filtering all of routes, we need to define permit any at last. + +.. code-block:: frr router bgp 7675 neighbor 192.168.0.1 remote-as 100 @@ -1276,7 +1288,9 @@ Communities value keyword `internet` has special meanings in standard community lists. In below example `internet` act as match any. It matches all of BGP routes even if the route does not have communities attribute at all. So community list ``INTERNET`` -is same as above example's ``FILTER``.:: +is same as above example's ``FILTER``. + +.. code-block:: frr ip community-list standard INTERNET deny 1:1 ip community-list standard INTERNET permit internet @@ -1285,7 +1299,9 @@ is same as above example's ``FILTER``.:: Following configuration is an example of communities value deletion. With this configuration communities value 100:1 and 100:2 is removed from BGP updates. For communities value deletion, only `permit` -community-list is used. `deny` community-list is ignored.:: +community-list is used. `deny` community-list is ignored. + +.. code-block:: frr router bgp 7675 neighbor 192.168.0.1 remote-as 100 @@ -1382,11 +1398,9 @@ Lists. .. clicmd:: show ip extcommunity-list NAME This command displays current extcommunity-list information. When `name` is - specified the community list's information is shown. - -:: + specified the community list's information is shown.:: - # show ip extcommunity-list + # show ip extcommunity-list .. _bgp-extended-communities-in-route-map: @@ -1607,14 +1621,17 @@ address-family: Deletes any previously-configured import or export route-target list. -.. index:: label vpn export (0..1048575) -.. clicmd:: label vpn export (0..1048575) +.. index:: label vpn export (0..1048575)|auto +.. clicmd:: label vpn export (0..1048575)|auto Specifies an optional MPLS label to be attached to a route exported from the - current unicast VRF to VPN. + current unicast VRF to VPN. If label is specified as ``auto``, the label + value is automatically assigned from a pool maintained by the zebra + daemon. If zebra is not running, automatic label assignment will not + complete, which will block corresponding route export. -.. index:: no label vpn export [(0..1048575)] -.. clicmd:: no label vpn export [(0..1048575)] +.. index:: no label vpn export [(0..1048575)|auto] +.. clicmd:: no label vpn export [(0..1048575)|auto] Deletes any previously-configured export label. @@ -1933,7 +1950,9 @@ neighbor. If a user manually disables the feature, the community attribute is not sent to the neighbor. When ``bgp config-type cisco`` is specified, the community attribute is not sent to the neighbor by default. To send the community attribute user has to specify *neighbor A.B.C.D send-community* -command.:: +command. + +.. code-block:: frr ! router bgp 1 @@ -1969,17 +1988,17 @@ multiple instance feature is enabled. Make a new BGP instance. You can use an arbitrary word for the `name`. - :: + .. code-block:: frr - bgp multiple-instance - ! - router bgp 1 - neighbor 10.0.0.1 remote-as 2 - neighbor 10.0.0.2 remote-as 3 - ! - router bgp 2 - neighbor 10.0.0.3 remote-as 4 - neighbor 10.0.0.4 remote-as 5 + bgp multiple-instance + ! + router bgp 1 + neighbor 10.0.0.1 remote-as 2 + neighbor 10.0.0.2 remote-as 3 + ! + router bgp 2 + neighbor 10.0.0.3 remote-as 4 + neighbor 10.0.0.4 remote-as 5 BGP view is almost same as normal BGP process. The result of route selection @@ -1994,7 +2013,7 @@ routing information. With this command, you can setup Route Server like below. - :: + .. code-block:: frr bgp multiple-instance ! @@ -2013,7 +2032,9 @@ Routing policy -------------- You can set different routing policy for a peer. For example, you can set -different filter for a peer.:: +different filter for a peer. + +.. code-block:: frr bgp multiple-instance ! @@ -2087,10 +2108,10 @@ _ How to set up a 6-Bone connection ================================= -:: +.. code-block:: frr - bgpd configuration - ================== + ! bgpd configuration + ! ================== ! ! MP-BGP configuration ! @@ -2174,7 +2195,9 @@ Dump BGP packets and table BGP Configuration Examples ========================== -Example of a session to an upstream, advertising only one prefix to it.:: +Example of a session to an upstream, advertising only one prefix to it. + +.. code-block:: frr router bgp 64512 bgp router-id 10.236.87.1 @@ -2199,7 +2222,7 @@ feature to support selective advertising of prefixes. This example is intended as guidance only, it has NOT been tested and almost certainly containts silly mistakes, if not serious flaws. -:: +.. code-block:: frr router bgp 64512 bgp router-id 10.236.87.1 diff --git a/doc/user/conf.py b/doc/user/conf.py index 7a77e492ce..886403b69d 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -15,6 +15,8 @@ import sys import os import re +import pygments +from sphinx.highlighting import lexers # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -51,7 +53,7 @@ master_doc = 'index' # General information about the project. project = u'FRR' copyright = u'2017, FRR' -author = u'FRR' +author = u'FRR authors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -67,34 +69,40 @@ release = u'?.?-?' # Extract values from codebase for substitution into docs. # ----------------------------------------------------------------------------- -# Various installation prefixes. Reasonable defaults are set where possible. -# Values are overridden by logic below. +# Various installation prefixes. Values are extracted from config.status. +# Reasonable defaults are set in case that file does not exist. replace_vars = { - 'AUTHORS': 'Kunihiro Ishiguro, et al.', + 'AUTHORS': author, 'COPYRIGHT_YEAR': '1999-2005', - 'COPYRIGHT_STR': None, + 'COPYRIGHT_STR': 'Copyright (c) 1999-2005', 'PACKAGE_NAME': project.lower(), 'PACKAGE_TARNAME': project.lower(), - 'PACKAGE_STRING': None, + 'PACKAGE_STRING': project.lower() + ' latest', 'PACKAGE_URL': 'https://frrouting.org/', - 'PACKAGE_VERSION': None, - 'INSTALL_PREFIX_ETC': None, - 'INSTALL_PREFIX_SBIN': None, - 'INSTALL_PREFIX_STATE': None, - 'INSTALL_PREFIX_MODULES': None, - 'INSTALL_USER': None, - 'INSTALL_GROUP': None, - 'INSTALL_VTY_GROUP': None, + 'PACKAGE_VERSION': 'latest', + 'INSTALL_PREFIX_ETC': '/etc/frr', + 'INSTALL_PREFIX_SBIN': '/usr/lib/frr', + 'INSTALL_PREFIX_STATE': '/var/run/frr', + 'INSTALL_PREFIX_MODULES': '/usr/lib/frr/modules', + 'INSTALL_USER': 'frr', + 'INSTALL_GROUP': 'frr', + 'INSTALL_VTY_GROUP': 'frrvty', + 'GROUP': 'frr', + 'USER': 'frr', } # extract version information, installation location, other stuff we need to # use when building final documents val = re.compile('^S\["([^"]+)"\]="(.*)"$') -with open('../../config.status', 'r') as cfgstatus: - for ln in cfgstatus.readlines(): - m = val.match(ln) - if not m or m.group(1) not in replace_vars.keys(): continue - replace_vars[m.group(1)] = m.group(2) +try: + with open('../../config.status', 'r') as cfgstatus: + for ln in cfgstatus.readlines(): + m = val.match(ln) + if not m or m.group(1) not in replace_vars.keys(): continue + replace_vars[m.group(1)] = m.group(2) +except IOError: + # if config.status doesn't exist, just ignore it + pass # manually fill out some of these we can't get from config.status replace_vars['COPYRIGHT_STR'] = "Copyright (c)" @@ -341,3 +349,13 @@ def setup(app): app.add_object_type('clicmd', 'clicmd') # css overrides for HTML theme app.add_stylesheet('overrides.css') + # load Pygments lexer for FRR config syntax + # + # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we + # do it manually since not all of our supported build platforms have 2.2 + # yet. + # + # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer") + custom_namespace = {} + exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace) + lexers['frr'] = custom_namespace['FRRLexer']() diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst index 309ee27cc4..c626faf4e2 100644 --- a/doc/user/eigrpd.rst +++ b/doc/user/eigrpd.rst @@ -60,8 +60,7 @@ Certain signals have special meanings to *eigrpd*. .. program:: eigrpd -.. option:: -r -.. option:: --retain +.. option:: -r, --retain When the program terminates, retain routes added by *eigrpd*. @@ -100,7 +99,7 @@ EIGRP Configuration Below is very simple EIGRP configuration. Interface `eth0` and interface which address match to `10.0.0.0/8` are EIGRP enabled. - :: + .. code-block:: frr ! router eigrp 1 diff --git a/doc/user/filter.rst b/doc/user/filter.rst index 57f9fbe426..9d7361443d 100644 --- a/doc/user/filter.rst +++ b/doc/user/filter.rst @@ -18,7 +18,7 @@ IP Access List Basic filtering is done by `access-list` as shown in the following example. - :: + .. code-block:: frr access-list filter deny 10.0.0.0/9 access-list filter permit 10.0.0.0/8 diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index b6989809a7..54f82f6832 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -559,7 +559,9 @@ Debugging ISIS ISIS Configuration Examples =========================== -A simple example, with MD5 authentication enabled::: +A simple example, with MD5 authentication enabled: + +.. code-block:: frr ! interface eth0 @@ -575,7 +577,9 @@ A simple example, with MD5 authentication enabled::: A Traffic Engineering configuration, with Inter-ASv2 support. -First, the 'zebra.conf' part::: +First, the :file:`zebra.conf` part: + +.. code-block:: frr hostname HOSTNAME password PASSWORD @@ -614,7 +618,9 @@ First, the 'zebra.conf' part::: neighbor 10.1.1.2 as 65000 -Then the 'isisd.conf' itself::: +Then the :file:`isisd.conf` itself: + +.. code-block:: frr hostname HOSTNAME password PASSWORD diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst index 28e78f66fb..33cdbd9591 100644 --- a/doc/user/nhrpd.rst +++ b/doc/user/nhrpd.rst @@ -52,7 +52,9 @@ hub nodes, these routes should be internally redistributed using some routing protocol (e.g. iBGP) to allow hubs to be able to relay all traffic. This can be achieved in hubs with the following bgp configuration (network -command defines the GRE subnet)::: +command defines the GRE subnet): + +.. code-block:: frr router bgp 65555 address-family ipv4 unicast @@ -82,12 +84,12 @@ using NFLOG. Typically you want to send Traffic Indications for network traffic that is routed from gre1 back to gre1 in rate limited manner. This can be achieved with the following iptables rule. -:: +.. code-block:: shell - iptables -A FORWARD -i gre1 -o gre1 \\ - -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\ - --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\ - --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128 + iptables -A FORWARD -i gre1 -o gre1 \\ + -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\ + --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\ + --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128 You can fine tune the src/dstmask according to the prefix lengths you @@ -95,15 +97,20 @@ announce internal, add additional IP range matches, or rate limitation if needed. However, the above should be good in most cases. This kernel NFLOG target's nflog-group is configured in global nhrp config -with::: +with: + +.. code-block:: frr - nhrp nflog-group 1 + nhrp nflog-group 1 To start sending these traffic notices out from hubs, use the nhrp -per-interface directive::: +per-interface directive: + +.. code-block:: frr + + interface gre1 + ip nhrp redirect - interface gre1 - ip nhrp redirect .. _integration-with-ike: diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index b0823f21e7..3c84135405 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -49,7 +49,9 @@ OSPF6 router will cause the holdtime to be increased by `initial-holdtime`, bounded by the `maximum-holdtime` configured with this command. If the adaptive hold-time elapses without any SPF-triggering event occuring then - the current holdtime is reset to the `initial-holdtime`.:: + the current holdtime is reset to the `initial-holdtime`. + + .. code-block:: frr router ospf6 timers throttle spf 200 400 10000 @@ -187,7 +189,7 @@ OSPF6 Configuration Examples Example of ospf6d configured on one interface and area: -:: +.. code-block:: frr interface eth0 ipv6 ospf6 instance-id 0 diff --git a/doc/user/ospf_fundamentals.rst b/doc/user/ospf_fundamentals.rst index 5a4f7095ed..c35df85ddf 100644 --- a/doc/user/ospf_fundamentals.rst +++ b/doc/user/ospf_fundamentals.rst @@ -336,61 +336,61 @@ are fully adjacent with 192.168.0.49. :: - # show ip ospf database router 192.168.0.49 - - OSPF Router with ID (192.168.0.53) - - Router Link States (Area 0.0.0.0) - - LS age: 38 - Options: 0x2 : *|-|-|-|-|-|E|* - LS Flags: 0x6 - Flags: 0x2 : ASBR - LS Type: router-LSA - Link State ID: 192.168.0.49 - Advertising Router: 192.168.0.49 - LS Seq Number: 80000f90 - Checksum: 0x518b - Length: 60 - Number of Links: 3 - - Link connected to: a Transit Network - (Link ID) Designated Router address: 192.168.1.3 - (Link Data) Router Interface address: 192.168.1.3 - Number of TOS metrics: 0 - TOS 0 Metric: 10 - - Link connected to: a Transit Network - (Link ID) Designated Router address: 192.168.0.49 - (Link Data) Router Interface address: 192.168.0.49 - Number of TOS metrics: 0 - TOS 0 Metric: 10 - - Link connected to: Stub Network - (Link ID) Net: 192.168.3.190 - (Link Data) Network Mask: 255.255.255.255 - Number of TOS metrics: 0 - TOS 0 Metric: 39063 - # show ip ospf database network 192.168.0.49 - - OSPF Router with ID (192.168.0.53) - - Net Link States (Area 0.0.0.0) - - LS age: 285 - Options: 0x2 : *|-|-|-|-|-|E|* - LS Flags: 0x6 - LS Type: network-LSA - Link State ID: 192.168.0.49 (address of Designated Router) - Advertising Router: 192.168.0.49 - LS Seq Number: 80000074 - Checksum: 0x0103 - Length: 40 - Network Mask: /29 - Attached Router: 192.168.0.49 - Attached Router: 192.168.0.52 - Attached Router: 192.168.0.53 - Attached Router: 192.168.0.54 + # show ip ospf database router 192.168.0.49 + + OSPF Router with ID (192.168.0.53) + + Router Link States (Area 0.0.0.0) + + LS age: 38 + Options: 0x2 : *|-|-|-|-|-|E|* + LS Flags: 0x6 + Flags: 0x2 : ASBR + LS Type: router-LSA + Link State ID: 192.168.0.49 + Advertising Router: 192.168.0.49 + LS Seq Number: 80000f90 + Checksum: 0x518b + Length: 60 + Number of Links: 3 + + Link connected to: a Transit Network + (Link ID) Designated Router address: 192.168.1.3 + (Link Data) Router Interface address: 192.168.1.3 + Number of TOS metrics: 0 + TOS 0 Metric: 10 + + Link connected to: a Transit Network + (Link ID) Designated Router address: 192.168.0.49 + (Link Data) Router Interface address: 192.168.0.49 + Number of TOS metrics: 0 + TOS 0 Metric: 10 + + Link connected to: Stub Network + (Link ID) Net: 192.168.3.190 + (Link Data) Network Mask: 255.255.255.255 + Number of TOS metrics: 0 + TOS 0 Metric: 39063 + # show ip ospf database network 192.168.0.49 + + OSPF Router with ID (192.168.0.53) + + Net Link States (Area 0.0.0.0) + + LS age: 285 + Options: 0x2 : *|-|-|-|-|-|E|* + LS Flags: 0x6 + LS Type: network-LSA + Link State ID: 192.168.0.49 (address of Designated Router) + Advertising Router: 192.168.0.49 + LS Seq Number: 80000074 + Checksum: 0x0103 + Length: 40 + Network Mask: /29 + Attached Router: 192.168.0.49 + Attached Router: 192.168.0.52 + Attached Router: 192.168.0.53 + Attached Router: 192.168.0.54 Note that from one LSA, you can find the other. E.g. Given the @@ -412,26 +412,26 @@ following partial topology: :: - ------------------------ Network: ...... - | Designated Router IP: 192.168.1.3 - | - IP: 192.168.1.3 - (transit link) - (cost: 10) - Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32 - (cost: 10) (cost: 39063) - (transit link) - IP: 192.168.0.49 - | - | - ------------------------------ Network: 192.168.0.48/29 - | | | Designated Router IP: 192.168.0.49 - | | | - | | Router ID: 192.168.0.54 - | | - | Router ID: 192.168.0.53 - | - Router ID: 192.168.0.52 + ------------------------ Network: ...... + | Designated Router IP: 192.168.1.3 + | + IP: 192.168.1.3 + (transit link) + (cost: 10) + Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32 + (cost: 10) (cost: 39063) + (transit link) + IP: 192.168.0.49 + | + | + ------------------------------ Network: 192.168.0.48/29 + | | | Designated Router IP: 192.168.0.49 + | | | + | | Router ID: 192.168.0.54 + | | + | Router ID: 192.168.0.53 + | + Router ID: 192.168.0.52 Note the Router IDs, though they look like IP addresses and often are @@ -495,22 +495,22 @@ should forward to the originating ASBR if selected. :: - # show ip ospf database external 192.168.165.0 - LS age: 995 - Options: 0x2 : *|-|-|-|-|-|E|* - LS Flags: 0x9 - LS Type: AS-external-LSA - Link State ID: 192.168.165.0 (External Network Number) - Advertising Router: 192.168.0.49 - LS Seq Number: 800001d8 - Checksum: 0xea27 - Length: 36 - Network Mask: /24 - Metric Type: 2 (Larger than any link state path) - TOS: 0 - Metric: 20 - Forward Address: 0.0.0.0 - External Route Tag: 0 + # show ip ospf database external 192.168.165.0 + LS age: 995 + Options: 0x2 : *|-|-|-|-|-|E|* + LS Flags: 0x9 + LS Type: AS-external-LSA + Link State ID: 192.168.165.0 (External Network Number) + Advertising Router: 192.168.0.49 + LS Seq Number: 800001d8 + Checksum: 0xea27 + Length: 36 + Network Mask: /24 + Metric Type: 2 (Larger than any link state path) + TOS: 0 + Metric: 20 + Forward Address: 0.0.0.0 + External Route Tag: 0 We can add this to our partial topology from above, which now looks diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 59917f5262..f1b77ffe09 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -163,7 +163,7 @@ writing, *ospfd* does not support multiple OSPF processes. holdtime can be viewed with :clicmd:`show ip ospf`, where it is expressed as a multiplier of the `initial-holdtime`. - :: + .. code-block:: frr router ospf timers throttle spf 200 400 10000 @@ -249,11 +249,10 @@ writing, *ospfd* does not support multiple OSPF processes. on this interface so router can provide network information to the other ospf routers via this interface. -:: - - router ospf - network 192.168.1.0/24 area 0.0.0.0 + .. code-block:: frr + router ospf + network 192.168.1.0/24 area 0.0.0.0 Prefix length in interface must be equal or bigger (ie. smaller network) than prefix length in network statement. For example statement above doesn't enable @@ -288,23 +287,23 @@ OSPF area .. index:: no area (0-4294967295) range A.B.C.D/M .. clicmd:: no area (0-4294967295) range A.B.C.D/M - Summarize intra area paths from specified area into one Type-3 summary-LSA - announced to other areas. This command can be used only in ABR and ONLY - router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can - be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS. - Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR. + Summarize intra area paths from specified area into one Type-3 summary-LSA + announced to other areas. This command can be used only in ABR and ONLY + router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can + be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS. + Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR. -:: + .. code-block:: frr - router ospf - network 192.168.1.0/24 area 0.0.0.0 - network 10.0.0.0/8 area 0.0.0.10 - area 0.0.0.10 range 10.0.0.0/8 + router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 10.0.0.0/8 area 0.0.0.10 + area 0.0.0.10 range 10.0.0.0/8 - With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is - announced into backbone area if area 0.0.0.10 contains at least one intra-area - network (ie. described with router or network LSA) from this range. + With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is + announced into backbone area if area 0.0.0.10 contains at least one intra-area + network (ie. described with router or network LSA) from this range. .. index:: area A.B.C.D range IPV4_PREFIX not-advertise .. clicmd:: area A.B.C.D range IPV4_PREFIX not-advertise @@ -324,12 +323,12 @@ OSPF area Substitute summarized prefix with another prefix. -:: + .. code-block:: frr - router ospf - network 192.168.1.0/24 area 0.0.0.0 - network 10.0.0.0/8 area 0.0.0.10 - area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8 + router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 10.0.0.0/8 area 0.0.0.10 + area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8 One Type-3 summary-LSA with routing info 11.0.0.0/8 is announced into backbone area if @@ -421,16 +420,15 @@ OSPF area Filter Type-3 summary-LSAs announced to other areas originated from intra- area paths from specified area. -:: - - router ospf - network 192.168.1.0/24 area 0.0.0.0 - network 10.0.0.0/8 area 0.0.0.10 - area 0.0.0.10 export-list foo - ! - access-list foo permit 10.10.0.0/16 - access-list foo deny any + .. code-block:: frr + router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 10.0.0.0/8 area 0.0.0.10 + area 0.0.0.10 export-list foo + ! + access-list foo permit 10.10.0.0/16 + access-list foo deny any With example above any intra-area paths from area 0.0.0.10 and from range 10.10.0.0/16 (for example 10.10.1.0/24 and 10.10.2.128/30) are announced into @@ -452,8 +450,8 @@ OSPF area .. index:: no area (0-4294967295) import-list NAME .. clicmd:: no area (0-4294967295) import-list NAME - Same as export-list, but it applies to paths announced into specified area as - Type-3 summary-LSAs. + Same as export-list, but it applies to paths announced into specified area + as Type-3 summary-LSAs. .. index:: area A.B.C.D filter-list prefix NAME in .. clicmd:: area A.B.C.D filter-list prefix NAME in @@ -479,8 +477,8 @@ OSPF area .. index:: no area (0-4294967295) filter-list prefix NAME out .. clicmd:: no area (0-4294967295) filter-list prefix NAME out - Filtering Type-3 summary-LSAs to/from area using prefix lists. This command - makes sense in ABR only. + Filtering Type-3 summary-LSAs to/from area using prefix lists. This command + makes sense in ABR only. .. index:: area A.B.C.D authentication .. clicmd:: area A.B.C.D authentication @@ -494,8 +492,8 @@ OSPF area .. index:: no area (0-4294967295) authentication .. clicmd:: no area (0-4294967295) authentication - Specify that simple password authentication should be used for the given - area. + Specify that simple password authentication should be used for the given + area. .. index:: area A.B.C.D authentication message-digest .. clicmd:: area A.B.C.D authentication message-digest @@ -568,12 +566,11 @@ OSPF interface Set OSPF authentication key to a cryptographic password. The cryptographic algorithm is MD5. - KEYID identifies secret key used to create the message digest. This ID - is part of the protocol and must be consistent across routers on a - link. + KEYID identifies secret key used to create the message digest. This ID is + part of the protocol and must be consistent across routers on a link. - KEY is the actual message digest key, of up to 16 chars (larger strings - will be truncated), and is associated with the given KEYID. + KEY is the actual message digest key, of up to 16 chars (larger strings will + be truncated), and is associated with the given KEYID. .. index:: ip ospf cost (1-65535) .. clicmd:: ip ospf cost (1-65535) @@ -581,8 +578,8 @@ OSPF interface .. index:: no ip ospf cost .. clicmd:: no ip ospf cost - Set link cost for the specified interface. The cost value is set to router-LSA's - metric field and used for SPF calculation. + Set link cost for the specified interface. The cost value is set to + router-LSA's metric field and used for SPF calculation. .. index:: ip ospf dead-interval (1-65535) .. clicmd:: ip ospf dead-interval (1-65535) @@ -635,10 +632,9 @@ OSPF interface .. index:: no ip ospf priority .. clicmd:: no ip ospf priority - Set RouterPriority integer value. The router with the highest priority - will be more eligible to become Designated Router. Setting the value - to 0, makes the router ineligible to become Designated Router. The - default value is 1. + Set RouterPriority integer value. The router with the highest priority will + be more eligible to become Designated Router. Setting the value to 0, makes + the router ineligible to become Designated Router. The default value is 1. .. index:: ip ospf retransmit-interval (1-65535) .. clicmd:: ip ospf retransmit-interval (1-65535) @@ -646,9 +642,9 @@ OSPF interface .. index:: no ip ospf retransmit interval .. clicmd:: no ip ospf retransmit interval - Set number of seconds for RxmtInterval timer value. This value is used - when retransmitting Database Description and Link State Request packets. - The default value is 5 seconds. + Set number of seconds for RxmtInterval timer value. This value is used when + retransmitting Database Description and Link State Request packets. The + default value is 5 seconds. .. index:: ip ospf transmit-delay .. clicmd:: ip ospf transmit-delay @@ -657,8 +653,7 @@ OSPF interface .. clicmd:: no ip ospf transmit-delay Set number of seconds for InfTransDelay value. LSAs' age should be - incremented by this value when transmitting. - The default value is 1 seconds. + incremented by this value when transmitting. The default value is 1 second. .. index:: ip ospf area (A.B.C.D|(0-4294967295)) .. clicmd:: ip ospf area (A.B.C.D|(0-4294967295)) @@ -666,7 +661,7 @@ OSPF interface .. index:: no ip ospf area .. clicmd:: no ip ospf area - Enable ospf on an interface and set associated area. + Enable ospf on an interface and set associated area. .. _redistribute-routes-to-ospf: @@ -702,16 +697,16 @@ Redistribute routes to OSPF .. _ospf-redistribute: - Redistribute routes of the specified protocol - or kind into OSPF, with the metric type and metric set if specified, - filtering the routes using the given route-map if specified. - Redistributed routes may also be filtered with distribute-lists, see + Redistribute routes of the specified protocol or kind into OSPF, with the + metric type and metric set if specified, filtering the routes using the + given route-map if specified. Redistributed routes may also be filtered + with distribute-lists, see :ref:`ospf distribute-list configuration <ospf-distribute-list>`. - Redistributed routes are distributed as into OSPF as Type-5 External - LSAs into links to areas that accept external routes, Type-7 External LSAs - for NSSA areas and are not redistributed at all into Stub areas, where - external routes are not permitted. + Redistributed routes are distributed as into OSPF as Type-5 External LSAs + into links to areas that accept external routes, Type-7 External LSAs for + NSSA areas and are not redistributed at all into Stub areas, where external + routes are not permitted. Note that for connected routes, one may instead use the `passive-interface` configuration. @@ -747,10 +742,10 @@ Redistribute routes to OSPF .. index:: no default-information originate .. clicmd:: no default-information originate - Originate an AS-External (type-5) LSA describing a default route into - all external-routing capable areas, of the specified metric and metric - type. If the 'always' keyword is given then the default is always - advertised, even when there is no default present in the routing table. + Originate an AS-External (type-5) LSA describing a default route into all + external-routing capable areas, of the specified metric and metric type. If + the 'always' keyword is given then the default is always advertised, even + when there is no default present in the routing table. .. index:: distribute-list NAME out (kernel|connected|static|rip|ospf .. clicmd:: distribute-list NAME out (kernel|connected|static|rip|ospf @@ -760,9 +755,9 @@ Redistribute routes to OSPF .. _ospf-distribute-list: - Apply the access-list filter, NAME, to - redistributed routes of the given type before allowing the routes to - redistributed into OSPF (:ref:`ospf redistribution <ospf-redistribute>`). + Apply the access-list filter, NAME, to redistributed routes of the given + type before allowing the routes to redistributed into OSPF + (:ref:`ospf redistribution <ospf-redistribute>`). .. index:: default-metric (0-16777214) .. clicmd:: default-metric (0-16777214) @@ -850,7 +845,8 @@ Showing OSPF information .. index:: show ip ospf route .. clicmd:: show ip ospf route - Show the OSPF routing table, as determined by the most recent SPF calculation. + Show the OSPF routing table, as determined by the most recent SPF + calculation. .. _opaque-lsa: @@ -869,9 +865,9 @@ Opaque LSA .. index:: no capability opaque .. clicmd:: no capability opaque - *ospfd* support Opaque LSA (RFC2370) as fondment for MPLS Traffic Engineering - LSA. Prior to used MPLS TE, opaque-lsa must be enable in the configuration - file. Alternate command could be "mpls-te on" + *ospfd* support Opaque LSA (:rfc:`2370`) as fondment for MPLS Traffic + Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the + configuration file. Alternate command could be "mpls-te on" (:ref:`ospf-traffic-engineering`). .. index:: show ip ospf database (opaque-link|opaque-area|opaque-external) @@ -981,18 +977,19 @@ Router Information .. index:: no pce scope .. clicmd:: no pce scope - The commands are conform to :rfc:`5088` and allow OSPF router announce Path - Compuatation Elemenent (PCE) capabilities through the Router Information (RI) - LSA. Router Information must be enable prior to this. The command set/unset - respectively the PCE IP adress, Autonomous System (AS) numbers of controlled - domains, neighbor ASs, flag and scope. For flag and scope, please refer to - :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor' command - could be specified in order to specify all PCE neighbours. + The commands are conform to :rfc:`5088` and allow OSPF router announce Path + Compuatation Elemenent (PCE) capabilities through the Router Information + (RI) LSA. Router Information must be enable prior to this. The command + set/unset respectively the PCE IP adress, Autonomous System (AS) numbers of + controlled domains, neighbor ASs, flag and scope. For flag and scope, please + refer to :rfc`5088` for the BITPATTERN recognition. Multiple 'pce neighbor' + command could be specified in order to specify all PCE neighbours. .. index:: show ip ospf router-info .. clicmd:: show ip ospf router-info Show Router Capabilities flag. + .. index:: show ip ospf router-info pce .. clicmd:: show ip ospf router-info pce @@ -1028,10 +1025,10 @@ This is an EXPERIMENTAL support of Segment Routing as per draft .. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] .. clicmd:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] - Set the Segment Rounting index for the specifyed prefix. Note - that, only prefix with /32 corresponding to a loopback interface are - currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that - allows SR node to request to its neighbor to not pop the label. + Set the Segment Rounting index for the specifyed prefix. Note that, only + prefix with /32 corresponding to a loopback interface are currently + supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR + node to request to its neighbor to not pop the label. .. index:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json] .. clicmd:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json] @@ -1140,7 +1137,7 @@ OSPF Configuration Examples A simple example, with MD5 authentication enabled: -:: +.. code-block:: frr ! interface bge0 @@ -1155,7 +1152,7 @@ A simple example, with MD5 authentication enabled: An :abbr:`ABR` router, with MD5 authentication and performing summarisation of networks between the areas: -:: +.. code-block:: frr ! password ABCDEF @@ -1189,7 +1186,9 @@ of networks between the areas: A Traffic Engineering configuration, with Inter-ASv2 support. -First, the 'zebra.conf' part::: +First, the :file:`zebra.conf` part: + +.. code-block:: frr interface eth0 ip address 198.168.1.1/24 @@ -1262,7 +1261,9 @@ First, the 'zebra.conf' part::: unrsv-bw 7 1.25e+06 neighbor 192.168.2.2 as 65000 -Then the 'ospfd.conf' itself::: +Then the :file:`ospfd.conf` itself: + +.. code-block:: frr hostname HOSTNAME password PASSWORD @@ -1288,8 +1289,9 @@ Then the 'ospfd.conf' itself::: ! line vty +A router information example with PCE advsertisement: -A router information example with PCE advsertisement::: +.. code-block:: frr ! router ospf diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 064dc436ea..38d55d68ad 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -95,17 +95,17 @@ architecture creates new possibilities for the routing system. :: - +----+ +----+ +-----+ +-----+ - |bgpd| |ripd| |ospfd| |zebra| - +----+ +----+ +-----+ +-----+ - | - +---------------------------|--+ - | v | - | UNIX Kernel routing table | - | | - +------------------------------+ - - FRR System Architecture + +----+ +----+ +-----+ +-----+ + |bgpd| |ripd| |ospfd| |zebra| + +----+ +----+ +-----+ +-----+ + | + +---------------------------|--+ + | v | + | UNIX Kernel routing table | + | | + +------------------------------+ + + FRR System Architecture Multi-process architecture brings extensibility, modularity and diff --git a/doc/user/pim.rst b/doc/user/pim.rst index b9945680cf..2dda88a6d1 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -214,8 +214,8 @@ is in a vrf, enter the interface command with the vrf keyword at the end. .. _pim-multicast-rib-insertion: -PIM Multicast RIB insertion:: -============================= +PIM Multicast RIB insertion: +============================ In order to influence Multicast RPF lookup, it is possible to insert into zebra routes for the Multicast RIB. These routes are only diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index b8cbd161c6..d2686d2eae 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -60,8 +60,7 @@ Certain signals have special meaningss to *ripd*. *ripd* invocation options. Common options that can be specified (:ref:`common-invocation-options`). -.. option:: -r -.. option:: --retain +.. option:: -r, --retain When the program terminates, retain routes added by *ripd*. @@ -147,7 +146,7 @@ RIP Configuration Below is very simple RIP configuration. Interface `eth0` and interface which address match to `10.0.0.0/8` are RIP enabled. - :: + .. code-block:: frr ! router rip @@ -355,7 +354,7 @@ RIP routes can be filtered by a distribute-list. the distribute-list command. For example, in the following configuration ``eth0`` will permit only the paths that match the route 10.0.0.0/8 - :: + .. code-block:: frr ! router rip @@ -447,11 +446,11 @@ Usage of *ripd*'s route-map support. Optional argument route-map MAP_NAME can be added to each `redistribute` statement. -:: +.. code-block:: frr - redistribute static [route-map MAP_NAME] - redistribute connected [route-map MAP_NAME] - ..... + redistribute static [route-map MAP_NAME] + redistribute connected [route-map MAP_NAME] + ..... Cisco applies route-map _before_ routes will exported to rip route table. In @@ -573,17 +572,17 @@ To prevent such unauthenticated querying of routes disable RIPv1, Specifiy Keyed MD5 chain. -:: + .. code-block:: frr - ! - key chain test - key 1 - key-string test - ! - interface eth1 - ip rip authentication mode md5 - ip rip authentication key-chain test - ! + ! + key chain test + key 1 + key-string test + ! + interface eth1 + ip rip authentication mode md5 + ip rip authentication key-chain test + ! .. _rip-timers: diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 97094c2d6b..a0f28b5fc8 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -302,11 +302,11 @@ Route Map Examples A simple example of a route-map: -:: +.. code-block:: frr - route-map test permit 10 - match ip address 10 - set local-preference 200 + route-map test permit 10 + match ip address 10 + set local-preference 200 This means that if a route matches ip access-list number 10 it's diff --git a/doc/user/routeserver.rst b/doc/user/routeserver.rst index 890897bb62..f2c2c6d33e 100644 --- a/doc/user/routeserver.rst +++ b/doc/user/routeserver.rst @@ -246,7 +246,7 @@ against the other two routers. These peerings have In and Out route-maps configured, named like 'PEER-X-IN' or 'PEER-X-OUT'. For example the configuration file for router RA could be the following: -:: +.. code-block:: frr #Configuration for router 'RA' ! @@ -319,29 +319,29 @@ modify the configuration of routers RA, RB and RC. Now they must not peer between them, but only with the route server. For example, RA's configuration would turn into: -:: +.. code-block:: frr + + # Configuration for router 'RA' + ! + hostname RA + password **** + ! + router bgp 65001 + no bgp default ipv4-unicast + neighbor 2001:0DB8::FFFF remote-as 65000 + ! + address-family ipv6 + network 2001:0DB8:AAAA:1::/64 + network 2001:0DB8:AAAA:2::/64 + network 2001:0DB8:0000:1::/64 + network 2001:0DB8:0000:2::/64 - # Configuration for router 'RA' - ! - hostname RA - password **** - ! - router bgp 65001 - no bgp default ipv4-unicast - neighbor 2001:0DB8::FFFF remote-as 65000 - ! - address-family ipv6 - network 2001:0DB8:AAAA:1::/64 - network 2001:0DB8:AAAA:2::/64 - network 2001:0DB8:0000:1::/64 - network 2001:0DB8:0000:2::/64 - - neighbor 2001:0DB8::FFFF activate - neighbor 2001:0DB8::FFFF soft-reconfiguration inbound - exit-address-family - ! - line vty - ! + neighbor 2001:0DB8::FFFF activate + neighbor 2001:0DB8::FFFF soft-reconfiguration inbound + exit-address-family + ! + line vty + ! Which is logically much simpler than its initial configuration, as it now @@ -362,84 +362,84 @@ server. This is a fragment of the route server configuration (we only show the policies for client RA): -:: +.. code-block:: frr - # Configuration for Route Server ('RS') - ! - hostname RS - password ix - ! - bgp multiple-instance - ! - router bgp 65000 view RS - no bgp default ipv4-unicast - neighbor 2001:0DB8::A remote-as 65001 - neighbor 2001:0DB8::B remote-as 65002 - neighbor 2001:0DB8::C remote-as 65003 - ! - address-family ipv6 - neighbor 2001:0DB8::A activate - neighbor 2001:0DB8::A route-server-client - neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import - neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export - neighbor 2001:0DB8::A soft-reconfiguration inbound - - neighbor 2001:0DB8::B activate - neighbor 2001:0DB8::B route-server-client - neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import - neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export - neighbor 2001:0DB8::B soft-reconfiguration inbound - - neighbor 2001:0DB8::C activate - neighbor 2001:0DB8::C route-server-client - neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import - neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export - neighbor 2001:0DB8::C soft-reconfiguration inbound - exit-address-family - ! - ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64 - ipv6 prefix-list COMMON-PREFIXES seq 10 deny any - ! - ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64 - ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any - ! - ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64 - ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any - ! - ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64 - ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any - ! - route-map RSCLIENT-A-IMPORT permit 10 - match peer 2001:0DB8::B - call A-IMPORT-FROM-B - route-map RSCLIENT-A-IMPORT permit 20 - match peer 2001:0DB8::C - call A-IMPORT-FROM-C - ! - route-map A-IMPORT-FROM-B permit 10 - match ipv6 address prefix-list COMMON-PREFIXES - set metric 100 - route-map A-IMPORT-FROM-B permit 20 - match ipv6 address prefix-list PEER-B-PREFIXES - set community 65001:11111 - ! - route-map A-IMPORT-FROM-C permit 10 - match ipv6 address prefix-list COMMON-PREFIXES - set metric 200 - route-map A-IMPORT-FROM-C permit 20 - match ipv6 address prefix-list PEER-C-PREFIXES - set community 65001:22222 - ! - route-map RSCLIENT-A-EXPORT permit 10 - match peer 2001:0DB8::B - match ipv6 address prefix-list PEER-A-PREFIXES - route-map RSCLIENT-A-EXPORT permit 20 - match peer 2001:0DB8::C - match ipv6 address prefix-list PEER-A-PREFIXES - ! - ... - ... - ... + # Configuration for Route Server ('RS') + ! + hostname RS + password ix + ! + bgp multiple-instance + ! + router bgp 65000 view RS + no bgp default ipv4-unicast + neighbor 2001:0DB8::A remote-as 65001 + neighbor 2001:0DB8::B remote-as 65002 + neighbor 2001:0DB8::C remote-as 65003 + ! + address-family ipv6 + neighbor 2001:0DB8::A activate + neighbor 2001:0DB8::A route-server-client + neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import + neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export + neighbor 2001:0DB8::A soft-reconfiguration inbound + + neighbor 2001:0DB8::B activate + neighbor 2001:0DB8::B route-server-client + neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import + neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export + neighbor 2001:0DB8::B soft-reconfiguration inbound + + neighbor 2001:0DB8::C activate + neighbor 2001:0DB8::C route-server-client + neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import + neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export + neighbor 2001:0DB8::C soft-reconfiguration inbound + exit-address-family + ! + ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64 + ipv6 prefix-list COMMON-PREFIXES seq 10 deny any + ! + ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64 + ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any + ! + ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64 + ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any + ! + ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64 + ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any + ! + route-map RSCLIENT-A-IMPORT permit 10 + match peer 2001:0DB8::B + call A-IMPORT-FROM-B + route-map RSCLIENT-A-IMPORT permit 20 + match peer 2001:0DB8::C + call A-IMPORT-FROM-C + ! + route-map A-IMPORT-FROM-B permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set metric 100 + route-map A-IMPORT-FROM-B permit 20 + match ipv6 address prefix-list PEER-B-PREFIXES + set community 65001:11111 + ! + route-map A-IMPORT-FROM-C permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set metric 200 + route-map A-IMPORT-FROM-C permit 20 + match ipv6 address prefix-list PEER-C-PREFIXES + set community 65001:22222 + ! + route-map RSCLIENT-A-EXPORT permit 10 + match peer 2001:0DB8::B + match ipv6 address prefix-list PEER-A-PREFIXES + route-map RSCLIENT-A-EXPORT permit 20 + match peer 2001:0DB8::C + match ipv6 address prefix-list PEER-A-PREFIXES + ! + ... + ... + ... If you compare the initial configuration of RA with the route server @@ -487,7 +487,7 @@ any limitation, as all kinds of filters can be included in import/export route-maps. For example suppose that in the non-route-server scenario peer RA had the following filters configured for input from peer B: -:: +.. code-block:: frr neighbor 2001:0DB8::B prefix-list LIST-1 in neighbor 2001:0DB8::B filter-list LIST-2 in @@ -507,7 +507,7 @@ the three filters (the community-list, the prefix-list and the route-map). That route-map can then be used inside the Import policy in the route server. Lets see how to do it: -:: +.. code-block:: frr neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import ... diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 86d88dcf9b..93a8e4396a 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -164,7 +164,7 @@ Validating BGP Updates In the following example, the router prefers valid routes over invalid prefixes because invalid routes have a lower local preference. - :: + .. code-block:: frr ! Allow for invalid routes in route selection process route bgp 60001 @@ -213,7 +213,7 @@ Displaying RPKI RPKI Configuration Example -------------------------- -:: +.. code-block:: frr hostname bgpd1 password zebra diff --git a/doc/user/snmp.rst b/doc/user/snmp.rst index 114f1f7dfc..1a24d56cb7 100644 --- a/doc/user/snmp.rst +++ b/doc/user/snmp.rst @@ -42,22 +42,23 @@ master SNMP agent (snmpd) and each of the FRR daemons must be configured. In :file:`/etc/snmp/snmpd.conf`, the ``master agentx`` directive should be added. In each of the FRR daemons, ``agentx`` command will enable AgentX support. -:: +:file:`/etc/snmp/snmpd.conf`: + # + # example access restrictions setup + # + com2sec readonly default public + group MyROGroup v1 readonly + view all included .1 80 + access MyROGroup "" any noauth exact all none none + # + # enable master agent for AgentX subagents + # + master agentx + +:file:`/etc/frr/ospfd.conf:` + + .. code-block:: frr - /etc/snmp/snmpd.conf: - # - # example access restrictions setup - # - com2sec readonly default public - group MyROGroup v1 readonly - view all included .1 80 - access MyROGroup "" any noauth exact all none none - # - # enable master agent for AgentX subagents - # - master agentx - - /etc/frr/ospfd.conf: ! ... the rest of ospfd.conf has been omitted for clarity ... ! agentx @@ -69,16 +70,16 @@ each FRR daemons: :: - 2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected + 2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected Then, you can use the following command to check everything works as expected: :: - # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1 - OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109 - [...] + # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1 + OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109 + [...] The AgentX protocol can be transported over a Unix socket or using TCP or UDP. @@ -88,10 +89,9 @@ need to configure FRR to use another transport, you can configure it through :: - /etc/snmp/frr.conf: - [snmpd] - # Use a remote master agent - agentXSocket tcp:192.168.15.12:705 + [snmpd] + # Use a remote master agent + agentXSocket tcp:192.168.15.12:705 .. _smux-configuration: @@ -112,26 +112,24 @@ In the following example the ospfd daemon will be connected to the snmpd daemon using the password "frr_ospfd". For testing it is recommending to take exactly the below snmpd.conf as wrong access restrictions can be hard to debug. -:: - - /etc/snmp/snmpd.conf: - # - # example access restrictions setup - # - com2sec readonly default public - group MyROGroup v1 readonly - view all included .1 80 - access MyROGroup "" any noauth exact all none none - # - # the following line is relevant for FRR - # - smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd - - /etc/frr/ospf: - ! ... the rest of ospfd.conf has been omitted for clarity ... - ! - smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd - ! +:file:`/etc/snmp/snmpd.conf`: + # + # example access restrictions setup + # + com2sec readonly default public + group MyROGroup v1 readonly + view all included .1 80 + access MyROGroup "" any noauth exact all none none + # + # the following line is relevant for FRR + # + smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd + +:file:`/etc/frr/ospf`: + ! ... the rest of ospfd.conf has been omitted for clarity ... + ! + smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd + ! After restarting snmpd and frr, a successful connection can be verified in the diff --git a/doc/user/snmptrap.rst b/doc/user/snmptrap.rst index 9e642a5d4d..4bc6d40122 100644 --- a/doc/user/snmptrap.rst +++ b/doc/user/snmptrap.rst @@ -42,7 +42,7 @@ The snmptrap_handle.sh script I personally use for handling BGP4 traps is below. You can of course do all sorts of things when handling traps, like sound a siren, have your display flash, etc., be creative ;). -:: +.. code-block:: shell #!/bin/bash diff --git a/doc/user/vnc.rst b/doc/user/vnc.rst index 8be4ff62de..ff6050ca68 100644 --- a/doc/user/vnc.rst +++ b/doc/user/vnc.rst @@ -90,7 +90,7 @@ Default values are overridden by :ref:`vnc-nve-group-configuration`. Enter VNC configuration mode for specifying VNC default behaviors. Use `exit-vnc` to leave VNC configuration mode. `vnc defaults` is optional. -:: +.. code-block:: frr vnc defaults ... various VNC defaults @@ -142,7 +142,7 @@ Defaults section. Enter VNC configuration mode for defining the NVE group `name`. Use `exit` or `exit-vnc` to exit group configuration mode. - :: + .. code-block:: frr vnc nve-group group1 ... configuration commands @@ -315,7 +315,7 @@ L2 Group Configuration. Enter VNC configuration mode for defining the L2 group `name`. Use `exit` or `exit-vnc` to exit group configuration mode. - :: + .. code-block:: frr vnc l2-group group1 ... configuration commands @@ -851,7 +851,9 @@ Tunnel Encapsulation Attribute. A three-way full mesh with three NVEs per NVA. -:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100)::: +:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100): + +.. code-block:: frr router bgp 64512 @@ -883,7 +885,9 @@ Tunnel Encapsulation Attribute. exit -:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101)::: +:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101): + +.. code-block:: frr router bgp 64512 @@ -905,7 +909,9 @@ Tunnel Encapsulation Attribute. exit-vnc exit -:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102)::: +:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102): + +.. code-block:: frr router bgp 64512 @@ -959,7 +965,9 @@ registrations exported this way have a next-hop address of the CE behind the connected (registering) NVE. Exporting VNC routes as IPv4 unicast is enabled with the ``vnc export`` command below. -The configuration for ``VNC-GW 1`` is shown below.:: +The configuration for ``VNC-GW 1`` is shown below. + +.. code-block:: frr router bgp 64512 bgp router-id 192.168.1.101 @@ -994,7 +1002,9 @@ have a statement disabling the IPv4 unicast address family. IPv4 unicast is on by default and this prevents the other VNC-GW and NVAs from learning unicast routes advertised by the route-reflector clients. -Configuration for ``NVA 2``::: +Configuration for ``NVA 2``: + +.. code-block:: frr router bgp 64512 bgp router-id 192.168.1.104 @@ -1077,7 +1087,9 @@ As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The 7``, and ``NVE 8`` are members of the NVE group ``group1``. The NVEs ``NVE 5``, ``NVE 6``, and ``NVE 9`` are members of the NVE group ``group2``. -:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100::: +:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100: + +.. code-block:: frr router bgp 64512 @@ -1106,7 +1118,9 @@ As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The exit -:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101::: +:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101: + +.. code-block:: frr router bgp 64512 @@ -1126,28 +1140,30 @@ As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The exit-vnc exit -:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102::: +:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102: - router bgp 64512 +.. code-block:: frr - bgp router-id 192.168.1.102 + router bgp 64512 - neighbor 192.168.1.100 remote-as 64512 + bgp router-id 192.168.1.102 - address-family ipv4 vpn - neighbor 192.168.1.100 activate - exit-address-family + neighbor 192.168.1.100 remote-as 64512 - vnc defaults - rd 64512:1 - response-lifetime 200 - rt both 1000:1 1000:2 - exit-vnc + address-family ipv4 vpn + neighbor 192.168.1.100 activate + exit-address-family - vnc nve-group group1 - prefix vn 172.16.128.0/17 - exit-vnc - exit + vnc defaults + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + + vnc nve-group group1 + prefix vn 172.16.128.0/17 + exit-vnc + exit While not shown, an NVA can also be configured as a route reflector. @@ -1218,7 +1234,9 @@ VNC-relevant configuration is provided. } } -:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101::: +:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101: + +.. code-block:: frr router bgp 64512 @@ -1238,7 +1256,9 @@ VNC-relevant configuration is provided. exit-vnc exit -:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102::: +:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102: + +.. code-block:: frr router bgp 64512 @@ -1277,7 +1297,9 @@ reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and FRR-based NVA with redundant route reflectors -:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100::: +:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100: + +.. code-block:: frr router bgp 64512 @@ -1304,29 +1326,33 @@ reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and exit-address-family exit -:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101::: +:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101: - router bgp 64512 +.. code-block:: frr - bgp router-id 192.168.1.101 + router bgp 64512 - neighbor 192.168.1.100 remote-as 64512 - neighbor 192.168.1.104 remote-as 64512 + bgp router-id 192.168.1.101 - address-family ipv4 vpn - neighbor 192.168.1.100 activate - neighbor 192.168.1.104 activate - exit-address-family + neighbor 192.168.1.100 remote-as 64512 + neighbor 192.168.1.104 remote-as 64512 - vnc nve-group group1 - prefix vn 172.16.0.0/17 - rd 64512:1 - response-lifetime 200 - rt both 1000:1 1000:2 - exit-vnc - exit + address-family ipv4 vpn + neighbor 192.168.1.100 activate + neighbor 192.168.1.104 activate + exit-address-family + + vnc nve-group group1 + prefix vn 172.16.0.0/17 + rd 64512:1 + response-lifetime 200 + rt both 1000:1 1000:2 + exit-vnc + exit + +:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102: -:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102::: +.. code-block:: frr router bgp 64512 @@ -1417,7 +1443,7 @@ reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and } } -.. [#] The nve-id is carriedin the route distinguisher. It is the second octet +.. [#] The nve-id is carried in the route distinguisher. It is the second octet of the eight-octet route distinguisher generated for Ethernet / L2 advertisements. The first octet is a constant 0xFF, and the third through eighth octets are set to the L2 diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 70a69cf0d4..7c886e785e 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -18,19 +18,16 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. program:: zebra -.. option:: -b -.. option:: --batch +.. option:: -b, --batch Runs in batch mode. *zebra* parses configuration file and terminates immediately. -.. option:: -k -.. option:: --keep_kernel +.. option:: -k, --keep_kernel When zebra starts up, don't delete old self inserted routes. -.. option:: -r -.. option:: --retain +.. option:: -r, --retain When program terminates, retain routes added by zebra. @@ -243,7 +240,8 @@ defines static prefix and gateway. Some example configuration: - :: + .. code-block:: frr + ip route 10.0.0.0/8 10.0.0.2 ip route 10.0.0.0/8 ppp0 ip route 10.0.0.0/8 null0 @@ -259,7 +257,7 @@ defines static prefix and gateway. A.B.C.D format, user must define NETMASK value with A.B.C.D format. GATEWAY is same option as above command. - :: + .. code-block:: frr ip route 10.0.0.0 255.255.255.0 10.0.0.2 ip route 10.0.0.0 255.255.255.0 ppp0 @@ -273,9 +271,9 @@ defines static prefix and gateway. Installs the route with the specified distance. -Multiple nexthop static route +Multiple nexthop static route: -:: +.. code-block:: frr ip route 10.0.0.1/32 10.0.0.2 ip route 10.0.0.1/32 10.0.0.3 @@ -297,7 +295,7 @@ nexthops, if the platform supports this. * is directly connected, eth0 -:: +.. code-block:: frr ip route 10.0.0.0/8 10.0.0.2 ip route 10.0.0.0/8 10.0.0.3 @@ -376,30 +374,30 @@ unicast topology! MODE sets the method used to perform RPF lookups. Supported modes: -urib-only - Performs the lookup on the Unicast RIB. The Multicast RIB is never used. + urib-only + Performs the lookup on the Unicast RIB. The Multicast RIB is never used. -mrib-only - Performs the lookup on the Multicast RIB. The Unicast RIB is never used. + mrib-only + Performs the lookup on the Multicast RIB. The Unicast RIB is never used. -mrib-then-urib - Tries to perform the lookup on the Multicast RIB. If any route is found, - that route is used. Otherwise, the Unicast RIB is tried. + mrib-then-urib + Tries to perform the lookup on the Multicast RIB. If any route is found, + that route is used. Otherwise, the Unicast RIB is tried. -lower-distance - Performs a lookup on the Multicast RIB and Unicast RIB each. The result - with the lower administrative distance is used; if they're equal, the - Multicast RIB takes precedence. + lower-distance + Performs a lookup on the Multicast RIB and Unicast RIB each. The result + with the lower administrative distance is used; if they're equal, the + Multicast RIB takes precedence. -longer-prefix - Performs a lookup on the Multicast RIB and Unicast RIB each. The result - with the longer prefix length is used; if they're equal, the - Multicast RIB takes precedence. + longer-prefix + Performs a lookup on the Multicast RIB and Unicast RIB each. The result + with the longer prefix length is used; if they're equal, the + Multicast RIB takes precedence. - The `mrib-then-urib` setting is the default behavior if nothing is - configured. If this is the desired behavior, it should be explicitly - configured to make the configuration immune against possible changes in - what the default behavior is. + The `mrib-then-urib` setting is the default behavior if nothing is + configured. If this is the desired behavior, it should be explicitly + configured to make the configuration immune against possible changes in + what the default behavior is. .. warning:: Unreachable routes do not receive special treatment and do not cause @@ -480,7 +478,7 @@ The following creates a prefix-list that matches all addresses, a route-map that sets the preferred source address, and applies the route-map to all *rip* routes. -:: +.. code-block:: frr ip prefix-list ANY permit 0.0.0.0/0 le 32 route-map RM1 permit 10 diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 6bd7d90aef..dd29358b07 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -23,11 +23,13 @@ FROM alpine:3.7 as alpine-builder RUN apk add --no-cache abuild alpine-sdk && mkdir -p /pkgs/apk ADD docker/alpine/alpine-build.sh /usr/bin/ ADD docker/alpine/builder /etc/sudoers.d -COPY --from=source-builder /src/*.tar.gz /src/alpine/APKBUILD /dist/ +COPY --from=source-builder /src/*.tar.gz /src/alpine/* /src/tools/etc/frr/daemons* /dist/ RUN adduser -D -G abuild builder && chown -R builder /dist /pkgs USER builder RUN /usr/bin/alpine-build.sh FROM alpine:3.7 RUN mkdir -p /pkgs/apk COPY --from=alpine-builder /pkgs/apk/ /pkgs/apk/ +RUN apk add --no-cache tini RUN apk add --no-cache --allow-untrusted /pkgs/apk/x86_64/*.apk +ENTRYPOINT [ "/sbin/tini", "--", "/usr/lib/frr/docker-start" ] diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c index e418b40343..7dd95b7bc6 100644 --- a/eigrpd/eigrp_snmp.c +++ b/eigrpd/eigrp_snmp.c @@ -516,7 +516,7 @@ eigrp_snmp_nbr_lookup_next(struct in_addr *nbr_addr, unsigned int *ifindex, struct eigrp_neighbor *nbr; struct route_node *rn; struct eigrp_neighbor *min = NULL; - struct eigrp *eigrp = eigrp; + struct eigrp *eigrp; eigrp = eigrp_lookup(); diff --git a/lib/buffer.c b/lib/buffer.c index 03202f1253..b573981c1b 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -429,6 +429,9 @@ in one shot. */ size_t iovcnt = 0; size_t nbyte = 0; + if (fd < 0) + return BUFFER_ERROR; + for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH); d = d->next, iovcnt++) { iov[iovcnt].iov_base = d->data + d->sp; diff --git a/lib/command_lex.l b/lib/command_lex.l index 530900659b..0d6e6ee7e5 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -23,6 +23,9 @@ */ %{ +/* ignore flex generated code in static analyzer */ +#ifndef __clang_analyzer__ + /* ignore harmless bugs in old versions of flex */ #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -91,3 +94,5 @@ void cleanup_lexer (yyscan_t *scn) // yy_delete_buffer (buffer, *scn); yylex_destroy(*scn); } + +#endif /* __clang_analyzer__ */ diff --git a/lib/command_match.c b/lib/command_match.c index f6b07a0b20..99ec03e0c2 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -99,6 +99,9 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline, struct listnode *head = listhead(*argv); struct listnode *tail = listtail(*argv); + assert(head); + assert(tail); + // delete dummy start node cmd_token_del((struct cmd_token *)head->data); list_delete_node(*argv, head); diff --git a/lib/imsg.c b/lib/imsg.c index 0ea1dd6302..6419f805ab 100644 --- a/lib/imsg.c +++ b/lib/imsg.c @@ -241,6 +241,8 @@ struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, struct ibuf *wbuf; struct imsg_hdr hdr; + memset(&hdr, 0x00, IMSG_HEADER_SIZE); + datalen += IMSG_HEADER_SIZE; if (datalen > MAX_IMSGSIZE) { errno = ERANGE; diff --git a/lib/libfrr.c b/lib/libfrr.c index 8d4a3ff8df..9931183634 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -531,12 +531,12 @@ struct thread_master *frr_init(void) snprintf(p_instance, sizeof(p_instance), "-%d", di->instance); } if (di->pathspace) - snprintf(p_pathspace, sizeof(p_pathspace), "/%s", + snprintf(p_pathspace, sizeof(p_pathspace), "%s/", di->pathspace); snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf", frr_sysconfdir, p_pathspace, di->name, p_instance); - snprintf(pidfile_default, sizeof(pidfile_default), "%s%s/%s%s.pid", + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid", frr_vtydir, p_pathspace, di->name, p_instance); zprivs_preinit(di->privs); @@ -968,6 +968,10 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_TABLE_MANAGER_CONNECT), DESC_ENTRY(ZEBRA_GET_TABLE_CHUNK), DESC_ENTRY(ZEBRA_RELEASE_TABLE_CHUNK), + DESC_ENTRY(ZEBRA_IPSET_CREATE), + DESC_ENTRY(ZEBRA_IPSET_DESTROY), + DESC_ENTRY(ZEBRA_IPSET_ENTRY_ADD), + DESC_ENTRY(ZEBRA_IPSET_ENTRY_DELETE), }; #undef DESC_ENTRY diff --git a/lib/mpls.c b/lib/mpls.c new file mode 100644 index 0000000000..759fe1206d --- /dev/null +++ b/lib/mpls.c @@ -0,0 +1,100 @@ +/* + * mpls functions + * + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <mpls.h> +#include <memory.h> + +/* + * String to label conversion, labels separated by '/'. + * + * @param label_str labels separated by / + * @param num_labels number of labels; zero if conversion was unsuccessful + * @param labels preallocated mpls_label_t array of size MPLS_MAX_LABELS; only + * modified if the conversion succeeded + * @return 0 on success + * -1 if the string could not be parsed as integers + * -2 if a label was inside the reserved range (0-15) + * -3 if the number of labels given exceeds MPLS_MAX_LABELS + */ +int mpls_str2label(const char *label_str, uint8_t *num_labels, + mpls_label_t *labels) +{ + char *ostr; // copy of label string (start) + char *lstr; // copy of label string + char *nump; // pointer to next segment + char *endp; // end pointer + int i; // for iterating label_str + int rc; // return code + mpls_label_t pl[MPLS_MAX_LABELS]; // parsed labels + + /* labels to zero until we have a successful parse */ + ostr = lstr = XSTRDUP(MTYPE_TMP, label_str); + *num_labels = 0; + rc = 0; + + for (i = 0; i < MPLS_MAX_LABELS && lstr && !rc; i++) { + nump = strsep(&lstr, "/"); + pl[i] = strtoul(nump, &endp, 10); + + /* format check */ + if (*endp != '\0') + rc = -1; + /* validity check */ + else if (!IS_MPLS_UNRESERVED_LABEL(pl[i])) + rc = -2; + } + + /* excess labels */ + if (!rc && i == MPLS_MAX_LABELS && lstr) + rc = -3; + + if (!rc) { + *num_labels = i; + memcpy(labels, pl, *num_labels * sizeof(mpls_label_t)); + } + + XFREE(MTYPE_TMP, ostr); + + return rc; +} + +/* + * Label to string conversion, labels in string separated by '/'. + */ +char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, + int len, int pretty) +{ + char label_buf[BUFSIZ]; + int i; + + buf[0] = '\0'; + for (i = 0; i < num_labels; i++) { + if (i != 0) + strlcat(buf, "/", len); + if (pretty) + label2str(labels[i], label_buf, sizeof(label_buf)); + else + snprintf(label_buf, sizeof(label_buf), "%u", labels[i]); + strlcat(buf, label_buf, len); + } + + return buf; +} diff --git a/lib/mpls.h b/lib/mpls.h index 4e5c70cf8c..ff6f1d6c98 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -28,6 +28,10 @@ #undef MPLS_LABEL_MAX #endif +#define MPLS_LABEL_HELPSTR \ + "Specify label(s) for this route\nOne or more " \ + "labels in the range (16-1048575) separated by '/'\n" + /* Well-known MPLS label values (RFC 3032 etc). */ #define MPLS_LABEL_IPV4_EXPLICIT_NULL 0 /* [RFC3032] */ #define MPLS_LABEL_ROUTER_ALERT 1 /* [RFC3032] */ @@ -192,5 +196,16 @@ static inline char *label2str(mpls_label_t label, char *buf, size_t len) } } +/* + * String to label conversion, labels separated by '/'. + */ +int mpls_str2label(const char *label_str, uint8_t *num_labels, + mpls_label_t *labels); + +/* + * Label to string conversion, labels in string separated by '/'. + */ +char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, + int len, int pretty); #endif diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 3d61cecc03..a3fae8f39f 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -59,6 +59,26 @@ static int ns_default_ns_fd; static int ns_debug; +struct ns_map_nsid { + RB_ENTRY(ns_map_nsid) id_entry; + ns_id_t ns_id_external; + ns_id_t ns_id; +}; + +static inline int ns_map_compare(const struct ns_map_nsid *a, + const struct ns_map_nsid *b) +{ + return (a->ns_id - b->ns_id); +} + +RB_HEAD(ns_map_nsid_head, ns_map_nsid); +RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); +RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); +struct ns_map_nsid_head ns_map_nsid_list = RB_INITIALIZER(&ns_map_nsid_list); + +static ns_id_t ns_id_external_numbering; + + #ifndef CLONE_NEWNET #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ @@ -262,6 +282,38 @@ static void ns_disable_internal(struct ns *ns) } } +/* VRF list existance check by name. */ +static struct ns_map_nsid *ns_map_nsid_lookup_by_nsid(ns_id_t ns_id) +{ + struct ns_map_nsid ns_map; + + ns_map.ns_id = ns_id; + return RB_FIND(ns_map_nsid_head, &ns_map_nsid_list, &ns_map); +} + +ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map) +{ + struct ns_map_nsid *ns_map; + vrf_id_t ns_id_external; + + ns_map = ns_map_nsid_lookup_by_nsid(ns_id); + if (ns_map && !map) { + ns_id_external = ns_map->ns_id_external; + RB_REMOVE(ns_map_nsid_head, &ns_map_nsid_list, ns_map); + return ns_id_external; + } + if (ns_map) + return ns_map->ns_id_external; + ns_map = XCALLOC(MTYPE_NS, sizeof(struct ns_map_nsid)); + /* increase vrf_id + * default vrf is the first one : 0 + */ + ns_map->ns_id_external = ns_id_external_numbering++; + ns_map->ns_id = ns_id; + RB_INSERT(ns_map_nsid_head, &ns_map_nsid_list, ns_map); + return ns_map->ns_id_external; +} + struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) { return ns_get_created_internal(ns, name, ns_id); @@ -430,7 +482,7 @@ void ns_init(void) } /* Initialize NS module. */ -void ns_init_management(ns_id_t default_ns_id) +void ns_init_management(ns_id_t default_ns_id, ns_id_t internal_ns) { int fd; @@ -444,6 +496,8 @@ void ns_init_management(ns_id_t default_ns_id) fd = open(NS_DEFAULT_NAME, O_RDONLY); default_ns->fd = fd; } + default_ns->internal_ns_id = internal_ns; + /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); if (ns_debug) diff --git a/lib/netns_other.c b/lib/netns_other.c index 2402dd17d6..4c7be05fab 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -153,6 +153,11 @@ int ns_enable(struct ns *ns, int (*func)(ns_id_t, void *)) return 0; } +ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool maporunmap) +{ + return NS_UNKNOWN; +} + struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) { return NULL; diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 1ec49c2a02..5ac38d6685 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -20,6 +20,7 @@ #include <zebra.h> #include <vrf.h> +#include <sockunion.h> #include <nexthop.h> #include <nexthop_group.h> #include <vty.h> @@ -112,6 +113,9 @@ void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) if (nexthop->next) nexthop->next->prev = nexthop->prev; + + nh->prev = NULL; + nh->next = NULL; } void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, @@ -151,6 +155,7 @@ static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) while (nexthop) { struct nexthop *next = nexthop_next(nexthop); + nexthop_del(&nhgc->nhg, nexthop); if (nhg_hooks.del_nexthop) nhg_hooks.del_nexthop(nhgc, nexthop); @@ -169,6 +174,46 @@ struct nexthop_group_cmd *nhgc_find(const char *name) return RB_FIND(nhgc_entry_head, &nhgc_entries, &find); } +static int nhgc_cmp_helper(const char *a, const char *b) +{ + if (!a && !b) + return 0; + + if (a && !b) + return -1; + + if (!a && b) + return 1; + + return strcmp(a, b); +} + +static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2) +{ + int ret; + + ret = sockunion_cmp(&nh1->addr, &nh2->addr); + if (ret) + return ret; + + ret = nhgc_cmp_helper(nh1->intf, nh2->intf); + if (ret) + return ret; + + return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); +} + +static void nhgl_delete(struct nexthop_hold *nh) +{ + if (nh->intf) + XFREE(MTYPE_TMP, nh->intf); + + if (nh->nhvrf_name) + XFREE(MTYPE_TMP, nh->nhvrf_name); + + XFREE(MTYPE_TMP, nh); +} + static struct nexthop_group_cmd *nhgc_get(const char *name) { struct nexthop_group_cmd *nhgc; @@ -181,6 +226,10 @@ static struct nexthop_group_cmd *nhgc_get(const char *name) QOBJ_REG(nhgc, nexthop_group_cmd); RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc); + nhgc->nhg_list = list_new(); + nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp; + nhgc->nhg_list->del = (void (*)(void *))nhgl_delete; + if (nhg_hooks.new) nhg_hooks.new(name); } @@ -196,6 +245,10 @@ static void nhgc_delete(struct nexthop_group_cmd *nhgc) nhg_hooks.delete(nhgc->name); RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc); + + list_delete_and_null(&nhgc->nhg_list); + + XFREE(MTYPE_TMP, nhgc); } DEFINE_QOBJ_TYPE(nexthop_group_cmd) @@ -228,65 +281,125 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", return CMD_SUCCESS; } -DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, - "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]", - NO_STR - "Specify one of the nexthops in this ECMP group\n" - "v4 Address\n" - "v6 Address\n" - "Interface to use\n" - "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") +static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, + const char *nhvrf_name, + const union sockunion *addr, + const char *intf) +{ + struct nexthop_hold *nh; + + nh = XCALLOC(MTYPE_TMP, sizeof(*nh)); + + if (nhvrf_name) + nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name); + if (intf) + nh->intf = XSTRDUP(MTYPE_TMP, intf); + + nh->addr = *addr; + + listnode_add_sort(nhgc->nhg_list, nh); +} + +static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, + const char *nhvrf_name, + const union sockunion *addr, + const char *intf) +{ + struct nexthop_hold *nh; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { + if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 && + sockunion_cmp(addr, &nh->addr) == 0 && + nhgc_cmp_helper(intf, nh->intf) == 0) + break; + } + + /* + * Something has gone seriously wrong, fail gracefully + */ + if (!nh) + return; + + list_delete_node(nhgc->nhg_list, node); + + if (nh->nhvrf_name) + XFREE(MTYPE_TMP, nh->nhvrf_name); + if (nh->intf) + XFREE(MTYPE_TMP, nh->intf); + + XFREE(MTYPE_TMP, nh); +} + +static bool nexthop_group_parse_nexthop(struct nexthop *nhop, + const union sockunion *addr, + const char *intf, const char *name) { - VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); struct vrf *vrf; - struct nexthop nhop; - struct nexthop *nh; + + memset(nhop, 0, sizeof(*nhop)); if (name) vrf = vrf_lookup_by_name(name); else vrf = vrf_lookup_by_id(VRF_DEFAULT); - if (!vrf) { - vty_out(vty, "Specified: %s is non-existent\n", name); - return CMD_WARNING; - } + if (!vrf) + return false; - memset(&nhop, 0, sizeof(nhop)); - nhop.vrf_id = vrf->vrf_id; + nhop->vrf_id = vrf->vrf_id; if (addr->sa.sa_family == AF_INET) { - nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; + nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; if (intf) { - nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX; - nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); - if (nhop.ifindex == IFINDEX_INTERNAL) { - vty_out(vty, - "Specified Intf %s does not exist in vrf: %s\n", - intf, vrf->name); - return CMD_WARNING; - } + nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop->ifindex == IFINDEX_INTERNAL) + return false; } else - nhop.type = NEXTHOP_TYPE_IPV4; + nhop->type = NEXTHOP_TYPE_IPV4; } else { - memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16); + memcpy(&nhop->gate.ipv6, &addr->sin6.sin6_addr, 16); if (intf) { - nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX; - nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); - if (nhop.ifindex == IFINDEX_INTERNAL) { - vty_out(vty, - "Specified Intf %s does not exist in vrf: %s\n", - intf, vrf->name); - return CMD_WARNING; - } + nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop->ifindex == IFINDEX_INTERNAL) + return false; } else - nhop.type = NEXTHOP_TYPE_IPV6; + nhop->type = NEXTHOP_TYPE_IPV6; + } + + return true; +} + +DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, + "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]", + NO_STR + "Specify one of the nexthops in this ECMP group\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + struct nexthop nhop; + struct nexthop *nh; + bool legal; + + legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name); + + if (nhop.type == NEXTHOP_TYPE_IPV6 + && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { + vty_out(vty, + "Specified a v6 LL with no interface, rejecting\n"); + return CMD_WARNING_CONFIG_FAILED; } nh = nexthop_exists(&nhgc->nhg, &nhop); if (no) { + nexthop_group_unsave_nhop(nhgc, name, addr, intf); if (nh) { nexthop_del(&nhgc->nhg, nh); @@ -297,12 +410,16 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, } } else if (!nh) { /* must be adding new nexthop since !no and !nexthop_exists */ - nh = nexthop_new(); + if (legal) { + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + } - memcpy(nh, &nhop, sizeof(nhop)); - nexthop_add(&nhgc->nhg.nexthop, nh); + nexthop_group_save_nhop(nhgc, name, addr, intf); - if (nhg_hooks.add_nexthop) + if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); } @@ -353,17 +470,37 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) vty_out(vty, "\n"); } +static void nexthop_group_write_nexthop_internal(struct vty *vty, + struct nexthop_hold *nh) +{ + char buf[100]; + + vty_out(vty, "nexthop "); + + vty_out(vty, "%s", sockunion2str(&nh->addr, buf, sizeof(buf))); + + if (nh->intf) + vty_out(vty, " %s", nh->intf); + + if (nh->nhvrf_name) + vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); + + vty_out(vty, "\n"); +} + static int nexthop_group_write(struct vty *vty) { struct nexthop_group_cmd *nhgc; - struct nexthop *nh; + struct nexthop_hold *nh; RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + vty_out(vty, "nexthop-group %s\n", nhgc->name); - for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) { + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { vty_out(vty, " "); - nexthop_group_write_nexthop(vty, nh); + nexthop_group_write_nexthop_internal(vty, nh); } vty_out(vty, "!\n"); @@ -372,6 +509,152 @@ static int nexthop_group_write(struct vty *vty) return 1; } +void nexthop_group_enable_vrf(struct vrf *vrf) +{ + struct nexthop_group_cmd *nhgc; + struct nexthop_hold *nhh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + struct nexthop nhop; + struct nexthop *nh; + + if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr, + nhh->intf, + nhh->nhvrf_name)) + continue; + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (nh) + continue; + + if (nhop.vrf_id != vrf->vrf_id) + continue; + + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + } +} + +void nexthop_group_disable_vrf(struct vrf *vrf) +{ + struct nexthop_group_cmd *nhgc; + struct nexthop_hold *nhh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + struct nexthop nhop; + struct nexthop *nh; + + if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr, + nhh->intf, + nhh->nhvrf_name)) + continue; + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (!nh) + continue; + + if (nh->vrf_id != vrf->vrf_id) + continue; + + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } +} + +void nexthop_group_interface_state_change(struct interface *ifp, + ifindex_t oldifindex) +{ + struct nexthop_group_cmd *nhgc; + struct nexthop_hold *nhh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + struct nexthop *nh; + + if (if_is_up(ifp)) { + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + struct nexthop nhop; + + if (!nexthop_group_parse_nexthop( + &nhop, &nhh->addr, nhh->intf, + nhh->nhvrf_name)) + continue; + + switch (nhop.type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_BLACKHOLE: + continue; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + break; + } + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (nh) + continue; + + if (ifp->ifindex != nhop.ifindex) + continue; + + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + } else { + struct nexthop *next_nh; + + for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) { + next_nh = nh->next; + switch (nh->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_BLACKHOLE: + continue; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + break; + } + + if (oldifindex != nh->ifindex) + continue; + + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } + } +} + void nexthop_group_init(void (*new)(const char *name), void (*add_nexthop)(const struct nexthop_group_cmd *nhg, const struct nexthop *nhop), diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index c2e4c4d757..a44f4e3542 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -56,6 +56,13 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, (nhop); \ (nhop) = nexthop_next(nhop) + +struct nexthop_hold { + char *nhvrf_name; + union sockunion addr; + char *intf; +}; + struct nexthop_group_cmd { RB_ENTRY(nexthop_group_cmd) nhgc_entry; @@ -64,6 +71,8 @@ struct nexthop_group_cmd { struct nexthop_group nhg; + struct list *nhg_list; + QOBJ_FIELDS }; RB_HEAD(nhgc_entry_head, nexthp_group_cmd); @@ -85,6 +94,11 @@ void nexthop_group_init( const struct nexthop *nhop), void (*delete)(const char *name)); +void nexthop_group_enable_vrf(struct vrf *vrf); +void nexthop_group_disable_vrf(struct vrf *vrf); +void nexthop_group_interface_state_change(struct interface *ifp, + ifindex_t oldifindex); + extern struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh); @@ -46,6 +46,9 @@ struct ns { /* Identifier, same as the vector index */ ns_id_t ns_id; + /* Identifier, mapped on the NSID value */ + ns_id_t internal_ns_id; + /* Name */ char *name; @@ -100,7 +103,7 @@ extern void ns_terminate(void); /* API to initialize NETNS managerment * parameter is the default ns_id */ -extern void ns_init_management(ns_id_t ns_id); +extern void ns_init_management(ns_id_t ns_id, ns_id_t internal_ns_idx); /* @@ -133,6 +136,11 @@ extern int ns_have_netns(void); /* API to get context information of a NS */ extern void *ns_info_lookup(ns_id_t ns_id); +/* API to map internal ns id value with + * user friendly ns id external value + */ +extern ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map); + /* * NS init routine * should be called from backendx diff --git a/lib/pbr.h b/lib/pbr.h new file mode 100644 index 0000000000..b49cb562a1 --- /dev/null +++ b/lib/pbr.h @@ -0,0 +1,86 @@ +/* Policy Based Routing (PBR) main header + * Copyright (C) 2018 6WIND + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _PBR_H +#define _PBR_H + +/* + * A PBR filter + * + * The filter or match criteria in a PBR rule. + * For simplicity, all supported filters are grouped into a structure rather + * than delineating further. A bitmask denotes which filters are actually + * specified. + */ +struct pbr_filter { + uint32_t filter_bm; /* not encoded by zapi + */ +#define PBR_FILTER_SRC_IP (1 << 0) +#define PBR_FILTER_DST_IP (1 << 1) +#define PBR_FILTER_SRC_PORT (1 << 2) +#define PBR_FILTER_DST_PORT (1 << 3) +#define PBR_FILTER_FWMARK (1 << 4) + + /* Source and Destination IP address with masks. */ + struct prefix src_ip; + struct prefix dst_ip; + + /* Source and Destination higher-layer (TCP/UDP) port numbers. */ + uint16_t src_port; + uint16_t dst_port; + + /* Filter with fwmark */ + uint32_t fwmark; +}; + +/* + * A PBR action + * + * The action corresponding to a PBR rule. + * While the user specifies the action in a particular way, the forwarding + * plane implementation (Linux only) requires that to be encoded into a + * route table and the rule then point to that route table; in some cases, + * the user criteria may directly point to a table too. + */ +struct pbr_action { + uint32_t table; +}; + +/* + * A PBR rule + * + * This is a combination of the filter criteria and corresponding action. + * Rules also have a user-defined sequence number which defines the relative + * order amongst rules. + */ +struct pbr_rule { + vrf_id_t vrf_id; + + uint32_t seq; + uint32_t priority; + uint32_t unique; + struct pbr_filter filter; + struct pbr_action action; + uint32_t ifindex; +}; + +extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, + struct pbr_rule *zrule); + +#endif /* _PBR_H */ diff --git a/lib/route_types.pl b/lib/route_types.pl index 9d50acaaed..66384fe449 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -54,15 +54,15 @@ while (<STDIN>) { $_ =~ s/\s*,\s*/,/g; - # else: 7-field line + # else: 8-field line my @f = split(/,/, $_); - unless (@f == 7 || @f == 8) { + unless (@f == 8 || @f == 9) { die "invalid input on route_types line $.\n"; } my $proto = $f[0]; $f[3] = $1 if ($f[3] =~ /^'(.*)'$/); - $f[6] = $1 if ($f[6] =~ /^"(.*)"$/); + $f[7] = $1 if ($f[7] =~ /^"(.*)"$/); $protodetail{$proto} = { "number" => scalar @protos, @@ -72,8 +72,9 @@ while (<STDIN>) { "char" => $f[3], "ipv4" => int($f[4]), "ipv6" => int($f[5]), - "shorthelp" => $f[6], - "restrict2" => $f[7], + "redist" => int($f[6]), + "shorthelp" => $f[7], + "restrict2" => $f[8], }; push @protos, $proto; $daemons{$f[2]} = { @@ -136,8 +137,9 @@ sub collect { next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra"); next if ($protodetail{$p}->{"restrict2"} ne "" && $protodetail{$p}->{"restrict2"} ne $daemon); + next if ($protodetail{$p}->{"redist"} eq 0); next unless (($ipv4 && $protodetail{$p}->{"ipv4"}) - || ($ipv6 && $protodetail{$p}->{"ipv6"})); + || ($ipv6 && $protodetail{$p}->{"ipv6"})); push @names, $protodetail{$p}->{"cname"}; push @help, " \"".$protodetail{$p}->{"longhelp"}."\\n\""; } diff --git a/lib/route_types.txt b/lib/route_types.txt index 91eaf94d95..cfa55e468c 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -7,8 +7,8 @@ # Lines /beginning/ with # are comments. # #### -# 7 field line has format: -# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, short desc +# 9 field line has format: +# ZServ route type, canonical name, daemon, route char, ipv4, ipv6, redist, short desc, Restrictions # # Zserv route type: Corresponding with zebra.h. Key field. # canonical name: Typically derived from the route type definition. @@ -25,9 +25,11 @@ # 'X' is reserved as the 'not needed' placeholder. # ipv4: IPv4 capable? yes/no, or 1/0. # ipv6: IPv6 capable? ditto. +# redist: Allow this protocol to be used in redistribution statements # short desc: Very brief description. Used in header of # 'show ip route'. May be specified as NULL # if the canonical name suffices. +# Restriction: If this cannot be used with the listed protocol for redistribution events # # Key fields obviously must be a unique ASCII alpha-numeric word. # Lower-case is required, brevity is optional but highly desirable. @@ -43,43 +45,43 @@ # If you add a new routing protocol here, make sure you also update # meta_queue_map in zebra_rib.c # -## type cname daemon C 4 6 short help -ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, "Reserved" -ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, "kernel route" -ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, "connected" -ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, "static" -ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, "RIP" -ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, "RIPng" -ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF" -ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv3" -ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" -ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" -ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" -ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, "EIGRP" -ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP" +## type cname daemon C 4 6 Redist short help Restrictions +ZEBRA_ROUTE_SYSTEM, system, NULL, 'X', 0, 0, 0, "Reserved" +ZEBRA_ROUTE_KERNEL, kernel, zebra, 'K', 1, 1, 1, "kernel route" +ZEBRA_ROUTE_CONNECT, connected, zebra, 'C', 1, 1, 1, "connected" +ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, 1, "static" +ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, 1, "RIP" +ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, 1, "RIPng" +ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, 1, "OSPF" +ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, 1, "OSPFv3" +ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, 1, "IS-IS" +ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, 1, "BGP" +ZEBRA_ROUTE_PIM, pim, pimd, 'P', 0, 0, 0, "PIM" +ZEBRA_ROUTE_EIGRP, eigrp, eigrpd, 'E', 1, 0, 1, "EIGRP" +ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, 1, "NHRP" # HSLS and OLSR both are AFI independent (so: 1, 1), however # we want to disable for them for general Quagga distribution. # This at least makes it trivial for users of these protocols # to 'switch on' redist support (direct numeric entry remaining # possible). -ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" -ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" -ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, "Table" -ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, "LDP" +ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, 0, "HSLS" +ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, 0, "OLSR" +ZEBRA_ROUTE_TABLE, table, zebra, 'T', 1, 1, 1, "Table" +ZEBRA_ROUTE_LDP, ldp, ldpd, 'L', 0, 0, 0, "LDP" #vnc when sent to zebra -ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, "VNC" +ZEBRA_ROUTE_VNC, vnc, NULL, 'v', 1, 1, 1, "VNC" # vnc when sent to bgp -ZEBRA_ROUTE_VNC_DIRECT, vnc-direct,NULL, 'V', 1, 1, "VNC-Direct", bgpd +ZEBRA_ROUTE_VNC_DIRECT, vnc-direct,NULL, 'V', 1, 1, 1, "VNC-Direct", bgpd # vnc when sent to bgp (resolve NVE mode) -ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, "VNC-RN" +ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, 0, "VNC-RN" # bgp unicast -> vnc -ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct" +ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, 0, "BGP-Direct" # bgp unicast -> vnc -ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" -ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" -ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP" -ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, "PBR" -ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-" +ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, 0, "BGP2VNC" +ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, 1, "Babel" +ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP" +ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR" +ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" ## help strings diff --git a/lib/subdir.am b/lib/subdir.am index 7d85b7a24d..3b469d4524 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -41,6 +41,7 @@ lib_libfrr_la_SOURCES = \ lib/memory.c \ lib/memory_vty.c \ lib/module.c \ + lib/mpls.c \ lib/network.c \ lib/nexthop.c \ lib/netns_linux.c \ @@ -167,6 +168,7 @@ pkginclude_HEADERS += \ lib/zclient.h \ lib/zebra.h \ lib/logicalrouter.h \ + lib/pbr.h \ # end nodist_pkginclude_HEADERS += \ @@ -34,6 +34,7 @@ #include "command.h" #include "ns.h" #include "privs.h" +#include "nexthop_group.h" /* default VRF ID value used when VRF backend is not NETNS */ #define VRF_DEFAULT_INTERNAL 0 @@ -159,11 +160,13 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) if (!name && vrf_id == VRF_UNKNOWN) return NULL; + /* attempt to find already available VRF + */ + if (name) + vrf = vrf_lookup_by_name(name); /* Try to find VRF both by ID and name */ - if (vrf_id != VRF_UNKNOWN) + if (!vrf && vrf_id != VRF_UNKNOWN) vrf = vrf_lookup_by_id(vrf_id); - if (!vrf && name) - vrf = vrf_lookup_by_name(name); if (vrf == NULL) { vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf)); @@ -267,6 +270,13 @@ int vrf_enable(struct vrf *vrf) if (vrf_master.vrf_enable_hook) (*vrf_master.vrf_enable_hook)(vrf); + /* + * If we have any nexthop group entries that + * are awaiting vrf initialization then + * let's let people know about it + */ + nexthop_group_enable_vrf(vrf); + return 1; } @@ -537,7 +547,8 @@ void vrf_configure_backend(int vrf_backend_netns) vrf_backend = vrf_backend_netns; } -int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) +int vrf_handler_create(struct vty *vty, const char *vrfname, + struct vrf **vrf) { struct vrf *vrfp; @@ -564,7 +575,7 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) } int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id) + ns_id_t ns_id, ns_id_t internal_ns_id) { struct ns *ns = NULL; @@ -611,6 +622,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, return CMD_WARNING_CONFIG_FAILED; } ns = ns_get_created(ns, pathname, ns_id); + ns->internal_ns_id = internal_ns_id; ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; /* update VRF netns NAME */ @@ -716,7 +728,8 @@ DEFUN_NOSH (vrf_netns, vrf_daemon_privs->change(ZPRIVS_RAISE)) zlog_err("%s: Can't raise privileges", __func__); - ret = vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN); + ret = vrf_netns_handler_create(vty, vrf, pathname, + NS_UNKNOWN, NS_UNKNOWN); if (vrf_daemon_privs && vrf_daemon_privs->change(ZPRIVS_LOWER)) @@ -825,6 +838,9 @@ vrf_id_t vrf_get_default_id(void) if (vrf) return vrf->vrf_id; + /* backend netns is only known by zebra + * for other daemons, we return VRF_DEFAULT_INTERNAL + */ if (vrf_is_backend_netns()) return ns_get_default_id(); else @@ -272,7 +272,8 @@ extern int vrf_handler_create(struct vty *vty, const char *name, * should be called from zebra only */ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, - char *pathname, ns_id_t ns_id); + char *pathname, ns_id_t ext_ns_id, + ns_id_t ns_id); /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF diff --git a/lib/zclient.c b/lib/zclient.c index d23f62dcd7..48182d6b2c 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -36,6 +36,8 @@ #include "nexthop.h" #include "mpls.h" #include "sockopt.h" +#include "pbr.h" +#include "nexthop_group.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") @@ -1229,6 +1231,55 @@ stream_failure: return 0; } +static void zapi_encode_prefix(struct stream *s, + struct prefix *p, + uint8_t family) +{ + struct prefix any; + + if (!p) { + memset(&any, 0, sizeof(any)); + any.family = family; + p = &any; + } + + stream_putc(s, p->family); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, prefix_blen(p)); +} + +int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, + struct pbr_rule *zrule) +{ + stream_reset(s); + zclient_create_header(s, cmd, zrule->vrf_id); + + /* + * We are sending one item at a time at the moment + */ + stream_putl(s, 1); + + stream_putl(s, zrule->seq); + stream_putl(s, zrule->priority); + stream_putl(s, zrule->unique); + + zapi_encode_prefix(s, &(zrule->filter.src_ip), + zrule->filter.src_ip.family); + stream_putw(s, zrule->filter.src_port); /* src port */ + zapi_encode_prefix(s, &(zrule->filter.dst_ip), + zrule->filter.src_ip.family); + stream_putw(s, zrule->filter.dst_port); /* dst port */ + stream_putw(s, zrule->filter.fwmark); /* fwmark */ + + stream_putl(s, zrule->action.table); + stream_putl(s, zrule->ifindex); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note) @@ -1279,6 +1330,50 @@ stream_failure: return false; } +bool zapi_ipset_notify_decode(struct stream *s, + uint32_t *unique, + enum zapi_ipset_notify_owner *note) +{ + uint32_t uni; + + STREAM_GET(note, s, sizeof(*note)); + + STREAM_GETL(s, uni); + + if (zclient_debug) + zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); + *unique = uni; + + return true; + +stream_failure: + return false; +} + +bool zapi_ipset_entry_notify_decode(struct stream *s, + uint32_t *unique, + char *ipset_name, + enum zapi_ipset_entry_notify_owner *note) +{ + uint32_t uni; + + STREAM_GET(note, s, sizeof(*note)); + + STREAM_GETL(s, uni); + + STREAM_GET(ipset_name, s, + ZEBRA_IPSET_NAME_SIZE); + + if (zclient_debug) + zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); + *unique = uni; + + return true; + +stream_failure: + return false; +} + struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) { struct nexthop *n = nexthop_new(); @@ -1603,7 +1698,9 @@ struct interface *zebra_interface_link_params_read(struct stream *s) void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) { uint8_t link_params_status = 0; + ifindex_t old_ifindex; + old_ifindex = ifp->ifindex; /* Read interface's index. */ if_set_index(ifp, stream_getl(s)); ifp->status = stream_getc(s); @@ -1630,6 +1727,8 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) struct if_link_params *iflp = if_link_params_get(ifp); link_params_set_value(s, iflp); } + + nexthop_group_interface_state_change(ifp, old_ifindex); } size_t zebra_interface_link_params_write(struct stream *s, @@ -1991,6 +2090,40 @@ int lm_label_manager_connect(struct zclient *zclient) return (int)result; } +/* + * Asynchronous label chunk request + * + * @param zclient Zclient used to connect to label manager (zebra) + * @param keep Avoid garbage collection + * @param chunk_size Amount of labels requested + * @result 0 on success, -1 otherwise + */ +int zclient_send_get_label_chunk( + struct zclient *zclient, + uint8_t keep, + uint32_t chunk_size) +{ + struct stream *s; + + if (zclient_debug) + zlog_debug("Getting Label Chunk"); + + if (zclient->sock < 0) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT); + stream_putc(s, keep); + stream_putl(s, chunk_size); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + /** * Function to request a label chunk in a syncronous way * @@ -2604,6 +2737,12 @@ static int zclient_read(struct thread *thread) if (zclient->rule_notify_owner) (*zclient->rule_notify_owner)(command, zclient, length, vrf_id); + break; + case ZEBRA_GET_LABEL_CHUNK: + if (zclient->label_chunk) + (*zclient->label_chunk)(command, zclient, length, + vrf_id); + break; default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index 9d3e5c3702..985239b326 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -47,6 +47,17 @@ */ #define ZAPI_TCP_PATHNAME "@tcp" +/* IPset size name stands for the name of the ipset entry + * that can be created by using some zapi interfaces + */ +#define ZEBRA_IPSET_NAME_SIZE 32 + +/* IPTable action is defined by two values: either + * forward or drop + */ +#define ZEBRA_IPTABLES_FORWARD 0 +#define ZEBRA_IPTABLES_DROP 1 + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; @@ -135,6 +146,15 @@ typedef enum { ZEBRA_TABLE_MANAGER_CONNECT, ZEBRA_GET_TABLE_CHUNK, ZEBRA_RELEASE_TABLE_CHUNK, + ZEBRA_IPSET_CREATE, + ZEBRA_IPSET_DESTROY, + ZEBRA_IPSET_ENTRY_ADD, + ZEBRA_IPSET_ENTRY_DELETE, + ZEBRA_IPSET_NOTIFY_OWNER, + ZEBRA_IPSET_ENTRY_NOTIFY_OWNER, + ZEBRA_IPTABLE_ADD, + ZEBRA_IPTABLE_DELETE, + ZEBRA_IPTABLE_NOTIFY_OWNER, } zebra_message_types_t; struct redist_proto { @@ -223,6 +243,14 @@ struct zclient { uint16_t length, vrf_id_t vrf_id); int (*rule_notify_owner)(int command, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id); + void (*label_chunk)(int command, struct zclient *zclient, + uint16_t length, vrf_id_t vrf_id); + int (*ipset_notify_owner)(int command, struct zclient *zclient, + uint16_t length, vrf_id_t vrf_id); + int (*ipset_entry_notify_owner)(int command, + struct zclient *zclient, + uint16_t length, + vrf_id_t vrf_id); }; /* Zebra API message flag. */ @@ -371,6 +399,31 @@ enum zapi_rule_notify_owner { ZAPI_RULE_REMOVED, }; +enum ipset_type { + IPSET_NET_NET = 1, + IPSET_NET_PORT_NET, + IPSET_NET_PORT, + IPSET_NET +}; + +enum zapi_ipset_notify_owner { + ZAPI_IPSET_FAIL_INSTALL, + ZAPI_IPSET_INSTALLED, + ZAPI_IPSET_REMOVED, +}; + +enum zapi_ipset_entry_notify_owner { + ZAPI_IPSET_ENTRY_FAIL_INSTALL, + ZAPI_IPSET_ENTRY_INSTALLED, + ZAPI_IPSET_ENTRY_REMOVED, +}; + +enum zapi_iptable_notify_owner { + ZAPI_IPTABLE_FAIL_INSTALL, + ZAPI_IPTABLE_INSTALLED, + ZAPI_IPTABLE_REMOVED, +}; + /* Zebra MAC types */ #define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/ #define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/ @@ -535,6 +588,11 @@ extern int zapi_ipv4_route(uint8_t, struct zclient *, struct prefix_ipv4 *, extern struct interface *zebra_interface_link_params_read(struct stream *); extern size_t zebra_interface_link_params_write(struct stream *, struct interface *); +extern int zclient_send_get_label_chunk( + struct zclient *zclient, + uint8_t keep, + uint32_t chunk_size); + extern int lm_label_manager_connect(struct zclient *zclient); extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t chunk_size, uint32_t *start, @@ -605,6 +663,17 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, uint32_t *priority, uint32_t *unique, ifindex_t *ifindex, enum zapi_rule_notify_owner *note); +bool zapi_ipset_notify_decode(struct stream *s, + uint32_t *unique, + enum zapi_ipset_notify_owner *note); + +#define ZEBRA_IPSET_NAME_SIZE 32 + +bool zapi_ipset_entry_notify_decode(struct stream *s, + uint32_t *unique, + char *ipset_name, + enum zapi_ipset_entry_notify_owner *note); + extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 01b8055b66..b895b5ad8b 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -786,6 +786,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) /* (3) if the prefix is equal to an active configured address range */ /* or if the NU bit is set in the prefix */ if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) { + /* must have been set in previous block */ + assert(prefix_lsa); + range = ospf6_route_lookup(&prefix, oa->range_table); if (range) { if (is_debug) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 7c60749d7e..cc8577b94e 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -349,28 +349,6 @@ void ospf6_interface_if_add(struct interface *ifp) ospf6_interface_state_update(oi->interface); } -void ospf6_interface_if_del(struct interface *ifp) -{ - struct ospf6_interface *oi; - - oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) - return; - - /* interface stop */ - if (oi->area) - thread_execute(master, interface_down, oi, 0); - - listnode_delete(oi->area->if_list, oi); - oi->area = (struct ospf6_area *)NULL; - - /* cut link */ - oi->interface = NULL; - ifp->info = NULL; - - ospf6_interface_delete(oi); -} - void ospf6_interface_state_update(struct interface *ifp) { struct ospf6_interface *oi; diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 553e89a227..8fd43f099a 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -176,7 +176,6 @@ extern void ospf6_interface_enable(struct ospf6_interface *); extern void ospf6_interface_disable(struct ospf6_interface *); extern void ospf6_interface_if_add(struct interface *); -extern void ospf6_interface_if_del(struct interface *); extern void ospf6_interface_state_update(struct interface *); extern void ospf6_interface_connected_route_update(struct interface *); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 519c5e170f..de4ee2e1ac 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1844,6 +1844,7 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) 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, diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 6455f606bd..b5a1812ffa 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -193,8 +193,9 @@ static void ospf6_neighbor_state_change(uint8_t next_state, OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area); - if (prev_state == OSPF6_NEIGHBOR_LOADING - && next_state == OSPF6_NEIGHBOR_FULL) { + if ((prev_state == OSPF6_NEIGHBOR_LOADING || + prev_state == OSPF6_NEIGHBOR_EXCHANGE) && + next_state == OSPF6_NEIGHBOR_FULL) { OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if); on->ospf6_if->area->full_nbrs++; } diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 8c2e706d17..8458d19952 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -127,13 +127,6 @@ static int ospf6_zebra_if_del(int command, struct zclient *zclient, zlog_debug("Zebra Interface delete: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); -#if 0 - /* XXX: ospf6_interface_if_del is not the right way to handle this, - * because among other thinkable issues, it will also clear all - * settings as they are contained in the struct ospf6_interface. */ - ospf6_interface_if_del (ifp); -#endif /*0*/ - if_set_index(ifp, IFINDEX_INTERNAL); return 0; } diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index fd3da45f78..6623790837 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1756,6 +1756,7 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, ei.route_map_set.metric = -1; ei.route_map_set.metric_type = -1; ei.tag = 0; + ei.instance = 0; if ((new = ospf_external_lsa_new(ospf, &ei, &type7->data->id)) == NULL) { diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 7464b14b1f..6825be83ac 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -2117,7 +2117,7 @@ static struct ospf_neighbor *ospf_snmp_nbr_lookup_next(struct in_addr *nbr_addr, struct ospf_neighbor *nbr; struct route_node *rn; struct ospf_neighbor *min = NULL; - struct ospf *ospf = ospf; + struct ospf *ospf; ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 05adc5aa4f..26df7a24cd 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -1161,16 +1161,13 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, /* Set opaque-LSA header fields depending of the type of RFC */ if (IS_INTER_AS(lp->type)) { - if - IS_FLOOD_AS(lp->type) - { - options |= OSPF_OPTION_E; /* Enable AS external - as we flood - Inter-AS with - Opaque Type 11 */ - lsa_type = OSPF_OPAQUE_AS_LSA; - } - else { + if (IS_FLOOD_AS(lp->type)) { + /* Enable AS external as we flood Inter-AS with Opaque + * Type 11 + */ + options |= OSPF_OPTION_E; + lsa_type = OSPF_OPAQUE_AS_LSA; + } else { options |= LSA_OPTIONS_GET( area); /* Get area default option */ options |= LSA_OPTIONS_NSSA_GET(area); @@ -1210,12 +1207,12 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, /* Now, create an OSPF LSA instance. */ if ((new = ospf_lsa_new()) == NULL) { - zlog_warn("ospf_mpls_te_lsa_new: ospf_lsa_new() ?"); + zlog_warn("%s: ospf_lsa_new() ?", __func__); stream_free(s); return NULL; } if ((new->data = ospf_lsa_data_new(length)) == NULL) { - zlog_warn("ospf_mpls_te_lsa_new: ospf_lsa_data_new() ?"); + zlog_warn("%s: ospf_lsa_data_new() ?", __func__); ospf_lsa_unlock(&new); new = NULL; stream_free(s); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 23d00633d4..6487596706 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -118,7 +118,8 @@ static int ospf_interface_add(int command, struct zclient *zclient, assert(ifp->info); - if (!OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) { + if (IF_DEF_PARAMS(ifp) + && !OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) { SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); } diff --git a/pbrd/pbr_debug.c b/pbrd/pbr_debug.c index e9b4a52955..82f045c462 100644 --- a/pbrd/pbr_debug.c +++ b/pbrd/pbr_debug.c @@ -43,16 +43,7 @@ const char *pbr_debugs_conflines[] = { "debug pbr events", }; -/* - * Set or unset flags on all debugs for pbrd. - * - * flags - * The flags to set - * - * set - * Whether to set or unset the specified flags - */ -static void pbr_debug_set_all(uint32_t flags, bool set) +void pbr_debug_set_all(uint32_t flags, bool set) { for (unsigned int i = 0; i < array_size(pbr_debugs); i++) { DEBUG_FLAGS_SET(pbr_debugs[i], flags, set); @@ -63,36 +54,13 @@ static void pbr_debug_set_all(uint32_t flags, bool set) } } -/* - * Check flags on all debugs for pbrd. - * - * flags - * The flags to set - * - * Returns: - * The subset of the given flags that were set in all pbrd debugs - */ -static uint32_t pbr_debug_check_all(uint32_t flags) -{ - uint32_t mode = DEBUG_MODE_ALL; - - for (unsigned int i = 0; i < array_size(pbr_debugs); i++) - mode &= DEBUG_MODE_CHECK(pbr_debugs[i], flags); - return mode; -} - -static int pbr_debug_config_write_helper(struct vty *vty, bool config) +int pbr_debug_config_write_helper(struct vty *vty, bool config) { uint32_t mode = DEBUG_MODE_ALL; if (config) mode = DEBUG_MODE_CONF; - if (pbr_debug_check_all(DEBUG_MODE_CONF) == mode) { - vty_out(vty, "debug pbr\n"); - return 0; - } - for (unsigned int i = 0; i < array_size(pbr_debugs); i++) if (DEBUG_MODE_CHECK(pbr_debugs[i], mode)) vty_out(vty, "%s\n", pbr_debugs_conflines[i]); @@ -104,70 +72,9 @@ int pbr_debug_config_write(struct vty *vty) return pbr_debug_config_write_helper(vty, true); } -/* PBR debugging CLI ------------------------------------------------------- */ -/* clang-format off */ - -DEFPY(debug_pbr, - debug_pbr_cmd, - "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]", - NO_STR - DEBUG_STR - "Policy Based Routing\n" - "Policy maps\n" - "PBRD <-> Zebra communications\n" - "Nexthop tracking\n" - "Events\n") -{ - uint32_t mode = DEBUG_NODE2MODE(vty->node); - - if (map) - DEBUG_MODE_SET(&pbr_dbg_map, mode, !no); - if (zebra) - DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no); - if (nht) - DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no); - if (events) - DEBUG_MODE_SET(&pbr_dbg_event, mode, !no); - - /* no specific debug --> act on all of them */ - if (strmatch(argv[argc - 1]->text, "pbr")) - pbr_debug_set_all(mode, !no); - - return CMD_SUCCESS; -} - -DEFUN_NOSH(show_debugging_pbr, - show_debugging_pbr_cmd, - "show debugging [pbr]", - SHOW_STR - DEBUG_STR - "Policy Based Routing\n") -{ - vty_out(vty, "PBR debugging status:\n"); - - pbr_debug_config_write_helper(vty, false); - - return CMD_SUCCESS; -} - -/* clang-format on */ -/* ------------------------------------------------------------------------- */ - -static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; - struct debug_callbacks pbr_dbg_cbs = {.debug_set_all = pbr_debug_set_all}; void pbr_debug_init(void) { debug_init(&pbr_dbg_cbs); } - -void pbr_debug_init_vty(void) -{ - install_node(&debug_node, pbr_debug_config_write); - - install_element(VIEW_NODE, &debug_pbr_cmd); - install_element(CONFIG_NODE, &debug_pbr_cmd); - - install_element(VIEW_NODE, &show_debugging_pbr_cmd); -} diff --git a/pbrd/pbr_debug.h b/pbrd/pbr_debug.h index 2744724629..e72fb88beb 100644 --- a/pbrd/pbr_debug.h +++ b/pbrd/pbr_debug.h @@ -38,9 +38,29 @@ extern struct debug pbr_dbg_event; void pbr_debug_init(void); /* - * Install PBR debugging VTY commands. + * Set or unset flags on all debugs for pbrd. + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +void pbr_debug_set_all(uint32_t flags, bool set); + +/* + * Config write helper. + * + * vty + * Vty to write to + * + * config + * Whether we are writing to show run or saving config file + * + * Returns: + * 0 for convenience */ -void pbr_debug_init_vty(void); +int pbr_debug_config_write_helper(struct vty *vty, bool config); /* * Print PBR debugging configuration. diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index 638e284a1a..ba09621083 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -152,6 +152,11 @@ int main(int argc, char **argv, char **envp) pbr_nhgroup_del_nexthop_cb, pbr_nhgroup_delete_cb); + /* + * So we safely ignore these commands since + * we are getting them at this point in time + */ + access_list_init(); pbr_nht_init(); pbr_map_init(); pbr_zebra_init(); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index ea79320a71..eb2c082fb9 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -152,8 +152,9 @@ void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp_add) pmi->pbrm = pbrm; listnode_add_sort(pbrm->incoming, pmi); + bf_assign_index(pbrm->ifi_bitfield, pmi->install_bit); pbr_map_check_valid(pbrm->name); - if (pbrm->valid && !pbrm->installed) + if (pbrm->valid) pbr_map_install(pbrm); } @@ -193,6 +194,8 @@ extern void pbr_map_delete(struct pbr_map_sequence *pbrms) if (pbrm->seqnumbers->count == 0) { RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm); + + bf_free(pbrm->ifi_bitfield); XFREE(MTYPE_PBR_MAP, pbrm); } } @@ -210,13 +213,12 @@ void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms) pbrm->valid = false; pbrms->nhs_installed = false; - pbrms->installed = false; pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; pbrms->nhgrp_name = NULL; } -struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, - ifindex_t ifindex) +struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, + struct pbr_map_interface **ppmi) { struct pbr_map_sequence *pbrms; struct listnode *snode, *inode; @@ -228,6 +230,9 @@ struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, if (pmi->ifp->ifindex != ifindex) continue; + if (ppmi) + *ppmi = pmi; + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) { DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u", @@ -268,7 +273,7 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) pbrm = pbrm_find(name); if (!pbrm) { pbrm = XCALLOC(MTYPE_PBR_MAP, sizeof(*pbrm)); - strcpy(pbrm->name, name); + snprintf(pbrm->name, sizeof(pbrm->name), "%s", name); pbrm->seqnumbers = list_new(); pbrm->seqnumbers->cmp = @@ -284,6 +289,7 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm); + bf_init(pbrm->ifi_bitfield, 64); pbr_map_add_interfaces(pbrm); } @@ -305,8 +311,6 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) QOBJ_REG(pbrms, pbr_map_sequence); listnode_add_sort(pbrm->seqnumbers, pbrms); - - pbrm->installed = false; } return pbrms; @@ -463,6 +467,8 @@ void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi) listnode_delete(pbrm->incoming, pmi); pmi->pbrm = NULL; + + bf_release_index(pbrm->ifi_bitfield, pmi->install_bit); XFREE(MTYPE_PBR_MAP_INTERFACE, pmi); } @@ -541,8 +547,9 @@ void pbr_map_check(struct pbr_map_sequence *pbrms) pbrms->seqno, pbrms->reason); } - for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { pbr_send_pbr_map(pbrms, pmi, install); + } } void pbr_map_install(struct pbr_map *pbrm) @@ -557,8 +564,6 @@ void pbr_map_install(struct pbr_map *pbrm) for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) pbr_send_pbr_map(pbrms, pmi, true); - - pbrm->installed = true; } void pbr_map_init(void) diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 5cb22d7429..7cd079d169 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -20,6 +20,8 @@ #ifndef __PBR_MAP_H__ #define __PBR_MAP_H__ +#include <bitfield.h> + struct pbr_map { /* * RB Tree of the pbr_maps @@ -40,20 +42,21 @@ struct pbr_map { */ struct list *incoming; + bitfield_t ifi_bitfield; /* * If valid is true we think the pbr_map is valid, * If false, look in individual pbrms to see * what we think is the invalid reason */ bool valid; - - bool installed; }; RB_HEAD(pbr_map_entry_head, pbr_map); RB_PROTOTYPE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare) struct pbr_map_interface { + uint32_t install_bit; + struct interface *ifp; struct pbr_map *pbrm; @@ -112,7 +115,7 @@ struct pbr_map_sequence { /* * Are we installed */ - bool installed; + uint64_t installed; /* * A reason of 0 means we think the pbr_map_sequence is good to go @@ -134,8 +137,9 @@ DECLARE_QOBJ_TYPE(pbr_map_sequence) extern struct pbr_map_entry_head pbr_maps; extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno); -extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, - ifindex_t ifindex); +extern struct pbr_map_sequence * +pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, + struct pbr_map_interface **ppmi); extern struct pbr_map *pbrm_find(const char *name); extern void pbr_map_delete(struct pbr_map_sequence *pbrms); diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 1ce8c2104d..1ccf3ebffa 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -209,6 +209,13 @@ void pbr_nhgroup_add_cb(const char *name) struct nexthop_group_cmd *nhgc; nhgc = nhgc_find(name); + + if (!nhgc) { + DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n", + __PRETTY_FUNCTION__, name); + return; + } + pnhgc = pbr_nht_add_group(name); DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__, @@ -312,8 +319,16 @@ static void pbr_nht_find_nhg_from_table_install(struct hash_backet *b, if (pnhgc->table_id == *table_id) { DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s", __PRETTY_FUNCTION__, *table_id, pnhgc->name); - pnhgc->installed = true; - pbr_map_schedule_policy_from_nhg(pnhgc->name); + + /* + * If the table has been re-handled by zebra + * and we are already installed no need to do + * anything here. + */ + if (!pnhgc->installed) { + pnhgc->installed = true; + pbr_map_schedule_policy_from_nhg(pnhgc->name); + } } } @@ -402,8 +417,6 @@ static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc, install_afi = pbr_nht_which_afi(nhg, nh_afi); - pnhgc->installed = false; - route_add(pnhgc, nhg, install_afi); } @@ -433,7 +446,7 @@ void pbr_nht_change_group(const char *name) return; memset(&find, 0, sizeof(find)); - strcpy(find.name, name); + snprintf(find.name, sizeof(find.name), "%s", name); pnhgc = hash_lookup(pbr_nhg_hash, &find); if (!pnhgc) { @@ -504,11 +517,10 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) pbrm->valid = false; pbrms->nhs_installed = false; - pbrms->installed = false; pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; memset(&find, 0, sizeof(find)); - strcpy(&find.name[0], pbrms->internal_nhg_name); + snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name); pnhgc = hash_lookup(pbr_nhg_hash, &find); nh = pbrms->nhg->nexthop; @@ -543,7 +555,7 @@ struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) return NULL; } - strcpy(lookup.name, name); + snprintf(lookup.name, sizeof(lookup.name), "%s", name); pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc); DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__, pnhgc); @@ -602,7 +614,7 @@ bool pbr_nht_nexthop_group_valid(const char *name) DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name); - strcpy(lookup.name, name); + snprintf(lookup.name, sizeof(lookup.name), "%s", name); pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL); if (!pnhgc) return false; @@ -757,7 +769,7 @@ uint32_t pbr_nht_get_table(const char *name) struct pbr_nexthop_group_cache *pnhgc; memset(&find, 0, sizeof(find)); - strcpy(find.name, name); + snprintf(find.name, sizeof(find.name), "%s", name); pnhgc = hash_lookup(pbr_nhg_hash, &find); if (!pnhgc) { @@ -776,7 +788,7 @@ bool pbr_nht_get_installed(const char *name) struct pbr_nexthop_group_cache *pnhgc; memset(&find, 0, sizeof(find)); - strcpy(find.name, name); + snprintf(find.name, sizeof(find.name), "%s", name); pnhgc = hash_lookup(pbr_nhg_hash, &find); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 87ec3804a5..475ad86b58 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -251,8 +251,14 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, intf, vrf->name); return CMD_WARNING_CONFIG_FAILED; } - } else + } else { + if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { + vty_out(vty, + "Specified a v6 LL with no interface, rejecting\n"); + return CMD_WARNING_CONFIG_FAILED; + } nhop.type = NEXTHOP_TYPE_IPV6; + } } if (pbrms->nhg) @@ -313,36 +319,30 @@ DEFPY (pbr_policy, pbrm = pbrm_find(mapname); if (!pbr_ifp) { - /* - * Some one could have fat fingered the interface - * name - */ + /* we don't want one and we don't have one, so... */ + if (no) + return CMD_SUCCESS; + + /* Some one could have fat fingered the interface name */ pbr_ifp = pbr_if_new(ifp); } if (no) { if (strcmp(pbr_ifp->mapname, mapname) == 0) { - strcpy(pbr_ifp->mapname, ""); - + pbr_ifp->mapname[0] = '\0'; if (pbrm) pbr_map_interface_delete(pbrm, ifp); } } else { - if (strcmp(pbr_ifp->mapname, "") == 0) { - strcpy(pbr_ifp->mapname, mapname); - - if (pbrm) - pbr_map_add_interface(pbrm, ifp); - } else { - if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) { - old_pbrm = pbrm_find(pbr_ifp->mapname); - if (old_pbrm) - pbr_map_interface_delete(old_pbrm, ifp); - strcpy(pbr_ifp->mapname, mapname); - if (pbrm) - pbr_map_add_interface(pbrm, ifp); - } + if (strcmp(pbr_ifp->mapname, "") != 0) { + old_pbrm = pbrm_find(pbr_ifp->mapname); + if (old_pbrm) + pbr_map_interface_delete(old_pbrm, ifp); } + snprintf(pbr_ifp->mapname, sizeof(pbr_ifp->mapname), + "%s", mapname); + if (pbrm) + pbr_map_add_interface(pbrm, ifp); } return CMD_SUCCESS; @@ -389,7 +389,7 @@ DEFPY (show_pbr_map, pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); vty_out(vty, - " Seq: %u rule: %u Installed: %d(%u) Reason: %s\n", + " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n", pbrms->seqno, pbrms->ruleno, pbrms->installed, pbrms->unique, pbrms->reason ? rbuf : "Valid"); @@ -483,6 +483,58 @@ DEFPY (show_pbr_interface, return CMD_SUCCESS; } +/* PBR debugging CLI ------------------------------------------------------- */ +/* clang-format off */ + +static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; + +DEFPY(debug_pbr, + debug_pbr_cmd, + "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]", + NO_STR + DEBUG_STR + "Policy Based Routing\n" + "Policy maps\n" + "PBRD <-> Zebra communications\n" + "Nexthop tracking\n" + "Events\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + + if (map) + DEBUG_MODE_SET(&pbr_dbg_map, mode, !no); + if (zebra) + DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no); + if (nht) + DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no); + if (events) + DEBUG_MODE_SET(&pbr_dbg_event, mode, !no); + + /* no specific debug --> act on all of them */ + if (strmatch(argv[argc - 1]->text, "pbr")) + pbr_debug_set_all(mode, !no); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(show_debugging_pbr, + show_debugging_pbr_cmd, + "show debugging [pbr]", + SHOW_STR + DEBUG_STR + "Policy Based Routing\n") +{ + vty_out(vty, "PBR debugging status:\n"); + + pbr_debug_config_write_helper(vty, false); + + return CMD_SUCCESS; +} + +/* clang-format on */ +/* ------------------------------------------------------------------------- */ + + static struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */ }; @@ -567,6 +619,12 @@ void pbr_vty_init(void) install_node(&pbr_map_node, pbr_vty_map_config_write); + /* debug */ + install_node(&debug_node, pbr_debug_config_write); + install_element(VIEW_NODE, &debug_pbr_cmd); + install_element(CONFIG_NODE, &debug_pbr_cmd); + install_element(VIEW_NODE, &show_debugging_pbr_cmd); + install_default(PBRMAP_NODE); install_element(CONFIG_NODE, &pbr_map_cmd); @@ -580,6 +638,4 @@ void pbr_vty_init(void) install_element(VIEW_NODE, &show_pbr_map_cmd); install_element(VIEW_NODE, &show_pbr_interface_cmd); install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd); - - pbr_debug_init_vty(); } diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index a1a2d34ac1..4e5b5f3dde 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -45,17 +45,6 @@ DEFINE_MTYPE_STATIC(PBRD, PBR_INTERFACE, "PBR Interface") /* Zebra structure to hold current status. */ struct zclient *zclient; -static struct interface *zebra_interface_if_lookup(struct stream *s) -{ - char ifname_tmp[INTERFACE_NAMSIZ]; - - /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); - - /* And look it up. */ - return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); -} - struct pbr_interface *pbr_if_new(struct interface *ifp) { struct pbr_interface *pbr_ifp; @@ -140,7 +129,7 @@ static int interface_state_up(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { - zebra_interface_if_lookup(zclient->ibuf); + zebra_interface_state_read(zclient->ibuf, vrf_id); return 0; } @@ -206,13 +195,16 @@ static int rule_notify_owner(int command, struct zclient *zclient, uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; ifindex_t ifi; + uint64_t installed; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, &ifi, ¬e)) return -1; - pbrms = pbrms_lookup_unique(unique, ifi); + pmi = NULL; + pbrms = pbrms_lookup_unique(unique, ifi, &pmi); if (!pbrms) { DEBUGD(&pbr_dbg_zebra, "%s: Failure to lookup pbrms based upon %u", @@ -220,18 +212,21 @@ static int rule_notify_owner(int command, struct zclient *zclient, return 0; } + installed = 1 << pmi->install_bit; + switch (note) { case ZAPI_RULE_FAIL_INSTALL: DEBUGD(&pbr_dbg_zebra, "%s: Recieved RULE_FAIL_INSTALL", __PRETTY_FUNCTION__); - pbrms->installed = false; + pbrms->installed &= ~installed; break; case ZAPI_RULE_INSTALLED: - pbrms->installed = true; + pbrms->installed |= installed; DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED", __PRETTY_FUNCTION__); break; case ZAPI_RULE_REMOVED: + pbrms->installed &= ~installed; DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED", __PRETTY_FUNCTION__); break; @@ -499,9 +494,23 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, { struct pbr_map *pbrm = pbrms->parent; struct stream *s; + uint64_t is_installed = 1 << pmi->install_bit; - DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name, - install); + is_installed &= pbrms->installed; + + DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")", + __PRETTY_FUNCTION__, pbrm->name, install, is_installed); + + /* + * If we are installed and asked to do so again + * just return. If we are not installed and asked + * and asked to delete just return; + */ + if (install && is_installed) + return; + + if (!install && !is_installed) + return; s = zclient->obuf; stream_reset(s); diff --git a/pimd/mtracebis_routeget.c b/pimd/mtracebis_routeget.c index d75aaa3708..8c1cd8d963 100644 --- a/pimd/mtracebis_routeget.c +++ b/pimd/mtracebis_routeget.c @@ -81,7 +81,7 @@ int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw) ret = rtnl_open(&rth, 0); - if (ret < 0) + if (ret < 0 || rth.fd <= 0) return ret; if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) { diff --git a/tests/ospf6d/test_lsdb.c b/tests/ospf6d/test_lsdb.c index 633e88e769..ec0835c719 100644 --- a/tests/ospf6d/test_lsdb.c +++ b/tests/ospf6d/test_lsdb.c @@ -38,9 +38,15 @@ static size_t lsa_count = 0; static void lsa_check_resize(size_t len) { + struct ospf6_lsa **templsas; + if (lsa_count >= len) return; - lsas = realloc(lsas, len * sizeof(lsas[0])); + templsas = realloc(lsas, len * sizeof(lsas[0])); + if (templsas) + lsas = templsas; + else + return; memset(lsas + lsa_count, 0, sizeof(lsas[0]) * (len - lsa_count)); lsa_count = len; @@ -25,7 +25,9 @@ DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd MAX_INSTANCES=5 RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py -. /lib/lsb/init-functions +if [ -e /lib/lsb/init-functions ]; then + . /lib/lsb/init-functions +fi if [ -f /usr/lib/frr/ssd ]; then SSD=/usr/lib/frr/ssd diff --git a/tools/lsan-suppressions.txt b/tools/lsan-suppressions.txt index dd5577bd24..5184b55699 100644 --- a/tools/lsan-suppressions.txt +++ b/tools/lsan-suppressions.txt @@ -1 +1,5 @@ leak:clippy +leak:PyObject_Malloc +leak:PyObject_Realloc +leak:PyList_Append +leak:malloc diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 4de671b7fa..cd59d8094b 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -306,6 +306,7 @@ int main(int argc, char **argv, char **env) char *homedir = NULL; int ditch_suid = 0; char sysconfdir[MAXPATHLEN]; + const char *pathspace_arg = NULL; char pathspace[MAXPATHLEN] = ""; /* SUID: drop down to calling user & go back up when needed */ @@ -363,7 +364,8 @@ int main(int argc, char **argv, char **env) "slashes or dots are not permitted in the --pathspace option.\n"); exit(1); } - snprintf(pathspace, sizeof(pathspace), "/%s", optarg); + pathspace_arg = optarg; + snprintf(pathspace, sizeof(pathspace), "%s/", optarg); break; case 'd': daemon_name = optarg; @@ -419,7 +421,11 @@ int main(int argc, char **argv, char **env) pathspace, VTYSH_CONFIG_NAME); snprintf(frr_config, sizeof(frr_config), "%s%s%s", sysconfdir, pathspace, FRR_CONFIG_NAME); - strlcat(vtydir, pathspace, sizeof(vtydir)); + + if (pathspace_arg) { + strlcat(vtydir, "/", sizeof(vtydir)); + strlcat(vtydir, pathspace_arg, sizeof(vtydir)); + } /* Initialize user input buffer. */ line_read = NULL; diff --git a/zebra/client_main.c b/zebra/client_main.c index 4035e53f7c..1ead7ee1fd 100644 --- a/zebra/client_main.c +++ b/zebra/client_main.c @@ -148,9 +148,8 @@ void zebra_sim(FILE *fp) continue; } - for (i = 0; i < 10; i++) { - if (!zebra_type[i].str) - break; + i = 0; + while (zebra_type[i++].str) { if (strcmp(zebra_type[i].str, str) == 0) { type = zebra_type[i].type; break; diff --git a/zebra/connected.c b/zebra/connected.c index 2198ddf5ea..23f2f666a0 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -495,7 +495,8 @@ void connected_delete_ipv4(struct interface *ifp, int flags, /* Add connected IPv6 route to the interface. */ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, - uint8_t prefixlen, const char *label) + struct in6_addr *broad, uint8_t prefixlen, + const char *label) { struct prefix_ipv6 *p; struct connected *ifc; @@ -518,6 +519,20 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, p->prefixlen = prefixlen; ifc->address = (struct prefix *)p; + if (broad) { + p = prefix_ipv6_new(); + p->family = AF_INET6; + IPV6_ADDR_COPY(&p->prefix, broad); + p->prefixlen = prefixlen; + ifc->destination = (struct prefix *)p; + } else { + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) { + zlog_warn("warning: %s called for interface %s with peer flag set, but no peer address supplied", + __func__, ifp->name); + UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + } + } + /* Label of this address. */ if (label) ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); @@ -536,9 +551,9 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, } void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, - uint8_t prefixlen) + struct in6_addr *broad, uint8_t prefixlen) { - struct prefix p; + struct prefix p, d; struct connected *ifc; memset(&p, 0, sizeof(struct prefix)); @@ -546,7 +561,14 @@ void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, memcpy(&p.u.prefix6, address, sizeof(struct in6_addr)); p.prefixlen = prefixlen; - ifc = connected_check(ifp, &p); + if (broad) { + memset(&d, 0, sizeof(struct prefix)); + d.family = AF_INET6; + IPV6_ADDR_COPY(&d.u.prefix, broad); + d.prefixlen = prefixlen; + ifc = connected_check_ptp(ifp, &p, &d); + } else + ifc = connected_check_ptp(ifp, &p, NULL); connected_delete_helper(ifc, &p); } diff --git a/zebra/connected.h b/zebra/connected.h index 9b69a3f246..2a2b093395 100644 --- a/zebra/connected.h +++ b/zebra/connected.h @@ -42,10 +42,11 @@ extern void connected_up(struct interface *ifp, struct connected *ifc); extern void connected_down(struct interface *ifp, struct connected *ifc); extern void connected_add_ipv6(struct interface *ifp, int flags, - struct in6_addr *address, uint8_t prefixlen, - const char *label); + struct in6_addr *address, struct in6_addr *broad, + uint8_t prefixlen, const char *label); extern void connected_delete_ipv6(struct interface *ifp, - struct in6_addr *address, uint8_t prefixlen); + struct in6_addr *address, + struct in6_addr *broad, uint8_t prefixlen); extern int connected_is_unnumbered(struct interface *); diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index b506315ebf..f5ed945527 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -249,7 +249,7 @@ static int if_getaddrs(void) } #endif - connected_add_ipv6(ifp, flags, &addr->sin6_addr, + connected_add_ipv6(ifp, flags, &addr->sin6_addr, NULL, prefixlen, NULL); } } diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index e9182304dd..6cf98e85f5 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -315,7 +315,7 @@ static int if_get_addr(struct interface *ifp, struct sockaddr *addr, connected_add_ipv4(ifp, flags, &SIN(addr)->sin_addr, prefixlen, (struct in_addr *)dest_pnt, label); else if (af == AF_INET6) - connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr, + connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr, NULL, prefixlen, label); return 0; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 4a37c14b92..e28c189f86 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1006,9 +1006,11 @@ int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h, & (IFA_F_DADFAILED | IFA_F_TENTATIVE))) connected_add_ipv6(ifp, flags, (struct in6_addr *)addr, + (struct in6_addr *)broad, ifa->ifa_prefixlen, label); } else connected_delete_ipv6(ifp, (struct in6_addr *)addr, + (struct in6_addr *)broad, ifa->ifa_prefixlen); } diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 4ac3bed4b4..1a94807317 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -771,10 +771,11 @@ int ifam_read(struct ifa_msghdr *ifam) if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv6(ifp, flags, &addr.sin6.sin6_addr, + NULL, ip6_masklen(mask.sin6.sin6_addr), (isalias ? ifname : NULL)); else - connected_delete_ipv6(ifp, &addr.sin6.sin6_addr, + connected_delete_ipv6(ifp, &addr.sin6.sin6_addr, NULL, ip6_masklen(mask.sin6.sin6_addr)); break; default: diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 15e7d4437a..2e19d6fb75 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -531,6 +531,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, re->tag, rmap_name); if (ret != RMAP_MATCH) { + UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); zebra_del_import_table_entry(rn, re); return 0; } @@ -547,8 +548,10 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, break; } - if (same) + if (same) { + UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED); zebra_del_import_table_entry(rn, same); + } newre = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); newre->type = ZEBRA_ROUTE_TABLE; diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index dc942204a4..310f0952fa 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -66,7 +66,7 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) char buf2[PREFIX_STRLEN]; memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); - family = PREFIX_FAMILY(&rule->filter.src_ip); + family = PREFIX_FAMILY(&rule->rule.filter.src_ip); bytelen = (family == AF_INET ? 4 : 16); req.n.nlmsg_type = cmd; @@ -78,7 +78,7 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) req.frh.action = FR_ACT_TO_TBL; /* rule's pref # */ - addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->priority); + addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); /* interface on which applied */ if (rule->ifp) @@ -87,24 +87,24 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) /* source IP, if specified */ if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { - req.frh.src_len = rule->filter.src_ip.prefixlen; + req.frh.src_len = rule->rule.filter.src_ip.prefixlen; addattr_l(&req.n, sizeof(req), FRA_SRC, - &rule->filter.src_ip.u.prefix, bytelen); + &rule->rule.filter.src_ip.u.prefix, bytelen); } /* destination IP, if specified */ if (IS_RULE_FILTERING_ON_DST_IP(rule)) { - req.frh.dst_len = rule->filter.dst_ip.prefixlen; + req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen; addattr_l(&req.n, sizeof(req), FRA_DST, - &rule->filter.dst_ip.u.prefix, bytelen); + &rule->rule.filter.dst_ip.u.prefix, bytelen); } /* Route table to use to forward, if filter criteria matches. */ - if (rule->action.table < 256) - req.frh.table = rule->action.table; + if (rule->rule.action.table < 256) + req.frh.table = rule->rule.action.table; else { req.frh.table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), FRA_TABLE, - rule->action.table); + rule->rule.action.table); } if (IS_ZEBRA_DEBUG_KERNEL) @@ -112,10 +112,12 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) "Tx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", nl_msg_type_to_str(cmd), nl_family_to_str(family), rule->ifp ? rule->ifp->name : "Unknown", - rule->ifp ? rule->ifp->ifindex : 0, rule->priority, - prefix2str(&rule->filter.src_ip, buf1, sizeof(buf1)), - prefix2str(&rule->filter.dst_ip, buf2, sizeof(buf2)), - rule->action.table); + rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, + prefix2str(&rule->rule.filter.src_ip, buf1, + sizeof(buf1)), + prefix2str(&rule->rule.filter.dst_ip, buf2, + sizeof(buf2)), + rule->rule.action.table); /* Ship off the message. * Note: Currently, netlink_talk() is a blocking call which returns @@ -210,44 +212,46 @@ int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, memset(&rule, 0, sizeof(rule)); if (tb[FRA_PRIORITY]) - rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); + rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); if (tb[FRA_SRC]) { if (frh->family == AF_INET) - memcpy(&rule.filter.src_ip.u.prefix4, + memcpy(&rule.rule.filter.src_ip.u.prefix4, RTA_DATA(tb[FRA_SRC]), 4); else - memcpy(&rule.filter.src_ip.u.prefix6, + memcpy(&rule.rule.filter.src_ip.u.prefix6, RTA_DATA(tb[FRA_SRC]), 16); - rule.filter.src_ip.prefixlen = frh->src_len; - rule.filter.filter_bm |= PBR_FILTER_SRC_IP; + rule.rule.filter.src_ip.prefixlen = frh->src_len; + rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; } if (tb[FRA_DST]) { if (frh->family == AF_INET) - memcpy(&rule.filter.dst_ip.u.prefix4, + memcpy(&rule.rule.filter.dst_ip.u.prefix4, RTA_DATA(tb[FRA_DST]), 4); else - memcpy(&rule.filter.dst_ip.u.prefix6, + memcpy(&rule.rule.filter.dst_ip.u.prefix6, RTA_DATA(tb[FRA_DST]), 16); - rule.filter.dst_ip.prefixlen = frh->dst_len; - rule.filter.filter_bm |= PBR_FILTER_DST_IP; + rule.rule.filter.dst_ip.prefixlen = frh->dst_len; + rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; } if (tb[FRA_TABLE]) - rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); + rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); else - rule.action.table = frh->table; + rule.rule.action.table = frh->table; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(frh->family), rule.ifp->name, - rule.ifp->ifindex, rule.priority, - prefix2str(&rule.filter.src_ip, buf1, sizeof(buf1)), - prefix2str(&rule.filter.dst_ip, buf2, sizeof(buf2)), - rule.action.table); + rule.ifp->ifindex, rule.rule.priority, + prefix2str(&rule.rule.filter.src_ip, buf1, + sizeof(buf1)), + prefix2str(&rule.rule.filter.dst_ip, buf2, + sizeof(buf2)), + rule.rule.action.table); return kernel_pbr_rule_del(&rule); } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 3cc1848ee3..7df03efc10 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1723,83 +1723,6 @@ void kernel_lsp_pass_fail(zebra_lsp_t *lsp, enum southbound_results res) } /* - * String to label conversion, labels separated by '/'. - * - * @param label_str labels separated by / - * @param num_labels number of labels; zero if conversion was unsuccessful - * @param labels preallocated mpls_label_t array of size MPLS_MAX_LABELS; only - * modified if the conversion succeeded - * @return 0 on success - * -1 if the string could not be parsed as integers - * -2 if a label was inside the reserved range (0-15) - * -3 if the number of labels given exceeds MPLS_MAX_LABELS - */ -int mpls_str2label(const char *label_str, uint8_t *num_labels, - mpls_label_t *labels) -{ - char *ostr; // copy of label string (start) - char *lstr; // copy of label string - char *nump; // pointer to next segment - char *endp; // end pointer - int i; // for iterating label_str - int rc; // return code - mpls_label_t pl[MPLS_MAX_LABELS]; // parsed labels - - /* labels to zero until we have a successful parse */ - ostr = lstr = XSTRDUP(MTYPE_TMP, label_str); - *num_labels = 0; - rc = 0; - - for (i = 0; i < MPLS_MAX_LABELS && lstr && !rc; i++) { - nump = strsep(&lstr, "/"); - pl[i] = strtoul(nump, &endp, 10); - - /* format check */ - if (*endp != '\0') - rc = -1; - /* validity check */ - else if (!IS_MPLS_UNRESERVED_LABEL(pl[i])) - rc = -2; - } - - /* excess labels */ - if (!rc && i == MPLS_MAX_LABELS && lstr) - rc = -3; - - if (!rc) { - *num_labels = i; - memcpy(labels, pl, *num_labels * sizeof(mpls_label_t)); - } - - XFREE(MTYPE_TMP, ostr); - - return rc; -} - -/* - * Label to string conversion, labels in string separated by '/'. - */ -char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, - int len, int pretty) -{ - char label_buf[BUFSIZ]; - int i; - - buf[0] = '\0'; - for (i = 0; i < num_labels; i++) { - if (i != 0) - strlcat(buf, "/", len); - if (pretty) - label2str(labels[i], label_buf, sizeof(label_buf)); - else - snprintf(label_buf, sizeof(label_buf), "%u", labels[i]); - strlcat(buf, label_buf, len); - } - - return buf; -} - -/* * Install dynamic LSP entry. */ int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn, diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 2637327a7e..98905a2831 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -43,10 +43,6 @@ ? AF_INET6 \ : AF_INET) -#define MPLS_LABEL_HELPSTR \ - "Specify label(s) for this route\nOne or more " \ - "labels in the range (16-1048575) separated by '/'\n" - /* Typedefs */ typedef struct zebra_ile_t_ zebra_ile_t; @@ -168,18 +164,6 @@ struct zebra_fec_t_ { /* Function declarations. */ /* - * String to label conversion, labels separated by '/'. - */ -int mpls_str2label(const char *label_str, uint8_t *num_labels, - mpls_label_t *labels); - -/* - * Label to string conversion, labels in string separated by '/'. - */ -char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, - int len, int pretty); - -/* * Add/update global label block. */ int zebra_mpls_label_block_add(struct zebra_vrf *zvrf, uint32_t start_label, diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index f9beb32ac4..30f850597c 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -70,7 +70,7 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id; + ns_id_t ns_id, ns_id_external; if (netnspath == NULL) return; @@ -80,8 +80,9 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_id = zebra_ns_id_get(netnspath); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); + ns_id_external = ns_map_nsid_with_external(ns_id, true); /* if VRF with NS ID already present */ - vrf = vrf_lookup_by_id((vrf_id_t)ns_id); + vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external); if (vrf) { zlog_warn( "NS notify : same NSID used by VRF %s. Ignore NS %s creation", @@ -90,15 +91,18 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) } if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { zlog_warn("NS notify : failed to create VRF %s", name); + ns_map_nsid_with_external(ns_id, false); return; } if (zserv_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); - ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id); + ret = vrf_netns_handler_create(NULL, vrf, netnspath, + ns_id_external, ns_id); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (ret != CMD_SUCCESS) { zlog_warn("NS notify : failed to create NS %s", netnspath); + ns_map_nsid_with_external(ns_id, false); return; } zlog_info("NS notify : created VRF %s NS %s", name, netnspath); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 7393f767af..5c62e366a6 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -140,6 +140,20 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) hash_create_size(8, zebra_pbr_rules_hash_key, zebra_pbr_rules_hash_equal, "Rules Hash"); + zns->ipset_hash = + hash_create_size(8, zebra_pbr_ipset_hash_key, + zebra_pbr_ipset_hash_equal, "IPset Hash"); + + zns->ipset_entry_hash = + hash_create_size(8, zebra_pbr_ipset_entry_hash_key, + zebra_pbr_ipset_entry_hash_equal, + "IPset Hash Entry"); + + zns->iptable_hash = + hash_create_size(8, zebra_pbr_iptable_hash_key, + zebra_pbr_iptable_hash_equal, + "IPtable Hash Entry"); + #if defined(HAVE_RTADV) rtadv_init(zns); #endif @@ -248,6 +262,15 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) hash_clean(zns->rules_hash, zebra_pbr_rules_free); hash_free(zns->rules_hash); + hash_clean(zns->ipset_hash, zebra_pbr_ipset_free); + hash_free(zns->ipset_hash); + hash_clean(zns->ipset_entry_hash, + zebra_pbr_ipset_entry_free), + hash_free(zns->ipset_entry_hash); + hash_clean(zns->iptable_hash, + zebra_pbr_iptable_free); + hash_free(zns->iptable_hash); + while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) { znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables); @@ -274,6 +297,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) int zebra_ns_init(void) { ns_id_t ns_id; + ns_id_t ns_id_external; dzns = zebra_ns_alloc(); @@ -282,8 +306,8 @@ int zebra_ns_init(void) ns_id = zebra_ns_id_get_default(); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); - - ns_init_management(ns_id); + ns_id_external = ns_map_nsid_with_external(ns_id, true); + ns_init_management(ns_id_external, ns_id); logicalrouter_init(logicalrouter_config_write); @@ -295,7 +319,7 @@ int zebra_ns_init(void) zebra_vrf_init(); /* Default NS is activated */ - zebra_ns_enable(ns_id, (void **)&dzns); + zebra_ns_enable(ns_id_external, (void **)&dzns); if (vrf_is_backend_netns()) { ns_add_hook(NS_NEW_HOOK, zebra_ns_new); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 66b73148d2..fbf88ae6ea 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -73,6 +73,12 @@ struct zebra_ns { struct hash *rules_hash; + struct hash *ipset_hash; + + struct hash *ipset_entry_hash; + + struct hash *iptable_hash; + /* Back pointer */ struct ns *ns; }; diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 4b93168846..758365d716 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -50,16 +50,22 @@ uint32_t zebra_pbr_rules_hash_key(void *arg) uint32_t key; rule = (struct zebra_pbr_rule *)arg; - key = jhash_3words(rule->seq, rule->priority, rule->action.table, - prefix_hash_key(&rule->filter.src_ip)); + key = jhash_3words(rule->rule.seq, rule->rule.priority, + rule->rule.action.table, + prefix_hash_key(&rule->rule.filter.src_ip)); if (rule->ifp) key = jhash_1word(rule->ifp->ifindex, key); else key = jhash_1word(0, key); - return jhash_3words(rule->filter.src_port, rule->filter.dst_port, - prefix_hash_key(&rule->filter.dst_ip), - jhash_1word(rule->unique, key)); + if (rule->rule.filter.fwmark) + key = jhash_1word(rule->rule.filter.fwmark, key); + else + key = jhash_1word(0, key); + return jhash_3words(rule->rule.filter.src_port, + rule->rule.filter.dst_port, + prefix_hash_key(&rule->rule.filter.dst_ip), + jhash_1word(rule->rule.unique, key)); } int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) @@ -69,28 +75,31 @@ int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) r1 = (const struct zebra_pbr_rule *)arg1; r2 = (const struct zebra_pbr_rule *)arg2; - if (r1->seq != r2->seq) + if (r1->rule.seq != r2->rule.seq) return 0; - if (r1->priority != r2->priority) + if (r1->rule.priority != r2->rule.priority) return 0; - if (r1->unique != r2->unique) + if (r1->rule.unique != r2->rule.unique) return 0; - if (r1->action.table != r2->action.table) + if (r1->rule.action.table != r2->rule.action.table) return 0; - if (r1->filter.src_port != r2->filter.src_port) + if (r1->rule.filter.src_port != r2->rule.filter.src_port) return 0; - if (r1->filter.dst_port != r2->filter.dst_port) + if (r1->rule.filter.dst_port != r2->rule.filter.dst_port) return 0; - if (!prefix_same(&r1->filter.src_ip, &r2->filter.src_ip)) + if (r1->rule.filter.fwmark != r2->rule.filter.fwmark) return 0; - if (!prefix_same(&r1->filter.dst_ip, &r2->filter.dst_ip)) + if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip)) + return 0; + + if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip)) return 0; if (r1->ifp != r2->ifp) @@ -99,17 +108,18 @@ int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) return 1; } -struct pbr_unique_lookup { +struct pbr_rule_unique_lookup { struct zebra_pbr_rule *rule; uint32_t unique; + struct interface *ifp; }; static int pbr_rule_lookup_unique_walker(struct hash_backet *b, void *data) { - struct pbr_unique_lookup *pul = data; + struct pbr_rule_unique_lookup *pul = data; struct zebra_pbr_rule *rule = b->data; - if (pul->unique == rule->unique) { + if (pul->unique == rule->rule.unique && pul->ifp == rule->ifp) { pul->rule = rule; return HASHWALK_ABORT; } @@ -118,17 +128,139 @@ static int pbr_rule_lookup_unique_walker(struct hash_backet *b, void *data) } static struct zebra_pbr_rule *pbr_rule_lookup_unique(struct zebra_ns *zns, - uint32_t unique) + uint32_t unique, + struct interface *ifp) { - struct pbr_unique_lookup pul; + struct pbr_rule_unique_lookup pul; pul.unique = unique; + pul.ifp = ifp; pul.rule = NULL; hash_walk(zns->rules_hash, &pbr_rule_lookup_unique_walker, &pul); return pul.rule; } +void zebra_pbr_ipset_free(void *arg) +{ + struct zebra_pbr_ipset *ipset; + + ipset = (struct zebra_pbr_ipset *)arg; + + XFREE(MTYPE_TMP, ipset); +} + +uint32_t zebra_pbr_ipset_hash_key(void *arg) +{ + struct zebra_pbr_ipset *ipset = (struct zebra_pbr_ipset *)arg; + uint32_t *pnt = (uint32_t *)&ipset->ipset_name; + + return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de); +} + +int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_pbr_ipset *r1, *r2; + + r1 = (const struct zebra_pbr_ipset *)arg1; + r2 = (const struct zebra_pbr_ipset *)arg2; + + if (r1->type != r2->type) + return 0; + if (r1->unique != r2->unique) + return 0; + if (strncmp(r1->ipset_name, r2->ipset_name, + ZEBRA_IPSET_NAME_SIZE)) + return 0; + return 1; +} + +void zebra_pbr_ipset_entry_free(void *arg) +{ + struct zebra_pbr_ipset_entry *ipset; + + ipset = (struct zebra_pbr_ipset_entry *)arg; + + XFREE(MTYPE_TMP, ipset); +} + +uint32_t zebra_pbr_ipset_entry_hash_key(void *arg) +{ + struct zebra_pbr_ipset_entry *ipset; + uint32_t key; + + ipset = (struct zebra_pbr_ipset_entry *)arg; + key = prefix_hash_key(&ipset->src); + key = jhash_1word(ipset->unique, key); + key = jhash_1word(prefix_hash_key(&ipset->dst), key); + + return key; +} + +int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_pbr_ipset_entry *r1, *r2; + + r1 = (const struct zebra_pbr_ipset_entry *)arg1; + r2 = (const struct zebra_pbr_ipset_entry *)arg2; + + if (r1->unique != r2->unique) + return 0; + + if (!prefix_same(&r1->src, &r2->src)) + return 0; + + if (!prefix_same(&r1->dst, &r2->dst)) + return 0; + + return 1; +} + +void zebra_pbr_iptable_free(void *arg) +{ + struct zebra_pbr_iptable *iptable; + + iptable = (struct zebra_pbr_iptable *)arg; + + XFREE(MTYPE_TMP, iptable); +} + +uint32_t zebra_pbr_iptable_hash_key(void *arg) +{ + struct zebra_pbr_iptable *iptable = (struct zebra_pbr_iptable *)arg; + uint32_t *pnt = (uint32_t *)&(iptable->ipset_name); + uint32_t key; + + key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, + 0x63ab42de); + key = jhash_1word(iptable->fwmark, key); + return jhash_3words(iptable->filter_bm, iptable->type, + iptable->unique, key); +} + +int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) +{ + const struct zebra_pbr_iptable *r1, *r2; + + r1 = (const struct zebra_pbr_iptable *)arg1; + r2 = (const struct zebra_pbr_iptable *)arg2; + + if (r1->type != r2->type) + return 0; + if (r1->unique != r2->unique) + return 0; + if (r1->filter_bm != r2->filter_bm) + return 0; + if (r1->fwmark != r2->fwmark) + return 0; + if (r1->action != r2->action) + return 0; + if (strncmp(r1->ipset_name, r2->ipset_name, + ZEBRA_IPSET_NAME_SIZE)) + return 0; + return 1; +} + static void *pbr_rule_alloc_intern(void *arg) { struct zebra_pbr_rule *zpr; @@ -146,7 +278,7 @@ static void *pbr_rule_alloc_intern(void *arg) void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule) { struct zebra_pbr_rule *unique = - pbr_rule_lookup_unique(zns, rule->unique); + pbr_rule_lookup_unique(zns, rule->rule.unique, rule->ifp); (void)hash_get(zns->rules_hash, rule, pbr_rule_alloc_intern); kernel_add_pbr_rule(rule); @@ -194,6 +326,162 @@ void zebra_pbr_client_close_cleanup(int sock) hash_iterate(zns->rules_hash, zebra_pbr_cleanup_rules, &sock); } +static void *pbr_ipset_alloc_intern(void *arg) +{ + struct zebra_pbr_ipset *zpi; + struct zebra_pbr_ipset *new; + + zpi = (struct zebra_pbr_ipset *)arg; + + new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset)); + + memcpy(new, zpi, sizeof(*zpi)); + + return new; +} + +void zebra_pbr_create_ipset(struct zebra_ns *zns, + struct zebra_pbr_ipset *ipset) +{ + (void)hash_get(zns->ipset_hash, ipset, pbr_ipset_alloc_intern); + /* TODO: + * - Netlink call + */ +} + +void zebra_pbr_destroy_ipset(struct zebra_ns *zns, + struct zebra_pbr_ipset *ipset) +{ + struct zebra_pbr_ipset *lookup; + + lookup = hash_lookup(zns->ipset_hash, ipset); + /* TODO: + * - Netlink destroy from kernel + * - ?? destroy ipset entries before + */ + if (lookup) + XFREE(MTYPE_TMP, lookup); + else + zlog_warn("%s: IPSet Entry being deleted we know nothing about", + __PRETTY_FUNCTION__); +} + +struct pbr_ipset_name_lookup { + struct zebra_pbr_ipset *ipset; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; +}; + +static int zebra_pbr_ipset_pername_walkcb(struct hash_backet *backet, void *arg) +{ + struct pbr_ipset_name_lookup *pinl = + (struct pbr_ipset_name_lookup *)arg; + struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)backet->data; + + if (!strncmp(pinl->ipset_name, zpi->ipset_name, + ZEBRA_IPSET_NAME_SIZE)) { + pinl->ipset = zpi; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(struct zebra_ns *zns, + char *ipsetname) +{ + struct pbr_ipset_name_lookup pinl; + struct pbr_ipset_name_lookup *ptr = &pinl; + + if (!ipsetname) + return NULL; + memset(ptr, 0, sizeof(struct pbr_ipset_name_lookup)); + snprintf((char *)ptr->ipset_name, ZEBRA_IPSET_NAME_SIZE, "%s", + ipsetname); + hash_walk(zns->ipset_hash, zebra_pbr_ipset_pername_walkcb, ptr); + return ptr->ipset; +} + +static void *pbr_ipset_entry_alloc_intern(void *arg) +{ + struct zebra_pbr_ipset_entry *zpi; + struct zebra_pbr_ipset_entry *new; + + zpi = (struct zebra_pbr_ipset_entry *)arg; + + new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset_entry)); + + memcpy(new, zpi, sizeof(*zpi)); + + return new; +} + +void zebra_pbr_add_ipset_entry(struct zebra_ns *zns, + struct zebra_pbr_ipset_entry *ipset) +{ + (void)hash_get(zns->ipset_entry_hash, ipset, + pbr_ipset_entry_alloc_intern); + /* TODO: + * - attach to ipset list + * - Netlink add to kernel + */ +} + +void zebra_pbr_del_ipset_entry(struct zebra_ns *zns, + struct zebra_pbr_ipset_entry *ipset) +{ + struct zebra_pbr_ipset_entry *lookup; + + lookup = hash_lookup(zns->ipset_entry_hash, ipset); + /* TODO: + * - Netlink destroy + * - detach from ipset list + * - ?? if no more entres, delete ipset + */ + if (lookup) + XFREE(MTYPE_TMP, lookup); + else + zlog_warn("%s: IPSet being deleted we know nothing about", + __PRETTY_FUNCTION__); +} + +static void *pbr_iptable_alloc_intern(void *arg) +{ + struct zebra_pbr_iptable *zpi; + struct zebra_pbr_iptable *new; + + zpi = (struct zebra_pbr_iptable *)arg; + + new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable)); + + memcpy(new, zpi, sizeof(*zpi)); + + return new; +} + +void zebra_pbr_add_iptable(struct zebra_ns *zns, + struct zebra_pbr_iptable *iptable) +{ + (void)hash_get(zns->iptable_hash, iptable, + pbr_iptable_alloc_intern); + /* TODO call netlink layer */ +} + +void zebra_pbr_del_iptable(struct zebra_ns *zns, + struct zebra_pbr_iptable *iptable) +{ + struct zebra_pbr_ipset_entry *lookup; + + lookup = hash_lookup(zns->iptable_hash, iptable); + /* TODO: + * - call netlink layer + * - detach from iptable list + */ + if (lookup) + XFREE(MTYPE_TMP, lookup); + else + zlog_warn("%s: IPTable being deleted we know nothing about", + __PRETTY_FUNCTION__); +} + /* * Handle success or failure of rule (un)install in the kernel. */ @@ -208,8 +496,73 @@ void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL); break; case SOUTHBOUND_DELETE_SUCCESS: + zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED); + break; + case SOUTHBOUND_DELETE_FAILURE: + zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED); + break; + } +} + +/* + * Handle success or failure of ipset (un)install in the kernel. + */ +void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset, + enum southbound_results res) +{ + switch (res) { + case SOUTHBOUND_INSTALL_SUCCESS: + zsend_ipset_notify_owner(ipset, ZAPI_IPSET_INSTALLED); + break; + case SOUTHBOUND_INSTALL_FAILURE: + zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_INSTALL); + break; + case SOUTHBOUND_DELETE_SUCCESS: + case SOUTHBOUND_DELETE_FAILURE: + /* TODO : handling of delete event */ + break; + } +} + +/* + * Handle success or failure of ipset (un)install in the kernel. + */ +void kernel_pbr_ipset_entry_add_del_status( + struct zebra_pbr_ipset_entry *ipset, + enum southbound_results res) +{ + switch (res) { + case SOUTHBOUND_INSTALL_SUCCESS: + zsend_ipset_entry_notify_owner(ipset, + ZAPI_IPSET_ENTRY_INSTALLED); + break; + case SOUTHBOUND_INSTALL_FAILURE: + zsend_ipset_entry_notify_owner(ipset, + ZAPI_IPSET_ENTRY_FAIL_INSTALL); + break; + case SOUTHBOUND_DELETE_SUCCESS: + case SOUTHBOUND_DELETE_FAILURE: + /* TODO : handling of delete event */ break; + } +} + +/* + * Handle success or failure of ipset (un)install in the kernel. + */ +void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, + enum southbound_results res) +{ + switch (res) { + case SOUTHBOUND_INSTALL_SUCCESS: + zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_INSTALLED); + break; + case SOUTHBOUND_INSTALL_FAILURE: + zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_FAIL_INSTALL); + break; + case SOUTHBOUND_DELETE_SUCCESS: case SOUTHBOUND_DELETE_FAILURE: + /* TODO : handling of delete event */ break; } } diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index f910d8e742..0ac629cc65 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -30,77 +30,114 @@ #include "if.h" #include "rt.h" +#include "pbr.h" -/* - * A PBR filter - * - * The filter or match criteria in a PBR rule. - * For simplicity, all supported filters are grouped into a structure rather - * than delineating further. A bitmask denotes which filters are actually - * specified. - */ -struct zebra_pbr_filter { - uint32_t filter_bm; -#define PBR_FILTER_SRC_IP (1 << 0) -#define PBR_FILTER_DST_IP (1 << 1) -#define PBR_FILTER_SRC_PORT (1 << 2) -#define PBR_FILTER_DST_PORT (1 << 3) - - /* Source and Destination IP address with masks. */ - struct prefix src_ip; - struct prefix dst_ip; - - /* Source and Destination higher-layer (TCP/UDP) port numbers. */ - uint16_t src_port; - uint16_t dst_port; +struct zebra_pbr_rule { + int sock; + + struct pbr_rule rule; + + struct interface *ifp; }; #define IS_RULE_FILTERING_ON_SRC_IP(r) \ - (r->filter.filter_bm & PBR_FILTER_SRC_IP) + (r->rule.filter.filter_bm & PBR_FILTER_SRC_IP) #define IS_RULE_FILTERING_ON_DST_IP(r) \ - (r->filter.filter_bm & PBR_FILTER_DST_IP) + (r->rule.filter.filter_bm & PBR_FILTER_DST_IP) #define IS_RULE_FILTERING_ON_SRC_PORT(r) \ - (r->filter.filter_bm & PBR_FILTER_SRC_PORT) + (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT) #define IS_RULE_FILTERING_ON_DST_PORT(r) \ - (r->filter.filter_bm & PBR_FILTER_DST_PORT) + (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT) /* - * A PBR action + * An IPSet Entry Filter * - * The action corresponding to a PBR rule. - * While the user specifies the action in a particular way, the forwarding - * plane implementation (Linux only) requires that to be encoded into a - * route table and the rule then point to that route table; in some cases, - * the user criteria may directly point to a table too. + * This is a filter mapped on ipset entries */ -struct zebra_pbr_action { - uint32_t table; +struct zebra_pbr_ipset { + /* + * Originating zclient sock fd, so we can know who to send + * back to. + */ + int sock; + + uint32_t unique; + + /* type is encoded as uint32_t + * but value is an enum ipset_type + */ + uint32_t type; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; /* - * A PBR rule + * An IPSet Entry Filter * - * This is a combination of the filter criteria and corresponding action. - * Rules also have a user-defined sequence number which defines the relative - * order amongst rules. + * This is a filter mapped on ipset entries */ -struct zebra_pbr_rule { +struct zebra_pbr_ipset_entry { /* * Originating zclient sock fd, so we can know who to send * back to. */ int sock; - uint32_t seq; - uint32_t priority; - struct interface *ifp; uint32_t unique; - struct zebra_pbr_filter filter; - struct zebra_pbr_action action; + + struct prefix src; + struct prefix dst; + + uint32_t filter_bm; + + struct zebra_pbr_ipset *backpointer; +}; + +/* + * An IPTables Action + * + * This is a filter mapped on ipset entries + */ +struct zebra_pbr_iptable { + /* + * Originating zclient sock fd, so we can know who to send + * back to. + */ + int sock; + + uint32_t unique; + + /* include ipset type + */ + uint32_t type; + + /* include which IP is to be filtered + */ + uint32_t filter_bm; + + uint32_t fwmark; + + uint32_t action; + + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule); void zebra_pbr_del_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule); +void zebra_pbr_create_ipset(struct zebra_ns *zns, + struct zebra_pbr_ipset *ipset); +void zebra_pbr_destroy_ipset(struct zebra_ns *zns, + struct zebra_pbr_ipset *ipset); +struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(struct zebra_ns *zns, + char *ipsetname); +void zebra_pbr_add_ipset_entry(struct zebra_ns *zns, + struct zebra_pbr_ipset_entry *ipset); +void zebra_pbr_del_ipset_entry(struct zebra_ns *zns, + struct zebra_pbr_ipset_entry *ipset); + +void zebra_pbr_add_iptable(struct zebra_ns *zns, + struct zebra_pbr_iptable *iptable); +void zebra_pbr_del_iptable(struct zebra_ns *zns, + struct zebra_pbr_iptable *iptable); /* * Install specified rule for a specific interface. @@ -128,6 +165,19 @@ extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, enum southbound_results res); /* + * Handle success or failure of ipset kinds (un)install in the kernel. + */ +extern void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset, + enum southbound_results res); + +extern void kernel_pbr_ipset_entry_add_del_status( + struct zebra_pbr_ipset_entry *ipset, + enum southbound_results res); + +extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, + enum southbound_results res); + +/* * Handle rule delete notification from kernel. */ extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule); @@ -138,4 +188,21 @@ extern void zebra_pbr_rules_free(void *arg); extern uint32_t zebra_pbr_rules_hash_key(void *arg); extern int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); +/* has operates on 32bit pointer + * and field is a string of 8bit + */ +#define ZEBRA_IPSET_NAME_HASH_SIZE (ZEBRA_IPSET_NAME_SIZE / 4) + +extern void zebra_pbr_ipset_free(void *arg); +extern uint32_t zebra_pbr_ipset_hash_key(void *arg); +extern int zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2); + +extern void zebra_pbr_ipset_entry_free(void *arg); +extern uint32_t zebra_pbr_ipset_entry_hash_key(void *arg); +extern int zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2); + +extern void zebra_pbr_iptable_free(void *arg); +extern uint32_t zebra_pbr_iptable_hash_key(void *arg); +extern int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2); + #endif /* _ZEBRA_PBR_H */ diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 879da092f0..22a04ee23d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -561,8 +561,10 @@ static void zebra_rnh_process_pbr_tables(int family, * just rethink it. Yes this is a hammer, but * a small one */ - if (o_re) + if (o_re) { + SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED); rib_queue_add(o_rn); + } } } } diff --git a/zebra/zserv.c b/zebra/zserv.c index c06efbfb4b..94c20c1d13 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -725,10 +725,9 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, struct zserv *client; struct stream *s; - if (IS_ZEBRA_DEBUG_PACKET) { + if (IS_ZEBRA_DEBUG_PACKET) zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, - rule->unique); - } + rule->rule.unique); for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) { if (rule->sock == client->sock) @@ -739,13 +738,12 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, return; s = stream_new(ZEBRA_MAX_PACKET_SIZ); - stream_reset(s); zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT); stream_put(s, ¬e, sizeof(note)); - stream_putl(s, rule->seq); - stream_putl(s, rule->priority); - stream_putl(s, rule->unique); + stream_putl(s, rule->rule.seq); + stream_putl(s, rule->rule.priority); + stream_putl(s, rule->rule.unique); if (rule->ifp) stream_putl(s, rule->ifp->ifindex); else @@ -756,6 +754,98 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, zebra_server_send_message(client, s); } +void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset, + enum zapi_ipset_notify_owner note) +{ + struct listnode *node; + struct zserv *client; + struct stream *s; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, + ipset->unique); + + for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) { + if (ipset->sock == client->sock) + break; + } + + if (!client) + return; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_IPSET_NOTIFY_OWNER, VRF_DEFAULT); + stream_put(s, ¬e, sizeof(note)); + stream_putl(s, ipset->unique); + stream_put(s, ipset->ipset_name, ZEBRA_IPSET_NAME_SIZE); + stream_putw_at(s, 0, stream_get_endp(s)); + + zebra_server_send_message(client, s); +} + +void zsend_ipset_entry_notify_owner( + struct zebra_pbr_ipset_entry *ipset, + enum zapi_ipset_entry_notify_owner note) +{ + struct listnode *node; + struct zserv *client; + struct stream *s; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, + ipset->unique); + + for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) { + if (ipset->sock == client->sock) + break; + } + + if (!client) + return; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_IPSET_ENTRY_NOTIFY_OWNER, + VRF_DEFAULT); + stream_put(s, ¬e, sizeof(note)); + stream_putl(s, ipset->unique); + stream_put(s, ipset->backpointer->ipset_name, + ZEBRA_IPSET_NAME_SIZE); + stream_putw_at(s, 0, stream_get_endp(s)); + + zebra_server_send_message(client, s); +} + +void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable, + enum zapi_iptable_notify_owner note) +{ + struct listnode *node; + struct zserv *client; + struct stream *s; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, + iptable->unique); + + for (ALL_LIST_ELEMENTS_RO(zebrad.client_list, node, client)) { + if (iptable->sock == client->sock) + break; + } + + if (!client) + return; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_IPTABLE_NOTIFY_OWNER, VRF_DEFAULT); + stream_put(s, ¬e, sizeof(note)); + stream_putl(s, iptable->unique); + stream_putw_at(s, 0, stream_get_endp(s)); + + zebra_server_send_message(client, s); +} + /* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ int zsend_router_id_update(struct zserv *client, struct prefix *p, vrf_id_t vrf_id) @@ -2667,39 +2757,47 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) memset(&zpr, 0, sizeof(zpr)); zpr.sock = client->sock; - STREAM_GETL(s, zpr.seq); - STREAM_GETL(s, zpr.priority); - STREAM_GETL(s, zpr.unique); - STREAM_GETC(s, zpr.filter.src_ip.family); - STREAM_GETC(s, zpr.filter.src_ip.prefixlen); - STREAM_GET(&zpr.filter.src_ip.u.prefix, s, - prefix_blen(&zpr.filter.src_ip)); - STREAM_GETW(s, zpr.filter.src_port); - STREAM_GETC(s, zpr.filter.dst_ip.family); - STREAM_GETC(s, zpr.filter.dst_ip.prefixlen); - STREAM_GET(&zpr.filter.dst_ip.u.prefix, s, - prefix_blen(&zpr.filter.dst_ip)); - STREAM_GETW(s, zpr.filter.dst_port); - STREAM_GETL(s, zpr.action.table); + zpr.rule.vrf_id = hdr->vrf_id; + STREAM_GETL(s, zpr.rule.seq); + STREAM_GETL(s, zpr.rule.priority); + STREAM_GETL(s, zpr.rule.unique); + STREAM_GETC(s, zpr.rule.filter.src_ip.family); + STREAM_GETC(s, zpr.rule.filter.src_ip.prefixlen); + STREAM_GET(&zpr.rule.filter.src_ip.u.prefix, s, + prefix_blen(&zpr.rule.filter.src_ip)); + STREAM_GETW(s, zpr.rule.filter.src_port); + STREAM_GETC(s, zpr.rule.filter.dst_ip.family); + STREAM_GETC(s, zpr.rule.filter.dst_ip.prefixlen); + STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s, + prefix_blen(&zpr.rule.filter.dst_ip)); + STREAM_GETW(s, zpr.rule.filter.dst_port); + STREAM_GETL(s, zpr.rule.filter.fwmark); + STREAM_GETL(s, zpr.rule.action.table); STREAM_GETL(s, ifindex); - zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN); - if (!zpr.ifp) { - zlog_debug("FAiled to lookup ifindex: %u", ifindex); - return; + if (ifindex) { + zpr.ifp = if_lookup_by_index(ifindex, VRF_UNKNOWN); + if (!zpr.ifp) { + zlog_debug("Failed to lookup ifindex: %u", + ifindex); + return; + } } - if (!is_default_prefix(&zpr.filter.src_ip)) - zpr.filter.filter_bm |= PBR_FILTER_SRC_IP; + if (!is_default_prefix(&zpr.rule.filter.src_ip)) + zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; + + if (!is_default_prefix(&zpr.rule.filter.dst_ip)) + zpr.rule.filter.filter_bm |= PBR_FILTER_DST_IP; - if (!is_default_prefix(&zpr.filter.dst_ip)) - zpr.filter.filter_bm |= PBR_FILTER_DST_IP; + if (zpr.rule.filter.src_port) + zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_PORT; - if (zpr.filter.src_port) - zpr.filter.filter_bm |= PBR_FILTER_SRC_PORT; + if (zpr.rule.filter.dst_port) + zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT; - if (zpr.filter.dst_port) - zpr.filter.filter_bm |= PBR_FILTER_DST_PORT; + if (zpr.rule.filter.fwmark) + zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK; if (hdr->command == ZEBRA_RULE_ADD) zebra_pbr_add_rule(zvrf->zns, &zpr); @@ -2711,6 +2809,107 @@ stream_failure: return; } + +static inline void zread_ipset(ZAPI_HANDLER_ARGS) +{ + struct zebra_pbr_ipset zpi; + struct stream *s; + uint32_t total, i; + + s = msg; + STREAM_GETL(s, total); + + for (i = 0; i < total; i++) { + memset(&zpi, 0, sizeof(zpi)); + + zpi.sock = client->sock; + STREAM_GETL(s, zpi.unique); + STREAM_GETL(s, zpi.type); + STREAM_GET(&zpi.ipset_name, s, + ZEBRA_IPSET_NAME_SIZE); + + if (hdr->command == ZEBRA_IPSET_CREATE) + zebra_pbr_create_ipset(zvrf->zns, &zpi); + else + zebra_pbr_destroy_ipset(zvrf->zns, &zpi); + } + +stream_failure: + return; +} + +static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) +{ + struct zebra_pbr_ipset_entry zpi; + struct zebra_pbr_ipset ipset; + struct stream *s; + uint32_t total, i; + + s = msg; + STREAM_GETL(s, total); + + for (i = 0; i < total; i++) { + memset(&zpi, 0, sizeof(zpi)); + memset(&ipset, 0, sizeof(ipset)); + + zpi.sock = client->sock; + STREAM_GETL(s, zpi.unique); + STREAM_GET(&ipset.ipset_name, s, + ZEBRA_IPSET_NAME_SIZE); + STREAM_GETC(s, zpi.src.family); + STREAM_GETC(s, zpi.src.prefixlen); + STREAM_GET(&zpi.src.u.prefix, s, + prefix_blen(&zpi.src)); + STREAM_GETC(s, zpi.dst.family); + STREAM_GETC(s, zpi.dst.prefixlen); + STREAM_GET(&zpi.dst.u.prefix, s, + prefix_blen(&zpi.dst)); + + if (!is_default_prefix(&zpi.src)) + zpi.filter_bm |= PBR_FILTER_SRC_IP; + + if (!is_default_prefix(&zpi.dst)) + zpi.filter_bm |= PBR_FILTER_DST_IP; + + /* calculate backpointer */ + zpi.backpointer = zebra_pbr_lookup_ipset_pername(zvrf->zns, + ipset.ipset_name); + if (hdr->command == ZEBRA_IPSET_ENTRY_ADD) + zebra_pbr_add_ipset_entry(zvrf->zns, &zpi); + else + zebra_pbr_del_ipset_entry(zvrf->zns, &zpi); + } + +stream_failure: + return; +} + +static inline void zread_iptable(ZAPI_HANDLER_ARGS) +{ + struct zebra_pbr_iptable zpi; + struct stream *s; + + s = msg; + + memset(&zpi, 0, sizeof(zpi)); + + zpi.sock = client->sock; + STREAM_GETL(s, zpi.unique); + STREAM_GETL(s, zpi.type); + STREAM_GETL(s, zpi.filter_bm); + STREAM_GETL(s, zpi.action); + STREAM_GETL(s, zpi.fwmark); + STREAM_GET(&zpi.ipset_name, s, + ZEBRA_IPSET_NAME_SIZE); + + if (hdr->command == ZEBRA_IPTABLE_ADD) + zebra_pbr_add_iptable(zvrf->zns, &zpi); + else + zebra_pbr_del_iptable(zvrf->zns, &zpi); +stream_failure: + return; +} + void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add, [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, @@ -2771,6 +2970,12 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_TABLE_MANAGER_CONNECT] = zread_table_manager_request, [ZEBRA_GET_TABLE_CHUNK] = zread_table_manager_request, [ZEBRA_RELEASE_TABLE_CHUNK] = zread_table_manager_request, + [ZEBRA_IPSET_CREATE] = zread_ipset, + [ZEBRA_IPSET_DESTROY] = zread_ipset, + [ZEBRA_IPSET_ENTRY_ADD] = zread_ipset_entry, + [ZEBRA_IPSET_ENTRY_DELETE] = zread_ipset_entry, + [ZEBRA_IPTABLE_ADD] = zread_iptable, + [ZEBRA_IPTABLE_DELETE] = zread_iptable, }; static inline void zserv_handle_commands(struct zserv *client, diff --git a/zebra/zserv.h b/zebra/zserv.h index 947e11e35b..503d85f5b6 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -28,6 +28,7 @@ #include "routemap.h" #include "vty.h" #include "zclient.h" +#include "pbr.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_pw.h" @@ -187,9 +188,21 @@ extern int zsend_pw_update(struct zserv *, struct zebra_pw *); extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, enum zapi_route_notify_owner note); +struct zebra_pbr_ipset; +struct zebra_pbr_ipset_entry; +struct zebra_pbr_iptable; struct zebra_pbr_rule; extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, enum zapi_rule_notify_owner note); +extern void zsend_ipset_notify_owner( + struct zebra_pbr_ipset *ipset, + enum zapi_ipset_notify_owner note); +extern void zsend_ipset_entry_notify_owner( + struct zebra_pbr_ipset_entry *ipset, + enum zapi_ipset_entry_notify_owner note); +extern void zsend_iptable_notify_owner( + struct zebra_pbr_iptable *iptable, + enum zapi_iptable_notify_owner note); extern void zserv_nexthop_num_warn(const char *, const struct prefix *, const unsigned int); |
