From: Russ White Date: Thu, 6 Feb 2020 13:04:42 +0000 (-0500) Subject: Merge pull request #5722 from donaldsharp/kernel_routes X-Git-Tag: frr-7.5.1~829 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=5bf7fe566d74c1093e0c032436a657b4f44ad5f6;hp=abd2a1ff3f5be9140183e5156aa62a97cbd0a803;p=mirror_frr.git Merge pull request #5722 from donaldsharp/kernel_routes Kernel routes --- diff --git a/babeld/babeld.c b/babeld/babeld.c index 6ad004a4a..71bab2fcb 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -165,7 +165,6 @@ babel_create_routing_process (void) return 0; fail: XFREE(MTYPE_BABEL, babel_routing_process); - babel_routing_process = NULL; return -1; } @@ -324,7 +323,6 @@ babel_clean_routing_process(void) distribute_list_delete(&babel_routing_process->distribute_ctx); XFREE(MTYPE_BABEL, babel_routing_process); - babel_routing_process = NULL; } /* Function used with timeout. */ diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 97702aab5..2ae74d788 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -427,27 +427,20 @@ void pl_free(struct peer_label *pl); /* - * log.c - * - * Contains code that does the logging procedures. Might implement multiple - * backends (e.g. zebra log, syslog or other logging lib). + * logging - alias to zebra log */ -enum blog_level { - /* level vs syslog equivalent */ - BLOG_DEBUG = 0, /* LOG_DEBUG */ - BLOG_INFO = 1, /* LOG_INFO */ - BLOG_WARNING = 2, /* LOG_WARNING */ - BLOG_ERROR = 3, /* LOG_ERR */ - BLOG_FATAL = 4, /* LOG_CRIT */ -}; -void log_init(int foreground, enum blog_level level, - struct frr_daemon_info *fdi); -void log_info(const char *fmt, ...); -void log_debug(const char *fmt, ...); -void log_warning(const char *fmt, ...); -void log_error(const char *fmt, ...); -void log_fatal(const char *fmt, ...); +#define log_debug zlog_debug +#define log_info zlog_info +#define log_warning zlog_warn +#define log_error zlog_err + +#define log_fatal(msg, ...) \ + do { \ + zlog_err(msg, ## __VA_ARGS__); \ + assert(!msg); \ + abort(); \ + } while (0) /* diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 6da5e2cdf..1ec761e3b 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -116,7 +116,7 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, return -1; } if (rv < (ssize_t)datalen) - log_debug("packet-send: send partial", strerror(errno)); + log_debug("packet-send: send partial: %s", strerror(errno)); return 0; } @@ -799,7 +799,7 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, log_debug("udp-send: loopback failure: (%d) %s", errno, strerror(errno)); return -1; } else if (wlen < (ssize_t)datalen) { - log_debug("udp-send: partial send: %ld expected %ld", wlen, + log_debug("udp-send: partial send: %zd expected %zu", wlen, datalen); return -1; } diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 341edbe5c..69f268ab0 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -216,15 +216,12 @@ int main(int argc, char *argv[]) parse_config(conf); #endif - /* Initialize logging API. */ - log_init(1, BLOG_DEBUG, &bfdd_di); + /* Initialize FRR infrastructure. */ + master = frr_init(); /* Initialize control socket. */ control_init(ctl_path); - /* Initialize FRR infrastructure. */ - master = frr_init(); - /* Initialize BFD data structures. */ bfd_initialize(); diff --git a/bfdd/config.c b/bfdd/config.c index 0c0bac0aa..dd4a19269 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -215,7 +215,8 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) if (strlcpy(bpc->bpc_localif, sval, sizeof(bpc->bpc_localif)) > sizeof(bpc->bpc_localif)) { - log_debug("\tlocal-interface: %s (truncated)"); + log_debug("\tlocal-interface: %s (truncated)", + sval); error++; } else { log_debug("\tlocal-interface: %s", sval); @@ -235,7 +236,7 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) bpc->bpc_detectmultiplier = json_object_get_int64(jo_val); bpc->bpc_has_detectmultiplier = true; - log_debug("\tdetect-multiplier: %llu", + log_debug("\tdetect-multiplier: %u", bpc->bpc_detectmultiplier); } else if (strcmp(key, "receive-interval") == 0) { bpc->bpc_recvinterval = json_object_get_int64(jo_val); diff --git a/bfdd/control.c b/bfdd/control.c index 5c5421c04..ae6f5a3e7 100644 --- a/bfdd/control.c +++ b/bfdd/control.c @@ -407,7 +407,6 @@ static void control_reset_buf(struct bfd_control_buffer *bcb) { /* Get ride of old data. */ XFREE(MTYPE_BFDD_NOTIFICATION, bcb->bcb_buf); - bcb->bcb_buf = NULL; bcb->bcb_pos = 0; bcb->bcb_left = 0; } @@ -471,7 +470,7 @@ static int control_read(struct thread *t) bcb->bcb_buf = XMALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(bcm) + bcb->bcb_left + 1); if (bcb->bcb_buf == NULL) { - log_warning("%s: not enough memory for message size: %u", + log_warning("%s: not enough memory for message size: %zu", __func__, bcb->bcb_left); control_free(bcs); return 0; diff --git a/bfdd/log.c b/bfdd/log.c deleted file mode 100644 index d81d7cd9b..000000000 --- a/bfdd/log.c +++ /dev/null @@ -1,125 +0,0 @@ -/********************************************************************* - * Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF") - * - * 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 - * - * log.c: implements an abstraction between loggers interface. Implement all - * log backends in this file. - * - * Authors - * ------- - * Rafael Zalamena - */ - -#include - -#include "bfd.h" - -#include "lib/log_int.h" - -void log_msg(int level, const char *fmt, va_list vl); - - -static int log_fg; -static int log_level = BLOG_DEBUG; - -void log_init(int foreground, enum blog_level level, - struct frr_daemon_info *fdi) -{ - log_fg = foreground; - log_level = level; - - openzlog(fdi->progname, fdi->logname, 0, - LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); -} - -void log_msg(int level, const char *fmt, va_list vl) -{ - if (level < log_level) - return; - - switch (level) { - case BLOG_DEBUG: - vzlog(LOG_DEBUG, fmt, vl); - break; - - case BLOG_INFO: - vzlog(LOG_INFO, fmt, vl); - break; - - case BLOG_WARNING: - vzlog(LOG_WARNING, fmt, vl); - break; - - case BLOG_ERROR: - vzlog(LOG_ERR, fmt, vl); - break; - - case BLOG_FATAL: - vzlog(LOG_EMERG, fmt, vl); - break; - - default: - vfprintf(stderr, fmt, vl); - break; - } -} - -void log_info(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_INFO, fmt, vl); - va_end(vl); -} - -void log_debug(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_DEBUG, fmt, vl); - va_end(vl); -} - -void log_error(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_ERROR, fmt, vl); - va_end(vl); -} - -void log_warning(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_WARNING, fmt, vl); - va_end(vl); -} - -void log_fatal(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_FATAL, fmt, vl); - va_end(vl); - - exit(1); -} diff --git a/bfdd/subdir.am b/bfdd/subdir.am index 9aa522f3f..254329e22 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -22,7 +22,6 @@ bfdd_libbfd_a_SOURCES = \ bfdd/config.c \ bfdd/control.c \ bfdd/event.c \ - bfdd/log.c \ bfdd/ptm_adapter.c \ # end diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 76a65f7f0..8d1c83cf5 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -256,6 +256,5 @@ void bgp_sync_delete(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) { XFREE(MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]); - peer->sync[afi][safi] = NULL; } } diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 16de59b72..c0d88fc52 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -32,6 +32,7 @@ #include "table.h" #include "filter.h" #include "command.h" +#include "srv6.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -201,6 +202,8 @@ static struct hash *encap_hash = NULL; #if ENABLE_BGP_VNC static struct hash *vnc_hash = NULL; #endif +static struct hash *srv6_l3vpn_hash; +static struct hash *srv6_vpn_hash; struct bgp_attr_encap_subtlv *encap_tlv_dup(struct bgp_attr_encap_subtlv *orig) { @@ -434,6 +437,158 @@ static void transit_unintern(struct transit **transit) } } +static void *srv6_l3vpn_hash_alloc(void *p) +{ + return p; +} + +static void srv6_l3vpn_free(struct bgp_attr_srv6_l3vpn *l3vpn) +{ + XFREE(MTYPE_BGP_SRV6_L3VPN, l3vpn); +} + +static struct bgp_attr_srv6_l3vpn * +srv6_l3vpn_intern(struct bgp_attr_srv6_l3vpn *l3vpn) +{ + struct bgp_attr_srv6_l3vpn *find; + + find = hash_get(srv6_l3vpn_hash, l3vpn, srv6_l3vpn_hash_alloc); + if (find != l3vpn) + srv6_l3vpn_free(l3vpn); + find->refcnt++; + return find; +} + +static void srv6_l3vpn_unintern(struct bgp_attr_srv6_l3vpn **l3vpnp) +{ + struct bgp_attr_srv6_l3vpn *l3vpn = *l3vpnp; + + if (l3vpn->refcnt) + l3vpn->refcnt--; + + if (l3vpn->refcnt == 0) { + hash_release(srv6_l3vpn_hash, l3vpn); + srv6_l3vpn_free(l3vpn); + *l3vpnp = NULL; + } +} + +static void *srv6_vpn_hash_alloc(void *p) +{ + return p; +} + +static void srv6_vpn_free(struct bgp_attr_srv6_vpn *vpn) +{ + XFREE(MTYPE_BGP_SRV6_VPN, vpn); +} + +static struct bgp_attr_srv6_vpn *srv6_vpn_intern(struct bgp_attr_srv6_vpn *vpn) +{ + struct bgp_attr_srv6_vpn *find; + + find = hash_get(srv6_vpn_hash, vpn, srv6_vpn_hash_alloc); + if (find != vpn) + srv6_vpn_free(vpn); + find->refcnt++; + return find; +} + +static void srv6_vpn_unintern(struct bgp_attr_srv6_vpn **vpnp) +{ + struct bgp_attr_srv6_vpn *vpn = *vpnp; + + if (vpn->refcnt) + vpn->refcnt--; + + if (vpn->refcnt == 0) { + hash_release(srv6_vpn_hash, vpn); + srv6_vpn_free(vpn); + *vpnp = NULL; + } +} + +static uint32_t srv6_l3vpn_hash_key_make(const void *p) +{ + const struct bgp_attr_srv6_l3vpn *l3vpn = p; + uint32_t key = 0; + + key = jhash(&l3vpn->sid, 16, key); + key = jhash_1word(l3vpn->sid_flags, key); + key = jhash_1word(l3vpn->endpoint_behavior, key); + return key; +} + +static bool srv6_l3vpn_hash_cmp(const void *p1, const void *p2) +{ + const struct bgp_attr_srv6_l3vpn *l3vpn1 = p1; + const struct bgp_attr_srv6_l3vpn *l3vpn2 = p2; + + return sid_same(&l3vpn1->sid, &l3vpn2->sid) + && l3vpn1->sid_flags == l3vpn2->sid_flags + && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior; +} + +static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1, + const struct bgp_attr_srv6_l3vpn *h2) +{ + if (h1 == h2) + return true; + else if (h1 == NULL || h2 == NULL) + return false; + else + return srv6_l3vpn_hash_cmp((const void *)h1, (const void *)h2); +} + +static unsigned int srv6_vpn_hash_key_make(const void *p) +{ + const struct bgp_attr_srv6_vpn *vpn = p; + uint32_t key = 0; + + key = jhash(&vpn->sid, 16, key); + key = jhash_1word(vpn->sid_flags, key); + return key; +} + +static bool srv6_vpn_hash_cmp(const void *p1, const void *p2) +{ + const struct bgp_attr_srv6_vpn *vpn1 = p1; + const struct bgp_attr_srv6_vpn *vpn2 = p2; + + return sid_same(&vpn1->sid, &vpn2->sid) + && vpn1->sid_flags == vpn2->sid_flags; +} + +static bool srv6_vpn_same(const struct bgp_attr_srv6_vpn *h1, + const struct bgp_attr_srv6_vpn *h2) +{ + if (h1 == h2) + return true; + else if (h1 == NULL || h2 == NULL) + return false; + else + return srv6_vpn_hash_cmp((const void *)h1, (const void *)h2); +} + +static void srv6_init(void) +{ + srv6_l3vpn_hash = + hash_create(srv6_l3vpn_hash_key_make, srv6_l3vpn_hash_cmp, + "BGP Prefix-SID SRv6-L3VPN-Service-TLV"); + srv6_vpn_hash = hash_create(srv6_vpn_hash_key_make, srv6_vpn_hash_cmp, + "BGP Prefix-SID SRv6-VPN-Service-TLV"); +} + +static void srv6_finish(void) +{ + hash_clean(srv6_l3vpn_hash, (void (*)(void *))srv6_l3vpn_free); + hash_free(srv6_l3vpn_hash); + srv6_l3vpn_hash = NULL; + hash_clean(srv6_vpn_hash, (void (*)(void *))srv6_vpn_free); + hash_free(srv6_vpn_hash); + srv6_vpn_hash = NULL; +} + static unsigned int transit_hash_key_make(const void *p) { const struct transit *transit = p; @@ -557,7 +712,9 @@ bool attrhash_cmp(const void *p1, const void *p2) && overlay_index_same(attr1, attr2) && attr1->nh_ifindex == attr2->nh_ifindex && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex - && attr1->distance == attr2->distance) + && attr1->distance == attr2->distance + && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) + && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)) return true; } @@ -588,12 +745,22 @@ static void attrhash_finish(void) static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) { struct attr *attr = bucket->data; + char sid_str[BUFSIZ]; vty_out(vty, "attr[%ld] nexthop %s\n", attr->refcnt, inet_ntoa(attr->nexthop)); - vty_out(vty, "\tflags: %" PRIu64 " med: %u local_pref: %u origin: %u weight: %u label: %u\n", + + sid_str[0] = '\0'; + if (attr->srv6_l3vpn) + inet_ntop(AF_INET6, &attr->srv6_l3vpn->sid, sid_str, BUFSIZ); + else if (attr->srv6_vpn) + inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ); + + vty_out(vty, + "\tflags: %" PRIu64 + " med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n", attr->flag, attr->med, attr->local_pref, attr->origin, - attr->weight, attr->label); + attr->weight, attr->label, sid_str); } void attr_show_all(struct vty *vty) @@ -618,6 +785,11 @@ static void *bgp_attr_hash_alloc(void *p) val->vnc_subtlvs = NULL; } #endif + if (val->srv6_l3vpn) + val->srv6_l3vpn = NULL; + if (val->srv6_vpn) + val->srv6_vpn = NULL; + attr->refcnt = 0; return attr; } @@ -672,6 +844,18 @@ struct attr *bgp_attr_intern(struct attr *attr) else attr->encap_subtlvs->refcnt++; } + if (attr->srv6_l3vpn) { + if (!attr->srv6_l3vpn->refcnt) + attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn); + else + attr->srv6_l3vpn->refcnt++; + } + if (attr->srv6_vpn) { + if (!attr->srv6_vpn->refcnt) + attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn); + else + attr->srv6_vpn->refcnt++; + } #if ENABLE_BGP_VNC if (attr->vnc_subtlvs) { if (!attr->vnc_subtlvs->refcnt) @@ -862,6 +1046,12 @@ void bgp_attr_unintern_sub(struct attr *attr) if (attr->vnc_subtlvs) encap_unintern(&attr->vnc_subtlvs, VNC_SUBTLV_TYPE); #endif + + if (attr->srv6_l3vpn) + srv6_l3vpn_unintern(&attr->srv6_l3vpn); + + if (attr->srv6_vpn) + srv6_vpn_unintern(&attr->srv6_vpn); } /* @@ -1742,7 +1932,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, case BGP_ATTR_NHLEN_IPV4: stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN); /* Probably needed for RFC 2283 */ - if (attr->nexthop.s_addr == 0) + if (attr->nexthop.s_addr == INADDR_ANY) memcpy(&attr->nexthop.s_addr, &attr->mp_nexthop_global_in, IPV4_MAX_BYTELEN); break; @@ -2147,6 +2337,9 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length, uint32_t srgb_base; uint32_t srgb_range; int srgb_count; + uint8_t sid_type, sid_flags; + uint16_t endpoint_behavior; + char buf[BUFSIZ]; if (type == BGP_PREFIX_SID_LABEL_INDEX) { if (STREAM_READABLE(peer->curr) < length @@ -2268,13 +2461,81 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length, } } - /* - * Placeholder code for Unsupported TLV - * - SRv6 L3 Service TLV (type5) - * - SRv6 L2 Service TLV (type6) - */ - else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE - || type == BGP_PREFIX_SID_SRV6_L2_SERVICE) { + /* Placeholder code for the VPN-SID Service type */ + else if (type == BGP_PREFIX_SID_VPN_SID) { + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_VPN_SID_LENGTH) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID VPN SID length is %" PRIu16 + " instead of %u", + length, BGP_PREFIX_SID_VPN_SID_LENGTH); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* Parse VPN-SID Sub-TLV */ + stream_getc(peer->curr); /* reserved */ + sid_type = stream_getc(peer->curr); /* sid_type */ + sid_flags = stream_getc(peer->curr); /* sid_flags */ + stream_get(&ipv6_sid, peer->curr, + sizeof(ipv6_sid)); /* sid_value */ + + /* Log VPN-SID Sub-TLV */ + if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { + inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); + zlog_debug( + "%s: vpn-sid: sid %s, sid-type 0x%02x sid-flags 0x%02x", + __func__, buf, sid_type, sid_flags); + } + + /* Configure from Info */ + attr->srv6_vpn = XMALLOC(MTYPE_BGP_SRV6_VPN, + sizeof(struct bgp_attr_srv6_vpn)); + attr->srv6_vpn->refcnt = 0; + attr->srv6_vpn->sid_flags = sid_flags; + sid_copy(&attr->srv6_vpn->sid, &ipv6_sid); + } + + /* Placeholder code for the SRv6 L3 Service type */ + else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) { + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID SRv6 L3-Service length is %" PRIu16 + " instead of %u", + length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* Parse L3-SERVICE Sub-TLV */ + stream_getc(peer->curr); /* reserved */ + stream_get(&ipv6_sid, peer->curr, + sizeof(ipv6_sid)); /* sid_value */ + sid_flags = stream_getc(peer->curr); /* sid_flags */ + endpoint_behavior = stream_getw(peer->curr); /* endpoint */ + stream_getc(peer->curr); /* reserved */ + + /* Log L3-SERVICE Sub-TLV */ + if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { + inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); + zlog_debug( + "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x", + __func__, buf, sid_flags, endpoint_behavior); + } + + /* Configure from Info */ + attr->srv6_l3vpn = XMALLOC(MTYPE_BGP_SRV6_L3VPN, + sizeof(struct bgp_attr_srv6_l3vpn)); + attr->srv6_l3vpn->sid_flags = sid_flags; + attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior; + sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid); + } + + /* Placeholder code for Unsupported TLV */ + else { if (STREAM_READABLE(peer->curr) < length) { flog_err( @@ -2966,10 +3227,11 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, /* Nexthop AFI */ if (afi == AFI_IP - && (safi == SAFI_UNICAST || - safi == SAFI_LABELED_UNICAST || - safi == SAFI_MULTICAST)) + && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST + || safi == SAFI_MPLS_VPN || safi == SAFI_MULTICAST)) nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP; + else if (safi == SAFI_FLOWSPEC) + nh_afi = afi; else nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len); @@ -2996,7 +3258,12 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, stream_put(s, &attr->mp_nexthop_global_in, 4); break; case SAFI_FLOWSPEC: - stream_putc(s, 0); /* no nexthop for flowspec */ + if (attr->mp_nexthop_len == 0) + stream_putc(s, 0); /* no nexthop for flowspec */ + else { + stream_putc(s, attr->mp_nexthop_len); + stream_put_ipv4(s, attr->nexthop.s_addr); + } default: break; } @@ -3090,13 +3357,9 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, stream_put_labeled_prefix(s, p, label, addpath_encode, addpath_tx_id); } else if (safi == SAFI_FLOWSPEC) { - if (PSIZE (p->prefixlen)+2 < FLOWSPEC_NLRI_SIZELIMIT) - stream_putc(s, PSIZE (p->prefixlen)+2); - else - stream_putw(s, (PSIZE (p->prefixlen)+2)|(0xf<<12)); - stream_putc(s, 2);/* Filter type */ - stream_putc(s, p->prefixlen);/* Prefix length */ - stream_put(s, &p->u.prefix, PSIZE (p->prefixlen)); + stream_putc(s, p->u.prefix_flowspec.prefixlen); + stream_put(s, (const void *)p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen); } else stream_put_prefix_addpath(s, p, addpath_encode, addpath_tx_id); } @@ -3610,6 +3873,36 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, } } + /* SRv6 Service Information Attribute. */ + if (afi == AFI_IP && safi == SAFI_MPLS_VPN) { + if (attr->srv6_l3vpn) { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_PREFIX_SID); + stream_putc(s, 24); /* tlv len */ + stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE); + stream_putw(s, 21); /* sub-tlv len */ + stream_putc(s, 0); /* reserved */ + stream_put(s, &attr->srv6_l3vpn->sid, + sizeof(attr->srv6_l3vpn->sid)); /* sid */ + stream_putc(s, 0); /* sid_flags */ + stream_putw(s, 0xffff); /* endpoint */ + stream_putc(s, 0); /* reserved */ + } else if (attr->srv6_vpn) { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_PREFIX_SID); + stream_putc(s, 22); /* tlv len */ + stream_putc(s, BGP_PREFIX_SID_VPN_SID); + stream_putw(s, 0x13); /* tlv len */ + stream_putc(s, 0x00); /* reserved */ + stream_putc(s, 0x01); /* sid_type */ + stream_putc(s, 0x00); /* sif_flags */ + stream_put(s, &attr->srv6_vpn->sid, + sizeof(attr->srv6_vpn->sid)); /* sid */ + } + } + if (send_as4_path) { /* If the peer is NOT As4 capable, AND */ /* there are ASnums > 65535 in path THEN @@ -3738,6 +4031,7 @@ void bgp_attr_init(void) cluster_init(); transit_init(); encap_init(); + srv6_init(); } void bgp_attr_finish(void) @@ -3750,6 +4044,7 @@ void bgp_attr_finish(void) cluster_finish(); transit_finish(); encap_finish(); + srv6_finish(); } /* Make attribute packet. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 8bd527c0f..2e91f56df 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -62,12 +62,15 @@ #define BGP_PREFIX_SID_LABEL_INDEX 1 #define BGP_PREFIX_SID_IPV6 2 #define BGP_PREFIX_SID_ORIGINATOR_SRGB 3 +#define BGP_PREFIX_SID_VPN_SID 4 #define BGP_PREFIX_SID_SRV6_L3_SERVICE 5 #define BGP_PREFIX_SID_SRV6_L2_SERVICE 6 #define BGP_PREFIX_SID_LABEL_INDEX_LENGTH 7 #define BGP_PREFIX_SID_IPV6_LENGTH 19 #define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6 +#define BGP_PREFIX_SID_VPN_SID_LENGTH 19 +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH 21 #define BGP_ATTR_NH_AFI(afi, attr) \ ((afi != AFI_L2VPN) ? afi : \ @@ -111,6 +114,29 @@ enum pta_type { PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP }; +/* + * Prefix-SID type-4 + * SRv6-VPN-SID-TLV + * draft-dawra-idr-srv6-vpn-04 + */ +struct bgp_attr_srv6_vpn { + unsigned long refcnt; + uint8_t sid_flags; + struct in6_addr sid; +}; + +/* + * Prefix-SID type-5 + * SRv6-L3VPN-Service-TLV + * draft-dawra-idr-srv6-vpn-05 + */ +struct bgp_attr_srv6_l3vpn { + unsigned long refcnt; + uint8_t sid_flags; + uint16_t endpoint_behavior; + struct in6_addr sid; +}; + /* BGP core attribute structure. */ struct attr { /* AS Path structure */ @@ -198,6 +224,12 @@ struct attr { /* MPLS label */ mpls_label_t label; + /* SRv6 VPN SID */ + struct bgp_attr_srv6_vpn *srv6_vpn; + + /* SRv6 L3VPN SID */ + struct bgp_attr_srv6_l3vpn *srv6_l3vpn; + uint16_t encap_tunneltype; /* grr */ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 29e668d17..7ca48a5be 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -37,6 +37,37 @@ #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" +/* Calculate new sequential number. */ +static int64_t bgp_clist_new_seq_get(struct community_list *list) +{ + int64_t maxseq; + int64_t newseq; + struct community_entry *entry; + + maxseq = newseq = 0; + + for (entry = list->head; entry; entry = entry->next) { + if (maxseq < entry->seq) + maxseq = entry->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return (newseq > UINT_MAX) ? UINT_MAX : newseq; +} + +/* Return community-list entry which has same seq number. */ +static struct community_entry *bgp_clist_seq_check(struct community_list *list, + int64_t seq) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + if (entry->seq == seq) + return entry; + return NULL; +} + static uint32_t bgp_clist_hash_key_community_list(const void *data) { struct community_list *cl = (struct community_list *) data; @@ -285,20 +316,6 @@ static int community_list_empty_p(struct community_list *list) return (list->head == NULL && list->tail == NULL) ? 1 : 0; } -/* Add community-list entry to the list. */ -static void community_list_entry_add(struct community_list *list, - struct community_entry *entry) -{ - entry->next = NULL; - entry->prev = list->tail; - - if (list->tail) - list->tail->next = entry; - else - list->head = entry; - list->tail = entry; -} - /* Delete community-list entry from the list. */ static void community_list_entry_delete(struct community_list_master *cm, struct community_list *list, @@ -320,6 +337,57 @@ static void community_list_entry_delete(struct community_list_master *cm, community_list_delete(cm, list); } +/* Add community-list entry to the list. */ +static void community_list_entry_add(struct community_list *list, + struct community_entry *entry, + struct community_list_handler *ch, + int master) +{ + struct community_list_master *cm = NULL; + struct community_entry *replace; + struct community_entry *point; + + cm = community_list_master_lookup(ch, master); + + /* Automatic assignment of seq no. */ + if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO) + entry->seq = bgp_clist_new_seq_get(list); + + if (list->tail && entry->seq > list->tail->seq) + point = NULL; + else { + replace = bgp_clist_seq_check(list, entry->seq); + if (replace) + community_list_entry_delete(cm, list, entry); + + /* Check insert point. */ + for (point = list->head; point; point = point->next) + if (point->seq >= entry->seq) + break; + } + + /* In case of this is the first element of the list. */ + entry->next = point; + + if (point) { + if (point->prev) + point->prev->next = entry; + else + list->head = entry; + + entry->prev = point->prev; + point->prev = entry; + } else { + if (list->tail) + list->tail->next = entry; + else + list->head = entry; + + entry->prev = list->tail; + list->tail = entry; + } +} + /* Lookup community-list entry from the list. */ static struct community_entry * community_list_entry_lookup(struct community_list *list, const void *arg, @@ -878,12 +946,16 @@ static int community_list_dup_check(struct community_list *list, /* Set community-list. */ int community_list_set(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, int style) { struct community_entry *entry = NULL; struct community_list *list; struct community *com = NULL; regex_t *regex = NULL; + int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO; + + if (seq) + seqnum = (int64_t)atol(seq); /* Get community list. */ list = community_list_get(ch, name, COMMUNITY_LIST_MASTER); @@ -919,6 +991,7 @@ int community_list_set(struct community_list_handler *ch, const char *name, entry->any = (str ? 0 : 1); entry->u.com = com; entry->reg = regex; + entry->seq = seqnum; entry->config = (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL); @@ -926,7 +999,8 @@ int community_list_set(struct community_list_handler *ch, const char *name, if (community_list_dup_check(list, entry)) community_entry_free(entry); else { - community_list_entry_add(list, entry); + community_list_entry_add(list, entry, ch, + COMMUNITY_LIST_MASTER); route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED); } @@ -935,7 +1009,8 @@ 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) + const char *str, const char *seq, int direct, + int style) { struct community_list_master *cm = NULL; struct community_entry *entry = NULL; @@ -1057,12 +1132,16 @@ static int lcommunity_list_valid(const char *community) /* Set lcommunity-list. */ int lcommunity_list_set(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, int style) { struct community_entry *entry = NULL; struct community_list *list; struct lcommunity *lcom = NULL; regex_t *regex = NULL; + int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO; + + if (seq) + seqnum = (int64_t)atol(seq); /* Get community list. */ list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER); @@ -1101,6 +1180,7 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, entry->any = (str ? 0 : 1); entry->u.lcom = lcom; entry->reg = regex; + entry->seq = seqnum; entry->config = (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL); @@ -1108,7 +1188,8 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, if (community_list_dup_check(list, entry)) community_entry_free(entry); else { - community_list_entry_add(list, entry); + community_list_entry_add(list, entry, ch, + LARGE_COMMUNITY_LIST_MASTER); route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED); } @@ -1118,7 +1199,8 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, /* Unset community-list. When str is NULL, delete all of community-list entry belongs to the specified name. */ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_list_master *cm = NULL; struct community_entry *entry = NULL; @@ -1168,12 +1250,17 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, /* Set extcommunity-list. */ int extcommunity_list_set(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_entry *entry = NULL; struct community_list *list; struct ecommunity *ecom = NULL; regex_t *regex = NULL; + int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO; + + if (seq) + seqnum = (int64_t)atol(seq); if (str == NULL) return COMMUNITY_LIST_ERR_MALFORMED_VAL; @@ -1220,12 +1307,14 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, entry->u.ecom = ecom; entry->reg = regex; + entry->seq = seqnum; /* Do not put duplicated community entry. */ if (community_list_dup_check(list, entry)) community_entry_free(entry); else { - community_list_entry_add(list, entry); + community_list_entry_add(list, entry, ch, + EXTCOMMUNITY_LIST_MASTER); route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED); } @@ -1238,7 +1327,8 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, * specified name. */ int extcommunity_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_list_master *cm = NULL; struct community_entry *entry = NULL; diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 87b29ac3b..c5718aeca 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -36,6 +36,8 @@ #define COMMUNITY_LIST_STRING 0 #define COMMUNITY_LIST_NUMBER 1 +#define COMMUNITY_SEQ_NUMBER_AUTO -1 + /* Community-list entry types. */ #define COMMUNITY_LIST_STANDARD 0 /* Standard community-list. */ #define COMMUNITY_LIST_EXPANDED 1 /* Expanded community-list. */ @@ -81,6 +83,9 @@ struct community_entry { /* Any match. */ uint8_t any; + /* Sequence number. */ + int64_t seq; + /* Community structure. */ union { struct community *com; @@ -135,23 +140,23 @@ extern struct community_list_handler *community_list_init(void); extern void community_list_terminate(struct community_list_handler *); extern int community_list_set(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int community_list_unset(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int extcommunity_list_set(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int extcommunity_list_unset(struct community_list_handler *ch, const char *name, const char *str, - int direct, int style); + const char *seq, int direct, int style); extern int lcommunity_list_set(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int lcommunity_list_unset(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern struct community_list_master * community_list_master_lookup(struct community_list_handler *, int); diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 432c922ea..496d7ed2b 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -89,7 +89,6 @@ void community_del_val(struct community *com, uint32_t *val) com->val, com_length(com)); else { XFREE(MTYPE_COMMUNITY_VAL, com->val); - com->val = NULL; } return; } diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index f716c4f30..f3bd9f59f 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -63,6 +63,7 @@ unsigned long conf_bgp_debug_vpn; unsigned long conf_bgp_debug_flowspec; unsigned long conf_bgp_debug_labelpool; unsigned long conf_bgp_debug_pbr; +unsigned long conf_bgp_debug_graceful_restart; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -80,6 +81,7 @@ unsigned long term_bgp_debug_vpn; unsigned long term_bgp_debug_flowspec; unsigned long term_bgp_debug_labelpool; unsigned long term_bgp_debug_pbr; +unsigned long term_bgp_debug_graceful_restart; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -1644,6 +1646,23 @@ DEFUN (debug_bgp_zebra, return CMD_SUCCESS; } +DEFUN (debug_bgp_graceful_restart, + debug_bgp_graceful_restart_cmd, + "debug bgp graceful-restart", + DEBUG_STR + BGP_STR + GR_DEBUG) +{ + if (vty->node == CONFIG_NODE) { + DEBUG_ON(graceful_restart, GRACEFUL_RESTART); + } else { + TERM_DEBUG_ON(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "BGP Graceful Restart debugging is on\n"); + } + return CMD_SUCCESS; +} + + DEFUN (debug_bgp_zebra_prefix, debug_bgp_zebra_prefix_cmd, "debug bgp zebra prefix ", @@ -1702,6 +1721,23 @@ DEFUN (no_debug_bgp_zebra, return CMD_SUCCESS; } +DEFUN (no_debug_bgp_graceful_restart, + no_debug_bgp_graceful_restart_cmd, + "no debug bgp graceful-restart", + DEBUG_STR + BGP_STR + GR_DEBUG + NO_STR) +{ + if (vty->node == CONFIG_NODE) { + DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + } else { + TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "BGP Graceful Restart debugging is off\n"); + } + return CMD_SUCCESS; +} + DEFUN (no_debug_bgp_zebra_prefix, no_debug_bgp_zebra_prefix_cmd, "no debug bgp zebra prefix ", @@ -2039,6 +2075,8 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(labelpool, LABELPOOL); TERM_DEBUG_OFF(pbr, PBR); TERM_DEBUG_OFF(pbr, PBR_ERROR); + TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -2096,6 +2134,9 @@ DEFUN_NOSH (show_debugging_bgp, bgp_debug_list_print(vty, " BGP zebra debugging is on", bgp_debug_zebra_prefixes); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + vty_out(vty, " BGP graceful-restart debugging is on"); + if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) vty_out(vty, " BGP allow martian next hop debugging is on\n"); @@ -2229,6 +2270,11 @@ static int bgp_config_write_debug(struct vty *vty) vty_out(vty, "debug bgp pbr error\n"); write++; } + + if (CONF_BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) { + vty_out(vty, "debug bgp graceful-restart\n"); + write++; + } return write; } @@ -2262,6 +2308,9 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &debug_bgp_bestpath_prefix_cmd); install_element(CONFIG_NODE, &debug_bgp_bestpath_prefix_cmd); + install_element(ENABLE_NODE, &debug_bgp_graceful_restart_cmd); + install_element(CONFIG_NODE, &debug_bgp_graceful_restart_cmd); + /* debug bgp updates (in|out) */ install_element(ENABLE_NODE, &debug_bgp_update_direct_cmd); install_element(CONFIG_NODE, &debug_bgp_update_direct_cmd); @@ -2327,6 +2376,9 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd); install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_graceful_restart_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_graceful_restart_cmd); + install_element(ENABLE_NODE, &debug_bgp_vpn_cmd); install_element(CONFIG_NODE, &debug_bgp_vpn_cmd); install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd); diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index e1072c3df..1e6482e96 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -76,6 +76,7 @@ extern unsigned long conf_bgp_debug_vpn; extern unsigned long conf_bgp_debug_flowspec; extern unsigned long conf_bgp_debug_labelpool; extern unsigned long conf_bgp_debug_pbr; +extern unsigned long conf_bgp_debug_graceful_restart; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -91,6 +92,7 @@ extern unsigned long term_bgp_debug_vpn; extern unsigned long term_bgp_debug_flowspec; extern unsigned long term_bgp_debug_labelpool; extern unsigned long term_bgp_debug_pbr; +extern unsigned long term_bgp_debug_graceful_restart; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -131,6 +133,8 @@ struct bgp_debug_filter { #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 +#define BGP_DEBUG_GRACEFUL_RESTART 0x01 + #define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b)) #define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b)) diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index f850cb49c..298f9d021 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -667,10 +667,7 @@ static int bgp_dump_set(struct vty *vty, struct bgp_dump *bgp_dump, static int bgp_dump_unset(struct bgp_dump *bgp_dump) { /* Removing file name. */ - if (bgp_dump->filename) { - XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->filename); - bgp_dump->filename = NULL; - } + XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->filename); /* Closing file. */ if (bgp_dump->fp) { @@ -687,10 +684,7 @@ static int bgp_dump_unset(struct bgp_dump *bgp_dump) bgp_dump->interval = 0; /* Removing interval string. */ - if (bgp_dump->interval_str) { - XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->interval_str); - bgp_dump->interval_str = NULL; - } + XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->interval_str); return CMD_SUCCESS; } diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 3049a00ce..c5f7927c4 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -687,14 +687,22 @@ static void show_esi_routes(struct bgp *bgp, add_prefix_to_json = 1; } - if (json && add_prefix_to_json) { - json_object_string_add(json_prefix, "prefix", - prefix_str); - json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json, prefix_str, json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, + json_prefix); + } else { + json_object_free(json_paths); + json_object_free(json_prefix); + json_paths = NULL; + json_prefix = NULL; + } } } @@ -763,7 +771,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, for (; pi; pi = pi->next) { json_object *json_path = NULL; - if (vtep_ip.s_addr + if (vtep_ip.s_addr != INADDR_ANY && !IPV4_ADDR_SAME(&(vtep_ip), &(pi->attr->nexthop))) continue; @@ -786,14 +794,22 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, add_prefix_to_json = 1; } - if (json && add_prefix_to_json) { - json_object_string_add(json_prefix, "prefix", - prefix_str); - json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json, prefix_str, json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, + json_prefix); + } else { + json_object_free(json_paths); + json_object_free(json_prefix); + json_paths = NULL; + json_prefix = NULL; + } } } @@ -2579,18 +2595,29 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, add_rd_to_json = 1; } - if (json && add_prefix_to_json) { - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json_rd, prefix_str, - json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); + } else { + json_object_free(json_paths); + json_object_free(json_prefix); + json_paths = NULL; + json_prefix = NULL; + } } } - if (json && add_rd_to_json) - json_object_object_add(json, rd_str, json_rd); - if (json) { + if (add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + else { + json_object_free(json_rd); + json_rd = NULL; + } + json_object_int_add(json, "numPrefix", prefix_cnt); json_object_int_add(json, "numPaths", path_cnt); } else { @@ -2732,16 +2759,31 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, json_path); } - if (json && add_prefix_to_json) { - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json_rd, prefix_str, - json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_object_add(json_prefix, + "paths", + json_paths); + json_object_object_add(json_rd, + prefix_str, + json_prefix); + } else { + json_object_free(json_prefix); + json_object_free(json_paths); + json_prefix = NULL; + json_paths = NULL; + } } } - if (json && add_rd_to_json) - json_object_object_add(json, rd_str, json_rd); + if (json) { + if (add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + else { + json_object_free(json_rd); + json_rd = NULL; + } + } } if (json) { diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index f08f9b2fb..a03551e79 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -168,10 +168,7 @@ static struct as_list *as_list_new(void) static void as_list_free(struct as_list *aslist) { - if (aslist->name) { - XFREE(MTYPE_AS_STR, aslist->name); - aslist->name = NULL; - } + XFREE(MTYPE_AS_STR, aslist->name); XFREE(MTYPE_AS_LIST, aslist); } diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index b9a0d81cc..002aae561 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -455,7 +455,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, * ignore that rule */ if (prefix->family == AF_INET - && prefix->u.prefix4.s_addr == 0) + && prefix->u.prefix4.s_addr == INADDR_ANY) bpem->match_bitmask_iprule |= bitmask; else bpem->match_bitmask |= bitmask; diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 3667dae83..ac8fe5e91 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -35,7 +35,7 @@ #include "filter.h" #include "command.h" #include "lib_errors.h" - +#include "zclient.h" #include "lib/json.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -58,7 +58,7 @@ DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) DEFINE_HOOK(peer_status_changed, (struct peer * peer), (peer)) - +extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); /* Definition of display strings corresponding to FSM events. This should be * kept consistent with the events defined in bgpd.h */ @@ -250,6 +250,22 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) peer->remote_id = from_peer->remote_id; peer->last_reset = from_peer->last_reset; + peer->peer_gr_present_state = from_peer->peer_gr_present_state; + peer->peer_gr_new_status_flag = from_peer->peer_gr_new_status_flag; + bgp_peer_gr_flags_update(peer); + + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + + if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) { + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { + peer_nsf_stop(peer); + } + } + if (from_peer->hostname != NULL) { if (peer->hostname) { XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); @@ -613,6 +629,34 @@ static int bgp_graceful_stale_timer_expire(struct thread *thread) return 0; } +/* Selection deferral timer processing function */ +static int bgp_graceful_deferral_timer_expire(struct thread *thread) +{ + struct afi_safi_info *info; + afi_t afi; + safi_t safi; + struct bgp *bgp; + + info = THREAD_ARG(thread); + afi = info->afi; + safi = info->safi; + bgp = info->bgp; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "afi %d, safi %d : graceful restart deferral timer expired", + afi, safi); + + bgp->gr_info[afi][safi].t_select_deferral = NULL; + + bgp->gr_info[afi][safi].eor_required = 0; + bgp->gr_info[afi][safi].eor_received = 0; + XFREE(MTYPE_TMP, info); + + /* Best path selection */ + return bgp_best_path_select_defer(bgp, afi, safi); +} + static int bgp_update_delay_applicable(struct bgp *bgp) { /* update_delay_over flag should be reset (set to 0) for any new @@ -1076,6 +1120,10 @@ int bgp_stop(struct peer *peer) safi_t safi; char orf_name[BUFSIZ]; int ret = 0; + struct bgp *bgp = peer->bgp; + struct graceful_restart_info *gr_info = NULL; + + peer->nsf_af_count = 0; if (peer_dynamic_neighbor(peer) && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) { @@ -1098,6 +1146,7 @@ int bgp_stop(struct peer *peer) /* bgp log-neighbor-changes of neighbor Down */ if (bgp_flag_check(peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); + zlog_info( "%%ADJCHANGE: neighbor %s(%s) in vrf %s Down %s", peer->host, @@ -1141,6 +1190,38 @@ int bgp_stop(struct peer *peer) peer->nsf[afi][safi] = 0; } + /* If peer reset before receiving EOR, decrement EOR count and + * cancel the selection deferral timer if there are no + * pending EOR messages to be received + */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) { + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc_nego[afi][safi] + || CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) + continue; + + gr_info = &bgp->gr_info[afi][safi]; + if (!gr_info) + continue; + + if (gr_info->eor_required) + gr_info->eor_required--; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("peer %s, EOR_required %d", + peer->host, + gr_info->eor_required); + + /* There is no pending EOR message */ + if (gr_info->eor_required == 0) { + BGP_TIMER_OFF( + gr_info->t_select_deferral); + gr_info->eor_received = 0; + } + } + } + /* set last reset time */ peer->resettime = peer->uptime = bgp_clock(); @@ -1249,7 +1330,6 @@ int bgp_stop(struct peer *peer) } else { bgp_peer_conf_if_to_su_update(peer); } - return ret; } @@ -1447,7 +1527,7 @@ int bgp_start(struct peer *peer) } /* Clear remote router-id. */ - peer->remote_id.s_addr = 0; + peer->remote_id.s_addr = INADDR_ANY; /* Clear peer capability flag. */ peer->cap = 0; @@ -1539,6 +1619,10 @@ static int bgp_reconnect(struct peer *peer) if (bgp_stop(peer) < 0) return -1; + /* Send graceful restart capabilty */ + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + bgp_start(peer); return 0; } @@ -1574,6 +1658,78 @@ static int bgp_fsm_holdtime_expire(struct peer *peer) return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0); } +/* Start the selection deferral timer thread for the specified AFI, SAFI */ +static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi, + struct graceful_restart_info *gr_info) +{ + struct afi_safi_info *thread_info; + + /* If the deferral timer is active, then increment eor count */ + if (gr_info->t_select_deferral) { + gr_info->eor_required++; + return 0; + } + + /* Start the deferral timer when the first peer enabled for the graceful + * restart is established + */ + if (gr_info->eor_required == 0) { + thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); + + thread_info->afi = afi; + thread_info->safi = safi; + thread_info->bgp = bgp; + + thread_add_timer(bm->master, bgp_graceful_deferral_timer_expire, + thread_info, bgp->select_defer_time, + &gr_info->t_select_deferral); + } + gr_info->eor_required++; + /* Send message to RIB indicating route update pending */ + if (gr_info->af_enabled[afi][safi] == false) { + gr_info->af_enabled[afi][safi] = true; + /* Send message to RIB */ + bgp_zebra_update(afi, safi, bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_PENDING); + } + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("Started the deferral timer for %s eor_required %d", + get_afi_safi_str(afi, safi, false), + gr_info->eor_required); + return 0; +} + +/* Update the graceful restart information for the specified AFI, SAFI */ +static int bgp_update_gr_info(struct peer *peer, afi_t afi, safi_t safi) +{ + struct graceful_restart_info *gr_info; + struct bgp *bgp = peer->bgp; + int ret = 0; + + if ((afi < AFI_IP) || (afi >= AFI_MAX)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : invalid afi %d", __func__, afi); + return -1; + } + + if ((safi < SAFI_UNICAST) || (safi > SAFI_MPLS_VPN)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : invalid safi %d", __func__, safi); + return -1; + } + + /* Restarting router */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) + && BGP_PEER_RESTARTING_MODE(peer)) { + /* Check if the forwarding state is preserved */ + if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) { + gr_info = &(bgp->gr_info[afi][safi]); + ret = bgp_start_deferral_timer(bgp, afi, safi, gr_info); + } + } + return ret; +} + /** * Transition to Established state. * @@ -1587,6 +1743,7 @@ static int bgp_establish(struct peer *peer) int nsf_af_count = 0; int ret = 0; struct peer *other; + int status; other = peer->doppelganger; peer = peer_xfer_conn(peer); @@ -1626,6 +1783,12 @@ static int bgp_establish(struct peer *peer) /* graceful restart */ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + if (bgp_debug_neighbor_events(peer)) { + if (BGP_PEER_RESTARTING_MODE(peer)) + zlog_debug("peer %s BGP_RESTARTING_MODE", peer->host); + else if (BGP_PEER_HELPER_MODE(peer)) + zlog_debug("peer %s BGP_HELPER_MODE", peer->host); + } for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { if (peer->afc_nego[afi][safi] @@ -1645,8 +1808,32 @@ static int bgp_establish(struct peer *peer) bgp_clear_stale_route(peer, afi, safi); peer->nsf[afi][safi] = 0; } + /* Update the graceful restart information */ + if (peer->afc_nego[afi][safi]) { + if (!BGP_SELECT_DEFER_DISABLE(peer->bgp)) { + status = bgp_update_gr_info(peer, afi, + safi); + if (status < 0) + zlog_err( + "Error in updating graceful restart for %s", + get_afi_safi_str( + afi, safi, + false)); + } else { + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE( + peer) + && BGP_PEER_RESTARTING_MODE(peer) + && bgp_flag_check( + peer->bgp, + BGP_FLAG_GR_PRESERVE_FWD)) + peer->bgp->gr_info[afi][safi] + .eor_required++; + } + } } + peer->nsf_af_count = nsf_af_count; + if (nsf_af_count) SET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); else { @@ -2063,3 +2250,479 @@ int bgp_event_update(struct peer *peer, int event) return ret; } +/* BGP GR Code */ + +int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, + enum global_mode global_new_state, + enum global_mode global_old_state) +{ + struct peer *peer = {0}; + struct listnode *node = {0}; + struct listnode *nnode = {0}; + enum peer_mode peer_old_state = PEER_INVALID; + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR] Peer: (%s) :", __func__, + peer->host); + + peer_old_state = bgp_peer_gr_mode_get(peer); + + if (peer_old_state == PEER_GLOBAL_INHERIT) { + + /* + *Reset only these peers and send a + *new open message with the change capabilities. + *Considering the mode to be "global_new_state" and + *do all operation accordingly + */ + + switch (global_new_state) { + case GLOBAL_HELPER: + BGP_PEER_GR_HELPER_ENABLE(peer); + break; + case GLOBAL_GR: + BGP_PEER_GR_ENABLE(peer); + break; + case GLOBAL_DISABLE: + BGP_PEER_GR_DISABLE(peer); + break; + case GLOBAL_INVALID: + zlog_debug("%s [BGP_GR] GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + } + } + } + + bgp->global_gr_present_state = global_new_state; + + return BGP_GR_SUCCESS; +} + +int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd) +{ + enum global_mode global_new_state = GLOBAL_INVALID; + enum global_mode global_old_state = GLOBAL_INVALID; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR]START: global_gr_cmd :%s:", __func__, + print_global_gr_cmd(global_gr_cmd)); + + global_old_state = bgp_global_gr_mode_get(bgp); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] global_old_gr_state :%s:", + print_global_gr_mode(global_old_state)); + + if (global_old_state != GLOBAL_INVALID) { + global_new_state = + bgp->GLOBAL_GR_FSM[global_old_state][global_gr_cmd]; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] global_new_gr_state :%s:", + print_global_gr_mode(global_new_state)); + } else { + zlog_err("%s [BGP_GR] global_old_state == GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + } + + if (global_new_state == GLOBAL_INVALID) { + zlog_err("%s [BGP_GR] global_new_state == GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_INVALID_CMD; + } + if (global_new_state == global_old_state) { + /* Trace msg */ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] global_new_state == global_old_state :%s", + __func__, + print_global_gr_mode(global_new_state)); + return BGP_GR_NO_OPERATION; + } + + return bgp_gr_lookup_n_update_all_peer(bgp, global_new_state, + global_old_state); +} + +const char *print_peer_gr_mode(enum peer_mode pr_mode) +{ + const char *peer_gr_mode = NULL; + + switch (pr_mode) { + case PEER_HELPER: + peer_gr_mode = "PEER_HELPER"; + break; + case PEER_GR: + peer_gr_mode = "PEER_GR"; + break; + case PEER_DISABLE: + peer_gr_mode = "PEER_DISABLE"; + break; + case PEER_INVALID: + peer_gr_mode = "PEER_INVALID"; + break; + case PEER_GLOBAL_INHERIT: + peer_gr_mode = "PEER_GLOBAL_INHERIT"; + break; + } + + return peer_gr_mode; +} + +const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd) +{ + const char *peer_gr_cmd = NULL; + + switch (pr_gr_cmd) { + case PEER_GR_CMD: + peer_gr_cmd = "PEER_GR_CMD"; + break; + case NO_PEER_GR_CMD: + peer_gr_cmd = "NO_PEER_GR_CMD"; + break; + case PEER_DISABLE_CMD: + peer_gr_cmd = "PEER_GR_CMD"; + break; + case NO_PEER_DISABLE_CMD: + peer_gr_cmd = "NO_PEER_GR_CMD"; + break; + case PEER_HELPER_CMD: + peer_gr_cmd = "PEER_HELPER_CMD"; + break; + case NO_PEER_HELPER_CMD: + peer_gr_cmd = "NO_PEER_HELPER_CMD"; + break; + } + + return peer_gr_cmd; +} + +const char *print_global_gr_mode(enum global_mode gl_mode) +{ + const char *global_gr_mode = NULL; + + switch (gl_mode) { + case GLOBAL_HELPER: + global_gr_mode = "GLOBAL_HELPER"; + break; + case GLOBAL_GR: + global_gr_mode = "GLOBAL_GR"; + break; + case GLOBAL_DISABLE: + global_gr_mode = "GLOBAL_DISABLE"; + break; + case GLOBAL_INVALID: + global_gr_mode = "GLOBAL_INVALID"; + break; + } + + return global_gr_mode; +} + +const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd) +{ + const char *global_gr_cmd = NULL; + + switch (gl_gr_cmd) { + case GLOBAL_GR_CMD: + global_gr_cmd = "GLOBAL_GR_CMD"; + break; + case NO_GLOBAL_GR_CMD: + global_gr_cmd = "NO_GLOBAL_GR_CMD"; + break; + case GLOBAL_DISABLE_CMD: + global_gr_cmd = "GLOBAL_DISABLE_CMD"; + break; + case NO_GLOBAL_DISABLE_CMD: + global_gr_cmd = "NO_GLOBAL_DISABLE_CMD"; + break; + } + + return global_gr_cmd; +} + +enum global_mode bgp_global_gr_mode_get(struct bgp *bgp) +{ + return bgp->global_gr_present_state; +} + +enum peer_mode bgp_peer_gr_mode_get(struct peer *peer) +{ + return peer->peer_gr_present_state; +} + +int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd) +{ + enum peer_mode peer_new_state = PEER_INVALID; + enum peer_mode peer_old_state = PEER_INVALID; + struct bgp_peer_gr peer_state; + int result = BGP_GR_FAILURE; + + /* + * fetch peer_old_state from peer structure also + * fetch global_old_state from bgp structure, + * peer had a back pointer to bgpo struct ; + */ + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR] START:Peer: (%s) : peer_gr_cmd :%s:", + __func__, peer->host, + print_peer_gr_cmd(peer_gr_cmd)); + + peer_old_state = bgp_peer_gr_mode_get(peer); + + if (peer_old_state == PEER_INVALID) { + zlog_debug("[BGP_GR] peer_old_state == Invalid state !"); + return BGP_ERR_GR_OPERATION_FAILED; + } + + peer_state = peer->PEER_GR_FSM[peer_old_state][peer_gr_cmd]; + peer_new_state = peer_state.next_state; + + if (peer_new_state == PEER_INVALID) { + zlog_debug( + "[BGP_GR] Invalid bgp graceful restart command used !"); + return BGP_ERR_GR_INVALID_CMD; + } + + if (peer_new_state != peer_old_state) { + result = peer_state.action_fun(peer, peer_old_state, + peer_new_state); + } else { + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] peer_old_state == peer_new_state !"); + return BGP_GR_NO_OPERATION; + } + + if (result == BGP_GR_SUCCESS) { + + /* Update the mode i.e peer_new_state into the peer structure */ + peer->peer_gr_present_state = peer_new_state; + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Succesfully change the state of the peer to : %s : !", + print_peer_gr_mode(peer_new_state)); + + return BGP_GR_SUCCESS; + } + + return result; +} + +unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state, + int new_peer_state) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] Move peer from old_peer_state :%s: to new_peer_state :%s: !!!!", + __func__, print_peer_gr_mode(old_peer_state), + print_peer_gr_mode(new_peer_state)); + + int bgp_gr_global_mode = GLOBAL_INVALID; + unsigned int ret = BGP_GR_FAILURE; + + if (old_peer_state == new_peer_state) { + /* Nothing to do over here as the present and old state is the + * same */ + return BGP_GR_NO_OPERATION; + } + if ((old_peer_state == PEER_INVALID) + || (new_peer_state == PEER_INVALID)) { + /* something bad happend , print error message */ + return BGP_ERR_GR_INVALID_CMD; + } + + bgp_gr_global_mode = bgp_global_gr_mode_get(peer->bgp); + + if ((old_peer_state == PEER_GLOBAL_INHERIT) + && (new_peer_state != PEER_GLOBAL_INHERIT)) { + + /* fetch the Mode running in the Global state machine + *from the bgp structure into a variable called + *bgp_gr_global_mode + */ + + /* Here we are checking if the + *1. peer_new_state == global_mode == helper_mode + *2. peer_new_state == global_mode == GR_mode + *3. peer_new_state == global_mode == disabled_mode + */ + + BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer); + + if (new_peer_state == bgp_gr_global_mode) { + /*This is incremental updates i.e no tear down + *of the existing session + *as the peer is already working in the same mode. + */ + ret = BGP_GR_SUCCESS; + } else { + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer state changed from :%s ", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, new_peer_state); + + ret = BGP_GR_SUCCESS; + } + } + /* In the case below peer is going into Global inherit mode i.e. + * the peer would work as the mode configured at the global level + */ + else if ((new_peer_state == PEER_GLOBAL_INHERIT) + && (old_peer_state != PEER_GLOBAL_INHERIT)) { + /* Here in this case it would be destructive + * in all the cases except one case when, + * Global GR is configured Disabled + * and present_peer_state is not disable + */ + + BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); + + if (old_peer_state == bgp_gr_global_mode) { + + /* This is incremental updates + *i.e no tear down of the existing session + *as the peer is already working in the same mode. + */ + ret = BGP_GR_SUCCESS; + } else { + /* Destructive always */ + /* Tear down the old session + * and send the new capability + * as per the bgp_gr_global_mode + */ + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer state changed from :%s", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, bgp_gr_global_mode); + + ret = BGP_GR_SUCCESS; + } + } else { + /* + *This else case, it include all the cases except --> + *(new_peer_state != Peer_Global) && + *( old_peer_state != Peer_Global ) + */ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed from :%s", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, new_peer_state); + + ret = BGP_GR_SUCCESS; + } + + return ret; +} + +inline void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state) + +{ + int bgp_global_gr_mode = bgp_global_gr_mode_get(peer->bgp); + + switch (new_state) { + case PEER_HELPER: + BGP_PEER_GR_HELPER_ENABLE(peer); + break; + case PEER_GR: + BGP_PEER_GR_ENABLE(peer); + break; + case PEER_DISABLE: + BGP_PEER_GR_DISABLE(peer); + break; + case PEER_GLOBAL_INHERIT: + BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); + + if (bgp_global_gr_mode == GLOBAL_HELPER) { + BGP_PEER_GR_HELPER_ENABLE(peer); + } else if (bgp_global_gr_mode == GLOBAL_GR) { + BGP_PEER_GR_ENABLE(peer); + } else if (bgp_global_gr_mode == GLOBAL_DISABLE) { + BGP_PEER_GR_DISABLE(peer); + } else { + zlog_err( + "[BGP_GR] Default switch inherit mode ::: SOMETHING IS WRONG !!!"); + } + break; + default: + zlog_err( + "[BGP_GR] Default switch mode ::: SOMETHING IS WRONG !!!"); + break; + } + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed --to--> : %d : !", + new_state); +} + +void bgp_peer_gr_flags_update(struct peer *peer) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR] called !", __func__); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) + SET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER); + else + UNSET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_HELPER : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER) + ? "Set" + : "UnSet")); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) + SET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART); + else + UNSET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + ? "Set" + : "UnSet")); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) + SET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT); + else + UNSET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT) + ? "Set" + : "UnSet")); + + if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) { + zlog_debug("[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_MODE!", + peer->host); + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { + + peer_nsf_stop(peer); + zlog_debug( + "[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_WAIT!", + peer->host); + } + } +} diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 6f955c71b..6feabbf57 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -56,6 +56,58 @@ #define FSM_PEER_TRANSFERRED 2 #define FSM_PEER_TRANSITIONED 3 +#define BGP_PEER_GR_HELPER_ENABLE(peer) \ + do { \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \ + SET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_ENABLE(peer)\ + do { \ + SET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_DISABLE(peer)\ + do { \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART);\ + UNSET_FLAG(\ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_GLOBAL_INHERIT_SET(peer) \ + SET_FLAG(peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT) + +#define BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer) \ + UNSET_FLAG(peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT) + +#define BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) \ + (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) \ + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) + +#define BGP_PEER_RESTARTING_MODE(peer) \ + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) \ + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV) \ + && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV)) + +#define BGP_PEER_HELPER_MODE(peer) \ + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER) \ + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV) \ + && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV)) + /* Prototypes. */ extern void bgp_fsm_event_update(struct peer *peer, int valid); extern int bgp_event(struct thread *); @@ -87,6 +139,28 @@ extern void bgp_start_routeadv(struct bgp *); extern void bgp_adjust_routeadv(struct peer *); #include "hook.h" -DECLARE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) +DECLARE_HOOK(peer_backward_transition, (struct peer *peer), (peer)) +DECLARE_HOOK(peer_established, (struct peer *peer), (peer)) +int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd); +int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd); +unsigned int bgp_peer_gr_action(struct peer *peer, + int old_peer_state, int new_peer_state); +void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state); +unsigned int bgp_peer_gr_helper_enable(struct peer *peer); +unsigned int bgp_peer_gr_enable(struct peer *peer); +unsigned int bgp_peer_gr_global_inherit(struct peer *peer); +unsigned int bgp_peer_gr_disable(struct peer *peer); +enum peer_mode bgp_peer_gr_mode_get(struct peer *peer); +enum global_mode bgp_global_gr_mode_get(struct bgp *bgp); +enum peer_mode bgp_get_peer_gr_mode_from_flags(struct peer *peer); +unsigned int bgp_peer_gr_global_inherit_unset(struct peer *peer); +int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, + enum global_mode global_new_state, + enum global_mode global_old_state); +void bgp_peer_gr_flags_update(struct peer *peer); +const char *print_peer_gr_mode(enum peer_mode pr_mode); +const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd); +const char *print_global_gr_mode(enum global_mode gl_mode); +const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd); #endif /* _QUAGGA_BGP_FSM_H */ diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index fed34e5b6..ab50c545b 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -462,7 +462,10 @@ static uint16_t bgp_read(struct peer *peer) safe_strerror(errno)); if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + || CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); } else @@ -478,7 +481,10 @@ static uint16_t bgp_read(struct peer *peer) peer->host, peer->fd); if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + || CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); } else diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 674686b3c..f88969300 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -527,7 +527,6 @@ void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr) lcom->val, lcom_length(lcom)); else { XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val); - lcom->val = NULL; } return; } diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 9cb3957a8..74e561c74 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -272,7 +272,6 @@ static int bgp_vrf_enable(struct vrf *vrf) if (bgp && bgp->vrf_id != vrf->vrf_id) { if (bgp->name && strmatch(vrf->name, VRF_DEFAULT_NAME)) { XFREE(MTYPE_BGP, bgp->name); - bgp->name = NULL; XFREE(MTYPE_BGP, bgp->name_pretty); bgp->name_pretty = XSTRDUP(MTYPE_BGP, "VRF default"); bgp->inst_type = BGP_INSTANCE_TYPE_DEFAULT; @@ -360,6 +359,8 @@ static void bgp_vrf_terminate(void) } static const struct frr_yang_module_info *const bgpd_yang_modules[] = { + &frr_interface_info, + &frr_route_map_info, }; FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, @@ -487,6 +488,7 @@ int main(int argc, char **argv) frr_config_fork(); /* must be called after fork() */ + bgp_gr_apply_running_config(); bgp_pthreads_run(); frr_run(bm->master); diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 3e4dfb11a..41c4108c0 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -128,3 +128,6 @@ DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_RULE_STR, "BGP flowspec rule str") DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_COMPILED, "BGP flowspec compiled") DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_NAME, "BGP flowspec name") DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_INDEX, "BGP flowspec index") + +DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie") +DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 03715f562..542802255 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -126,4 +126,7 @@ DECLARE_MTYPE(BGP_FLOWSPEC_COMPILED) DECLARE_MTYPE(BGP_FLOWSPEC_NAME) DECLARE_MTYPE(BGP_FLOWSPEC_INDEX) +DECLARE_MTYPE(BGP_SRV6_L3VPN) +DECLARE_MTYPE(BGP_SRV6_VPN) + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 77448ec15..6bc8134f3 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -280,7 +280,6 @@ void bgp_path_info_mpath_free(struct bgp_path_info_mpath **mpath) if ((*mpath)->mp_attr) bgp_attr_unintern(&(*mpath)->mp_attr); XFREE(MTYPE_BGP_MPATH_INFO, *mpath); - *mpath = NULL; } } @@ -798,7 +797,7 @@ void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best, } /* Zap multipath attr nexthop so we set nexthop to self */ - attr.nexthop.s_addr = 0; + attr.nexthop.s_addr = INADDR_ANY; memset(&attr.mp_nexthop_global, 0, sizeof(struct in6_addr)); /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */ diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 4031d2dfd..8b585704d 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -487,6 +487,20 @@ static int bgp_accept(struct thread *thread) hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); peer_xfer_config(peer, peer1); + bgp_peer_gr_flags_update(peer); + + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + + if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) { + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { + peer_nsf_stop(peer); + } + } + UNSET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); peer->doppelganger = peer1; @@ -497,7 +511,6 @@ static int bgp_accept(struct thread *thread) BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); - /* Make dummy peer until read Open packet. */ if (peer1->status == Established && CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) { @@ -508,7 +521,12 @@ static int bgp_accept(struct thread *thread) * existing established connection and move state to connect. */ peer1->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer1->sflags, PEER_STATUS_NSF_WAIT); + + if (CHECK_FLAG(peer1->flags, PEER_FLAG_GRACEFUL_RESTART) + || CHECK_FLAG(peer1->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + SET_FLAG(peer1->sflags, PEER_STATUS_NSF_WAIT); + bgp_event_update(peer1, TCP_connection_closed); } diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 23b893c1c..473a51c5a 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -462,6 +462,8 @@ static int bgp_capability_restart(struct peer *peer, restart_flag_time = stream_getw(s); if (CHECK_FLAG(restart_flag_time, RESTART_R_BIT)) SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); + else + UNSET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); UNSET_FLAG(restart_flag_time, 0xF000); peer->v_gr_restart = restart_flag_time; @@ -656,7 +658,7 @@ static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr) nh_afi = afi_iana2int(pkt_nh_afi); if (afi != AFI_IP || nh_afi != AFI_IP6 - || !(safi == SAFI_UNICAST + || !(safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN || safi == SAFI_LABELED_UNICAST)) { flog_warn( EC_BGP_CAPABILITY_INVALID_DATA, @@ -708,15 +710,8 @@ static int bgp_capability_hostname(struct peer *peer, if (len) { str[len] = '\0'; - if (peer->hostname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); - peer->hostname = NULL; - } - - if (peer->domainname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); } @@ -748,10 +743,7 @@ static int bgp_capability_hostname(struct peer *peer, if (len) { str[len] = '\0'; - if (peer->domainname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); } @@ -828,6 +820,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, int ret; struct stream *s = BGP_INPUT(peer); size_t end = stream_get_getp(s) + length; + uint16_t restart_flag_time = 0; assert(STREAM_READABLE(s) >= length); @@ -1004,6 +997,11 @@ static int bgp_capability_parse(struct peer *peer, size_t length, caphdr.length); stream_set_getp(s, start + caphdr.length); } + + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + UNSET_FLAG(restart_flag_time, 0xF000); + peer->v_gr_restart = restart_flag_time; + } } return 0; } @@ -1299,6 +1297,86 @@ static void bgp_open_capability_orf(struct stream *s, struct peer *peer, stream_putc_at(s, capp, cap_len); } +static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer, + unsigned long cp) +{ + int len; + iana_afi_t pkt_afi; + afi_t afi; + safi_t safi; + iana_safi_t pkt_safi; + uint32_t restart_time; + unsigned long capp = 0; + unsigned long rcapp = 0; + + if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) + return; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Sending helper Capability for Peer :%s :", + peer->host); + + SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); + stream_putc(s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp(s); /* Set Capability Len Pointer */ + stream_putc(s, 0); /* Capability Length */ + stream_putc(s, CAPABILITY_CODE_RESTART); + /* Set Restart Capability Len Pointer */ + rcapp = stream_get_endp(s); + stream_putc(s, 0); + restart_time = peer->bgp->restart_time; + if (peer->bgp->t_startup) { + SET_FLAG(restart_time, RESTART_R_BIT); + SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Sending R-Bit for Peer :%s :", + peer->host); + } + + stream_putw(s, restart_time); + + /* Send address-family specific graceful-restart capability + * only when GR config is present + */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) { + if (bgp_flag_check(peer->bgp, BGP_FLAG_GR_PRESERVE_FWD) + && BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] F bit Set"); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc[afi][safi]) + continue; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Sending GR Capability for AFI :%d :, SAFI :%d:", + afi, safi); + + /* Convert AFI, SAFI to values for + * packet. + */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, + &pkt_safi); + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + if (bgp_flag_check(peer->bgp, BGP_FLAG_GR_PRESERVE_FWD)) + stream_putc(s, RESTART_F_BIT); + else + stream_putc(s, 0); + } + } + + /* Total Graceful restart capability Len. */ + len = stream_get_endp(s) - rcapp - 1; + stream_putc_at(s, rcapp, len); + + /* Total Capability Len. */ + len = stream_get_endp(s) - capp - 1; + stream_putc_at(s, capp, len); +} + /* Fill in capability open option to the packet. */ void bgp_open_capability(struct stream *s, struct peer *peer) { @@ -1309,7 +1387,6 @@ void bgp_open_capability(struct stream *s, struct peer *peer) safi_t safi; iana_safi_t pkt_safi; as_t local_as; - uint32_t restart_time; uint8_t afi_safi_count = 0; int adv_addpath_tx = 0; @@ -1347,7 +1424,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) && peer->su.sa.sa_family == AF_INET6 && afi == AFI_IP - && (safi == SAFI_UNICAST + && (safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN || safi == SAFI_LABELED_UNICAST)) { /* RFC 5549 Extended Next Hop Encoding */ @@ -1502,50 +1579,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) cmd_domainname_get()); } - /* Sending base graceful-restart capability irrespective of the config - */ - SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); - stream_putc(s, BGP_OPEN_OPT_CAP); - capp = stream_get_endp(s); /* Set Capability Len Pointer */ - stream_putc(s, 0); /* Capability Length */ - stream_putc(s, CAPABILITY_CODE_RESTART); - rcapp = stream_get_endp(s); /* Set Restart Capability Len Pointer */ - stream_putc(s, 0); - restart_time = peer->bgp->restart_time; - if (peer->bgp->t_startup) { - SET_FLAG(restart_time, RESTART_R_BIT); - SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV); - } - stream_putw(s, restart_time); - - /* Send address-family specific graceful-restart capability only when GR - config - is present */ - if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { - FOREACH_AFI_SAFI (afi, safi) { - if (peer->afc[afi][safi]) { - /* Convert AFI, SAFI to values for - * packet. */ - bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, - &pkt_safi); - stream_putw(s, pkt_afi); - stream_putc(s, pkt_safi); - if (bgp_flag_check(peer->bgp, - BGP_FLAG_GR_PRESERVE_FWD)) - stream_putc(s, RESTART_F_BIT); - else - stream_putc(s, 0); - } - } - } - - /* Total Graceful restart capability Len. */ - len = stream_get_endp(s) - rcapp - 1; - stream_putc_at(s, rcapp, len); - - /* Total Capability Len. */ - len = stream_get_endp(s) - capp - 1; - stream_putc_at(s, capp, len); + bgp_peer_send_gr_capability(s, peer, cp); /* Total Opt Parm Len. */ len = stream_get_endp(s) - cp - 1; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index b7e9af002..afb3a09b6 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -444,23 +444,37 @@ int bgp_generate_updgrp_packets(struct thread *thread) && peer->afc_nego[afi][safi] && peer->synctime && !CHECK_FLAG( - peer->af_sflags[afi] - [safi], - PEER_STATUS_EOR_SEND)) { - SET_FLAG(peer->af_sflags[afi] + peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + /* If EOR is disabled, + * the message is not sent + */ + if (BGP_SEND_EOR(peer->bgp, afi, + safi)) { + SET_FLAG( + peer->af_sflags + [afi] [safi], - PEER_STATUS_EOR_SEND); - - if ((s = bgp_update_packet_eor( - peer, afi, - safi))) { - bgp_packet_add(peer, s); + PEER_STATUS_EOR_SEND); + + /* Update EOR + * send time + */ + peer->eor_stime[afi] + [safi] = + monotime(NULL); + + BGP_UPDATE_EOR_PKT( + peer, afi, safi, + s); } } } continue; } + /* Update packet send time */ + peer->pkt_stime[afi][safi] = monotime(NULL); /* Found a packet template to send, overwrite * packet with appropriate attributes from peer @@ -723,11 +737,14 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, if (first) { snprintf(c, sizeof(c), " %02x", data[i]); + strlcat(bgp_notify.data, c, bgp_notify.length); + } else { first = 1; snprintf(c, sizeof(c), "%02x", data[i]); + strlcpy(bgp_notify.data, c, bgp_notify.length); } @@ -736,7 +753,6 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, if (bgp_notify.data) { XFREE(MTYPE_TMP, bgp_notify.data); - bgp_notify.data = NULL; bgp_notify.length = 0; } } @@ -755,6 +771,10 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, /* Add packet to peer's output queue */ stream_fifo_push(peer->obuf, s); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + bgp_write_notify(peer); } @@ -1166,7 +1186,8 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) } /* remote router-id check. */ - if (remote_id.s_addr == 0 || IPV4_CLASS_DE(ntohl(remote_id.s_addr)) + if (remote_id.s_addr == INADDR_ANY + || IPV4_CLASS_DE(ntohl(remote_id.s_addr)) || ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr)) { if (bgp_debug_neighbor_events(peer)) zlog_debug("%s bad OPEN, wrong router identifier %s", @@ -1334,7 +1355,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) || peer->afc_nego[AFI_IP][SAFI_MULTICAST] || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP][SAFI_ENCAP]) { - if (!peer->nexthop.v4.s_addr) { + if (peer->nexthop.v4.s_addr == INADDR_ANY) { #if defined(HAVE_CUMULUS) flog_err( EC_BGP_SND_FAIL, @@ -1404,6 +1425,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) bgp_size_t attribute_len; bgp_size_t update_len; bgp_size_t withdraw_len; + bool restart = false; enum NLRI_TYPES { NLRI_UPDATE, @@ -1626,6 +1648,12 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) { afi_t afi = 0; safi_t safi; + struct graceful_restart_info *gr_info; + + /* Restarting router */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) + && BGP_PEER_RESTARTING_MODE(peer)) + restart = true; /* Non-MP IPv4/Unicast is a completely emtpy UPDATE - already * checked @@ -1652,6 +1680,33 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED); bgp_update_explicit_eors(peer); + /* Update graceful restart information */ + gr_info = &(peer->bgp->gr_info[afi][safi]); + if (restart) + gr_info->eor_received++; + /* If EOR received from all peers and selection + * deferral timer is running, cancel the timer + * and invoke the best path calculation + */ + if (gr_info->eor_required + == gr_info->eor_received) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s %d, %s %d", + "EOR REQ", + gr_info->eor_required, + "EOR RCV", + gr_info->eor_received); + BGP_TIMER_OFF( + gr_info->t_select_deferral); + gr_info->eor_required = 0; + gr_info->eor_received = 0; + /* Best path selection */ + if (bgp_best_path_select_defer( + peer->bgp, afi, safi) + < 0) + return BGP_Stop; + } } /* NSF delete stale route */ @@ -1691,7 +1746,6 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) if (peer->notify.data) { XFREE(MTYPE_TMP, peer->notify.data); - peer->notify.data = NULL; peer->notify.length = 0; } @@ -1723,14 +1777,17 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) if (first) { snprintf(c, sizeof(c), " %02x", stream_getc(peer->curr)); + strlcat(bgp_notify.data, c, - bgp_notify.length); + bgp_notify.length * 3); + } else { first = 1; snprintf(c, sizeof(c), "%02x", stream_getc(peer->curr)); + strlcpy(bgp_notify.data, c, - bgp_notify.length); + bgp_notify.length * 3); } bgp_notify.raw_data = (uint8_t *)peer->notify.data; } @@ -1738,7 +1795,6 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) bgp_notify_print(peer, &bgp_notify, "received"); if (bgp_notify.data) { XFREE(MTYPE_TMP, bgp_notify.data); - bgp_notify.data = NULL; bgp_notify.length = 0; } } @@ -1756,6 +1812,10 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) && bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM) UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + return Receive_NOTIFICATION_message; } @@ -2374,3 +2434,14 @@ int bgp_process_packet(struct thread *thread) return 0; } + +/* Send EOR when routes are processed by selection deferral timer */ +void bgp_send_delayed_eor(struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + /* EOR message sent in bgp_write_proceed_actions */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) + bgp_write_proceed_actions(peer); +} diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 49e401790..e83f7d950 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -48,6 +48,14 @@ DECLARE_HOOK(bgp_packet_send, #define ORF_COMMON_PART_PERMIT 0x00 #define ORF_COMMON_PART_DENY 0x20 +#define BGP_UPDATE_EOR_PKT(_peer, _afi, _safi, _s) \ + do { \ + _s = bgp_update_packet_eor(_peer, _afi, _safi); \ + if (_s) { \ + bgp_packet_add(_peer, _s); \ + } \ + } while (0) + /* Packet send and receive function prototypes. */ extern void bgp_keepalive_send(struct peer *); extern void bgp_open_send(struct peer *); @@ -73,4 +81,5 @@ extern int bgp_packet_set_size(struct stream *s); extern int bgp_generate_updgrp_packets(struct thread *); extern int bgp_process_packet(struct thread *); +extern void bgp_send_delayed_eor(struct bgp *bgp); #endif /* _QUAGGA_BGP_PACKET_H */ diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 14f5fefb2..eea20d721 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -754,13 +754,15 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, * draft-ietf-idr-flowspec-redirect */ if (api_action_redirect_ip) { - if (api_action_redirect_ip->u - .zr.redirect_ip_v4.s_addr) + if (api_action_redirect_ip->u.zr + .redirect_ip_v4.s_addr + != INADDR_ANY) continue; - if (!path->attr->nexthop.s_addr) + if (path->attr->nexthop.s_addr + == INADDR_ANY) continue; - api_action_redirect_ip->u - .zr.redirect_ip_v4.s_addr = + api_action_redirect_ip->u.zr + .redirect_ip_v4.s_addr = path->attr->nexthop.s_addr; api_action_redirect_ip->u.zr.duplicate = ecom_eval->val[7]; @@ -1260,7 +1262,6 @@ void bgp_pbr_cleanup(struct bgp *bgp) return; bgp_pbr_reset(bgp, AFI_IP); XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); - bgp->bgp_pbr_cfg = NULL; } void bgp_pbr_init(struct bgp *bgp) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 5f4486b80..5e41f82d4 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -38,9 +38,10 @@ #include "workqueue.h" #include "queue.h" #include "memory.h" +#include "srv6.h" #include "lib/json.h" #include "lib_errors.h" - +#include "zclient.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" @@ -89,7 +90,7 @@ /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; - +const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); /* PMSI strings. */ #define PMSI_TNLTYPE_STR_NO_INFO "No info" #define PMSI_TNLTYPE_STR_DEFAULT PMSI_TNLTYPE_STR_NO_INFO @@ -233,8 +234,6 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) if ((*extra)->bgp_fs_pbr) list_delete(&((*extra)->bgp_fs_pbr)); XFREE(MTYPE_BGP_ROUTE_EXTRA, *extra); - - *extra = NULL; } /* Get bgp_path_info extra information for the given bgp_path_info, lazy @@ -295,6 +294,87 @@ struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path) return path; } +/* This function sets flag BGP_NODE_SELECT_DEFER based on condition */ +static int bgp_node_set_defer_flag(struct bgp_node *rn, bool delete) +{ + struct peer *peer; + struct bgp_path_info *old_pi, *nextpi; + bool set_flag = 0; + struct bgp *bgp = NULL; + struct bgp_table *table = NULL; + afi_t afi = 0; + safi_t safi = 0; + char buf[PREFIX2STR_BUFFER]; + + /* If the flag BGP_NODE_SELECT_DEFER is set and new path is added + * then the route selection is deferred + */ + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER) && (!delete)) + return 0; + + if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) { + if (BGP_DEBUG(update, UPDATE_OUT)) { + prefix2str(&rn->p, buf, PREFIX2STR_BUFFER); + zlog_debug( + "Route %s is in workqueue and being processed, not deferred.", + buf); + } + return 0; + } + + table = bgp_node_table(rn); + if (table) { + bgp = table->bgp; + afi = table->afi; + safi = table->safi; + } + + for (old_pi = bgp_node_get_bgp_path_info(rn); + (old_pi != NULL) && (nextpi = old_pi->next, 1); old_pi = nextpi) { + if (CHECK_FLAG(old_pi->flags, BGP_PATH_SELECTED)) + continue; + + /* Route selection is deferred if there is a stale path which + * which indicates peer is in restart mode + */ + if (CHECK_FLAG(old_pi->flags, BGP_PATH_STALE) + && (old_pi->sub_type == BGP_ROUTE_NORMAL)) { + set_flag = 1; + } else { + /* If the peer is graceful restart capable and peer is + * restarting mode, set the flag BGP_NODE_SELECT_DEFER + */ + peer = old_pi->peer; + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) + && BGP_PEER_RESTARTING_MODE(peer) + && (old_pi + && old_pi->sub_type == BGP_ROUTE_NORMAL)) { + set_flag = 1; + } + } + if (set_flag) + break; + } + + /* Set the flag BGP_NODE_SELECT_DEFER if route selection deferral timer + * is active + */ + if (set_flag && table) { + if (bgp && (bgp->gr_info[afi][safi].t_select_deferral)) { + SET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); + prefix2str(&rn->p, buf, PREFIX2STR_BUFFER); + if (rn->rt_node == NULL) + rn->rt_node = listnode_add( + bgp->gr_info[afi][safi].route_list, rn); + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("DEFER route %s, rn %p, node %p", + buf, rn, rn->rt_node); + return 0; + } + } + return -1; +} + void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi) { struct bgp_path_info *top; @@ -310,6 +390,7 @@ void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi) bgp_path_info_lock(pi); bgp_lock_node(rn); peer_lock(pi->peer); /* bgp_path_info peer reference */ + bgp_node_set_defer_flag(rn, false); } /* Do the actual removal of info from RIB, for use by bgp_process @@ -1454,8 +1535,8 @@ void bgp_attr_add_gshut_community(struct attr *attr) static void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr) { if (family == AF_INET) { - attr->nexthop.s_addr = 0; - attr->mp_nexthop_global_in.s_addr = 0; + attr->nexthop.s_addr = INADDR_ANY; + attr->mp_nexthop_global_in.s_addr = INADDR_ANY; } if (family == AF_INET6) memset(&attr->mp_nexthop_global, 0, IPV6_MAX_BYTELEN); @@ -1973,6 +2054,30 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, return 1; } +static int bgp_route_select_timer_expire(struct thread *thread) +{ + struct afi_safi_info *info; + afi_t afi; + safi_t safi; + struct bgp *bgp; + + info = THREAD_ARG(thread); + afi = info->afi; + safi = info->safi; + bgp = info->bgp; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("afi %d, safi %d : route select timer expired", afi, + safi); + + bgp->gr_info[afi][safi].t_route_select = NULL; + + XFREE(MTYPE_TMP, info); + + /* Best path selection */ + return bgp_best_path_select_defer(bgp, afi, safi); +} + void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, struct bgp_maxpaths_cfg *mpath_cfg, struct bgp_path_info_pair *result, afi_t afi, @@ -2376,6 +2481,15 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, afi2str(afi), safi2str(safi)); } + /* The best path calculation for the route is deferred if + * BGP_NODE_SELECT_DEFER is set + */ + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("SELECT_DEFER falg set for route %p", rn); + return; + } + /* Best path selection. */ bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new, afi, safi); @@ -2603,6 +2717,65 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, return; } +/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */ +int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + int cnt = 0; + struct afi_safi_info *thread_info; + struct listnode *node = NULL, *nnode = NULL; + + if (bgp->gr_info[afi][safi].t_route_select) + BGP_TIMER_OFF(bgp->gr_info[afi][safi].t_route_select); + + if (BGP_DEBUG(update, UPDATE_OUT)) { + zlog_debug("%s: processing route for %s : cnt %d", __func__, + get_afi_safi_str(afi, safi, false), + listcount(bgp->gr_info[afi][safi].route_list)); + } + + /* Process the route list */ + node = listhead(bgp->gr_info[afi][safi].route_list); + while (node) { + rn = listgetdata(node); + nnode = node->next; + list_delete_node(bgp->gr_info[afi][safi].route_list, node); + rn->rt_node = NULL; + + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + UNSET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); + bgp_process_main_one(bgp, rn, afi, safi); + cnt++; + if (cnt >= BGP_MAX_BEST_ROUTE_SELECT) + break; + } + node = nnode; + } + + /* Send EOR message when all routes are processed */ + if (list_isempty(bgp->gr_info[afi][safi].route_list)) { + bgp_send_delayed_eor(bgp); + /* Send route processing complete message to RIB */ + bgp_zebra_update(afi, safi, bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); + return 0; + } + + thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); + + thread_info->afi = afi; + thread_info->safi = safi; + thread_info->bgp = bgp; + + /* If there are more routes to be processed, start the + * selection timer + */ + thread_add_timer(bm->master, bgp_route_select_timer_expire, thread_info, + BGP_ROUTE_SELECT_DELAY, + &bgp->gr_info[afi][safi].t_route_select); + return 0; +} + static wq_item_status bgp_process_wq(struct work_queue *wq, void *data) { struct bgp_process_queue *pqnode = data; @@ -2681,6 +2854,16 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) return; + /* If the flag BGP_NODE_SELECT_DEFER is set, do not add route to + * the workqueue + */ + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("BGP_NODE_SELECT_DEFER set for route %p", + rn); + return; + } + if (wq == NULL) return; @@ -2844,13 +3027,38 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi) { + + struct bgp *bgp = NULL; + bool delete_route = false; + bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, safi); - if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) + if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { bgp_path_info_delete(rn, pi); /* keep historical info */ - hook_call(bgp_process, peer->bgp, afi, safi, rn, peer, true); + /* If the selected path is removed, reset BGP_NODE_SELECT_DEFER + * flag + */ + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + delete_route = true; + else if (bgp_node_set_defer_flag(rn, true) < 0) + delete_route = true; + if (delete_route) { + if (CHECK_FLAG(rn->flags, BGP_NODE_SELECT_DEFER)) { + UNSET_FLAG(rn->flags, BGP_NODE_SELECT_DEFER); + bgp = pi->peer->bgp; + if ((rn->rt_node) + && (bgp->gr_info[afi][safi].route_list)) { + list_delete_node(bgp->gr_info[afi][safi] + .route_list, + rn->rt_node); + rn->rt_node = NULL; + } + } + } + } + hook_call(bgp_process, peer->bgp, afi, safi, rn, peer, true); bgp_process(peer->bgp, rn, afi, safi); } @@ -2992,10 +3200,9 @@ static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, /* If NEXT_HOP is present, validate it. */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { - if (attr->nexthop.s_addr == 0 + if (attr->nexthop.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(attr->nexthop.s_addr)) - || bgp_nexthop_self(bgp, afi, type, stype, - attr, rn)) + || bgp_nexthop_self(bgp, afi, type, stype, attr, rn)) return 1; } @@ -3008,11 +3215,11 @@ static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, switch (attr->mp_nexthop_len) { case BGP_ATTR_NHLEN_IPV4: case BGP_ATTR_NHLEN_VPNV4: - ret = (attr->mp_nexthop_global_in.s_addr == 0 - || IPV4_CLASS_DE(ntohl( - attr->mp_nexthop_global_in.s_addr)) - || bgp_nexthop_self(bgp, afi, type, stype, - attr, rn)); + ret = (attr->mp_nexthop_global_in.s_addr == INADDR_ANY + || IPV4_CLASS_DE( + ntohl(attr->mp_nexthop_global_in.s_addr)) + || bgp_nexthop_self(bgp, afi, type, stype, attr, + rn)); break; case BGP_ATTR_NHLEN_IPV6_GLOBAL: @@ -3302,6 +3509,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) { bgp_path_info_unset_flag( rn, pi, BGP_PATH_STALE); + bgp_node_set_defer_flag(rn, false); bgp_process(bgp, rn, afi, safi); } } @@ -3337,8 +3545,10 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, } /* graceful restart STALE flag unset. */ - if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) + if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) { bgp_path_info_unset_flag(rn, pi, BGP_PATH_STALE); + bgp_node_set_defer_flag(rn, false); + } /* The attribute is changed. */ bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); @@ -3431,6 +3641,22 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, bgp_set_valid_label(&extra->label[0]); } + /* Update SRv6 SID */ + if (attr->srv6_l3vpn) { + extra = bgp_path_info_extra_get(pi); + if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) { + sid_copy(&extra->sid[0], + &attr->srv6_l3vpn->sid); + extra->num_sids = 1; + } + } else if (attr->srv6_vpn) { + extra = bgp_path_info_extra_get(pi); + if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) { + sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); + extra->num_sids = 1; + } + } + #if ENABLE_BGP_VNC if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { @@ -3610,6 +3836,18 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, bgp_set_valid_label(&extra->label[0]); } + /* Update SRv6 SID */ + if (safi == SAFI_MPLS_VPN) { + extra = bgp_path_info_extra_get(new); + if (attr->srv6_l3vpn) { + sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid); + extra->num_sids = 1; + } else if (attr->srv6_vpn) { + sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); + extra->num_sids = 1; + } + } + /* Update Overlay Index */ if (afi == AFI_L2VPN) { overlay_index_update(new->attr, @@ -5226,7 +5464,6 @@ static int bgp_static_set(struct vty *vty, const char *negate, bgp_static->rmap.name); route_map_counter_decrement( bgp_static->rmap.map); - bgp_static->rmap.name = NULL; bgp_static->rmap.map = NULL; bgp_static->valid = 0; } @@ -5237,7 +5474,7 @@ static int bgp_static_set(struct vty *vty, const char *negate, bgp_static->backdoor = backdoor; bgp_static->valid = 0; bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = 0; + bgp_static->igpnexthop.s_addr = INADDR_ANY; bgp_static->label_index = label_index; if (rmap) { @@ -5528,7 +5765,7 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, bgp_static->backdoor = 0; bgp_static->valid = 0; bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = 0; + bgp_static->igpnexthop.s_addr = INADDR_ANY; bgp_static->label = label; bgp_static->prd = prd; @@ -5648,7 +5885,6 @@ static int bgp_table_map_set(struct vty *vty, afi_t afi, safi_t safi, } else { XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name); route_map_counter_decrement(rmap->map); - rmap->name = NULL; rmap->map = NULL; } @@ -5667,7 +5903,6 @@ static int bgp_table_map_unset(struct vty *vty, afi_t afi, safi_t safi, rmap = &bgp->table_map[afi][safi]; XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name); route_map_counter_decrement(rmap->map); - rmap->name = NULL; rmap->map = NULL; if (bgp_fibupd_safi(safi)) @@ -7304,7 +7539,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, : inet_ntoa(attr->nexthop), vrf_id_str); } else if (safi == SAFI_FLOWSPEC) { - if (attr->nexthop.s_addr != 0) { + if (attr->nexthop.s_addr != INADDR_ANY) { if (json_paths) { json_nexthop_global = json_object_new_object(); @@ -8994,6 +9229,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, vty_out(vty, " Remote label: %d\n", label); } + /* Remote SID */ + if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) { + inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf)); + if (json_paths) + json_object_string_add(json_path, "remoteSid", buf); + else + vty_out(vty, " Remote SID: %s\n", buf); + } + /* Label Index */ if (attr->label_index != BGP_INVALID_LABEL_INDEX) { if (json_paths) @@ -10494,6 +10738,7 @@ DEFUN (show_ip_bgp_regexp, if (argv_find(argv, argc, "REGEX", &idx)) regstr = argv[idx]->arg; + assert(regstr); return bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi, bgp_show_type_regexp, uj); } @@ -11938,10 +12183,7 @@ static int bgp_distance_set(struct vty *vty, const char *distance_str, bdistance->distance = distance; /* Reset access-list configuration. */ - if (bdistance->access_list) { - XFREE(MTYPE_AS_LIST, bdistance->access_list); - bdistance->access_list = NULL; - } + XFREE(MTYPE_AS_LIST, bdistance->access_list); if (access_list_str) bdistance->access_list = XSTRDUP(MTYPE_AS_LIST, access_list_str); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index b9f3f3f76..8f31cd38d 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -78,6 +78,9 @@ enum bgp_show_adj_route_type { */ #define BGP_MAX_LABELS 2 +/* Maximum number of sids we can process or send with a prefix. */ +#define BGP_MAX_SIDS 6 + /* Error codes for handling NLRI */ #define BGP_NLRI_PARSE_OK 0 #define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1 @@ -118,6 +121,10 @@ struct bgp_path_info_extra { uint16_t af_flags; #define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0) + /* SRv6 SID(s) for SRv6-VPN */ + struct in6_addr sid[BGP_MAX_SIDS]; + uint32_t num_sids; + #if ENABLE_BGP_VNC union { @@ -635,4 +642,5 @@ extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, bool use_json); +extern int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi); #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index ecd770a1d..f5de9ac8c 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1705,7 +1705,7 @@ route_set_ip_nexthop(void *rule, const struct prefix *prefix, */ SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS); - path->attr->nexthop.s_addr = 0; + path->attr->nexthop.s_addr = INADDR_ANY; } } else { /* Set next hop value. */ diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index f31f8cd31..77b547c0a 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -755,7 +755,7 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], oid2in_addr(offset, len, &paddr); } else - paddr.s_addr = 0; + paddr.s_addr = INADDR_ANY; if (!rn) return NULL; @@ -804,7 +804,7 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], return min; } - paddr.s_addr = 0; + paddr.s_addr = INADDR_ANY; } while ((rn = bgp_route_next(rn)) != NULL); } return NULL; diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index b75246b17..04181d38b 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -127,6 +127,43 @@ struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi) return rt; } +/* Delete the route node from the selection deferral route list */ +void bgp_delete_listnode(struct bgp_node *node) +{ + struct route_node *rn = NULL; + struct bgp_table *table = NULL; + struct bgp *bgp = NULL; + afi_t afi; + safi_t safi; + + /* If the route to be deleted is selection pending, update the + * route node in gr_info + */ + if (CHECK_FLAG(node->flags, BGP_NODE_SELECT_DEFER)) { + table = bgp_node_table(node); + + if (table) { + bgp = table->bgp; + afi = table->afi; + safi = table->safi; + } else + return; + + rn = bgp_node_to_rnode(node); + + if (bgp && rn && rn->lock == 1) { + /* Delete the route from the selection pending list */ + if ((node->rt_node) + && (bgp->gr_info[afi][safi].route_list)) { + list_delete_node( + bgp->gr_info[afi][safi].route_list, + node->rt_node); + node->rt_node = NULL; + } + } + } +} + static struct bgp_node * bgp_route_next_until_maxlen(struct bgp_node *node, const struct bgp_node *limit, const uint8_t maxlen) diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index b3542e784..69cca9eee 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -28,6 +28,8 @@ #include "bgpd.h" #include "bgp_advertise.h" +extern void bgp_delete_listnode(struct bgp_node *node); + struct bgp_table { /* table belongs to this instance */ struct bgp *bgp; @@ -95,7 +97,9 @@ struct bgp_node { #define BGP_NODE_USER_CLEAR (1 << 1) #define BGP_NODE_LABEL_CHANGED (1 << 2) #define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3) - +#define BGP_NODE_SELECT_DEFER (1 << 4) + /* list node pointer */ + struct listnode *rt_node; struct bgp_addpath_node_data tx_addpath; enum bgp_path_selection_reason reason; @@ -162,6 +166,7 @@ static inline struct bgp_node *bgp_node_parent_nolock(struct bgp_node *node) */ static inline void bgp_unlock_node(struct bgp_node *node) { + bgp_delete_listnode(node); route_unlock_node(bgp_node_to_rnode(node)); } diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 5b3eb2c71..3dbc67521 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -111,7 +111,6 @@ static void sync_init(struct update_subgroup *subgrp) static void sync_delete(struct update_subgroup *subgrp) { XFREE(MTYPE_BGP_SYNCHRONISE, subgrp->sync); - subgrp->sync = NULL; if (subgrp->hash) hash_free(subgrp->hash); subgrp->hash = NULL; @@ -220,7 +219,6 @@ static void conf_release(struct peer *src, afi_t afi, safi_t safi) XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->usmap.name); XFREE(MTYPE_BGP_PEER_HOST, src->host); - src->host = NULL; } static void peer2_updgrp_copy(struct update_group *updgrp, struct peer_af *paf) @@ -735,7 +733,6 @@ static void update_group_delete(struct update_group *updgrp) conf_release(updgrp->conf, updgrp->afi, updgrp->safi); XFREE(MTYPE_BGP_PEER_HOST, updgrp->conf->host); - updgrp->conf->host = NULL; XFREE(MTYPE_BGP_PEER_IFNAME, updgrp->conf->ifname); diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 39eb06528..86750c0fc 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -467,16 +467,16 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, mod_v4nh = &peer->nexthop.v4; nh_modified = 1; } - } else if (!v4nh.s_addr) { + } else if (v4nh.s_addr == INADDR_ANY) { mod_v4nh = &peer->nexthop.v4; nh_modified = 1; - } else if ( - peer->sort == BGP_PEER_EBGP - && (bgp_multiaccess_check_v4(v4nh, peer) == 0) - && !CHECK_FLAG( + } else if (peer->sort == BGP_PEER_EBGP + && (bgp_multiaccess_check_v4(v4nh, peer) + == 0) + && !CHECK_FLAG( vec->flags, BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) - && !peer_af_flag_check( + && !peer_af_flag_check( peer, paf->afi, paf->safi, PEER_FLAG_NEXTHOP_UNCHANGED)) { /* NOTE: not handling case where NH has new AFI @@ -628,7 +628,7 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, mod_v4nh = &v4nh; /* No route-map changes allowed for EVPN nexthops. */ - if (!v4nh.s_addr) { + if (v4nh.s_addr == INADDR_ANY) { mod_v4nh = &peer->nexthop.v4; nh_modified = 1; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9dc6549d9..f1ec9b493 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -105,9 +105,36 @@ DEFINE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), (bgp, vty)) +#define GR_NO_OPER \ + "The Graceful Restart No Operation was executed as cmd same as previous one." +#define GR_INVALID \ + "The Graceful Restart command used is not valid at this moment." static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); +/* Show BGP peer's information. */ +enum show_type { + show_all, + show_peer, + show_ipv4_all, + show_ipv6_all, + show_ipv4_peer, + show_ipv6_peer +}; + +static struct peer_group *listen_range_exists(struct bgp *bgp, + struct prefix *range, int exact); + +static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, + struct bgp *bgp, + bool use_json, + json_object *json); + +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, bool use_json); + static enum node_type bgp_node_type(afi_t afi, safi_t safi) { switch (afi) { @@ -690,7 +717,16 @@ int bgp_vty_return(struct vty *vty, int ret) str = "Operation not allowed on a directly connected neighbor"; break; case BGP_ERR_PEER_SAFI_CONFLICT: - str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'"; + str = GR_INVALID; + break; + case BGP_ERR_GR_INVALID_CMD: + str = "The Graceful Restart command used is not valid at this moment."; + break; + case BGP_ERR_GR_OPERATION_FAILED: + str = "The Graceful Restart Operation failed due to an err."; + break; + case BGP_GR_NO_OPERATION: + str = GR_NO_OPER; break; } if (str) { @@ -785,7 +821,8 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int ret = 0; bool found = false; struct peer *peer; - struct listnode *node, *nnode; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; /* Clear all neighbors. */ /* @@ -795,6 +832,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, */ if (sort == clear_all) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + ret = bgp_peer_clear(peer, afi, safi, nnode, stype); @@ -802,6 +845,14 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, bgp_clear_vty_error(vty, peer, afi, safi, ret); } + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + /* This is to apply read-only mode on this clear. */ if (stype == BGP_CLEAR_SOFT_NONE) bgp->update_delay_over = 0; @@ -836,6 +887,9 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } } + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + ret = bgp_peer_clear(peer, afi, safi, NULL, stype); /* if afi/safi not defined for this peer, let caller know */ @@ -881,6 +935,11 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (peer->sort == BGP_PEER_IBGP) continue; + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + ret = bgp_peer_clear(peer, afi, safi, nnode, stype); if (ret < 0) @@ -889,6 +948,14 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, found = true; } + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + if (!found) vty_out(vty, "%%BGP: No external %s peer is configured\n", @@ -905,6 +972,11 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (peer->as != as) continue; + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + ret = bgp_peer_clear(peer, afi, safi, nnode, stype); if (ret < 0) @@ -913,6 +985,14 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, found = true; } + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + if (!found) vty_out(vty, "%%BGP: No %s peer is configured with AS %s\n", @@ -2093,37 +2173,68 @@ DEFUN (no_bgp_deterministic_med, return CMD_SUCCESS; } -/* "bgp graceful-restart" configuration. */ +/* "bgp graceful-restart mode" configuration. */ DEFUN (bgp_graceful_restart, - bgp_graceful_restart_cmd, - "bgp graceful-restart", - "BGP specific commands\n" - "Graceful restart capability parameters\n") + bgp_graceful_restart_cmd, + "bgp graceful-restart", + "BGP specific commands\n" + GR_CMD + ) { + int ret = BGP_GR_FAILURE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : START "); + VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_RESTART); - return CMD_SUCCESS; + + ret = bgp_gr_update_all(bgp, GLOBAL_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer, + ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + return bgp_vty_return(vty, ret); } DEFUN (no_bgp_graceful_restart, - no_bgp_graceful_restart_cmd, - "no bgp graceful-restart", - NO_STR - "BGP specific commands\n" - "Graceful restart capability parameters\n") + no_bgp_graceful_restart_cmd, + "no bgp graceful-restart", + NO_STR + "BGP specific commands\n" + NO_GR_CMD + ) { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_RESTART); - return CMD_SUCCESS; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : START "); + + int ret = BGP_GR_FAILURE; + + ret = bgp_gr_update_all(bgp, NO_GLOBAL_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer, + ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); } DEFUN (bgp_graceful_restart_stalepath_time, - bgp_graceful_restart_stalepath_time_cmd, - "bgp graceful-restart stalepath-time (1-4095)", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the max time to hold onto restarting peer's stale paths\n" - "Delay value (seconds)\n") + bgp_graceful_restart_stalepath_time_cmd, + "bgp graceful-restart stalepath-time (1-4095)", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; @@ -2135,12 +2246,12 @@ DEFUN (bgp_graceful_restart_stalepath_time, } DEFUN (bgp_graceful_restart_restart_time, - bgp_graceful_restart_restart_time_cmd, - "bgp graceful-restart restart-time (1-4095)", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the time to wait to delete stale routes before a BGP open message is received\n" - "Delay value (seconds)\n") + bgp_graceful_restart_restart_time_cmd, + "bgp graceful-restart restart-time (1-4095)", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; @@ -2151,14 +2262,36 @@ DEFUN (bgp_graceful_restart_restart_time, return CMD_SUCCESS; } -DEFUN (no_bgp_graceful_restart_stalepath_time, - no_bgp_graceful_restart_stalepath_time_cmd, - "no bgp graceful-restart stalepath-time [(1-4095)]", - NO_STR +DEFUN (bgp_graceful_restart_select_defer_time, + bgp_graceful_restart_select_defer_time_cmd, + "bgp graceful-restart select-defer-time (0-3600)", "BGP specific commands\n" "Graceful restart capability parameters\n" - "Set the max time to hold onto restarting peer's stale paths\n" - "Delay value (seconds)\n") + "Set the time to defer the BGP route selection after restart\n" + "Delay value (seconds, 0 - disable)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 3; + uint32_t defer_time; + + defer_time = strtoul(argv[idx_number]->arg, NULL, 10); + bgp->select_defer_time = defer_time; + if (defer_time == 0) + bgp_flag_set(bgp, BGP_FLAG_SELECT_DEFER_DISABLE); + else + bgp_flag_unset(bgp, BGP_FLAG_SELECT_DEFER_DISABLE); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_stalepath_time, + no_bgp_graceful_restart_stalepath_time_cmd, + "no bgp graceful-restart stalepath-time [(1-4095)]", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2167,26 +2300,43 @@ DEFUN (no_bgp_graceful_restart_stalepath_time, } DEFUN (no_bgp_graceful_restart_restart_time, - no_bgp_graceful_restart_restart_time_cmd, - "no bgp graceful-restart restart-time [(1-4095)]", + no_bgp_graceful_restart_restart_time_cmd, + "no bgp graceful-restart restart-time [(1-4095)]", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_select_defer_time, + no_bgp_graceful_restart_select_defer_time_cmd, + "no bgp graceful-restart select-defer-time [(0-3600)]", NO_STR "BGP specific commands\n" "Graceful restart capability parameters\n" - "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Set the time to defer the BGP route selection after restart\n" "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; + bgp_flag_unset(bgp, BGP_FLAG_SELECT_DEFER_DISABLE); + return CMD_SUCCESS; } DEFUN (bgp_graceful_restart_preserve_fw, - bgp_graceful_restart_preserve_fw_cmd, - "bgp graceful-restart preserve-fw-state", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") + bgp_graceful_restart_preserve_fw_cmd, + "bgp graceful-restart preserve-fw-state", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); bgp_flag_set(bgp, BGP_FLAG_GR_PRESERVE_FWD); @@ -2194,15 +2344,364 @@ DEFUN (bgp_graceful_restart_preserve_fw, } DEFUN (no_bgp_graceful_restart_preserve_fw, - no_bgp_graceful_restart_preserve_fw_cmd, - "no bgp graceful-restart preserve-fw-state", + no_bgp_graceful_restart_preserve_fw_cmd, + "no bgp graceful-restart preserve-fw-state", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_flag_unset(bgp, BGP_FLAG_GR_PRESERVE_FWD); + return CMD_SUCCESS; +} + +DEFUN (bgp_graceful_restart_disable, + bgp_graceful_restart_disable_cmd, + "bgp graceful-restart-disable", + "BGP specific commands\n" + GR_DISABLE) +{ + int ret = BGP_GR_FAILURE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_graceful_restart_disable_cmd : START "); + + VTY_DECLVAR_CONTEXT(bgp, bgp); + + ret = bgp_gr_update_all(bgp, GLOBAL_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, + bgp->peer, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_graceful_restart_disable_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_graceful_restart_disable, + no_bgp_graceful_restart_disable_cmd, + "no bgp graceful-restart-disable", + NO_STR + "BGP specific commands\n" + NO_GR_DISABLE + ) +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_graceful_restart_disable_cmd : START "); + + int ret = BGP_GR_FAILURE; + + ret = bgp_gr_update_all(bgp, NO_GLOBAL_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer, + ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_graceful_restart_disable_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_set, + bgp_neighbor_graceful_restart_set_cmd, + "neighbor graceful-restart", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart, + no_bgp_neighbor_graceful_restart_set_cmd, + "no neighbor graceful-restart", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_helper_set, + bgp_neighbor_graceful_restart_helper_set_cmd, + "neighbor graceful-restart-helper", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_HELPER_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + + ret = bgp_neighbor_graceful_restart(peer, PEER_HELPER_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart_helper, + no_bgp_neighbor_graceful_restart_helper_set_cmd, + "no neighbor graceful-restart-helper", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_HELPER_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_HELPER_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_disable_set, + bgp_neighbor_graceful_restart_disable_set_cmd, + "neighbor graceful-restart-disable", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_DISABLE_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_disable_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_neighbor_graceful_restart(peer, PEER_DISABLE_CMD); + + if (peer->bgp->t_startup) + bgp_peer_gr_flags_update(peer); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR]bgp_neighbor_graceful_restart_disable_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart_disable, + no_bgp_neighbor_graceful_restart_disable_set_cmd, + "no neighbor graceful-restart-disable", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_DISABLE_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN_HIDDEN (bgp_graceful_restart_disable_eor, + bgp_graceful_restart_disable_eor_cmd, + "bgp graceful-restart disable-eor", + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Disable EOR Check\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_flag_set(bgp, BGP_FLAG_GR_DISABLE_EOR); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_bgp_graceful_restart_disable_eor, + no_bgp_graceful_restart_disable_eor_cmd, + "no bgp graceful-restart disable-eor", + NO_STR + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Disable EOR Check\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_flag_unset(bgp, BGP_FLAG_GR_DISABLE_EOR); + + return CMD_SUCCESS; +} + +DEFUN (bgp_graceful_restart_rib_stale_time, + bgp_graceful_restart_rib_stale_time_cmd, + "bgp graceful-restart rib-stale-time (1-3600)", + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Specify the stale route removal timer in rib\n" + "Delay value (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 3; + uint32_t stale_time; + + stale_time = strtoul(argv[idx_number]->arg, NULL, 10); + bgp->rib_stale_time = stale_time; + /* Send the stale timer update message to RIB */ + if (bgp_zebra_stale_timer_update(bgp)) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_rib_stale_time, + no_bgp_graceful_restart_rib_stale_time_cmd, + "no bgp graceful-restart rib-stale-time [(1-3600)]", NO_STR "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") + "Graceful restart configuration parameters\n" + "Specify the stale route removal timer in rib\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GR_PRESERVE_FWD); + + bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME; + /* Send the stale timer update message to RIB */ + if (bgp_zebra_stale_timer_update(bgp)) + return CMD_WARNING; + return CMD_SUCCESS; } @@ -8882,8 +9381,6 @@ const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json) return get_afi_safi_vty_str(afi, safi); } -/* Show BGP peer's information. */ -enum show_type { show_all, show_peer, show_ipv4_all, show_ipv6_all, show_ipv4_peer, show_ipv6_peer }; static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, afi_t afi, safi_t safi, @@ -8948,9 +9445,395 @@ static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, } } -static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, - safi_t safi, bool use_json, - json_object *json_neigh) +static void bgp_show_neighnor_graceful_restart_rbit(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + bool rbit_status = 0; + + if (!use_json) + vty_out(vty, "\n R bit : "); + + if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV) + && (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) + && (p->status == Established)) { + + if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_BIT_RCV)) + rbit_status = 1; + else + rbit_status = 0; + } + + if (rbit_status) { + if (use_json) + json_object_boolean_true_add(json, "rBit"); + else + vty_out(vty, "True\n"); + } else { + if (use_json) + json_object_boolean_false_add(json, "rBit"); + else + vty_out(vty, "False\n"); + } +} + +static void bgp_show_neighbor_graceful_restart_remote_mode(struct vty *vty, + struct peer *peer, + bool use_json, + json_object *json) +{ + const char *mode = "NotApplicable"; + + if (!use_json) + vty_out(vty, "\n Remote GR Mode : "); + + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) + && (peer->status == Established)) { + + if ((peer->nsf_af_count == 0) + && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Disable"; + + } else if (peer->nsf_af_count == 0 + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Helper"; + + } else if (peer->nsf_af_count != 0 + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Restart"; + } + } + + if (use_json) { + json_object_string_add(json, "remoteGrMode", mode); + } else + vty_out(vty, mode, "\n"); +} + +static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + const char *mode = "Invalid"; + + if (!use_json) + vty_out(vty, " Local GR Mode : "); + + if (bgp_peer_gr_mode_get(p) == PEER_HELPER) + mode = "Helper"; + else if (bgp_peer_gr_mode_get(p) == PEER_GR) + mode = "Restart"; + else if (bgp_peer_gr_mode_get(p) == PEER_DISABLE) + mode = "Disable"; + else if (bgp_peer_gr_mode_get(p) == PEER_GLOBAL_INHERIT) { + if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_HELPER) + mode = "Helper*"; + else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_GR) + mode = "Restart*"; + else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_DISABLE) + mode = "Disable*"; + else + mode = "Invalid*"; + } + + if (use_json) { + json_object_string_add(json, "localGrMode", mode); + } else { + vty_out(vty, mode, "\n"); + } +} + +static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( + struct vty *vty, struct peer *peer, bool use_json, json_object *json) +{ + afi_t afi; + safi_t safi; + json_object *json_afi_safi = NULL; + json_object *json_timer = NULL; + json_object *json_endofrib_status = NULL; + bool eor_flag = false; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { + if (!peer->afc[afi][safi]) + continue; + + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) + || !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) + continue; + + if (use_json) { + json_afi_safi = json_object_new_object(); + json_endofrib_status = json_object_new_object(); + json_timer = json_object_new_object(); + } + + if (peer->eor_stime[afi][safi] + >= peer->pkt_stime[afi][safi]) + eor_flag = true; + else + eor_flag = false; + + if (!use_json) { + vty_out(vty, " %s :\n", + get_afi_safi_str(afi, safi, false)); + + vty_out(vty, " F bit : "); + } + + if (peer->nsf[afi][safi] + && CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV)) { + + if (use_json) { + json_object_boolean_true_add( + json_afi_safi, "fBit"); + } else + vty_out(vty, "True\n"); + } else { + if (use_json) + json_object_boolean_false_add( + json_afi_safi, "fBit"); + else + vty_out(vty, "False\n"); + } + + if (!use_json) + vty_out(vty, " End-of-RIB Received : "); + + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + if (use_json) + json_object_boolean_true_add( + json_endofrib_status, + "endOfRibRecv"); + else + vty_out(vty, "Yes\n"); + } else { + if (use_json) + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibRecv"); + else + vty_out(vty, "No\n"); + } + + if (!use_json) + vty_out(vty, " End-of-RIB Send : "); + + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + if (use_json) { + json_object_boolean_true_add( + json_endofrib_status, + "endOfRibSend"); + + PRINT_EOR_JSON(eor_flag); + } else { + vty_out(vty, "Yes\n"); + vty_out(vty, + " EoRSentAfterUpdate : "); + + PRINT_EOR(eor_flag); + } + } else { + if (use_json) { + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibSend"); + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibSentAfterUpdate"); + } else { + vty_out(vty, "No\n"); + vty_out(vty, + " EoRSentAfterUpdate : "); + vty_out(vty, "No\n"); + } + } + + if (use_json) { + json_object_int_add(json_timer, + "stalePathTimer", + peer->bgp->stalepath_time); + + if (peer->t_gr_stale != NULL) { + json_object_int_add( + json_timer, + "stalePathTimerRemaining", + thread_timer_remain_second( + peer->t_gr_stale)); + } + + /* Display Configured Selection + * Deferral only when when + * Gr mode is enabled. + */ + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) { + json_object_int_add( + json_timer, + "selectionDeferralTimer", + peer->bgp->stalepath_time); + } + + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral + != NULL) { + + json_object_int_add( + json_timer, + "selectionDeferralTimerRemaining", + thread_timer_remain_second( + peer->bgp + ->gr_info[afi] + [safi] + .t_select_deferral)); + } + } else { + vty_out(vty, " Timers:\n"); + + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Configured Stale Path Time(sec)%*s: %u\n", + 8, "", peer->bgp->stalepath_time); + + if (peer->t_gr_stale != NULL) { + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Stale Path Remaining(sec)%*s: %ld\n", + 14, "", + thread_timer_remain_second( + peer->t_gr_stale)); + } + /* Display Configured Selection + * Deferral only when when + * Gr mode is enabled. + */ + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) { + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Configured Selection Deferral Time(sec): %u\n", + peer->bgp->select_defer_time); + } + + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral + != NULL) { + + vty_out(vty, "%*s", 6, ""); + vty_out(vty, + "Selection Deferral Time Remaining(sec) : %ld\n", + thread_timer_remain_second( + peer->bgp + ->gr_info[afi] + [safi] + .t_select_deferral)); + } + } + if (use_json) { + json_object_object_add(json_afi_safi, + "endOfRibStatus", + json_endofrib_status); + json_object_object_add(json_afi_safi, "timers", + json_timer); + json_object_object_add( + json, get_afi_safi_str(afi, safi, true), + json_afi_safi); + } + } + } +} + +static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + if (use_json) { + json_object *json_timer = NULL; + + json_timer = json_object_new_object(); + + json_object_int_add(json_timer, "configuredRestartTimer", + p->bgp->restart_time); + + json_object_int_add(json_timer, "receivedRestartTimer", + p->v_gr_restart); + + if (p->t_gr_restart != NULL) + json_object_int_add( + json_timer, "restartTimerRemaining", + thread_timer_remain_second(p->t_gr_restart)); + + json_object_object_add(json, "timers", json_timer); + } else { + + vty_out(vty, " Timers :\n"); + vty_out(vty, " Configured Restart Time(sec) : %u\n", + p->bgp->restart_time); + + vty_out(vty, " Received Restart Time(sec) : %u\n", + p->v_gr_restart); + if (p->t_gr_restart != NULL) + vty_out(vty, + " Restart Time Remaining(sec) : %ld\n", + thread_timer_remain_second(p->t_gr_restart)); + if (p->t_gr_restart != NULL) { + vty_out(vty, + " Restart Time Remaining(sec) : %ld\n", + thread_timer_remain_second(p->t_gr_restart)); + } + } +} + +static void bgp_show_peer_gr_status(struct vty *vty, struct peer *p, + bool use_json, json_object *json) +{ + char buf[SU_ADDRSTRLEN] = {0}; + char dn_flag[2] = {0}; + char neighborAddr[INET6_ADDRSTRLEN] = {0}; + + if (!p->conf_if && peer_dynamic_neighbor(p)) + dn_flag[0] = '*'; + + if (p->conf_if) { + if (use_json) + json_object_string_add( + json, "neighborAddr", + BGP_PEER_SU_UNSPEC(p) + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); + else + vty_out(vty, "BGP neighbor on %s: %s\n", p->conf_if, + BGP_PEER_SU_UNSPEC(p) + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); + } else { + sprintf(neighborAddr, "%s%s", dn_flag, p->host); + + if (use_json) + json_object_string_add(json, "neighborAddr", + neighborAddr); + else + vty_out(vty, "BGP neighbor is %s\n", neighborAddr); + } + + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json); +} + +static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, + safi_t safi, bool use_json, + json_object *json_neigh) { struct bgp_filter *filter; struct peer_af *paf; @@ -9262,7 +10145,8 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, "prefixAllowedRestartIntervalMsecs", p->pmax_restart[afi][safi] * 60000); } - json_object_object_add(json_neigh, get_afi_safi_str(afi, safi, true), + json_object_object_add(json_neigh, + get_afi_safi_str(afi, safi, true), json_addr); } else { @@ -10701,14 +11585,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "none"); vty_out(vty, "\n"); } - } + } /* Gracefull Restart */ } } } /* graceful restart information */ - if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || p->t_gr_restart - || p->t_gr_stale) { json_object *json_grace = NULL; json_object *json_grace_send = NULL; json_object *json_grace_recv = NULL; @@ -10720,7 +11602,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_grace_send = json_object_new_object(); json_grace_recv = json_object_new_object(); - if (p->status == Established) { + if ((p->status == Established) + && CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) { FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(p->af_sflags[afi][safi], PEER_STATUS_EOR_SEND)) { @@ -10745,12 +11628,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } } - json_object_object_add(json_grace, "endOfRibSend", json_grace_send); json_object_object_add(json_grace, "endOfRibRecv", json_grace_recv); + if (p->t_gr_restart) json_object_int_add(json_grace, "gracefulRestartTimerMsecs", @@ -10765,12 +11648,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, thread_timer_remain_second( p->t_gr_stale) * 1000); - + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, + json_grace); json_object_object_add( json_neigh, "gracefulRestartInfo", json_grace); } else { - vty_out(vty, " Graceful restart information:\n"); - if (p->status == Established) { + vty_out(vty, " Graceful restart informations:\n"); + if ((p->status == Established) + && CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) { + vty_out(vty, " End-of-RIB send: "); FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(p->af_sflags[afi][safi], @@ -10778,9 +11665,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "%s%s", eor_send_af_count ? ", " : "", - get_afi_safi_str(afi, - safi, - false)); + get_afi_safi_str( + afi, safi, + false)); eor_send_af_count++; } } @@ -10814,8 +11701,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, " The remaining time of stalepath timer is %ld\n", thread_timer_remain_second( p->t_gr_stale)); + + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, NULL); } - } + if (use_json) { json_object *json_stat = NULL; json_stat = json_object_new_object(); @@ -11229,6 +12119,82 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } +static int bgp_show_neighbor_graceful_restart(struct vty *vty, struct bgp *bgp, + enum show_type type, + union sockunion *su, + const char *conf_if, afi_t afi, + bool use_json, json_object *json) +{ + struct listnode *node, *nnode; + struct peer *peer; + int find = 0; + safi_t safi = SAFI_UNICAST; + json_object *json_neighbor = NULL; + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + + if ((peer->afc[afi][safi]) == 0) + continue; + + if (use_json) + json_neighbor = json_object_new_object(); + + if (type == show_all) { + bgp_show_peer_gr_status(vty, peer, use_json, + json_neighbor); + + if (use_json) + json_object_object_add(json, peer->host, + json_neighbor); + + } else if (type == show_peer) { + if (conf_if) { + if ((peer->conf_if + && !strcmp(peer->conf_if, conf_if)) + || (peer->hostname + && !strcmp(peer->hostname, conf_if))) { + find = 1; + bgp_show_peer_gr_status(vty, peer, + use_json, + json_neighbor); + } + } else { + if (sockunion_same(&peer->su, su)) { + find = 1; + bgp_show_peer_gr_status(vty, peer, + use_json, + json_neighbor); + } + } + if (use_json && find) + json_object_object_add(json, peer->host, + json_neighbor); + } + + if (find) + break; + } + + if (type == show_peer && !find) { + if (use_json) + json_object_boolean_true_add(json, "bgpNoSuchNeighbor"); + else + vty_out(vty, "%% No such neighbor\n"); + } + if (use_json) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, enum show_type type, union sockunion *su, const char *conf_if, bool use_json, @@ -11334,6 +12300,42 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, return CMD_SUCCESS; } +static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, bool use_json) +{ + + int ret; + struct bgp *bgp; + union sockunion su; + json_object *json = NULL; + + bgp = bgp_get_default(); + + if (!bgp) + return; + + if (!use_json) + bgp_show_global_graceful_restart_mode_vty(vty, bgp, use_json, + NULL); + + json = json_object_new_object(); + if (ip_str) { + ret = str2sockunion(ip_str, &su); + if (ret < 0) + bgp_show_neighbor_graceful_restart(vty, bgp, type, NULL, + ip_str, afi, + use_json, json); + else + bgp_show_neighbor_graceful_restart( + vty, bgp, type, &su, NULL, afi, use_json, json); + } else + bgp_show_neighbor_graceful_restart(vty, bgp, type, NULL, NULL, + afi, use_json, json); + json_object_free(json); +} + static void bgp_show_all_instances_neighbors_vty(struct vty *vty, enum show_type type, const char *ip_str, @@ -11471,6 +12473,50 @@ static int bgp_show_neighbor_vty(struct vty *vty, const char *name, return CMD_SUCCESS; } + + +/* "show [ip] bgp neighbors graceful-restart" commands. */ +DEFUN (show_ip_bgp_neighbors_gracrful_restart, + show_ip_bgp_neighbors_graceful_restart_cmd, + "show bgp [] neighbors [] graceful-restart [json]", + SHOW_STR + BGP_STR + IP_STR + IPV6_STR + NEIGHBOR_STR + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + GR_SHOW + JSON_STR) +{ + char *sh_arg = NULL; + enum show_type sh_type; + int idx = 0; + afi_t afi = AFI_MAX; + bool uj = use_json(argc, argv); + + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) + afi = AFI_MAX; + + idx++; + + if (argv_find(argv, argc, "A.B.C.D", &idx) + || argv_find(argv, argc, "X:X::X:X", &idx) + || argv_find(argv, argc, "WORD", &idx)) { + sh_type = show_peer; + sh_arg = argv[idx]->arg; + } else + sh_type = show_all; + + if (!argv_find(argv, argc, "graceful-restart", &idx)) + return CMD_SUCCESS; + + + return bgp_show_neighbor_graceful_restart_afi_all(vty, sh_type, sh_arg, + afi, uj); +} + /* "show [ip] bgp neighbors" commands. */ DEFUN (show_ip_bgp_neighbors, show_ip_bgp_neighbors_cmd, @@ -11609,7 +12655,65 @@ DEFUN (show_ip_bgp_lcommunity_info, return CMD_SUCCESS; } +/* Graceful Restart */ + +static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, + struct bgp *bgp, + bool use_json, + json_object *json) +{ + + + vty_out(vty, "\n%s", SHOW_GR_HEADER); + + enum global_mode bgp_global_gr_mode = bgp_global_gr_mode_get(bgp); + + switch (bgp_global_gr_mode) { + + case GLOBAL_HELPER: + vty_out(vty, "Global BGP GR Mode : Helper\n"); + break; + + case GLOBAL_GR: + vty_out(vty, "Global BGP GR Mode : Restart\n"); + break; + + case GLOBAL_DISABLE: + vty_out(vty, "Global BGP GR Mode : Disable\n"); + break; + + case GLOBAL_INVALID: + vty_out(vty, + "Global BGP GR Mode Invalid\n"); + break; + } + vty_out(vty, "\n"); +} + +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, bool use_json) +{ + if ((afi == AFI_MAX) && (ip_str == NULL)) { + afi = AFI_IP; + + while ((afi != AFI_L2VPN) && (afi < AFI_MAX)) { + bgp_show_neighbor_graceful_restart_vty( + vty, type, ip_str, afi, use_json); + afi++; + } + } else if (afi != AFI_MAX) { + bgp_show_neighbor_graceful_restart_vty(vty, type, ip_str, afi, + use_json); + } else { + return CMD_ERR_INCOMPLETE; + } + + return CMD_SUCCESS; +} +/* Graceful Restart */ DEFUN (show_ip_bgp_attr_info, show_ip_bgp_attr_info_cmd, @@ -13437,6 +14541,29 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (peer->as_path_loop_detection) vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", addr); + + if (!CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { + + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) { + vty_out(vty, + " neighbor %s graceful-restart-helper\n", addr); + } else if (CHECK_FLAG( + peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) { + vty_out(vty, + " neighbor %s graceful-restart\n", addr); + } else if ( + (!(CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) + && !(CHECK_FLAG( + peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)))) { + vty_out(vty, " neighbor %s graceful-restart-disable\n", + addr); + } + } } /* BGP peer configuration display function. */ @@ -13963,12 +15090,22 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp graceful-restart stalepath-time %u\n", bgp->stalepath_time); + if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) vty_out(vty, " bgp graceful-restart restart-time %u\n", bgp->restart_time); - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) + + if (bgp->select_defer_time != BGP_DEFAULT_SELECT_DEFERRAL_TIME) + vty_out(vty, + " bgp graceful-restart select-defer-time %u\n", + bgp->select_defer_time); + + if (bgp_global_gr_mode_get(bgp) == GLOBAL_GR) vty_out(vty, " bgp graceful-restart\n"); + if (bgp_global_gr_mode_get(bgp) == GLOBAL_DISABLE) + vty_out(vty, " bgp graceful-restart-disable\n"); + /* BGP graceful-shutdown */ if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) vty_out(vty, " bgp graceful-shutdown\n"); @@ -13978,6 +15115,12 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp graceful-restart preserve-fw-state\n"); + /* Stale timer for RIB */ + if (bgp->rib_stale_time != BGP_DEFAULT_RIB_STALE_TIME) + vty_out(vty, + " bgp graceful-restart rib-stale-time %u\n", + bgp->rib_stale_time); + /* BGP bestpath method. */ if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) vty_out(vty, " bgp bestpath as-path ignore\n"); @@ -14346,17 +15489,45 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_deterministic_med_cmd); install_element(BGP_NODE, &no_bgp_deterministic_med_cmd); - /* "bgp graceful-restart" commands */ + /* "bgp graceful-restart" command */ install_element(BGP_NODE, &bgp_graceful_restart_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_cmd); + + /* "bgp graceful-restart-disable" command */ + install_element(BGP_NODE, &bgp_graceful_restart_disable_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_disable_cmd); + + /* "neighbor a:b:c:d graceful-restart" command */ + install_element(BGP_NODE, &bgp_neighbor_graceful_restart_set_cmd); + install_element(BGP_NODE, &no_bgp_neighbor_graceful_restart_set_cmd); + + /* "neighbor a:b:c:d graceful-restart-disable" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_disable_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_disable_set_cmd); + + /* "neighbor a:b:c:d graceful-restart-helper" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_helper_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_helper_set_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd); install_element(BGP_NODE, &bgp_graceful_restart_restart_time_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_restart_time_cmd); - + install_element(BGP_NODE, &bgp_graceful_restart_select_defer_time_cmd); + install_element(BGP_NODE, + &no_bgp_graceful_restart_select_defer_time_cmd); install_element(BGP_NODE, &bgp_graceful_restart_preserve_fw_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_preserve_fw_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_disable_eor_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_disable_eor_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_rib_stale_time_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_rib_stale_time_cmd); + /* "bgp graceful-shutdown" commands */ install_element(BGP_NODE, &bgp_graceful_shutdown_cmd); install_element(BGP_NODE, &no_bgp_graceful_shutdown_cmd); @@ -15351,6 +16522,8 @@ void bgp_vty_init(void) /* "show [ip] bgp neighbors" commands. */ install_element(VIEW_NODE, &show_ip_bgp_neighbors_cmd); + install_element(VIEW_NODE, &show_ip_bgp_neighbors_graceful_restart_cmd); + /* "show [ip] bgp peer-group" commands. */ install_element(VIEW_NODE, &show_ip_bgp_peer_groups_cmd); @@ -15512,21 +16685,29 @@ static void community_list_perror(struct vty *vty, int ret) /*community-list standard */ DEFUN (community_list_standard, bgp_community_list_standard_cmd, - "bgp community-list <(1-99)|standard WORD> AA:NN...", + "bgp community-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", BGP_STR COMMUNITY_LIST_STR "Community list number (standard)\n" "Add an standard community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) { char *cl_name_or_number = NULL; + char *seq = NULL; int direct = 0; int style = COMMUNITY_LIST_STANDARD; int idx = 0; + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; argv_find(argv, argc, "(1-99)", &idx); argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; @@ -15535,8 +16716,8 @@ DEFUN (community_list_standard, argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); - int ret = community_list_set(bgp_clist, cl_name_or_number, str, direct, - style); + int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq, + direct, style); XFREE(MTYPE_TMP, str); @@ -15551,13 +16732,15 @@ DEFUN (community_list_standard, DEFUN (no_community_list_standard_all, no_bgp_community_list_standard_all_cmd, - "no bgp community-list <(1-99)|standard WORD> AA:NN...", + "no bgp community-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", NO_STR BGP_STR COMMUNITY_LIST_STR "Community list number (standard)\n" "Add an standard community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) @@ -15566,9 +16749,14 @@ DEFUN (no_community_list_standard_all, char *str = NULL; int direct = 0; int style = COMMUNITY_LIST_STANDARD; - + char *seq = NULL; int idx = 0; + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -15587,7 +16775,7 @@ DEFUN (no_community_list_standard_all, argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; - int ret = community_list_unset(bgp_clist, cl_name_or_number, str, + int ret = community_list_unset(bgp_clist, cl_name_or_number, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -15610,22 +16798,30 @@ ALIAS(no_community_list_standard_all, no_bgp_community_list_standard_all_list_cm /*community-list expanded */ DEFUN (community_list_expanded_all, bgp_community_list_expanded_all_cmd, - "bgp community-list <(100-500)|expanded WORD> AA:NN...", + "bgp community-list <(100-500)|expanded WORD> [seq (1-4294967295)] AA:NN...", BGP_STR COMMUNITY_LIST_STR "Community list number (expanded)\n" "Add an expanded community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) { char *cl_name_or_number = NULL; + char *seq = NULL; int direct = 0; int style = COMMUNITY_LIST_EXPANDED; - int idx = 0; + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "(100-500)", &idx); argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; @@ -15634,8 +16830,8 @@ DEFUN (community_list_expanded_all, argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); - int ret = community_list_set(bgp_clist, cl_name_or_number, str, direct, - style); + int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq, + direct, style); XFREE(MTYPE_TMP, str); @@ -15650,24 +16846,31 @@ DEFUN (community_list_expanded_all, DEFUN (no_community_list_expanded_all, no_bgp_community_list_expanded_all_cmd, - "no bgp community-list <(100-500)|expanded WORD> AA:NN...", + "no bgp community-list <(100-500)|expanded WORD> [seq (1-4294967295)] AA:NN...", NO_STR BGP_STR COMMUNITY_LIST_STR "Community list number (expanded)\n" "Add an expanded community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) { char *cl_name_or_number = NULL; + char *seq = NULL; char *str = NULL; int direct = 0; int style = COMMUNITY_LIST_EXPANDED; - int idx = 0; + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -15686,7 +16889,7 @@ DEFUN (no_community_list_expanded_all, argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; - int ret = community_list_unset(bgp_clist, cl_name_or_number, str, + int ret = community_list_unset(bgp_clist, cl_name_or_number, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -15813,7 +17016,13 @@ static int lcommunity_list_set_vty(struct vty *vty, int argc, char *str; int idx = 0; char *cl_name; + char *seq = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + idx = 0; direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; @@ -15837,7 +17046,7 @@ static int lcommunity_list_set_vty(struct vty *vty, int argc, else str = NULL; - ret = lcommunity_list_set(bgp_clist, cl_name, str, direct, style); + ret = lcommunity_list_set(bgp_clist, cl_name, str, seq, direct, style); /* Free temporary community list string allocated by argv_concat(). */ @@ -15857,7 +17066,13 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, int direct = 0; char *str = NULL; int idx = 0; + char *seq = NULL; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -15881,7 +17096,7 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, argv_find(argv, argc, "WORD", &idx); /* Unset community list. */ - ret = lcommunity_list_unset(bgp_clist, argv[idx]->arg, str, direct, + ret = lcommunity_list_unset(bgp_clist, argv[idx]->arg, str, seq, direct, style); /* Free temporary community list string allocated by @@ -15902,10 +17117,12 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, DEFUN (lcommunity_list_standard, bgp_lcommunity_list_standard_cmd, - "bgp large-community-list (1-99) AA:BB:CC...", + "bgp large-community-list (1-99) [seq (1-4294967295)] AA:BB:CC...", BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (standard)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -15916,10 +17133,12 @@ DEFUN (lcommunity_list_standard, DEFUN (lcommunity_list_expanded, bgp_lcommunity_list_expanded_cmd, - "bgp large-community-list (100-500) LINE...", + "bgp large-community-list (100-500) [seq (1-4294967295)] LINE...", BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (expanded)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -15930,11 +17149,13 @@ DEFUN (lcommunity_list_expanded, DEFUN (lcommunity_list_name_standard, bgp_lcommunity_list_name_standard_cmd, - "bgp large-community-list standard WORD AA:BB:CC...", + "bgp large-community-list standard WORD [seq (1-4294967295)] AA:BB:CC...", BGP_STR LCOMMUNITY_LIST_STR "Specify standard large-community-list\n" "Large Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -15945,11 +17166,13 @@ DEFUN (lcommunity_list_name_standard, DEFUN (lcommunity_list_name_expanded, bgp_lcommunity_list_name_expanded_cmd, - "bgp large-community-list expanded WORD LINE...", + "bgp large-community-list expanded WORD [seq (1-4294967295)] LINE...", BGP_STR LCOMMUNITY_LIST_STR "Specify expanded large-community-list\n" "Large Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -15958,8 +17181,8 @@ DEFUN (lcommunity_list_name_expanded, LARGE_COMMUNITY_LIST_EXPANDED, 1); } -DEFUN (no_lcommunity_list_standard_all, - no_bgp_lcommunity_list_standard_all_cmd, +DEFUN (no_lcommunity_list_all, + no_bgp_lcommunity_list_all_cmd, "no bgp large-community-list <(1-99)|(100-500)|WORD>", NO_STR BGP_STR @@ -15972,6 +17195,19 @@ DEFUN (no_lcommunity_list_standard_all, LARGE_COMMUNITY_LIST_STANDARD); } +DEFUN (no_lcommunity_list_name_standard_all, + no_bgp_lcommunity_list_name_standard_all_cmd, + "no bgp large-community-list standard WORD", + NO_STR + BGP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n") +{ + return lcommunity_list_unset_vty(vty, argc, argv, + LARGE_COMMUNITY_LIST_STANDARD); +} + DEFUN (no_lcommunity_list_name_expanded_all, no_bgp_lcommunity_list_name_expanded_all_cmd, "no bgp large-community-list expanded WORD", @@ -15987,11 +17223,13 @@ DEFUN (no_lcommunity_list_name_expanded_all, DEFUN (no_lcommunity_list_standard, no_bgp_lcommunity_list_standard_cmd, - "no bgp large-community-list (1-99) AA:AA:NN...", + "no bgp large-community-list (1-99) [seq (1-4294967295)] AA:AA:NN...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (standard)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -16002,11 +17240,13 @@ DEFUN (no_lcommunity_list_standard, DEFUN (no_lcommunity_list_expanded, no_bgp_lcommunity_list_expanded_cmd, - "no bgp large-community-list (100-500) LINE...", + "no bgp large-community-list (100-500) [seq (1-4294967295)] LINE...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (expanded)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -16017,12 +17257,14 @@ DEFUN (no_lcommunity_list_expanded, DEFUN (no_lcommunity_list_name_standard, no_bgp_lcommunity_list_name_standard_cmd, - "no bgp large-community-list standard WORD AA:AA:NN...", + "no bgp large-community-list standard WORD [seq (1-4294967295)] AA:AA:NN...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Specify standard large-community-list\n" "Large Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -16033,12 +17275,14 @@ DEFUN (no_lcommunity_list_name_standard, DEFUN (no_lcommunity_list_name_expanded, no_bgp_lcommunity_list_name_expanded_cmd, - "no bgp large-community-list expanded WORD LINE...", + "no bgp large-community-list expanded WORD [seq (1-4294967295)] LINE...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Specify expanded large-community-list\n" "Large community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -16133,12 +17377,14 @@ DEFUN (show_lcommunity_list_arg, DEFUN (extcommunity_list_standard, bgp_extcommunity_list_standard_cmd, - "bgp extcommunity-list <(1-99)|standard WORD> AA:NN...", + "bgp extcommunity-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (standard)\n" "Specify standard extcommunity-list\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" EXTCOMMUNITY_VAL_STR) @@ -16146,18 +17392,24 @@ DEFUN (extcommunity_list_standard, int style = EXTCOMMUNITY_LIST_STANDARD; int direct = 0; char *cl_number_or_name = NULL; + char *seq = NULL; int idx = 0; argv_find(argv, argc, "(1-99)", &idx); argv_find(argv, argc, "WORD", &idx); cl_number_or_name = argv[idx]->arg; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); - int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, + int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -16172,12 +17424,14 @@ DEFUN (extcommunity_list_standard, DEFUN (extcommunity_list_name_expanded, bgp_extcommunity_list_name_expanded_cmd, - "bgp extcommunity-list <(100-500)|expanded WORD> LINE...", + "bgp extcommunity-list <(100-500)|expanded WORD> [seq (1-4294967295)] LINE...", BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (expanded)\n" "Specify expanded extcommunity-list\n" "Extended Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" "An ordered list as a regular-expression\n") @@ -16185,17 +17439,23 @@ DEFUN (extcommunity_list_name_expanded, int style = EXTCOMMUNITY_LIST_EXPANDED; int direct = 0; char *cl_number_or_name = NULL; + char *seq = NULL; int idx = 0; argv_find(argv, argc, "(100-500)", &idx); argv_find(argv, argc, "WORD", &idx); cl_number_or_name = argv[idx]->arg; + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; argv_find(argv, argc, "LINE", &idx); char *str = argv_concat(argv, argc, idx); - int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, + int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -16210,13 +17470,15 @@ DEFUN (extcommunity_list_name_expanded, DEFUN (no_extcommunity_list_standard_all, no_bgp_extcommunity_list_standard_all_cmd, - "no bgp extcommunity-list <(1-99)|standard WORD> AA:NN...", + "no bgp extcommunity-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", NO_STR BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (standard)\n" "Specify standard extcommunity-list\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" EXTCOMMUNITY_VAL_STR) @@ -16225,11 +17487,16 @@ DEFUN (no_extcommunity_list_standard_all, int direct = 0; char *cl_number_or_name = NULL; char *str = NULL; + char *seq = NULL; int idx = 0; + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); - if (idx) { direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT @@ -16246,7 +17513,7 @@ DEFUN (no_extcommunity_list_standard_all, cl_number_or_name = argv[idx]->arg; int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str, - direct, style); + seq, direct, style); XFREE(MTYPE_TMP, str); @@ -16268,13 +17535,15 @@ ALIAS(no_extcommunity_list_standard_all, DEFUN (no_extcommunity_list_expanded_all, no_bgp_extcommunity_list_expanded_all_cmd, - "no bgp extcommunity-list <(100-500)|expanded WORD> LINE...", + "no bgp extcommunity-list <(100-500)|expanded WORD> [seq (1-4294967295)] LINE...", NO_STR BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (expanded)\n" "Specify expanded extcommunity-list\n" "Extended Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" "An ordered list as a regular-expression\n") @@ -16283,8 +17552,14 @@ DEFUN (no_extcommunity_list_expanded_all, int direct = 0; char *cl_number_or_name = NULL; char *str = NULL; + char *seq = NULL; int idx = 0; + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -16304,7 +17579,7 @@ DEFUN (no_extcommunity_list_expanded_all, cl_number_or_name = argv[idx]->arg; int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str, - direct, style); + seq, direct, style); XFREE(MTYPE_TMP, str); @@ -16415,18 +17690,22 @@ static int community_list_config_write(struct vty *vty) for (list = cm->num.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp community-list %s %s %s\n", list->name, + vty_out(vty, + "bgp community-list %s seq %" PRId64 " %s %s\n", + list->name, entry->seq, community_direct_str(entry->direct), community_list_config_str(entry)); write++; } for (list = cm->str.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp community-list %s %s %s %s\n", + vty_out(vty, + "bgp community-list %s %s seq %" PRId64 " %s %s\n", entry->style == COMMUNITY_LIST_STANDARD ? "standard" : "expanded", - list->name, community_direct_str(entry->direct), + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } @@ -16436,18 +17715,23 @@ static int community_list_config_write(struct vty *vty) for (list = cm->num.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp extcommunity-list %s %s %s\n", - list->name, community_direct_str(entry->direct), + vty_out(vty, + "bgp extcommunity-list %s seq %" PRId64 " %s %s\n", + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } for (list = cm->str.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp extcommunity-list %s %s %s %s\n", + vty_out(vty, + "bgp extcommunity-list %s %s seq %" PRId64 + " %s %s\n", entry->style == EXTCOMMUNITY_LIST_STANDARD ? "standard" : "expanded", - list->name, community_direct_str(entry->direct), + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } @@ -16459,18 +17743,24 @@ static int community_list_config_write(struct vty *vty) for (list = cm->num.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp large-community-list %s %s %s\n", - list->name, community_direct_str(entry->direct), + vty_out(vty, + "bgp large-community-list %s seq %" PRId64 + " %s %s\n", + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } for (list = cm->str.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp large-community-list %s %s %s %s\n", + vty_out(vty, + "bgp large-community-list %s %s seq %" PRId64 + " %s %s\n", + entry->style == LARGE_COMMUNITY_LIST_STANDARD ? "standard" : "expanded", - list->name, community_direct_str(entry->direct), + list->name, entry->seq, community_direct_str(entry->direct), community_list_config_str(entry)); write++; } @@ -16513,7 +17803,9 @@ static void community_list_vty(void) install_element(CONFIG_NODE, &bgp_lcommunity_list_expanded_cmd); install_element(CONFIG_NODE, &bgp_lcommunity_list_name_standard_cmd); install_element(CONFIG_NODE, &bgp_lcommunity_list_name_expanded_cmd); - install_element(CONFIG_NODE, &no_bgp_lcommunity_list_standard_all_cmd); + install_element(CONFIG_NODE, &no_bgp_lcommunity_list_all_cmd); + install_element(CONFIG_NODE, + &no_bgp_lcommunity_list_name_standard_all_cmd); install_element(CONFIG_NODE, &no_bgp_lcommunity_list_name_expanded_all_cmd); install_element(CONFIG_NODE, &no_bgp_lcommunity_list_standard_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 5f3ce9cd8..fa7b96d87 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -22,7 +22,7 @@ #define _QUAGGA_BGP_VTY_H #include "bgpd/bgpd.h" - +#include "stream.h" struct bgp; #define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n" @@ -46,6 +46,107 @@ struct bgp; "Address Family modifier\n" \ "Address Family modifier\n" +#define SHOW_GR_HEADER \ + "Codes: GR - Graceful Restart," \ + " * - Inheriting Global GR Config,\n" \ + " Restart - GR Mode-Restarting," \ + " Helper - GR Mode-Helper,\n" \ + " Disable - GR Mode-Disable.\n\n" + +#define BGP_SHOW_PEER_GR_CAPABILITY( \ + vty, p, use_json, json) \ + do { \ + bgp_show_neighbor_graceful_restart_local_mode( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_remote_mode( \ + vty, p, use_json, json); \ + bgp_show_neighnor_graceful_restart_rbit( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_time( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_capability_per_afi_safi(\ + vty, p, use_json, json); \ + } while (0) + +#define VTY_BGP_GR_DEFINE_LOOP_VARIABLE \ + struct peer *peer_loop = NULL; \ + struct listnode *node = NULL; \ + struct listnode *nnode = NULL; \ + bool gr_router_detected = false + +#define VTY_BGP_GR_ROUTER_DETECT(_bgp, _peer, _peer_list) \ + do { \ + if (_peer->bgp->t_startup) \ + bgp_peer_gr_flags_update(_peer); \ + for (ALL_LIST_ELEMENTS(_peer_list, node, nnode, peer_loop)) { \ + if (CHECK_FLAG(peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + } while (0) + + +#define VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(_bgp, _ret) \ + do { \ + if (gr_router_detected \ + && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, false)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } else if (!gr_router_detected \ + && _bgp->present_zebra_gr_state \ + == ZEBRA_GR_ENABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, true)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } \ + } while (0) + +#define VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( \ + _bgp, _peer_list, _ret) \ + do { \ + struct peer *peer_loop; \ + bool gr_router_detected = false; \ + struct listnode *node = {0}; \ + struct listnode *nnode = {0}; \ + for (ALL_LIST_ELEMENTS(_peer_list, node, nnode, peer_loop)) { \ + if (peer_loop->bgp->t_startup) \ + bgp_peer_gr_flags_update(peer_loop); \ + if (CHECK_FLAG(peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + if (gr_router_detected \ + && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, false)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } else if (!gr_router_detected \ + && _bgp->present_zebra_gr_state \ + == ZEBRA_GR_ENABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, true)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } \ + } while (0) + + +#define PRINT_EOR(_eor_flag) \ + do { \ + if (eor_flag) \ + vty_out(vty, "Yes\n"); \ + else \ + vty_out(vty, "No\n"); \ + } while (0) + +#define PRINT_EOR_JSON(_eor_flag) \ + do { \ + if (eor_flag) \ + json_object_boolean_true_add( \ + json_endofrib_status, \ + "endOfRibSentAfterUpdate"); \ + else \ + json_object_boolean_false_add( \ + json_endofrib_status, \ + "endOfRibSentAfterUpdate"); \ + } while (0) + extern void bgp_vty_init(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c99ddaf0a..076b6aabc 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -819,7 +819,7 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, /* IPv4 nexthop. */ ret = if_get_ipv4_address(ifp, &nexthop->v4); - if (!ret && peer->local_id.s_addr) + if (!ret && peer->local_id.s_addr != INADDR_ANY) nexthop->v4 = peer->local_id; /* Global address*/ @@ -1779,7 +1779,6 @@ int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type, /* Unset route-map. */ XFREE(MTYPE_ROUTE_MAP_NAME, red->rmap.name); route_map_counter_decrement(red->rmap.map); - red->rmap.name = NULL; red->rmap.map = NULL; /* Unset metric. */ @@ -2437,6 +2436,7 @@ static void bgp_zebra_connected(struct zclient *zclient) /* TODO - What if we have peers and networks configured, do we have to * kick-start them? */ + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); } static int bgp_zebra_process_local_es(ZAPI_CALLBACK_ARGS) @@ -2548,7 +2548,8 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( - bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, + bgp, vni, + vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id, tenant_vrf_id, mcast_grp); else return bgp_evpn_local_vni_del(bgp, vni); @@ -2975,7 +2976,7 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); /* redirect IP */ - if (nh->gate.ipv4.s_addr) { + if (nh->gate.ipv4.s_addr != INADDR_ANY) { char buff[PREFIX_STRLEN]; api_nh->vrf_id = nh->vrf_id; @@ -3016,3 +3017,120 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, return; } } + +/* Send capabilities to RIB */ +int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) +{ + struct zapi_cap api; + int ret = BGP_GR_SUCCESS; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + /* Check if capability is already sent. If the flag force is set + * send the capability since this can be initial bgp configuration + */ + memset(&api, 0, sizeof(struct zapi_cap)); + if (disable) { + api.cap = ZEBRA_CLIENT_GR_DISABLE; + api.vrf_id = bgp->vrf_id; + } else { + api.cap = ZEBRA_CLIENT_GR_CAPABILITIES; + api.stale_removal_time = bgp->rib_stale_time; + api.vrf_id = bgp->vrf_id; + } + + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) + < 0) { + zlog_err("error sending capability"); + ret = BGP_GR_FAILURE; + } else { + if (disable) + bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; + else + bgp->present_zebra_gr_state = ZEBRA_GR_ENABLE; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("send capabilty success"); + ret = BGP_GR_SUCCESS; + } + return ret; +} + +/* Send route update pesding or completed status to RIB for the + * specific AFI, SAFI + */ +int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type) +{ + struct zapi_cap api = {0}; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient == NULL, invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + api.afi = afi; + api.safi = safi; + api.vrf_id = vrf_id; + api.cap = type; + + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) + < 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("error sending capability"); + return BGP_GR_FAILURE; + } + return BGP_GR_SUCCESS; +} + + +/* Send RIB stale timer update */ +int bgp_zebra_stale_timer_update(struct bgp *bgp) +{ + struct zapi_cap api; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + memset(&api, 0, sizeof(struct zapi_cap)); + api.cap = ZEBRA_CLIENT_RIB_STALE_TIME; + api.stale_removal_time = bgp->rib_stale_time; + api.vrf_id = bgp->vrf_id; + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) + < 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("error sending capability"); + return BGP_GR_FAILURE; + } + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("send capabilty success"); + return BGP_GR_SUCCESS; +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 62c311cc1..5a02e2fbf 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -80,11 +80,11 @@ extern int bgp_zebra_num_connects(void); extern bool bgp_zebra_nexthop_set(union sockunion *, union sockunion *, struct bgp_nexthop *, struct peer *); - struct bgp_pbr_action; struct bgp_pbr_match; struct bgp_pbr_rule; struct bgp_pbr_match_entry; + extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, struct bgp_pbr_rule *pbr, bool install); @@ -98,5 +98,7 @@ extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, afi_t afi, uint32_t table_id, bool announce); - +extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable); +extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type); +extern int bgp_zebra_stale_timer_update(struct bgp *bgp); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9b0e81491..60bcc7b8e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -335,7 +335,8 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) int bgp_router_id_static_set(struct bgp *bgp, struct in_addr id) { bgp->router_id_static = id; - bgp_router_id_set(bgp, id.s_addr ? &id : &bgp->router_id_zebra, + bgp_router_id_set(bgp, + id.s_addr != INADDR_ANY ? &id : &bgp->router_id_zebra, true /* is config */); return 0; } @@ -1007,26 +1008,10 @@ static void peer_free(struct peer *peer) XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message); - if (peer->desc) { - XFREE(MTYPE_PEER_DESC, peer->desc); - peer->desc = NULL; - } - - /* Free allocated host character. */ - if (peer->host) { - XFREE(MTYPE_BGP_PEER_HOST, peer->host); - peer->host = NULL; - } - - if (peer->domainname) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } - - if (peer->ifname) { - XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname); - peer->ifname = NULL; - } + XFREE(MTYPE_PEER_DESC, peer->desc); + XFREE(MTYPE_BGP_PEER_HOST, peer->host); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname); /* Update source configuration. */ if (peer->update_source) { @@ -1034,10 +1019,7 @@ static void peer_free(struct peer *peer) peer->update_source = NULL; } - if (peer->update_if) { - XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if); - peer->update_if = NULL; - } + XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if); XFREE(MTYPE_TMP, peer->notify.data); memset(&peer->notify, 0, sizeof(struct bgp_notify)); @@ -1047,10 +1029,7 @@ static void peer_free(struct peer *peer) bgp_sync_delete(peer); - if (peer->conf_if) { - XFREE(MTYPE_PEER_CONF_IF, peer->conf_if); - peer->conf_if = NULL; - } + XFREE(MTYPE_PEER_CONF_IF, peer->conf_if); bfd_info_free(&(peer->bfd_info)); @@ -1102,6 +1081,119 @@ struct peer *peer_unlock_with_caller(const char *name, struct peer *peer) return peer; } +/* BGP GR changes */ + +int bgp_global_gr_init(struct bgp *bgp) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s called ..", __func__); + + int local_GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE][BGP_GLOBAL_GR_EVENT_CMD] = { + /* GLOBAL_HELPER Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_GR, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_DISABLE, GLOBAL_INVALID + }, + /* GLOBAL_GR Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_INVALID, GLOBAL_HELPER, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_DISABLE, GLOBAL_INVALID + }, + /* GLOBAL_DISABLE Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd */ /*no_Global_GR_cmd*/ + GLOBAL_GR, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*//*no_Global_Disable_cmd*/ + GLOBAL_INVALID, GLOBAL_HELPER + }, + /* GLOBAL_INVALID Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_INVALID, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_INVALID, GLOBAL_INVALID + } + }; + memcpy(bgp->GLOBAL_GR_FSM, local_GLOBAL_GR_FSM, + sizeof(local_GLOBAL_GR_FSM)); + + bgp->global_gr_present_state = GLOBAL_HELPER; + bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; + + return BGP_GR_SUCCESS; +} + +int bgp_peer_gr_init(struct peer *peer) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s called ..", __func__); + + struct bgp_peer_gr local_Peer_GR_FSM[BGP_PEER_GR_MODE] + [BGP_PEER_GR_EVENT_CMD] = { + { + /* PEER_HELPER Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, {PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + {PEER_DISABLE, bgp_peer_gr_action }, {PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_INVALID, NULL }, {PEER_GLOBAL_INHERIT, + bgp_peer_gr_action } + }, + { + /* PEER_GR Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT, + bgp_peer_gr_action }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + {PEER_DISABLE, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + }, + { + /* PEER_DISABLE Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT, + bgp_peer_gr_action }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + }, + { + /* PEER_INVALID Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + }, + { + /* PEER_GLOBAL_INHERIT Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_DISABLE, bgp_peer_gr_action}, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + } + }; + memcpy(&peer->PEER_GR_FSM, local_Peer_GR_FSM, + sizeof(local_Peer_GR_FSM)); + peer->peer_gr_present_state = PEER_GLOBAL_INHERIT; + bgp_peer_move_to_gr_mode(peer, PEER_GLOBAL_INHERIT); + + return BGP_GR_SUCCESS; +} /* Allocate new peer object, implicitely locked. */ struct peer *peer_new(struct bgp *bgp) @@ -1153,6 +1245,9 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + /* Initialize per peer bgp GR FSM */ + bgp_peer_gr_init(peer); + /* Create buffers. */ peer->ibuf = stream_fifo_new(); peer->obuf = stream_fifo_new(); @@ -1212,6 +1307,9 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->flags = peer_src->flags; peer_dst->cap = peer_src->cap; + peer_dst->peer_gr_present_state = peer_src->peer_gr_present_state; + peer_dst->peer_gr_new_status_flag = peer_src->peer_gr_new_status_flag; + peer_dst->local_as = peer_src->local_as; peer_dst->port = peer_src->port; (void)peer_sort(peer_dst); @@ -1251,10 +1349,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) if (peer_src->update_source) { if (peer_dst->update_source) sockunion_free(peer_dst->update_source); - if (peer_dst->update_if) { - XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if); - peer_dst->update_if = NULL; - } + XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if); peer_dst->update_source = sockunion_dup(peer_src->update_source); } else if (peer_src->update_if) { @@ -1534,6 +1629,9 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, else if (!active && peer_active(peer)) bgp_timer_set(peer); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); + return peer; } @@ -2098,7 +2196,7 @@ int peer_afc_set(struct peer *peer, afi_t afi, safi_t safi, int enable) return peer_deactivate(peer, afi, safi); } -static void peer_nsf_stop(struct peer *peer) +void peer_nsf_stop(struct peer *peer) { afi_t afi; safi_t safi; @@ -2261,57 +2359,24 @@ int peer_delete(struct peer *peer) filter = &peer->filter[afi][safi]; for (i = FILTER_IN; i < FILTER_MAX; i++) { - if (filter->dlist[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->dlist[i].name); - filter->dlist[i].name = NULL; - } - - if (filter->plist[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->plist[i].name); - filter->plist[i].name = NULL; - } - - if (filter->aslist[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->aslist[i].name); - filter->aslist[i].name = NULL; - } + XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[i].name); + XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[i].name); + XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[i].name); } for (i = RMAP_IN; i < RMAP_MAX; i++) { - if (filter->map[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->map[i].name); - filter->map[i].name = NULL; - } - } - - if (filter->usmap.name) { - XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); - filter->usmap.name = NULL; + XFREE(MTYPE_BGP_FILTER_NAME, filter->map[i].name); } - if (peer->default_rmap[afi][safi].name) { - XFREE(MTYPE_ROUTE_MAP_NAME, - peer->default_rmap[afi][safi].name); - peer->default_rmap[afi][safi].name = NULL; - } + XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); + XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name); } FOREACH_AFI_SAFI (afi, safi) peer_af_delete(peer, afi, safi); - if (peer->hostname) { - XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); - peer->hostname = NULL; - } - - if (peer->domainname) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); peer_unlock(peer); /* initial reference */ @@ -2868,18 +2933,12 @@ static struct bgp *bgp_create(as_t *as, const char *name, XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->host); bgp->peer_self->host = XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement"); - if (bgp->peer_self->hostname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname); - bgp->peer_self->hostname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname); if (cmd_hostname_get()) bgp->peer_self->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_hostname_get()); - if (bgp->peer_self->domainname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname); - bgp->peer_self->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname); if (cmd_domainname_get()) bgp->peer_self->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_domainname_get()); @@ -2902,6 +2961,12 @@ static struct bgp *bgp_create(as_t *as, const char *name, multipath_num, 0); bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_IBGP, multipath_num, 0); + /* Initialize graceful restart info */ + bgp->gr_info[afi][safi].eor_required = 0; + bgp->gr_info[afi][safi].eor_received = 0; + bgp->gr_info[afi][safi].t_select_deferral = NULL; + bgp->gr_info[afi][safi].t_route_select = NULL; + bgp->gr_info[afi][safi].route_list = list_new(); } bgp->v_update_delay = BGP_UPDATE_DELAY_DEF; @@ -2911,6 +2976,8 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_timers_unset(bgp); bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; + bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME; bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; bgp->dynamic_neighbors_count = 0; bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED; @@ -2941,14 +3008,11 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->vpn_policy[afi].export_vrf->del = bgp_vrf_string_name_delete; } - if (name) { + if (name) bgp->name = XSTRDUP(MTYPE_BGP, name); - } else { - /* TODO - The startup timer needs to be run for the whole of BGP - */ - thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, - bgp->restart_time, &bgp->t_startup); - } + + thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, + bgp->restart_time, &bgp->t_startup); /* printable name we can use in debug messages */ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) { @@ -2990,6 +3054,9 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_evpn_init(bgp); bgp_pbr_init(bgp); + + /*initilize global GR FSM */ + bgp_global_gr_init(bgp); return bgp; } @@ -3233,7 +3300,9 @@ int bgp_delete(struct bgp *bgp) struct listnode *node, *next; struct vrf *vrf; afi_t afi; + safi_t safi; int i; + struct graceful_restart_info *gr_info; assert(bgp); @@ -3247,6 +3316,18 @@ int bgp_delete(struct bgp *bgp) /* Set flag indicating bgp instance delete in progress */ bgp_flag_set(bgp, BGP_FLAG_DELETE_IN_PROGRESS); + /* Delete the graceful restart info */ + FOREACH_AFI_SAFI (afi, safi) { + gr_info = &bgp->gr_info[afi][safi]; + if (!gr_info) + continue; + + BGP_TIMER_OFF(gr_info->t_select_deferral); + BGP_TIMER_OFF(gr_info->t_route_select); + if (gr_info->route_list) + list_delete(&gr_info->route_list); + } + if (BGP_DEBUG(zebra, ZEBRA)) { if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) zlog_debug("Deleting Default VRF"); @@ -4360,8 +4441,6 @@ int peer_description_unset(struct peer *peer) { XFREE(MTYPE_PEER_DESC, peer->desc); - peer->desc = NULL; - return 0; } @@ -5111,7 +5190,6 @@ void peer_interface_set(struct peer *peer, const char *str) void peer_interface_unset(struct peer *peer) { XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname); - peer->ifname = NULL; } /* Allow-as in. */ @@ -7099,3 +7177,31 @@ struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, return peer; } +void bgp_gr_apply_running_config(void) +{ + struct peer *peer = NULL; + struct bgp *bgp = NULL; + struct listnode *node, *nnode; + bool gr_router_detected = false; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] %s called !", __func__); + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + bgp_peer_gr_flags_update(peer); + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + } + + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, true); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, false); + } + + gr_router_detected = false; + } +} diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 4c4787ed5..7d3bd22a6 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -65,6 +65,12 @@ enum { AS_UNSPECIFIED = 0, AS_EXTERNAL, }; +/* Zebra Gracaful Restart states */ +enum zebra_gr_mode { + ZEBRA_GR_DISABLE = 0, + ZEBRA_GR_ENABLE +}; + /* Typedef BGP specific types. */ typedef uint32_t as_t; typedef uint16_t as16_t; /* we may still encounter 16 Bit asnums */ @@ -232,6 +238,52 @@ enum bgp_instance_type { BGP_INSTANCE_TYPE_VIEW }; +#define BGP_SEND_EOR(bgp, afi, safi) \ + (!bgp_flag_check(bgp, BGP_FLAG_GR_DISABLE_EOR) \ + && ((bgp->gr_info[afi][safi].t_select_deferral == NULL) \ + || (bgp->gr_info[afi][safi].eor_required \ + == bgp->gr_info[afi][safi].eor_received))) + +/* BGP GR Global ds */ + +#define BGP_GLOBAL_GR_MODE 4 +#define BGP_GLOBAL_GR_EVENT_CMD 4 + +/* Graceful restart selection deferral timer info */ +struct graceful_restart_info { + /* Count of EOR message expected */ + uint32_t eor_required; + /* Count of EOR received */ + uint32_t eor_received; + /* Deferral Timer */ + struct thread *t_select_deferral; + /* Route list */ + struct list *route_list; + /* Best route select */ + struct thread *t_route_select; + /* AFI, SAFI enabled */ + bool af_enabled[AFI_MAX][SAFI_MAX]; + /* Route update completed */ + bool route_sync[AFI_MAX][SAFI_MAX]; +}; + +enum global_mode { + GLOBAL_HELPER = 0, /* This is the default mode */ + GLOBAL_GR, + GLOBAL_DISABLE, + GLOBAL_INVALID +}; + +enum global_gr_command { + GLOBAL_GR_CMD = 0, + NO_GLOBAL_GR_CMD, + GLOBAL_DISABLE_CMD, + NO_GLOBAL_DISABLE_CMD +}; + +#define BGP_GR_SUCCESS 0 +#define BGP_GR_FAILURE 1 + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -356,7 +408,10 @@ struct bgp { #define BGP_FLAG_IMPORT_CHECK (1 << 9) #define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 10) #define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 11) + +/* This flag is set when we have full BGP Graceful-Restart mode enable */ #define BGP_FLAG_GRACEFUL_RESTART (1 << 12) + #define BGP_FLAG_ASPATH_CONFED (1 << 13) #define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 14) #define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 15) @@ -367,6 +422,17 @@ struct bgp { #define BGP_FLAG_GR_PRESERVE_FWD (1 << 20) #define BGP_FLAG_GRACEFUL_SHUTDOWN (1 << 21) #define BGP_FLAG_DELETE_IN_PROGRESS (1 << 22) +#define BGP_FLAG_SELECT_DEFER_DISABLE (1 << 23) +#define BGP_FLAG_GR_DISABLE_EOR (1 << 24) + + enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE] + [BGP_GLOBAL_GR_EVENT_CMD]; + enum global_mode global_gr_present_state; + + /* This variable stores the current Graceful Restart state of Zebra + * - ZEBRA_GR_ENABLE / ZEBRA_GR_DISABLE + */ + enum zebra_gr_mode present_zebra_gr_state; /* BGP Per AF flags */ uint16_t af_flags[AFI_MAX][SAFI_MAX]; @@ -462,7 +528,12 @@ struct bgp { /* BGP graceful restart */ uint32_t restart_time; uint32_t stalepath_time; + uint32_t select_defer_time; + struct graceful_restart_info gr_info[AFI_MAX][SAFI_MAX]; + uint32_t rib_stale_time; +#define BGP_ROUTE_SELECT_DELAY 1 +#define BGP_MAX_BEST_ROUTE_SELECT 10000 /* Maximum-paths configuration */ struct bgp_maxpaths_cfg { uint16_t maxpaths_ebgp; @@ -548,7 +619,6 @@ struct bgp { #define BGP_VRF_RD_CFGD (1 << 3) #define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4) - /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; @@ -589,6 +659,13 @@ DECLARE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), (bgp, vty)) +/* Thread callback information */ +struct afi_safi_info { + afi_t afi; + safi_t safi; + struct bgp *bgp; +}; + #define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold) #define IS_BGP_INST_KNOWN_TO_ZEBRA(bgp) \ @@ -596,6 +673,9 @@ DECLARE_HOOK(bgp_inst_config_write, || (bgp->inst_type == BGP_INSTANCE_TYPE_VRF \ && bgp->vrf_id != VRF_UNKNOWN)) +#define BGP_SELECT_DEFER_DISABLE(bgp) \ + (bgp_flag_check(bgp, BGP_FLAG_SELECT_DEFER_DISABLE)) + /* BGP peer-group support. */ struct peer_group { /* Name of the peer-group. */ @@ -726,6 +806,36 @@ struct peer_af { safi_t safi; int afid; }; +/* BGP GR per peer ds */ + +#define BGP_PEER_GR_MODE 5 +#define BGP_PEER_GR_EVENT_CMD 6 + +enum peer_mode { + PEER_HELPER = 0, + PEER_GR, + PEER_DISABLE, + PEER_INVALID, + PEER_GLOBAL_INHERIT /* This is the default mode */ + +}; + +enum peer_gr_command { + PEER_GR_CMD = 0, + NO_PEER_GR_CMD, + PEER_DISABLE_CMD, + NO_PEER_DISABLE_CMD, + PEER_HELPER_CMD, + NO_PEER_HELPER_CMD +}; + +typedef unsigned int (*bgp_peer_gr_action_ptr)(struct peer *, int, int); + +struct bgp_peer_gr { + enum peer_mode next_state; + bgp_peer_gr_action_ptr action_fun; +}; + /* BGP neighbor structure. */ struct peer { @@ -947,11 +1057,36 @@ struct peer { #define PEER_FLAG_LOCAL_AS (1 << 21) /* local-as */ #define PEER_FLAG_UPDATE_SOURCE (1 << 22) /* update-source */ + /* BGP-GR Peer related flags */ +#define PEER_FLAG_GRACEFUL_RESTART_HELPER (1 << 23) /* Helper */ +#define PEER_FLAG_GRACEFUL_RESTART (1 << 24) /* Graceful Restart */ +#define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1 << 25) /* Global-Inherit */ + + /* + *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART + *& PEER_FLAG_GRACEFUL_RESTART_HELPER + *and PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT + */ + + struct bgp_peer_gr PEER_GR_FSM[BGP_PEER_GR_MODE][BGP_PEER_GR_EVENT_CMD]; + enum peer_mode peer_gr_present_state; + /* Non stop forwarding afi-safi count for BGP gr feature*/ + uint8_t nsf_af_count; + + uint8_t peer_gr_new_status_flag; +#define PEER_GRACEFUL_RESTART_NEW_STATE_HELPER (1 << 0) +#define PEER_GRACEFUL_RESTART_NEW_STATE_RESTART (1 << 1) +#define PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT (1 << 2) + /* outgoing message sent in CEASE_ADMIN_SHUTDOWN notify */ char *tx_shutdown_message; /* NSF mode (graceful restart) */ uint8_t nsf[AFI_MAX][SAFI_MAX]; + /* EOR Send time */ + time_t eor_stime[AFI_MAX][SAFI_MAX]; + /* Last update packet sent time */ + time_t pkt_stime[AFI_MAX][SAFI_MAX]; /* Peer Per AF flags */ /* @@ -1247,8 +1382,7 @@ DECLARE_QOBJ_TYPE(peer) ((peer)->attr = (group)->conf->attr) #define PEER_STR_ATTR_INHERIT(peer, group, attr, mt) \ do { \ - if ((peer)->attr) \ - XFREE(mt, (peer)->attr); \ + XFREE(mt, (peer)->attr); \ if ((group)->conf->attr) \ (peer)->attr = XSTRDUP(mt, (group)->conf->attr); \ else \ @@ -1456,6 +1590,8 @@ struct bgp_nlri { /* BGP graceful restart */ #define BGP_DEFAULT_RESTART_TIME 120 #define BGP_DEFAULT_STALEPATH_TIME 360 +#define BGP_DEFAULT_SELECT_DEFERRAL_TIME 360 +#define BGP_DEFAULT_RIB_STALE_TIME 500 /* BGP uptime string length. */ #define BGP_UPTIME_LEN 25 @@ -1521,6 +1657,11 @@ enum bgp_clear_type { #define BGP_ERR_INVALID_FOR_DIRECT_PEER -34 #define BGP_ERR_PEER_SAFI_CONFLICT -35 +/* BGP GR ERRORS */ +#define BGP_ERR_GR_INVALID_CMD -36 +#define BGP_ERR_GR_OPERATION_FAILED -37 +#define BGP_GR_NO_OPERATION -38 + /* * Enumeration of different policy kinds a peer can be configured with. */ @@ -1773,6 +1914,32 @@ extern int peer_af_delete(struct peer *, afi_t, safi_t); extern void bgp_close(void); extern void bgp_free(struct bgp *); +void bgp_gr_apply_running_config(void); + +/* BGP GR */ +int bgp_global_gr_init(struct bgp *bgp); +int bgp_peer_gr_init(struct peer *peer); + + +#define BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(_bgp, _peer_list) \ + do { \ + struct peer *peer_loop; \ + bool gr_router_detected = false; \ + struct listnode *node = {0}; \ + for (ALL_LIST_ELEMENTS_RO(_peer_list, node, peer_loop)) { \ + if (CHECK_FLAG(peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + if (gr_router_detected \ + && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + bgp_zebra_send_capabilities(_bgp, false); \ + } else if (!gr_router_detected \ + && _bgp->present_zebra_gr_state \ + == ZEBRA_GR_ENABLE) { \ + bgp_zebra_send_capabilities(_bgp, true); \ + } \ + } while (0) static inline struct bgp *bgp_lock(struct bgp *bgp) { @@ -1962,5 +2129,6 @@ extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, /* Hooks */ DECLARE_HOOK(peer_status_changed, (struct peer * peer), (peer)) +void peer_nsf_stop(struct peer *peer); #endif /* _QUAGGA_BGPD_H */ diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 7c4f8eaa0..4701d2e1f 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -1489,10 +1489,7 @@ void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p) while (p) { next = p->next; - if (p->value) { - XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); - p->value = NULL; - } + XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); XFREE(MTYPE_BGP_TEA_OPTIONS, p); p = next; diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c index f31342e19..a7bc909c5 100644 --- a/bgpd/rfapi/rfapi_encap_tlv.c +++ b/bgpd/rfapi/rfapi_encap_tlv.c @@ -248,7 +248,6 @@ struct rfapi_un_option *rfapi_encap_tlv_to_un_option(struct attr *attr) } if (rc) { XFREE(MTYPE_RFAPI_UN_OPTION, uo); - uo = NULL; } return uo; } diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 5ad822211..77fcf909c 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -431,6 +431,14 @@ void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p, else vty_out(vty, " label=%u", decode_label(&bpi->extra->label[0])); + + if (bpi->extra->num_sids) { + char buf[BUFSIZ]; + + vty_out(vty, " sid=%s", + inet_ntop(AF_INET6, &bpi->extra->sid[0], buf, + sizeof(buf))); + } } if (!rfapiGetVncLifetime(bpi->attr, &lifetime)) { diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index d9c4c5bf0..458cfa0ad 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -22,6 +22,11 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. yum install systemd-devel + For CentOS 7 and CentOS 8, the package will be built using python3 + and requires additional python3 packages:: + + yum install python3-devel python3-sphinx + .. note:: For CentOS 8 you need to install ``platform-python-devel`` package @@ -29,13 +34,6 @@ Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. yum install platform-python-devel - .. warning:: - - ``python2-sphinx`` is not shipped for CentOS 8. - Development reached the end of life for Python 2. - We need to install it using ```pip``:: - - pip2 install sphinx If ``yum`` is not present on your system, use ``dnf`` instead. diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst index 74a8605bf..e3526d184 100644 --- a/doc/developer/zebra.rst +++ b/doc/developer/zebra.rst @@ -169,175 +169,201 @@ Zebra Protocol Commands +------------------------------------+-------+ | ZEBRA_INTERFACE_SET_MASTER | 6 | +------------------------------------+-------+ -| ZEBRA_ROUTE_ADD | 7 | +| ZEBRA_INTERFACE_SET_PROTODOWN | 7 | +------------------------------------+-------+ -| ZEBRA_ROUTE_DELETE | 8 | +| ZEBRA_ROUTE_ADD | 8 | +------------------------------------+-------+ -| ZEBRA_ROUTE_NOTIFY_OWNER | 9 | +| ZEBRA_ROUTE_DELETE | 9 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_ADD | 10 | +| ZEBRA_ROUTE_NOTIFY_OWNER | 10 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_DELETE | 11 | +| ZEBRA_REDISTRIBUTE_ADD | 11 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_DEFAULT_ADD | 12 | +| ZEBRA_REDISTRIBUTE_DELETE | 12 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_DEFAULT_DELETE | 13 | +| ZEBRA_REDISTRIBUTE_DEFAULT_ADD | 13 | +------------------------------------+-------+ -| ZEBRA_ROUTER_ID_ADD | 14 | +| ZEBRA_REDISTRIBUTE_DEFAULT_DELETE | 14 | +------------------------------------+-------+ -| ZEBRA_ROUTER_ID_DELETE | 15 | +| ZEBRA_ROUTER_ID_ADD | 15 | +------------------------------------+-------+ -| ZEBRA_ROUTER_ID_UPDATE | 16 | +| ZEBRA_ROUTER_ID_DELETE | 16 | +------------------------------------+-------+ -| ZEBRA_HELLO | 17 | +| ZEBRA_ROUTER_ID_UPDATE | 17 | +------------------------------------+-------+ -| ZEBRA_CAPABILITIES | 18 | +| ZEBRA_HELLO | 18 | +------------------------------------+-------+ -| ZEBRA_NEXTHOP_REGISTER | 19 | +| ZEBRA_CAPABILITIES | 19 | +------------------------------------+-------+ -| ZEBRA_NEXTHOP_UNREGISTER | 20 | +| ZEBRA_NEXTHOP_REGISTER | 20 | +------------------------------------+-------+ -| ZEBRA_NEXTHOP_UPDATE | 21 | +| ZEBRA_NEXTHOP_UNREGISTER | 21 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_NBR_ADDRESS_ADD | 22 | +| ZEBRA_NEXTHOP_UPDATE | 22 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_NBR_ADDRESS_DELETE | 23 | +| ZEBRA_INTERFACE_NBR_ADDRESS_ADD | 23 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_BFD_DEST_UPDATE | 24 | +| ZEBRA_INTERFACE_NBR_ADDRESS_DELETE | 24 | +------------------------------------+-------+ -| ZEBRA_IMPORT_ROUTE_REGISTER | 25 | +| ZEBRA_INTERFACE_BFD_DEST_UPDATE | 25 | +------------------------------------+-------+ -| ZEBRA_IMPORT_ROUTE_UNREGISTER | 26 | +| ZEBRA_IMPORT_ROUTE_REGISTER | 26 | +------------------------------------+-------+ -| ZEBRA_IMPORT_CHECK_UPDATE | 27 | +| ZEBRA_IMPORT_ROUTE_UNREGISTER | 27 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_REGISTER | 28 | +| ZEBRA_IMPORT_CHECK_UPDATE | 28 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_DEREGISTER | 29 | +| ZEBRA_BFD_DEST_REGISTER | 29 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_UPDATE | 30 | +| ZEBRA_BFD_DEST_DEREGISTER | 30 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_REPLAY | 31 | +| ZEBRA_BFD_DEST_UPDATE | 31 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_ROUTE_ADD | 32 | +| ZEBRA_BFD_DEST_REPLAY | 32 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_ROUTE_DEL | 33 | +| ZEBRA_REDISTRIBUTE_ROUTE_ADD | 33 | +------------------------------------+-------+ -| ZEBRA_VRF_UNREGISTER | 34 | +| ZEBRA_REDISTRIBUTE_ROUTE_DEL | 34 | +------------------------------------+-------+ -| ZEBRA_VRF_ADD | 35 | +| ZEBRA_VRF_UNREGISTER | 35 | +------------------------------------+-------+ -| ZEBRA_VRF_DELETE | 36 | +| ZEBRA_VRF_ADD | 36 | +------------------------------------+-------+ -| ZEBRA_VRF_LABEL | 37 | +| ZEBRA_VRF_DELETE | 37 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_VRF_UPDATE | 38 | +| ZEBRA_VRF_LABEL | 38 | +------------------------------------+-------+ -| ZEBRA_BFD_CLIENT_REGISTER | 39 | +| ZEBRA_INTERFACE_VRF_UPDATE | 39 | +------------------------------------+-------+ -| ZEBRA_BFD_CLIENT_DEREGISTER | 40 | +| ZEBRA_BFD_CLIENT_REGISTER | 40 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_ENABLE_RADV | 41 | +| ZEBRA_BFD_CLIENT_DEREGISTER | 41 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_DISABLE_RADV | 42 | +| ZEBRA_INTERFACE_ENABLE_RADV | 42 | +------------------------------------+-------+ -| ZEBRA_IPV3_NEXTHOP_LOOKUP_MRIB | 43 | +| ZEBRA_INTERFACE_DISABLE_RADV | 43 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_LINK_PARAMS | 44 | +| ZEBRA_IPV3_NEXTHOP_LOOKUP_MRIB | 44 | +------------------------------------+-------+ -| ZEBRA_MPLS_LABELS_ADD | 45 | +| ZEBRA_INTERFACE_LINK_PARAMS | 45 | +------------------------------------+-------+ -| ZEBRA_MPLS_LABELS_DELETE | 46 | +| ZEBRA_MPLS_LABELS_ADD | 46 | +------------------------------------+-------+ -| ZEBRA_IPMR_ROUTE_STATS | 47 | +| ZEBRA_MPLS_LABELS_DELETE | 47 | +------------------------------------+-------+ -| ZEBRA_LABEL_MANAGER_CONNECT | 48 | +| ZEBRA_MPLS_LABELS_REPLACE | 48 | +------------------------------------+-------+ -| ZEBRA_LABEL_MANAGER_CONNECT_ASYNC | 49 | +| ZEBRA_IPMR_ROUTE_STATS | 49 | +------------------------------------+-------+ -| ZEBRA_GET_LABEL_CHUNK | 50 | +| ZEBRA_LABEL_MANAGER_CONNECT | 50 | +------------------------------------+-------+ -| ZEBRA_RELEASE_LABEL_CHUNK | 51 | +| ZEBRA_LABEL_MANAGER_CONNECT_ASYNC | 51 | +------------------------------------+-------+ -| ZEBRA_FEC_REGISTER | 52 | +| ZEBRA_GET_LABEL_CHUNK | 52 | +------------------------------------+-------+ -| ZEBRA_FEC_UNREGISTER | 53 | +| ZEBRA_RELEASE_LABEL_CHUNK | 53 | +------------------------------------+-------+ -| ZEBRA_FEC_UPDATE | 54 | +| ZEBRA_FEC_REGISTER | 54 | +------------------------------------+-------+ -| ZEBRA_ADVERTISE_DEFAULT_GW | 55 | +| ZEBRA_FEC_UNREGISTER | 55 | +------------------------------------+-------+ -| ZEBRA_ADVERTISE_SUBNET | 56 | +| ZEBRA_FEC_UPDATE | 56 | +------------------------------------+-------+ -| ZEBRA_ADVERTISE_ALL_VNI | 57 | +| ZEBRA_ADVERTISE_DEFAULT_GW | 57 | +------------------------------------+-------+ -| ZEBRA_LOCAL_ES_ADD | 58 | +| ZEBRA_ADVERTISE_SVI_MACIP | 58 | +------------------------------------+-------+ -| ZEBRA_LOCAL_ES_DEL | 59 | +| ZEBRA_ADVERTISE_SUBNET | 59 | +------------------------------------+-------+ -| ZEBRA_VNI_ADD | 60 | +| ZEBRA_ADVERTISE_ALL_VNI | 60 | +------------------------------------+-------+ -| ZEBRA_VNI_DEL | 61 | +| ZEBRA_LOCAL_ES_ADD | 61 | +------------------------------------+-------+ -| ZEBRA_L3VNI_ADD | 62 | +| ZEBRA_LOCAL_ES_DEL | 62 | +------------------------------------+-------+ -| ZEBRA_L3VNI_DEL | 63 | +| ZEBRA_VNI_ADD | 63 | +------------------------------------+-------+ -| ZEBRA_REMOTE_VTEP_ADD | 64 | +| ZEBRA_VNI_DEL | 64 | +------------------------------------+-------+ -| ZEBRA_REMOTE_VTEP_DEL | 65 | +| ZEBRA_L3VNI_ADD | 65 | +------------------------------------+-------+ -| ZEBRA_MACIP_ADD | 66 | +| ZEBRA_L3VNI_DEL | 66 | +------------------------------------+-------+ -| ZEBRA_MACIP_DEL | 67 | +| ZEBRA_REMOTE_VTEP_ADD | 67 | +------------------------------------+-------+ -| ZEBRA_IP_PREFIX_ROUTE_ADD | 68 | +| ZEBRA_REMOTE_VTEP_DEL | 68 | +------------------------------------+-------+ -| ZEBRA_IP_PREFIX_ROUTE_DEL | 69 | +| ZEBRA_MACIP_ADD | 69 | +------------------------------------+-------+ -| ZEBRA_REMOTE_MACIP_ADD | 70 | +| ZEBRA_MACIP_DEL | 70 | +------------------------------------+-------+ -| ZEBRA_REMOTE_MACIP_DEL | 71 | +| ZEBRA_IP_PREFIX_ROUTE_ADD | 71 | +------------------------------------+-------+ -| ZEBRA_PW_ADD | 72 | +| ZEBRA_IP_PREFIX_ROUTE_DEL | 72 | +------------------------------------+-------+ -| ZEBRA_PW_DELETE | 73 | +| ZEBRA_REMOTE_MACIP_ADD | 73 | +------------------------------------+-------+ -| ZEBRA_PW_SET | 74 | +| ZEBRA_REMOTE_MACIP_DEL | 74 | +------------------------------------+-------+ -| ZEBRA_PW_UNSET | 75 | +| ZEBRA_DUPLICATE_ADDR_DETECTION | 75 | +------------------------------------+-------+ -| ZEBRA_PW_STATUS_UPDATE | 76 | +| ZEBRA_PW_ADD | 76 | +------------------------------------+-------+ -| ZEBRA_RULE_ADD | 77 | +| ZEBRA_PW_DELETE | 77 | +------------------------------------+-------+ -| ZEBRA_RULE_DELETE | 78 | +| ZEBRA_PW_SET | 78 | +------------------------------------+-------+ -| ZEBRA_RULE_NOTIFY_OWNER | 79 | +| ZEBRA_PW_UNSET | 79 | +------------------------------------+-------+ -| ZEBRA_TABLE_MANAGER_CONNECT | 80 | +| ZEBRA_PW_STATUS_UPDATE | 80 | +------------------------------------+-------+ -| ZEBRA_GET_TABLE_CHUNK | 81 | +| ZEBRA_RULE_ADD | 81 | +------------------------------------+-------+ -| ZEBRA_RELEASE_TABLE_CHUNK | 82 | +| ZEBRA_RULE_DELETE | 82 | +------------------------------------+-------+ -| ZEBRA_IPSET_CREATE | 83 | +| ZEBRA_RULE_NOTIFY_OWNER | 83 | +------------------------------------+-------+ -| ZEBRA_IPSET_DESTROY | 84 | +| ZEBRA_TABLE_MANAGER_CONNECT | 84 | +------------------------------------+-------+ -| ZEBRA_IPSET_ENTRY_ADD | 85 | +| ZEBRA_GET_TABLE_CHUNK | 85 | +------------------------------------+-------+ -| ZEBRA_IPSET_ENTRY_DELETE | 86 | +| ZEBRA_RELEASE_TABLE_CHUNK | 86 | +------------------------------------+-------+ -| ZEBRA_IPSET_NOTIFY_OWNER | 87 | +| ZEBRA_IPSET_CREATE | 87 | +------------------------------------+-------+ -| ZEBRA_IPSET_ENTRY_NOTIFY_OWNER | 88 | +| ZEBRA_IPSET_DESTROY | 88 | +------------------------------------+-------+ -| ZEBRA_IPTABLE_ADD | 89 | +| ZEBRA_IPSET_ENTRY_ADD | 89 | +------------------------------------+-------+ -| ZEBRA_IPTABLE_DELETE | 90 | +| ZEBRA_IPSET_ENTRY_DELETE | 90 | +------------------------------------+-------+ -| ZEBRA_IPTABLE_NOTIFY_OWNER | 91 | +| ZEBRA_IPSET_NOTIFY_OWNER | 91 | +------------------------------------+-------+ -| ZEBRA_VXLAN_FLOOD_CONTROL | 92 | +| ZEBRA_IPSET_ENTRY_NOTIFY_OWNER | 92 | ++------------------------------------+-------+ +| ZEBRA_IPTABLE_ADD | 93 | ++------------------------------------+-------+ +| ZEBRA_IPTABLE_DELETE | 94 | ++------------------------------------+-------+ +| ZEBRA_IPTABLE_NOTIFY_OWNER | 95 | ++------------------------------------+-------+ +| ZEBRA_VXLAN_FLOOD_CONTROL | 96 | ++------------------------------------+-------+ +| ZEBRA_VXLAN_SG_ADD | 97 | ++------------------------------------+-------+ +| ZEBRA_VXLAN_SG_DEL | 98 | ++------------------------------------+-------+ +| ZEBRA_VXLAN_SG_REPLAY | 99 | ++------------------------------------+-------+ +| ZEBRA_MLAG_PROCESS_UP | 100 | ++------------------------------------+-------+ +| ZEBRA_MLAG_PROCESS_DOWN | 101 | ++------------------------------------+-------+ +| ZEBRA_MLAG_CLIENT_REGISTER | 102 | ++------------------------------------+-------+ +| ZEBRA_MLAG_CLIENT_UNREGISTER | 103 | ++------------------------------------+-------+ +| ZEBRA_MLAG_FORWARD_MSG | 104 | ++------------------------------------+-------+ +| ZEBRA_CLIENT_CAPABILITIES | 105 | +------------------------------------+-------+ diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index d3ac4b22a..81b4e3464 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -694,6 +694,181 @@ from eBGP peers, :ref:`bgp-route-selection`. MED as an intra-AS metric to steer equal-length AS_PATH routes to, e.g., desired exit points. + +.. _bgp-graceful-restart: + +Graceful Restart +---------------- + +BGP graceful restart functionality as defined in +`RFC-4724 `_ defines the mechanisms that +allows BGP speaker to continue to forward data packets along known routes +while the routing protocol information is being restored. + + +Usually, when BGP on a router restarts, all the BGP peers detect that the +session went down and then came up. This "down/up" transition results in a +"routing flap" and causes BGP route re-computation, generation of BGP routing +updates, and unnecessary churn to the forwarding tables. + +The following functionality is provided by graceful restart: + +1. The feature allows the restarting router to indicate to the helping peer the + routes it can preserve in its forwarding plane during control plane restart + by sending graceful restart capability in the OPEN message sent during + session establishment. +2. The feature allows helping router to advertise to all other peers the routes + received from the restarting router which are preserved in the forwarding + plane of the restarting router during control plane restart. + + +:: + + + + (R1)-----------------------------------------------------------------(R2) + + 1. BGP Graceful Restart Capability exchanged between R1 & R2. + + <---------------------------------------------------------------------> + + 2. Kill BGP Process at R1. + + ----------------------------------------------------------------------> + + 3. R2 Detects the above BGP Restart & verifies BGP Restarting + Capability of R1. + + 4. Start BGP Process at R1. + + 5. Re-establish the BGP session between R1 & R2. + + <---------------------------------------------------------------------> + + 6. R2 Send initial route updates, followed by End-Of-Rib. + + <---------------------------------------------------------------------- + + 7. R1 was waiting for End-Of-Rib from R2 & which has been received + now. + + 8. R1 now runs BGP Best-Path algorithm. Send Initial BGP Update, + followed by End-Of Rib + + <---------------------------------------------------------------------> + + +.. _bgp-end-of-rib-message: + +End-of-RIB (EOR) message +^^^^^^^^^^^^^^^^^^^^^^^^ + +An UPDATE message with no reachable Network Layer Reachability Information +(NLRI) and empty withdrawn NLRI is specified as the End-of-RIB marker that can +be used by a BGP speaker to indicate to its peer the completion of the initial +routing update after the session is established. + +For the IPv4 unicast address family, the End-of-RIB marker is an UPDATE message +with the minimum length. For any other address family, it is an UPDATE message +that contains only the MP_UNREACH_NLRI attribute with no withdrawn routes for +that . + +Although the End-of-RIB marker is specified for the purpose of BGP graceful +restart, it is noted that the generation of such a marker upon completion of +the initial update would be useful for routing convergence in general, and thus +the practice is recommended. + +.. _bgp-route-selection-deferral-timer: + +Route Selection Deferral Timer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Specifies the time the restarting router defers the route selection process +after restart. + +Restarting Router : The usage of route election deferral timer is specified +in https://tools.ietf.org/html/rfc4724#section-4.1 + +Once the session between the Restarting Speaker and the Receiving Speaker is +re-established, the Restarting Speaker will receive and process BGP messages +from its peers. + +However, it MUST defer route selection for an address family until it either. + +1. Receives the End-of-RIB marker from all its peers (excluding the ones with + the "Restart State" bit set in the received capability and excluding the ones + that do not advertise the graceful restart capability). +2. The Selection_Deferral_Timer timeout. + +.. index:: bgp graceful-restart select-defer-time (0-3600) +.. clicmd:: bgp graceful-restart select-defer-time (0-3600) + + This is command, will set deferral time to value specified. + + +.. index:: bgp graceful-restart rib-stale-time (1-3600) +.. clicmd:: bgp graceful-restart rib-stale-time (1-3600) + + This is command, will set the time for which stale routes are kept in RIB. + +.. _bgp-per-peer-graceful-restart: + +BGP Per Peer Graceful Restart +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Ability to enable and disable graceful restart, helper and no GR at all mode +functionality at peer level. + +So bgp graceful restart can be enabled at modes global BGP level or at per +peer level. There are two FSM, one for BGP GR global mode and other for peer +per GR. + +Default global mode is helper and default peer per mode is inherit from global. +If per peer mode is configured, the GR mode of this particular peer will +override the global mode. + +.. _bgp-GR-global-mode-cmd: + +BGP GR Global Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: bgp graceful-restart +.. clicmd:: bgp graceful-restart + + This command will enable BGP graceful restart ifunctionality at the global + level. + +.. index:: bgp graceful-restart disable +.. clicmd:: bgp graceful-restart disable + + This command will disable both the functionality graceful restart and helper + mode. + + +.. _bgp-GR-peer-mode-cmd: + +BGP GR Peer Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: neighbor A.B.C.D graceful-restart +.. clicmd:: neighbor A.B.C.D graceful-restart + + This command will enable BGP graceful restart ifunctionality at the peer + level. + +.. index:: neighbor A.B.C.D graceful-restart-helper +.. clicmd:: neighbor A.B.C.D graceful-restart-helper + + This command will enable BGP graceful restart helper only functionality + at the peer level. + +.. index:: neighbor A.B.C.D graceful-restart-disable +.. clicmd:: neighbor A.B.C.D graceful-restart-disable + + This command will disable the entire BGP graceful restart functionality + at the peer level. + + .. _bgp-network: Networks diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 2300cb0e1..cd095af86 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -128,6 +128,18 @@ OSPF6 interface Set explicitly network type for specified interface. +OSPF6 route-map +=============== + +Usage of *ospfd6*'s route-map support. + +.. index:: set metric [+|-](0-4294967295) +.. clicmd:: set metric [+|-](0-4294967295) + + Set a metric for matched route when sending announcement. Use plus (+) sign + to add a metric value to an existing metric. Use minus (-) sign to + substract a metric value from an existing metric. + .. _redistribute-routes-to-ospf6: Redistribute routes to OSPF6 diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index e6ff59d27..75768269f 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -710,6 +710,18 @@ Interfaces Enable ospf on an interface and set associated area. +OSPF route-map +============== + +Usage of *ospfd*'s route-map support. + +.. index:: set metric [+|-](0-4294967295) +.. clicmd:: set metric [+|-](0-4294967295) + + Set a metric for matched route when sending announcement. Use plus (+) sign + to add a metric value to an existing metric. Use minus (-) sign to + substract a metric value from an existing metric. + .. _redistribute-routes-to-ospf: Redistribution diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 68659fbf5..b9b28bace 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -136,6 +136,11 @@ causes the policy to be installed into the kernel. This command is available under interface sub-mode. This turns on the PBR map NAME and allows it to work properly. +.. note:: + This will not dynamically create PBR maps on sub-interfaces (i.e. vlans) + even if one is on the master. Each must have the PBR map explicitly added + to the interface. + .. _pbr-details: PBR Details diff --git a/doc/user/vtysh.rst b/doc/user/vtysh.rst index 6d569f5fb..dd754a92e 100644 --- a/doc/user/vtysh.rst +++ b/doc/user/vtysh.rst @@ -22,6 +22,14 @@ administrator with an external editor. have effect for vtysh) need to be manually updated in :file:`vtysh.conf`. +.. index:: copy FILENAME running-config +.. clicmd:: copy FILENAME running-config + + Process and load a configuration file manually; each line in the + file is read and processed as if it were being typed (or piped) to + vtysh. + + Pager usage =========== diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 23a062ab0..520080e83 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -911,10 +911,14 @@ zebra Terminal Mode Commands Reset statistics related to the zebra code that interacts with the optional Forwarding Plane Manager (FPM) component. -.. index:: show nexthop-group [ID] [vrf NAME] [ip|ipv6] -.. clicmd:: show nexthop-group [ID] [vrf NAME] [ip|ipv6] - - Display nexthop groups created by zebra. +.. index:: show nexthop-group rib [ID] [vrf NAME] [singleton [ip|ip6]] +.. clicmd:: show nexthop-group rib [ID] [vrf NAME] + + Display nexthop groups created by zebra. The [vrf NAME] option + is only meaningful if you have started zebra with the --vrfwnetns + option as that nexthop groups are per namespace in linux. + If you specify singleton you would like to see the singleton + nexthop groups that do have an afi. Router-id diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index ece0b4b0c..9ef4e8623 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -121,7 +121,6 @@ int eigrp_if_delete_hook(struct interface *ifp) eigrp_fifo_free(ei->obuf); XFREE(MTYPE_EIGRP_IF_INFO, ifp->info); - ifp->info = NULL; return 0; } diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index 0746b04ed..922c0fe3e 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -140,6 +140,7 @@ struct quagga_signal_t eigrp_signals[] = { static const struct frr_yang_module_info *const eigrpd_yang_modules[] = { &frr_eigrpd_info, &frr_interface_info, + &frr_route_map_info, }; FRR_DAEMON_INFO(eigrpd, EIGRP, .vty_port = EIGRP_VTY_PORT, diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c index 324f30929..39008a01c 100644 --- a/eigrpd/eigrp_network.c +++ b/eigrpd/eigrp_network.c @@ -230,7 +230,7 @@ int eigrp_network_set(struct eigrp *eigrp, struct prefix *p) rn->info = (void *)pref; /* Schedule Router ID Update. */ - if (eigrp->router_id.s_addr == 0) + if (eigrp->router_id.s_addr == INADDR_ANY) eigrp_router_id_update(eigrp); /* Run network config now. */ /* Get target interface. */ diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c index 4ccce2ebb..e03ebb4fc 100644 --- a/eigrpd/eigrp_northbound.c +++ b/eigrpd/eigrp_northbound.c @@ -161,7 +161,7 @@ static int eigrpd_instance_router_id_destroy(enum nb_event event, break; case NB_EV_APPLY: eigrp = nb_running_get_entry(dnode, NULL, true); - eigrp->router_id_static.s_addr = 0; + eigrp->router_id_static.s_addr = INADDR_ANY; break; } diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c index e93dc0cbf..820f015b5 100644 --- a/eigrpd/eigrpd.c +++ b/eigrpd/eigrpd.c @@ -99,10 +99,10 @@ void eigrp_router_id_update(struct eigrp *eigrp) router_id_old = eigrp->router_id; - if (eigrp->router_id_static.s_addr != 0) + if (eigrp->router_id_static.s_addr != INADDR_ANY) router_id = eigrp->router_id_static; - else if (eigrp->router_id.s_addr != 0) + else if (eigrp->router_id.s_addr != INADDR_ANY) router_id = eigrp->router_id; else @@ -142,8 +142,8 @@ static struct eigrp *eigrp_new(uint16_t as, vrf_id_t vrf_id) eigrp->vrf_id = vrf_id; eigrp->vrid = 0; eigrp->AS = as; - eigrp->router_id.s_addr = 0; - eigrp->router_id_static.s_addr = 0; + eigrp->router_id.s_addr = INADDR_ANY; + eigrp->router_id_static.s_addr = INADDR_ANY; eigrp->sequence_number = 1; /*Configure default K Values for EIGRP Process*/ diff --git a/include/linux/seg6.h b/include/linux/seg6.h new file mode 100644 index 000000000..329163e4a --- /dev/null +++ b/include/linux/seg6.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_H +#define _LINUX_SEG6_H + +#include +#include /* For struct in6_addr. */ + +/* + * SRH + */ +struct ipv6_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + __u8 first_segment; /* Represents the last_entry field of SRH */ + __u8 flags; + __u16 tag; + + struct in6_addr segments[0]; +}; + +#define SR6_FLAG1_PROTECTED (1 << 6) +#define SR6_FLAG1_OAM (1 << 5) +#define SR6_FLAG1_ALERT (1 << 4) +#define SR6_FLAG1_HMAC (1 << 3) + +#define SR6_TLV_INGRESS 1 +#define SR6_TLV_EGRESS 2 +#define SR6_TLV_OPAQUE 3 +#define SR6_TLV_PADDING 4 +#define SR6_TLV_HMAC 5 + +#define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC) + +struct sr6_tlv { + __u8 type; + __u8 len; + __u8 data[0]; +}; + +#endif diff --git a/include/linux/seg6_genl.h b/include/linux/seg6_genl.h new file mode 100644 index 000000000..0c230524e --- /dev/null +++ b/include/linux/seg6_genl.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_GENL_H +#define _LINUX_SEG6_GENL_H + +#define SEG6_GENL_NAME "SEG6" +#define SEG6_GENL_VERSION 0x1 + +enum { + SEG6_ATTR_UNSPEC, + SEG6_ATTR_DST, + SEG6_ATTR_DSTLEN, + SEG6_ATTR_HMACKEYID, + SEG6_ATTR_SECRET, + SEG6_ATTR_SECRETLEN, + SEG6_ATTR_ALGID, + SEG6_ATTR_HMACINFO, + __SEG6_ATTR_MAX, +}; + +#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1) + +enum { + SEG6_CMD_UNSPEC, + SEG6_CMD_SETHMAC, + SEG6_CMD_DUMPHMAC, + SEG6_CMD_SET_TUNSRC, + SEG6_CMD_GET_TUNSRC, + __SEG6_CMD_MAX, +}; + +#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1) + +#endif diff --git a/include/linux/seg6_hmac.h b/include/linux/seg6_hmac.h new file mode 100644 index 000000000..3fb3412e1 --- /dev/null +++ b/include/linux/seg6_hmac.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_HMAC_H +#define _LINUX_SEG6_HMAC_H + +#include +#include + +#define SEG6_HMAC_SECRET_LEN 64 +#define SEG6_HMAC_FIELD_LEN 32 + +struct sr6_tlv_hmac { + struct sr6_tlv tlvhdr; + __u16 reserved; + __be32 hmackeyid; + __u8 hmac[SEG6_HMAC_FIELD_LEN]; +}; + +enum { + SEG6_HMAC_ALGO_SHA1 = 1, + SEG6_HMAC_ALGO_SHA256 = 2, +}; + +#endif diff --git a/include/linux/seg6_iptunnel.h b/include/linux/seg6_iptunnel.h new file mode 100644 index 000000000..3004e982c --- /dev/null +++ b/include/linux/seg6_iptunnel.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_IPTUNNEL_H +#define _LINUX_SEG6_IPTUNNEL_H + +#include /* For struct ipv6_sr_hdr. */ + +enum { + SEG6_IPTUNNEL_UNSPEC, + SEG6_IPTUNNEL_SRH, + __SEG6_IPTUNNEL_MAX, +}; +#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) + +struct seg6_iptunnel_encap { + int mode; + struct ipv6_sr_hdr srh[0]; +}; + +#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3)) + +enum { + SEG6_IPTUN_MODE_INLINE, + SEG6_IPTUN_MODE_ENCAP, + SEG6_IPTUN_MODE_L2ENCAP, +}; + + +#endif diff --git a/include/linux/seg6_local.h b/include/linux/seg6_local.h new file mode 100644 index 000000000..5312de80b --- /dev/null +++ b/include/linux/seg6_local.h @@ -0,0 +1,80 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + SEG6_LOCAL_BPF, + __SEG6_LOCAL_MAX, +}; +#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) + +enum { + SEG6_LOCAL_ACTION_UNSPEC = 0, + /* node segment */ + SEG6_LOCAL_ACTION_END = 1, + /* adjacency segment (IPv6 cross-connect) */ + SEG6_LOCAL_ACTION_END_X = 2, + /* lookup of next seg NH in table */ + SEG6_LOCAL_ACTION_END_T = 3, + /* decap and L2 cross-connect */ + SEG6_LOCAL_ACTION_END_DX2 = 4, + /* decap and IPv6 cross-connect */ + SEG6_LOCAL_ACTION_END_DX6 = 5, + /* decap and IPv4 cross-connect */ + SEG6_LOCAL_ACTION_END_DX4 = 6, + /* decap and lookup of DA in v6 table */ + SEG6_LOCAL_ACTION_END_DT6 = 7, + /* decap and lookup of DA in v4 table */ + SEG6_LOCAL_ACTION_END_DT4 = 8, + /* binding segment with insertion */ + SEG6_LOCAL_ACTION_END_B6 = 9, + /* binding segment with encapsulation */ + SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + /* binding segment with MPLS encap */ + SEG6_LOCAL_ACTION_END_BM = 11, + /* lookup last seg in table */ + SEG6_LOCAL_ACTION_END_S = 12, + /* forward to SR-unaware VNF with static proxy */ + SEG6_LOCAL_ACTION_END_AS = 13, + /* forward to SR-unaware VNF with masquerading */ + SEG6_LOCAL_ACTION_END_AM = 14, + /* custom BPF action */ + SEG6_LOCAL_ACTION_END_BPF = 15, + + __SEG6_LOCAL_ACTION_MAX, +}; + +#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1) + +enum { + SEG6_LOCAL_BPF_PROG_UNSPEC, + SEG6_LOCAL_BPF_PROG, + SEG6_LOCAL_BPF_PROG_NAME, + __SEG6_LOCAL_BPF_PROG_MAX, +}; + +#define SEG6_LOCAL_BPF_PROG_MAX (__SEG6_LOCAL_BPF_PROG_MAX - 1) + +#endif diff --git a/include/subdir.am b/include/subdir.am index b1ca1be54..86129c4d6 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -11,4 +11,9 @@ noinst_HEADERS += \ include/linux/socket.h \ include/linux/net_namespace.h \ include/linux/fib_rules.h \ + include/linux/seg6.h \ + include/linux/seg6_genl.h \ + include/linux/seg6_hmac.h \ + include/linux/seg6_iptunnel.h \ + include/linux/seg6_local.h \ # end diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index cf4b84179..68be9c1a9 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -59,7 +59,6 @@ static void bfd_session_free(struct bfd_session **session) return; XFREE(MTYPE_BFD_SESSION, *session); - *session = NULL; } static bool bfd_session_same(const struct bfd_session *session, int family, diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 364441f79..f7fe089b9 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -39,6 +39,7 @@ #include "vrf.h" #include "qobj.h" #include "libfrr.h" +#include "routemap.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" @@ -166,6 +167,7 @@ static const struct frr_yang_module_info *const isisd_yang_modules[] = { #ifndef FABRICD &frr_isisd_info, #endif /* ifndef FABRICD */ + &frr_route_map_info, }; #ifdef FABRICD diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 4ef57f574..816fcc64b 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -250,9 +250,8 @@ ldp_config_write(struct vty *vty) vty_out (vty, "mpls ldp\n"); - if (ldpd_conf->rtr_id.s_addr != 0) - vty_out (vty, " router-id %s\n", - inet_ntoa(ldpd_conf->rtr_id)); + if (ldpd_conf->rtr_id.s_addr != INADDR_ANY) + vty_out(vty, " router-id %s\n", inet_ntoa(ldpd_conf->rtr_id)); if (ldpd_conf->lhello_holdtime != LINK_DFLT_HOLDTIME && ldpd_conf->lhello_holdtime != 0) diff --git a/lib/agentx.c b/lib/agentx.c index 2c6a43d1a..b479b5ea4 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -55,28 +55,42 @@ static int agentx_timeout(struct thread *t) static int agentx_read(struct thread *t) { fd_set fds; - int flags; + int flags, new_flags = 0; int nonblock = false; struct listnode *ln = THREAD_ARG(t); list_delete_node(events, ln); /* fix for non blocking socket */ flags = fcntl(THREAD_FD(t), F_GETFL, 0); - if (-1 == flags) + if (-1 == flags) { + flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)", + strerror(errno), errno); return -1; + } if (flags & O_NONBLOCK) nonblock = true; else - fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK); + new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK); + + if (new_flags == -1) + flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)", + strerror(errno), errno); FD_ZERO(&fds); FD_SET(THREAD_FD(t), &fds); snmp_read(&fds); /* Reset the flag */ - if (!nonblock) - fcntl(THREAD_FD(t), F_SETFL, flags); + if (!nonblock) { + new_flags = fcntl(THREAD_FD(t), F_SETFL, flags); + + if (new_flags == -1) + flog_err( + EC_LIB_SYSTEM_CALL, + "Failed to set snmp fd back to original settings: %s(%d)", + strerror(errno), errno); + } netsnmp_check_outstanding_agent_requests(); agentx_events_update(); diff --git a/lib/bfd.c b/lib/bfd.c index ffb3cbc1f..4e192422c 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -74,10 +74,7 @@ struct bfd_info *bfd_info_create(void) */ void bfd_info_free(struct bfd_info **bfd_info) { - if (*bfd_info) { - XFREE(MTYPE_BFD_INFO, *bfd_info); - *bfd_info = NULL; - } + XFREE(MTYPE_BFD_INFO, *bfd_info); } /* diff --git a/lib/command.c b/lib/command.c index d2145d9f5..8811b3a79 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2503,8 +2503,6 @@ static void disable_log_file(void) zlog_reset_file(); XFREE(MTYPE_HOST, host.logfile); - - host.logfile = NULL; } DEFUN (no_config_log_file, @@ -2661,8 +2659,7 @@ int cmd_banner_motd_file(const char *file) void cmd_banner_motd_line(const char *line) { - if (host.motd) - XFREE(MTYPE_HOST, host.motd); + XFREE(MTYPE_HOST, host.motd); host.motd = XSTRDUP(MTYPE_HOST, line); } diff --git a/lib/command.h b/lib/command.h index c8dd04604..ea8a76a96 100644 --- a/lib/command.h +++ b/lib/command.h @@ -418,8 +418,23 @@ struct cmd_node { #define DAEMONS_LIST \ "" +/* Graceful Restart cli help strings */ +#define GR_CMD "Global Graceful Restart command\n" +#define NO_GR_CMD "Undo Global Graceful Restart command\n" +#define GR "Global Graceful Restart - GR Mode\n" +#define GR_DISABLE "Global Graceful Restart - Disable Mode\n" +#define NO_GR_DISABLE "Undo Global Graceful Restart - Disable Mode\n" +#define GR_DEBUG "Graceful Restart - Enable Debug Logs\n" +#define GR_SHOW "Graceful Restart - Show command for Global and all neighbor mode\n" +#define GR_NEIGHBOR_CMD "Graceful Restart command for a neighbor\n" +#define NO_GR_NEIGHBOR_CMD "Undo Graceful Restart command for a neighbor\n" +#define GR_NEIGHBOR_DISABLE_CMD "Graceful Restart Disable command for a neighbor\n" +#define NO_GR_NEIGHBOR_DISABLE_CMD "Undo Graceful Restart Disable command for a neighbor\n" +#define GR_NEIGHBOR_HELPER_CMD "Graceful Restart Helper command for a neighbor\n" +#define NO_GR_NEIGHBOR_HELPER_CMD "Undo Graceful Restart Helper command for a neighbor\n" + /* Prototypes. */ -extern void install_node(struct cmd_node *, int (*)(struct vty *)); +extern void install_node(struct cmd_node *node, int (*)(struct vty *)); extern void install_default(enum node_type); extern void install_element(enum node_type, const struct cmd_element *); diff --git a/lib/distribute.c b/lib/distribute.c index 2aa6b927f..3ea60c877 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -186,7 +186,6 @@ static int distribute_list_unset(struct distribute_ctx *ctx, return 0; XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); - dist->list[type] = NULL; /* Apply this distribute-list to the interface. */ (ctx->distribute_delete_hook)(ctx, dist); @@ -232,7 +231,6 @@ static int distribute_list_prefix_unset(struct distribute_ctx *ctx, return 0; XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); - dist->prefix[type] = NULL; /* Apply this distribute-list to the interface. */ (ctx->distribute_delete_hook)(ctx, dist); diff --git a/lib/filter.c b/lib/filter.c index 31e25d600..911a8e53d 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -608,10 +608,7 @@ static int vty_access_list_remark_unset(struct vty *vty, afi_t afi, return CMD_WARNING_CONFIG_FAILED; } - if (access->remark) { - XFREE(MTYPE_TMP, access->remark); - access->remark = NULL; - } + XFREE(MTYPE_TMP, access->remark); if (access->head == NULL && access->tail == NULL) access_list_delete(access); @@ -2554,7 +2551,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) else { vty_out(vty, " %s", inet_ntoa(filter->addr)); - if (filter->addr_mask.s_addr != 0) + if (filter->addr_mask.s_addr + != INADDR_ANY) vty_out(vty, ", wildcard bits %s", inet_ntoa( @@ -2602,7 +2600,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) else { vty_out(vty, " %s", inet_ntoa(filter->addr)); - if (filter->addr_mask.s_addr != 0) + if (filter->addr_mask.s_addr + != INADDR_ANY) vty_out(vty, ", wildcard bits %s", inet_ntoa( @@ -2695,7 +2694,7 @@ static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) vty_out(vty, " ip"); if (filter->addr_mask.s_addr == 0xffffffff) vty_out(vty, " any"); - else if (filter->addr_mask.s_addr == 0) + else if (filter->addr_mask.s_addr == INADDR_ANY) vty_out(vty, " host %s", inet_ntoa(filter->addr)); else { vty_out(vty, " %s", inet_ntoa(filter->addr)); @@ -2704,7 +2703,7 @@ static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) if (filter->mask_mask.s_addr == 0xffffffff) vty_out(vty, " any"); - else if (filter->mask_mask.s_addr == 0) + else if (filter->mask_mask.s_addr == INADDR_ANY) vty_out(vty, " host %s", inet_ntoa(filter->mask)); else { vty_out(vty, " %s", inet_ntoa(filter->mask)); @@ -2716,7 +2715,7 @@ static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) vty_out(vty, " any\n"); else { vty_out(vty, " %s", inet_ntoa(filter->addr)); - if (filter->addr_mask.s_addr != 0) + if (filter->addr_mask.s_addr != INADDR_ANY) vty_out(vty, " %s", inet_ntoa(filter->addr_mask)); vty_out(vty, "\n"); diff --git a/lib/if.c b/lib/if.c index 7332dceb4..0e9471923 100644 --- a/lib/if.c +++ b/lib/if.c @@ -1281,7 +1281,6 @@ void if_link_params_free(struct interface *ifp) if (ifp->link_params == NULL) return; XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params); - ifp->link_params = NULL; } /* ----------- CLI commands ----------- */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c index ca6f512ec..1973d40be 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -164,7 +164,6 @@ static int if_rmap_unset(struct if_rmap_ctx *ctx, return 0; XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - if_rmap->routemap[IF_RMAP_IN] = NULL; } if (type == IF_RMAP_OUT) { @@ -174,7 +173,6 @@ static int if_rmap_unset(struct if_rmap_ctx *ctx, return 0; XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - if_rmap->routemap[IF_RMAP_OUT] = NULL; } if (ctx->if_rmap_delete_hook) diff --git a/lib/log.c b/lib/log.c index 798b776d0..7bf16a821 100644 --- a/lib/log.c +++ b/lib/log.c @@ -934,7 +934,6 @@ int zlog_reset_file(void) zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; XFREE(MTYPE_ZLOG, zl->filename); - zl->filename = NULL; return 1; } @@ -1094,7 +1093,7 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_VXLAN_SG_DEL), DESC_ENTRY(ZEBRA_VXLAN_SG_REPLAY), DESC_ENTRY(ZEBRA_ERROR), -}; + DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/netns_linux.c b/lib/netns_linux.c index d1a31ae35..4d4376250 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -371,7 +371,7 @@ int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)) void ns_disable(struct ns *ns) { - return ns_disable_internal(ns); + ns_disable_internal(ns); } struct ns *ns_lookup(ns_id_t ns_id) diff --git a/lib/plist.c b/lib/plist.c index a0976cd6b..f6ceba81e 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -496,7 +496,6 @@ static void prefix_list_trie_del(struct prefix_list *plist, for (; depth > 0; depth--) if (trie_table_empty(*tables[depth])) { XFREE(MTYPE_PREFIX_LIST_TRIE, *tables[depth]); - *tables[depth] = NULL; } } @@ -1093,10 +1092,7 @@ static int vty_prefix_list_desc_unset(struct vty *vty, afi_t afi, return CMD_WARNING_CONFIG_FAILED; } - if (plist->desc) { - XFREE(MTYPE_TMP, plist->desc); - plist->desc = NULL; - } + XFREE(MTYPE_TMP, plist->desc); if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) prefix_list_delete(plist); diff --git a/lib/prefix.c b/lib/prefix.c index 219f798dc..199ff3267 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -659,7 +659,7 @@ void apply_mask_ipv4(struct prefix_ipv4 *p) /* If prefix is 0.0.0.0/0 then return 1 else return 0. */ int prefix_ipv4_any(const struct prefix_ipv4 *p) { - return (p->prefix.s_addr == 0 && p->prefixlen == 0); + return (p->prefix.s_addr == INADDR_ANY && p->prefixlen == 0); } /* Allocate a new ip version 6 route */ @@ -1081,7 +1081,6 @@ void prefix_free_lists(void *arg) void prefix_free(struct prefix **p) { XFREE(MTYPE_PREFIX, *p); - *p = NULL; } /* Utility function to convert ipv4 prefixes to Classful prefixes */ @@ -1113,11 +1112,11 @@ in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen) masklen2ip(masklen, &mask); return (masklen != IPV4_MAX_PREFIXLEN - 1) ? - /* normal case */ - (hostaddr | ~mask.s_addr) - : - /* special case for /31 */ - (hostaddr ^ ~mask.s_addr); + /* normal case */ + (hostaddr | ~mask.s_addr) + : + /* For prefix 31 return 255.255.255.255 (RFC3021) */ + htonl(0xFFFFFFFF); } /* Utility function to convert ipv4 netmask to prefixes @@ -1145,7 +1144,7 @@ int netmask_str2prefix_str(const char *net_str, const char *mask_str, } else { destination = ntohl(network.s_addr); - if (network.s_addr == 0) + if (network.s_addr == INADDR_ANY) prefixlen = 0; else if (IN_CLASSC(destination)) prefixlen = 24; diff --git a/lib/prefix.h b/lib/prefix.h index 667627ddf..fb80c4ca6 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -451,8 +451,7 @@ extern uint8_t ip_masklen(struct in_addr); extern void masklen2ip(const int, struct in_addr *); /* given the address of a host on a network and the network mask length, * calculate the broadcast address for that network; - * special treatment for /31: returns the address of the other host - * on the network by flipping the host bit */ + * special treatment for /31 according to RFC3021 section 3.3 */ extern in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen); extern int netmask_str2prefix_str(const char *, const char *, char *); diff --git a/lib/routemap.c b/lib/routemap.c index 14fec0283..e46323028 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -50,178 +50,7 @@ static vector route_match_vec; /* Vector for route set rules. */ static vector route_set_vec; -struct route_map_match_set_hooks { - /* match interface */ - int (*match_interface)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match interface */ - int (*no_match_interface)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match ip address */ - int (*match_ip_address)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match ip address */ - int (*no_match_ip_address)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match ip address prefix list */ - int (*match_ip_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ip address prefix list */ - int (*no_match_ip_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ip next hop */ - int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match ip next hop */ - int (*no_match_ip_next_hop)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match ip next hop prefix list */ - int (*match_ip_next_hop_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ip next hop prefix list */ - int (*no_match_ip_next_hop_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ip next-hop type */ - int (*match_ip_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ip next-hop type */ - int (*no_match_ip_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ipv6 address */ - int (*match_ipv6_address)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match ipv6 address */ - int (*no_match_ipv6_address)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - - /* match ipv6 address prefix list */ - int (*match_ipv6_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ipv6 address prefix list */ - int (*no_match_ipv6_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ipv6 next-hop type */ - int (*match_ipv6_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ipv6 next-hop type */ - int (*no_match_ipv6_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match metric */ - int (*match_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match metric */ - int (*no_match_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match tag */ - int (*match_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match tag */ - int (*no_match_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* set ip nexthop */ - int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* no set ip nexthop */ - int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* set ipv6 nexthop local */ - int (*set_ipv6_nexthop_local)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg); - - /* no set ipv6 nexthop local */ - int (*no_set_ipv6_nexthop_local)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg); - - /* set metric */ - int (*set_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* no set metric */ - int (*no_set_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* set tag */ - int (*set_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* no set tag */ - int (*no_set_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); -}; - -static struct route_map_match_set_hooks rmap_match_set_hook; +struct route_map_match_set_hooks rmap_match_set_hook; /* match interface */ void route_map_match_interface_hook(int (*func)( @@ -479,15 +308,21 @@ int generic_match_add(struct vty *vty, struct route_map_index *index, ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", + frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: " + "%s %s", command, arg); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: /* * Nothing to do here move along @@ -524,13 +359,21 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index, ret = route_map_delete_match(index, command, dep_name, type); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", + frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: " + "%s %s", command, arg); retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_SUCCESS: @@ -554,15 +397,20 @@ int generic_set_add(struct vty *vty, struct route_map_index *index, ret = route_map_add_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: " + "%s %s", command, arg); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: break; } @@ -578,15 +426,20 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index, ret = route_map_delete_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: " + "%s %s", command, arg); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: break; } @@ -595,35 +448,9 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index, } -/* Route map rule. This rule has both `match' rule and `set' rule. */ -struct route_map_rule { - /* Rule type. */ - const struct route_map_rule_cmd *cmd; - - /* For pretty printing. */ - char *rule_str; - - /* Pre-compiled match rule. */ - void *value; - - /* Linked list. */ - struct route_map_rule *next; - struct route_map_rule *prev; -}; - -/* Making route map list. */ -struct route_map_list { - struct route_map *head; - struct route_map *tail; - - void (*add_hook)(const char *); - void (*delete_hook)(const char *); - void (*event_hook)(const char *); -}; - /* Master list of route map. */ -static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; -static struct hash *route_map_master_hash = NULL; +struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; +struct hash *route_map_master_hash = NULL; static unsigned int route_map_hash_key_make(const void *p) { @@ -691,8 +518,6 @@ static void route_map_rule_delete(struct route_map_rule_list *, struct route_map_rule *); static bool rmap_debug; -static void route_map_index_delete(struct route_map_index *, int); - /* New route map allocation. Please note route map's name must be specified. */ static struct route_map *route_map_new(const char *name) @@ -784,7 +609,7 @@ static void route_map_free_map(struct route_map *map) } /* Route map delete from list. */ -static void route_map_delete(struct route_map *map) +void route_map_delete(struct route_map *map) { struct route_map_index *index; char *name; @@ -883,7 +708,7 @@ static int route_map_clear_updated(struct route_map *map) /* Lookup route map. If there isn't route map create one and return it. */ -static struct route_map *route_map_get(const char *name) +struct route_map *route_map_get(const char *name) { struct route_map *map; @@ -958,14 +783,6 @@ static const char *route_map_result_str(route_map_result_t res) return "invalid"; } -static int route_map_empty(struct route_map *map) -{ - if (map->head == NULL && map->tail == NULL) - return 1; - else - return 0; -} - /* show route-map */ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map) { @@ -1092,12 +909,13 @@ static struct route_map_index *route_map_index_new(void) new = XCALLOC(MTYPE_ROUTE_MAP_INDEX, sizeof(struct route_map_index)); new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ + TAILQ_INIT(&new->rhclist); QOBJ_REG(new, route_map_index); return new; } /* Free route map index. */ -static void route_map_index_delete(struct route_map_index *index, int notify) +void route_map_index_delete(struct route_map_index *index, int notify) { struct route_map_rule *rule; @@ -1107,6 +925,10 @@ static void route_map_index_delete(struct route_map_index *index, int notify) zlog_debug("Deleting route-map %s sequence %d", index->map->name, index->pref); + /* Free route map northbound hook contexts. */ + while (!TAILQ_EMPTY(&index->rhclist)) + routemap_hook_context_free(TAILQ_FIRST(&index->rhclist)); + /* Free route match. */ while ((rule = index->match_list.head) != NULL) route_map_rule_delete(&index->match_list, rule); @@ -1202,7 +1024,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref) } /* Get route map index. */ -static struct route_map_index * +struct route_map_index * route_map_index_get(struct route_map *map, enum route_map_type type, int pref) { struct route_map_index *index; @@ -2063,7 +1885,6 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, hash_free(dep->dep_rmap_hash); XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); XFREE(MTYPE_ROUTE_MAP_DEP, dep); - dep = NULL; } break; case RMAP_EVENT_SET_ADDED: @@ -2209,871 +2030,6 @@ void route_map_notify_dependencies(const char *affected_name, /* VTY related functions. */ -DEFUN (match_interface, - match_interface_cmd, - "match interface WORD", - MATCH_STR - "match first hop interface of route\n" - "Interface name\n") -{ - int idx_word = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_interface) - return rmap_match_set_hook.match_interface( - vty, index, "interface", argv[idx_word]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; -} - -DEFUN (no_match_interface, - no_match_interface_cmd, - "no match interface [WORD]", - NO_STR - MATCH_STR - "Match first hop interface of route\n" - "Interface name\n") -{ - char *iface = (argc == 4) ? argv[3]->arg : NULL; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_interface) - return rmap_match_set_hook.no_match_interface( - vty, index, "interface", iface, - RMAP_EVENT_MATCH_DELETED); - return CMD_SUCCESS; -} - - -DEFUN (match_ip_address, - match_ip_address_cmd, - "match ip address <(1-199)|(1300-2699)|WORD>", - MATCH_STR - IP_STR - "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_acl = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ip_address) - return rmap_match_set_hook.match_ip_address( - vty, index, "ip address", argv[idx_acl]->arg, - RMAP_EVENT_FILTER_ADDED); - return CMD_SUCCESS; -} - - -DEFUN (no_match_ip_address, - no_match_ip_address_cmd, - "no match ip address [<(1-199)|(1300-2699)|WORD>]", - NO_STR - MATCH_STR - IP_STR - "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ip_address) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_address( - vty, index, "ip address", NULL, - RMAP_EVENT_FILTER_DELETED); - return rmap_match_set_hook.no_match_ip_address( - vty, index, "ip address", argv[idx_word]->arg, - RMAP_EVENT_FILTER_DELETED); - } - return CMD_SUCCESS; -} - - -DEFUN (match_ip_address_prefix_list, - match_ip_address_prefix_list_cmd, - "match ip address prefix-list WORD", - MATCH_STR - IP_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ip_address_prefix_list) - return rmap_match_set_hook.match_ip_address_prefix_list( - vty, index, "ip address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); - return CMD_SUCCESS; -} - - -DEFUN (no_match_ip_address_prefix_list, - no_match_ip_address_prefix_list_cmd, - "no match ip address prefix-list [WORD]", - NO_STR - MATCH_STR - IP_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ip_address_prefix_list) { - if (argc <= idx_word) - return rmap_match_set_hook - .no_match_ip_address_prefix_list( - vty, index, "ip address prefix-list", - NULL, RMAP_EVENT_PLIST_DELETED); - return rmap_match_set_hook.no_match_ip_address_prefix_list( - vty, index, "ip address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); - } - return CMD_SUCCESS; -} - - -DEFUN (match_ip_next_hop, - match_ip_next_hop_cmd, - "match ip next-hop <(1-199)|(1300-2699)|WORD>", - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_acl = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ip_next_hop) - return rmap_match_set_hook.match_ip_next_hop( - vty, index, "ip next-hop", argv[idx_acl]->arg, - RMAP_EVENT_FILTER_ADDED); - return CMD_SUCCESS; -} - - -DEFUN (no_match_ip_next_hop, - no_match_ip_next_hop_cmd, - "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]", - NO_STR - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ip_next_hop) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop", NULL, - RMAP_EVENT_FILTER_DELETED); - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop", argv[idx_word]->arg, - RMAP_EVENT_FILTER_DELETED); - } - return CMD_SUCCESS; -} - - -DEFUN (match_ip_next_hop_prefix_list, - match_ip_next_hop_prefix_list_cmd, - "match ip next-hop prefix-list WORD", - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ip_next_hop_prefix_list) - return rmap_match_set_hook.match_ip_next_hop_prefix_list( - vty, index, "ip next-hop prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); - return CMD_SUCCESS; -} - -DEFUN (no_match_ip_next_hop_prefix_list, - no_match_ip_next_hop_prefix_list_cmd, - "no match ip next-hop prefix-list [WORD]", - NO_STR - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ip_next_hop) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop prefix-list", NULL, - RMAP_EVENT_PLIST_DELETED); - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); - } - return CMD_SUCCESS; -} - -DEFUN(match_ip_next_hop_type, match_ip_next_hop_type_cmd, - "match ip next-hop type ", - MATCH_STR IP_STR - "Match next-hop address of route\n" - "Match entries by type\n" - "Blackhole\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ip_next_hop_type) - return rmap_match_set_hook.match_ip_next_hop_type( - vty, index, "ip next-hop type", argv[idx_word]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; -} - -DEFUN(no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd, - "no match ip next-hop type []", - NO_STR MATCH_STR IP_STR - "Match next-hop address of route\n" - "Match entries by type\n" - "Blackhole\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ip_next_hop) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop type", NULL, - RMAP_EVENT_MATCH_DELETED); - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop type", argv[idx_word]->arg, - RMAP_EVENT_MATCH_DELETED); - } - return CMD_SUCCESS; -} - - -DEFUN (match_ipv6_address, - match_ipv6_address_cmd, - "match ipv6 address WORD", - MATCH_STR - IPV6_STR - "Match IPv6 address of route\n" - "IPv6 access-list name\n") -{ - int idx_word = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ipv6_address) - return rmap_match_set_hook.match_ipv6_address( - vty, index, "ipv6 address", argv[idx_word]->arg, - RMAP_EVENT_FILTER_ADDED); - return CMD_SUCCESS; -} - -DEFUN (no_match_ipv6_address, - no_match_ipv6_address_cmd, - "no match ipv6 address WORD", - NO_STR - MATCH_STR - IPV6_STR - "Match IPv6 address of route\n" - "IPv6 access-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ipv6_address) - return rmap_match_set_hook.no_match_ipv6_address( - vty, index, "ipv6 address", argv[idx_word]->arg, - RMAP_EVENT_FILTER_DELETED); - return CMD_SUCCESS; -} - - -DEFUN (match_ipv6_address_prefix_list, - match_ipv6_address_prefix_list_cmd, - "match ipv6 address prefix-list WORD", - MATCH_STR - IPV6_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ipv6_address_prefix_list) - return rmap_match_set_hook.match_ipv6_address_prefix_list( - vty, index, "ipv6 address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); - return CMD_SUCCESS; -} - -DEFUN (no_match_ipv6_address_prefix_list, - no_match_ipv6_address_prefix_list_cmd, - "no match ipv6 address prefix-list WORD", - NO_STR - MATCH_STR - IPV6_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ipv6_address_prefix_list) - return rmap_match_set_hook.no_match_ipv6_address_prefix_list( - vty, index, "ipv6 address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); - return CMD_SUCCESS; -} - -DEFUN(match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd, - "match ipv6 next-hop type ", - MATCH_STR IPV6_STR - "Match next-hop address of route\n" - "Match entries by type\n" - "Blackhole\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ipv6_next_hop_type) - return rmap_match_set_hook.match_ipv6_next_hop_type( - vty, index, "ipv6 next-hop type", argv[idx_word]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; -} - -DEFUN(no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd, - "no match ipv6 next-hop type []", - NO_STR MATCH_STR IPV6_STR - "Match address of route\n" - "Match entries by type\n" - "Blackhole\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ipv6_next_hop_type) - return rmap_match_set_hook.no_match_ipv6_next_hop_type( - vty, index, "ipv6 next-hop type", - (argc <= idx_word) ? NULL : argv[idx_word]->arg, - RMAP_EVENT_MATCH_DELETED); - return CMD_SUCCESS; -} - -DEFUN (match_metric, - match_metric_cmd, - "match metric (0-4294967295)", - MATCH_STR - "Match metric of route\n" - "Metric value\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_metric) - return rmap_match_set_hook.match_metric(vty, index, "metric", - argv[idx_number]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; -} - - -DEFUN (no_match_metric, - no_match_metric_cmd, - "no match metric [(0-4294967295)]", - NO_STR - MATCH_STR - "Match metric of route\n" - "Metric value\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_metric) { - if (argc <= idx_number) - return rmap_match_set_hook.no_match_metric( - vty, index, "metric", NULL, - RMAP_EVENT_MATCH_DELETED); - return rmap_match_set_hook.no_match_metric( - vty, index, "metric", argv[idx_number]->arg, - RMAP_EVENT_MATCH_DELETED); - } - return CMD_SUCCESS; -} - - -DEFUN (match_tag, - match_tag_cmd, - "match tag (1-4294967295)", - MATCH_STR - "Match tag of route\n" - "Tag value\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_tag) - return rmap_match_set_hook.match_tag(vty, index, "tag", - argv[idx_number]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; -} - - -DEFUN (no_match_tag, - no_match_tag_cmd, - "no match tag [(1-4294967295)]", - NO_STR - MATCH_STR - "Match tag of route\n" - "Tag value\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - - int idx = 0; - char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) - ? argv[idx]->arg - : NULL; - - if (rmap_match_set_hook.no_match_tag) - return rmap_match_set_hook.no_match_tag( - vty, index, "tag", arg, RMAP_EVENT_MATCH_DELETED); - return CMD_SUCCESS; -} - - -DEFUN (set_ip_nexthop, - set_ip_nexthop_cmd, - "set ip next-hop A.B.C.D", - SET_STR - IP_STR - "Next hop address\n" - "IP address of next hop\n") -{ - int idx_ipv4 = 3; - union sockunion su; - int ret; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - ret = str2sockunion(argv[idx_ipv4]->arg, &su); - if (ret < 0) { - vty_out(vty, "%% Malformed nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (su.sin.sin_addr.s_addr == 0 - || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { - vty_out(vty, - "%% nexthop address cannot be 0.0.0.0, multicast or reserved\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (rmap_match_set_hook.set_ip_nexthop) - return rmap_match_set_hook.set_ip_nexthop( - vty, index, "ip next-hop", argv[idx_ipv4]->arg); - return CMD_SUCCESS; -} - - -DEFUN (no_set_ip_nexthop, - no_set_ip_nexthop_cmd, - "no set ip next-hop [A.B.C.D]", - NO_STR - SET_STR - IP_STR - "Next hop address\n" - "IP address of next hop\n") -{ - int idx = 0; - VTY_DECLVAR_CONTEXT(route_map_index, index); - const char *arg = NULL; - - if (argv_find(argv, argc, "A.B.C.D", &idx)) - arg = argv[idx]->arg; - - if (rmap_match_set_hook.no_set_ip_nexthop) - return rmap_match_set_hook.no_set_ip_nexthop( - vty, index, "ip next-hop", arg); - - return CMD_SUCCESS; -} - - -DEFUN (set_ipv6_nexthop_local, - set_ipv6_nexthop_local_cmd, - "set ipv6 next-hop local X:X::X:X", - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "IPv6 local address\n" - "IPv6 address of next hop\n") -{ - int idx_ipv6 = 4; - struct in6_addr addr; - int ret; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - ret = inet_pton(AF_INET6, argv[idx_ipv6]->arg, &addr); - if (!ret) { - vty_out(vty, "%% Malformed nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (!IN6_IS_ADDR_LINKLOCAL(&addr)) { - vty_out(vty, "%% Invalid link-local nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (rmap_match_set_hook.set_ipv6_nexthop_local) - return rmap_match_set_hook.set_ipv6_nexthop_local( - vty, index, "ipv6 next-hop local", argv[idx_ipv6]->arg); - return CMD_SUCCESS; -} - - -DEFUN (no_set_ipv6_nexthop_local, - no_set_ipv6_nexthop_local_cmd, - "no set ipv6 next-hop local [X:X::X:X]", - NO_STR - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "IPv6 local address\n" - "IPv6 address of next hop\n") -{ - int idx_ipv6 = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_set_ipv6_nexthop_local) { - if (argc <= idx_ipv6) - return rmap_match_set_hook.no_set_ipv6_nexthop_local( - vty, index, "ipv6 next-hop local", NULL); - return rmap_match_set_hook.no_set_ipv6_nexthop_local( - vty, index, "ipv6 next-hop local", argv[5]->arg); - } - return CMD_SUCCESS; -} - -DEFUN (set_metric, - set_metric_cmd, - "set metric <(0-4294967295)|rtt|+rtt|-rtt|+metric|-metric>", - SET_STR - "Metric value for destination routing protocol\n" - "Metric value\n" - "Assign round trip time\n" - "Add round trip time\n" - "Subtract round trip time\n" - "Add metric\n" - "Subtract metric\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - const char *pass = (argv[idx_number]->type == RANGE_TKN) - ? argv[idx_number]->arg - : argv[idx_number]->text; - - if (rmap_match_set_hook.set_metric) - return rmap_match_set_hook.set_metric(vty, index, "metric", - pass); - return CMD_SUCCESS; -} - - -DEFUN (no_set_metric, - no_set_metric_cmd, - "no set metric [(0-4294967295)]", - NO_STR - SET_STR - "Metric value for destination routing protocol\n" - "Metric value\n") -{ - int idx_number = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_set_metric) { - if (argc <= idx_number) - return rmap_match_set_hook.no_set_metric( - vty, index, "metric", NULL); - return rmap_match_set_hook.no_set_metric(vty, index, "metric", - argv[idx_number]->arg); - } - return CMD_SUCCESS; -} - - -DEFUN (set_tag, - set_tag_cmd, - "set tag (1-4294967295)", - SET_STR - "Tag value for routing protocol\n" - "Tag value\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - - int idx_number = 2; - if (rmap_match_set_hook.set_tag) - return rmap_match_set_hook.set_tag(vty, index, "tag", - argv[idx_number]->arg); - return CMD_SUCCESS; -} - - -DEFUN (no_set_tag, - no_set_tag_cmd, - "no set tag [(1-4294967295)]", - NO_STR - SET_STR - "Tag value for routing protocol\n" - "Tag value\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - - int idx_number = 3; - if (rmap_match_set_hook.no_set_tag) { - if (argc <= idx_number) - return rmap_match_set_hook.no_set_tag(vty, index, "tag", - NULL); - return rmap_match_set_hook.no_set_tag(vty, index, "tag", - argv[idx_number]->arg); - } - return CMD_SUCCESS; -} - - -DEFUN_NOSH (route_map, - route_map_cmd, - "route-map WORD (1-65535)", - "Create route-map or enter route-map command mode\n" - "Route map tag\n" - "Route map denies set operations\n" - "Route map permits set operations\n" - "Sequence to insert to/delete from existing route-map entry\n") -{ - int idx_word = 1; - int idx_permit_deny = 2; - int idx_number = 3; - struct route_map *map; - struct route_map_index *index; - char *endptr = NULL; - int permit = - argv[idx_permit_deny]->arg[0] == 'p' ? RMAP_PERMIT : RMAP_DENY; - unsigned long pref = strtoul(argv[idx_number]->arg, &endptr, 10); - const char *mapname = argv[idx_word]->arg; - - /* Get route map. */ - map = route_map_get(mapname); - index = route_map_index_get(map, permit, pref); - - VTY_PUSH_CONTEXT(RMAP_NODE, index); - return CMD_SUCCESS; -} - -DEFUN (no_route_map_all, - no_route_map_all_cmd, - "no route-map WORD", - NO_STR - "Create route-map or enter route-map command mode\n" - "Route map tag\n") -{ - int idx_word = 2; - const char *mapname = argv[idx_word]->arg; - struct route_map *map; - - map = route_map_lookup_by_name(mapname); - if (map == NULL) { - vty_out(vty, "%% Could not find route-map %s\n", mapname); - return CMD_WARNING_CONFIG_FAILED; - } - - route_map_delete(map); - - return CMD_SUCCESS; -} - -DEFUN (no_route_map, - no_route_map_cmd, - "no route-map WORD (1-65535)", - NO_STR - "Create route-map or enter route-map command mode\n" - "Route map tag\n" - "Route map denies set operations\n" - "Route map permits set operations\n" - "Sequence to insert to/delete from existing route-map entry\n") -{ - int idx_word = 2; - int idx_permit_deny = 3; - int idx_number = 4; - struct route_map *map; - struct route_map_index *index; - char *endptr = NULL; - int permit = strmatch(argv[idx_permit_deny]->text, "permit") - ? RMAP_PERMIT - : RMAP_DENY; - const char *prefstr = argv[idx_number]->arg; - const char *mapname = argv[idx_word]->arg; - unsigned long pref = strtoul(prefstr, &endptr, 10); - - /* Existence check. */ - map = route_map_lookup_by_name(mapname); - if (map == NULL) { - vty_out(vty, "%% Could not find route-map %s\n", mapname); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Lookup route map index. */ - index = route_map_index_lookup(map, permit, pref); - if (index == NULL) { - vty_out(vty, "%% Could not find route-map entry %s %s\n", - mapname, prefstr); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Delete index from route map. */ - route_map_index_delete(index, 1); - - /* If this route rule is the last one, delete route map itself. */ - if (route_map_empty(map)) - route_map_delete(map); - - return CMD_SUCCESS; -} - -DEFUN (rmap_onmatch_next, - rmap_onmatch_next_cmd, - "on-match next", - "Exit policy on matches\n" - "Next clause\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) { - if (index->type == RMAP_DENY) { - /* Under a deny clause, match means it's finished. No - * need to set next */ - vty_out(vty, - "on-match next not supported under route-map deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - index->exitpolicy = RMAP_NEXT; - } - return CMD_SUCCESS; -} - -DEFUN (no_rmap_onmatch_next, - no_rmap_onmatch_next_cmd, - "no on-match next", - NO_STR - "Exit policy on matches\n" - "Next clause\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) - index->exitpolicy = RMAP_EXIT; - - return CMD_SUCCESS; -} - -DEFUN (rmap_onmatch_goto, - rmap_onmatch_goto_cmd, - "on-match goto (1-65535)", - "Exit policy on matches\n" - "Goto Clause number\n" - "Number\n") -{ - int idx = 0; - char *num = argv_find(argv, argc, "(1-65535)", &idx) ? argv[idx]->arg - : NULL; - - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - int d = 0; - - if (index) { - if (index->type == RMAP_DENY) { - /* Under a deny clause, match means it's finished. No - * need to go anywhere */ - vty_out(vty, - "on-match goto not supported under route-map deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (num) - d = strtoul(num, NULL, 10); - else - d = index->pref + 1; - - if (d <= index->pref) { - /* Can't allow you to do that, Dave */ - vty_out(vty, "can't jump backwards in route-maps\n"); - return CMD_WARNING_CONFIG_FAILED; - } else { - index->exitpolicy = RMAP_GOTO; - index->nextpref = d; - } - } - return CMD_SUCCESS; -} - -DEFUN (no_rmap_onmatch_goto, - no_rmap_onmatch_goto_cmd, - "no on-match goto", - NO_STR - "Exit policy on matches\n" - "Goto Clause number\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) - index->exitpolicy = RMAP_EXIT; - - return CMD_SUCCESS; -} - -/* Cisco/GNU Zebra compatibility aliases */ -/* ALIAS_FIXME */ -DEFUN (rmap_continue, - rmap_continue_cmd, - "continue (1-65535)", - "Continue on a different entry within the route-map\n" - "Route-map entry sequence number\n") -{ - return rmap_onmatch_goto(self, vty, argc, argv); -} - -/* ALIAS_FIXME */ -DEFUN (no_rmap_continue, - no_rmap_continue_cmd, - "no continue [(1-65535)]", - NO_STR - "Continue on a different entry within the route-map\n" - "Route-map entry sequence number\n") -{ - return no_rmap_onmatch_goto(self, vty, argc, argv); -} - static void clear_route_map_helper(struct route_map *map) { struct route_map_index *index; @@ -3136,89 +2092,6 @@ DEFUN (rmap_show_unused, return vty_show_unused_route_map(vty); } -DEFUN (rmap_call, - rmap_call_cmd, - "call WORD", - "Jump to another Route-Map after match+set\n" - "Target route-map name\n") -{ - int idx_word = 1; - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - const char *rmap = argv[idx_word]->arg; - - assert(index); - - /* If "call" is invoked with the same route-map name as - * the one previously configured then, ignore the duplicate - * configuration. - */ - if (index->nextrm && (strcmp(index->nextrm, rmap) == 0)) - return CMD_SUCCESS; - - if (index->nextrm) { - route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, - index->nextrm, index->map->name); - XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm); - } - index->nextrm = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); - - /* Execute event hook. */ - route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, index->nextrm, - index->map->name); - return CMD_SUCCESS; -} - -DEFUN (no_rmap_call, - no_rmap_call_cmd, - "no call", - NO_STR - "Jump to another Route-Map after match+set\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index->nextrm) { - route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, - index->nextrm, index->map->name); - XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm); - index->nextrm = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN (rmap_description, - rmap_description_cmd, - "description LINE...", - "Route-map comment\n" - "Comment describing this route-map rule\n") -{ - int idx_line = 1; - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) { - if (index->description) - XFREE(MTYPE_TMP, index->description); - index->description = argv_concat(argv, argc, idx_line); - } - return CMD_SUCCESS; -} - -DEFUN (no_rmap_description, - no_rmap_description_cmd, - "no description", - NO_STR - "Route-map comment\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) { - if (index->description) - XFREE(MTYPE_TMP, index->description); - index->description = NULL; - } - return CMD_SUCCESS; -} - DEFUN (debug_rmap, debug_rmap_cmd, "debug route-map", @@ -3244,59 +2117,6 @@ DEFUN (no_debug_rmap, static struct cmd_node rmap_debug_node = {RMAP_DEBUG_NODE, "", 1}; /* Configuration write function. */ -static int route_map_config_write(struct vty *vty) -{ - struct route_map *map; - struct route_map_index *index; - struct route_map_rule *rule; - int first = 1; - int write = 0; - struct listnode *ln; - struct list *maplist = list_new(); - - for (map = route_map_master.head; map; map = map->next) - listnode_add(maplist, map); - - list_sort(maplist, sort_route_map); - - for (ALL_LIST_ELEMENTS_RO(maplist, ln, map)) - for (index = map->head; index; index = index->next) { - if (!first) - vty_out(vty, "!\n"); - else - first = 0; - - vty_out(vty, "route-map %s %s %d\n", map->name, - route_map_type_str(index->type), index->pref); - - if (index->description) - vty_out(vty, " description %s\n", - index->description); - - for (rule = index->match_list.head; rule; - rule = rule->next) - vty_out(vty, " match %s %s\n", rule->cmd->str, - rule->rule_str ? rule->rule_str : ""); - - for (rule = index->set_list.head; rule; - rule = rule->next) - vty_out(vty, " set %s %s\n", rule->cmd->str, - rule->rule_str ? rule->rule_str : ""); - if (index->nextrm) - vty_out(vty, " call %s\n", index->nextrm); - if (index->exitpolicy == RMAP_GOTO) - vty_out(vty, " on-match goto %d\n", - index->nextpref); - if (index->exitpolicy == RMAP_NEXT) - vty_out(vty, " on-match next\n"); - - write++; - } - - list_delete(&maplist); - return write; -} - static int rmap_config_write_debug(struct vty *vty) { int write = 0; @@ -3309,9 +2129,6 @@ static int rmap_config_write_debug(struct vty *vty) return write; } -/* Route map node structure. */ -static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1}; - /* Common route map rules */ void *route_map_rule_tag_compile(const char *arg) @@ -3370,14 +2187,6 @@ void route_map_finish(void) route_map_master_hash = NULL; } -static void rmap_autocomplete(vector comps, struct cmd_token *token) -{ - struct route_map *map; - - for (map = route_map_master.head; map; map = map->next) - vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name)); -} - /* Increment the use_count counter while attaching the route map */ void route_map_counter_increment(struct route_map *map) { @@ -3395,14 +2204,6 @@ void route_map_counter_decrement(struct route_map *map) } } -static const struct cmd_variable_handler rmap_var_handlers[] = { - {/* "route-map WORD" */ - .varname = "route_map", - .completions = rmap_autocomplete}, - {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete}, - {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete}, - {.completions = NULL}}; - /* Initialization of route map vector. */ void route_map_init(void) { @@ -3420,43 +2221,17 @@ void route_map_init(void) 8, route_map_dep_hash_make_key, route_map_dep_hash_cmp, "Route Map Dep Hash"); - cmd_variable_handler_register(rmap_var_handlers); - rmap_debug = false; - /* Install route map top node. */ - install_node(&rmap_node, route_map_config_write); + route_map_cli_init(); + /* Install route map top node. */ install_node(&rmap_debug_node, rmap_config_write_debug); /* Install route map commands. */ - install_default(RMAP_NODE); - install_element(CONFIG_NODE, &route_map_cmd); - install_element(CONFIG_NODE, &no_route_map_cmd); - install_element(CONFIG_NODE, &no_route_map_all_cmd); - install_element(CONFIG_NODE, &debug_rmap_cmd); install_element(CONFIG_NODE, &no_debug_rmap_cmd); - /* Install the on-match stuff */ - install_element(RMAP_NODE, &route_map_cmd); - install_element(RMAP_NODE, &rmap_onmatch_next_cmd); - install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd); - install_element(RMAP_NODE, &rmap_onmatch_goto_cmd); - install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd); - install_element(RMAP_NODE, &rmap_continue_cmd); - install_element(RMAP_NODE, &no_rmap_continue_cmd); - - /* Install the continue stuff (ALIAS of on-match). */ - - /* Install the call stuff. */ - install_element(RMAP_NODE, &rmap_call_cmd); - install_element(RMAP_NODE, &no_rmap_call_cmd); - - /* Install description commands. */ - install_element(RMAP_NODE, &rmap_description_cmd); - install_element(RMAP_NODE, &no_rmap_description_cmd); - /* Install show command */ install_element(ENABLE_NODE, &rmap_clear_counters_cmd); @@ -3465,49 +2240,4 @@ void route_map_init(void) install_element(ENABLE_NODE, &debug_rmap_cmd); install_element(ENABLE_NODE, &no_debug_rmap_cmd); - - install_element(RMAP_NODE, &match_interface_cmd); - install_element(RMAP_NODE, &no_match_interface_cmd); - - install_element(RMAP_NODE, &match_ip_address_cmd); - install_element(RMAP_NODE, &no_match_ip_address_cmd); - - install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd); - - install_element(RMAP_NODE, &match_ip_next_hop_cmd); - install_element(RMAP_NODE, &no_match_ip_next_hop_cmd); - - install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); - - install_element(RMAP_NODE, &match_ip_next_hop_type_cmd); - install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd); - - install_element(RMAP_NODE, &match_ipv6_address_cmd); - install_element(RMAP_NODE, &no_match_ipv6_address_cmd); - - install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); - - install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd); - install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd); - - install_element(RMAP_NODE, &match_metric_cmd); - install_element(RMAP_NODE, &no_match_metric_cmd); - - install_element(RMAP_NODE, &match_tag_cmd); - install_element(RMAP_NODE, &no_match_tag_cmd); - - install_element(RMAP_NODE, &set_ip_nexthop_cmd); - install_element(RMAP_NODE, &no_set_ip_nexthop_cmd); - - install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd); - install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); - - install_element(RMAP_NODE, &set_metric_cmd); - install_element(RMAP_NODE, &no_set_metric_cmd); - - install_element(RMAP_NODE, &set_tag_cmd); - install_element(RMAP_NODE, &no_set_tag_cmd); } diff --git a/lib/routemap.h b/lib/routemap.h index 1ffd0525a..05c958967 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -140,12 +140,31 @@ enum rmap_compile_rets { }; +/* Route map rule. This rule has both `match' rule and `set' rule. */ +struct route_map_rule { + /* Rule type. */ + const struct route_map_rule_cmd *cmd; + + /* For pretty printing. */ + char *rule_str; + + /* Pre-compiled match rule. */ + void *value; + + /* Linked list. */ + struct route_map_rule *next; + struct route_map_rule *prev; +}; + /* Route map rule list. */ struct route_map_rule_list { struct route_map_rule *head; struct route_map_rule *tail; }; +/* Forward struct declaration: the complete can be found later this file. */ +struct routemap_hook_context; + /* Route map index structure. */ struct route_map_index { struct route_map *map; @@ -178,6 +197,9 @@ struct route_map_index { uint64_t applied; uint64_t applied_clear; + /* List of match/sets contexts. */ + TAILQ_HEAD(, routemap_hook_context) rhclist; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(route_map_index) @@ -435,6 +457,247 @@ extern void route_map_counter_increment(struct route_map *map); /* Decrement the route-map used counter */ extern void route_map_counter_decrement(struct route_map *map); +/* Route map hooks data structure. */ +struct route_map_match_set_hooks { + /* match interface */ + int (*match_interface)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match interface */ + int (*no_match_interface)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match ip address */ + int (*match_ip_address)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match ip address */ + int (*no_match_ip_address)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match ip address prefix list */ + int (*match_ip_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip address prefix list */ + int (*no_match_ip_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip next hop */ + int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match ip next hop */ + int (*no_match_ip_next_hop)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match ip next hop prefix list */ + int (*match_ip_next_hop_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip next hop prefix list */ + int (*no_match_ip_next_hop_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip next-hop type */ + int (*match_ip_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip next-hop type */ + int (*no_match_ip_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ipv6 address */ + int (*match_ipv6_address)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match ipv6 address */ + int (*no_match_ipv6_address)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + + /* match ipv6 address prefix list */ + int (*match_ipv6_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ipv6 address prefix list */ + int (*no_match_ipv6_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ipv6 next-hop type */ + int (*match_ipv6_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ipv6 next-hop type */ + int (*no_match_ipv6_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match metric */ + int (*match_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match metric */ + int (*no_match_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match tag */ + int (*match_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match tag */ + int (*no_match_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* set ip nexthop */ + int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set ip nexthop */ + int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* set ipv6 nexthop local */ + int (*set_ipv6_nexthop_local)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg); + + /* no set ipv6 nexthop local */ + int (*no_set_ipv6_nexthop_local)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg); + + /* set metric */ + int (*set_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set metric */ + int (*no_set_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* set tag */ + int (*set_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set tag */ + int (*no_set_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); +}; + +extern struct route_map_match_set_hooks rmap_match_set_hook; + +/* Making route map list. */ +struct route_map_list { + struct route_map *head; + struct route_map *tail; + + void (*add_hook)(const char *); + void (*delete_hook)(const char *); + void (*event_hook)(const char *); +}; + +extern struct route_map_list route_map_master; + +extern struct route_map *route_map_get(const char *name); +extern void route_map_delete(struct route_map *map); +extern struct route_map_index *route_map_index_get(struct route_map *map, + enum route_map_type type, + int pref); +extern void route_map_index_delete(struct route_map_index *index, int notify); + +/* routemap_northbound.c */ +typedef int (*routemap_match_hook_fun)(struct vty *vty, + struct route_map_index *rmi, + const char *command, const char *arg, + route_map_event_t event); + +typedef int (*routemap_set_hook_fun)(struct vty *vty, + struct route_map_index *rmi, + const char *command, const char *arg); + +struct routemap_hook_context { + struct route_map_index *rhc_rmi; + const char *rhc_rule; + route_map_event_t rhc_event; + routemap_set_hook_fun rhc_shook; + routemap_match_hook_fun rhc_mhook; + TAILQ_ENTRY(routemap_hook_context) rhc_entry; +}; + +int lib_route_map_entry_match_destroy(enum nb_event event, + const struct lyd_node *dnode); +int lib_route_map_entry_set_destroy(enum nb_event event, + const struct lyd_node *dnode); + +struct routemap_hook_context * +routemap_hook_context_insert(struct route_map_index *rmi); +void routemap_hook_context_free(struct routemap_hook_context *rhc); + +extern const struct frr_yang_module_info frr_route_map_info; + +/* routemap_cli.c */ +extern void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_instance_show_end(struct vty *vty, + struct lyd_node *dnode); +extern void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_action_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_call_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_description_show(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void route_map_cli_init(void); + #ifdef __cplusplus } #endif diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c new file mode 100644 index 000000000..702371056 --- /dev/null +++ b/lib/routemap_cli.c @@ -0,0 +1,1129 @@ +/* + * Route map northbound CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/command.h" +#include "lib/northbound_cli.h" +#include "lib/routemap.h" + +#ifndef VTYSH_EXTRACT_PL +#include "lib/routemap_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +#define ROUTE_MAP_CMD_STR \ + "Create route-map or enter route-map command mode\n" \ + "Route map tag\n" +#define ROUTE_MAP_OP_CMD_STR \ + "Route map denies set operations\n" \ + "Route map permits set operations\n" +#define ROUTE_MAP_SEQUENCE_CMD_STR \ + "Sequence to insert to/delete from existing route-map entry\n" + +DEFPY_NOSH( + route_map, route_map_cmd, + "route-map WORD$name $action (1-65535)$sequence", + ROUTE_MAP_CMD_STR + ROUTE_MAP_OP_CMD_STR + ROUTE_MAP_SEQUENCE_CMD_STR) +{ + struct route_map_index *rmi; + struct route_map *rm; + int action_type; + char xpath_action[XPATH_MAXLEN + 64]; + char xpath_index[XPATH_MAXLEN + 32]; + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_index, sizeof(xpath_index), "%s/entry[sequence='%lu']", + xpath, sequence); + nb_cli_enqueue_change(vty, xpath_index, NB_OP_CREATE, NULL); + + snprintf(xpath_action, sizeof(xpath_action), "%s/action", xpath_index); + nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) { + VTY_PUSH_XPATH(RMAP_NODE, xpath_index); + + /* Add support for non-migrated route map users. */ + rm = route_map_get(name); + action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY; + rmi = route_map_index_get(rm, action_type, sequence); + VTY_PUSH_CONTEXT(RMAP_NODE, rmi); + } + + return rv; +} + +DEFPY( + no_route_map_all, no_route_map_all_cmd, + "no route-map WORD$name", + NO_STR + ROUTE_MAP_CMD_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_route_map, no_route_map_cmd, + "no route-map WORD$name $action (1-65535)$sequence", + NO_STR + ROUTE_MAP_CMD_STR + ROUTE_MAP_OP_CMD_STR + ROUTE_MAP_SEQUENCE_CMD_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']/entry[sequence='%lu']", + name, sequence); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct route_map_rule *rmr; + const struct route_map_index *rmi; + const char *name = yang_dnode_get_string(dnode, "../name"); + const char *action = yang_dnode_get_string(dnode, "./action"); + const char *sequence = yang_dnode_get_string(dnode, "./sequence"); + + vty_out(vty, "route-map %s %s %s\n", name, action, sequence); + + rmi = nb_running_get_entry(dnode, NULL, false); + if (rmi == NULL) { + /* + * We can't have outdated rules if route map hasn't + * been created yet. + */ + return; + } + +#define SKIP_RULE(name) if (strcmp((name), rmr->cmd->str) == 0) continue + + /* Print route map `match` for old CLI users. */ + for (rmr = rmi->match_list.head; rmr; rmr = rmr->next) { + /* Skip all matches implemented by northbound. */ + SKIP_RULE("interface"); + SKIP_RULE("ip address"); + SKIP_RULE("ip address prefix-list"); + SKIP_RULE("ip next-hop"); + SKIP_RULE("ip next-hop prefix-list"); + SKIP_RULE("ip next-hop type"); + SKIP_RULE("ipv6 address"); + SKIP_RULE("ipv6 address prefix-list"); + SKIP_RULE("ipv6 next-hop type"); + SKIP_RULE("metric"); + SKIP_RULE("tag"); + + vty_out(vty, " match %s %s\n", rmr->cmd->str, + rmr->rule_str ? rmr->rule_str : ""); + } + + /* Print route map `set` for old CLI users. */ + for (rmr = rmi->set_list.head; rmr; rmr = rmr->next) { + /* Skip all sets implemented by northbound. */ + SKIP_RULE("metric"); + SKIP_RULE("tag"); + + vty_out(vty, " set %s %s\n", rmr->cmd->str, + rmr->rule_str ? rmr->rule_str : ""); + } + +#undef SKIP_RULE +} + +void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, "!\n"); +} + +DEFPY( + match_interface, match_interface_cmd, + "match interface IFNAME", + MATCH_STR + "Match first hop interface of route\n" + INTERFACE_STR) +{ + const char *xpath = "./match-condition[condition='interface']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/interface", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ifname); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_interface, no_match_interface_cmd, + "no match interface [IFNAME]", + NO_STR + MATCH_STR + "Match first hop interface of route\n" + INTERFACE_STR) +{ + const char *xpath = "./match-condition[condition='interface']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ip_address, match_ip_address_cmd, + "match ip address <(1-199)$acll|(1300-2699)$aclh|WORD$name>", + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-address-list']"; + char xpath_value[XPATH_MAXLEN + 32]; + int acln = acll ? acll : aclh; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (name) { + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + } else /* if (acll || aclh) */ { + if ((acln >= 1 && acln <= 99) + || (acln >= 1300 && acln <= 1999)) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/access-list-num", xpath); + } else { + /* + * if ((acln >= 100 && acln <= 199) + * || (acln >= 2000 && acln <= 2699)) + */ + snprintf(xpath_value, sizeof(xpath_value), + "%s/access-list-num-extended", xpath); + } + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + acll_str ? acll_str : aclh_str); + } + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ip_address, no_match_ip_address_cmd, + "no match ip address [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-address-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ip_address_prefix_list, + match_ip_address_prefix_list_cmd, + "match ip address prefix-list WORD$name", + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd, + "no match ip address prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ip_next_hop, match_ip_next_hop_cmd, + "match ip next-hop <(1-199)$acll|(1300-2699)$aclh|WORD$name>", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-list']"; + char xpath_value[XPATH_MAXLEN + 32]; + int acln = acll ? acll : aclh; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (name) { + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + } else /* if (acll || aclh) */ { + if ((acln >= 1 && acln <= 99) + || (acln >= 1300 && acln <= 1999)) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/access-list-num", xpath); + } else { + /* + * if ((acln >= 100 && acln <= 199) + * || (acln >= 2000 && acln <= 2699)) + */ + snprintf(xpath_value, sizeof(xpath_value), + "%s/access-list-num-extended", xpath); + } + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + acll_str ? acll_str : aclh_str); + } + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ip_next_hop, no_match_ip_next_hop_cmd, + "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD$name", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = + "./match-condition[condition='ipv4-next-hop-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = + "./match-condition[condition='ipv4-next-hop-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ip_next_hop_type, match_ip_next_hop_type_cmd, + "match ip next-hop type $type", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-next-hop-type", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd, + "no match ip next-hop type []", + NO_STR MATCH_STR IP_STR + "Match next-hop address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ipv6_address, match_ipv6_address_cmd, + "match ipv6 address WORD$name", + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-address-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ipv6_address, no_match_ipv6_address_cmd, + "no match ipv6 address [WORD]", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-address-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd, + "match ipv6 address prefix-list WORD$name", + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_cmd, + "no match ipv6 address prefix-list [WORD]", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd, + "match ipv6 next-hop type $type", + MATCH_STR IPV6_STR + "Match next-hop address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv6-next-hop-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-next-hop-type", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd, + "no match ipv6 next-hop type []", + NO_STR MATCH_STR IPV6_STR + "Match address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv6-next-hop-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_metric, match_metric_cmd, + "match metric (0-4294967295)$metric", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + const char *xpath = "./match-condition[condition='metric']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/metric", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, metric_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_metric, no_match_metric_cmd, + "no match metric [(0-4294967295)]", + NO_STR + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + const char *xpath = "./match-condition[condition='metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + match_tag, match_tag_cmd, + "match tag (1-4294967295)$tag", + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + const char *xpath = "./match-condition[condition='tag']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_match_tag, no_match_tag_cmd, + "no match tag [(1-4294967295)]", + NO_STR + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + const char *xpath = "./match-condition[condition='tag']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int condition = yang_dnode_get_enum(dnode, "./condition"); + struct lyd_node *ln; + const char *acl; + + switch (condition) { + case 0: /* interface */ + vty_out(vty, " match interface %s\n", + yang_dnode_get_string(dnode, "./interface")); + break; + case 1: /* ipv4-address-list */ + case 3: /* ipv4-next-hop-list */ + acl = NULL; + if ((ln = yang_dnode_get(dnode, "./list-name")) != NULL) + acl = yang_dnode_get_string(ln, NULL); + else if ((ln = yang_dnode_get(dnode, "./access-list-num")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + else if ((ln = yang_dnode_get(dnode, + "./access-list-num-extended")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + switch (condition) { + case 1: + vty_out(vty, " match ip address %s\n", acl); + break; + case 3: + vty_out(vty, " match ip next-hop %s\n", acl); + break; + } + break; + case 2: /* ipv4-prefix-list */ + vty_out(vty, " match ip address prefix-list %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 4: /* ipv4-next-hop-prefix-list */ + vty_out(vty, " match ip next-hop prefix-list %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 5: /* ipv4-next-hop-type */ + vty_out(vty, " match ip next-hop type %s\n", + yang_dnode_get_string(dnode, "./ipv4-next-hop-type")); + break; + case 6: /* ipv6-address-list */ + vty_out(vty, " match ipv6 address %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 7: /* ipv6-prefix-list */ + vty_out(vty, " match ipv6 address prefix-list %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 8: /* ipv6-next-hop-type */ + vty_out(vty, " match ipv6 next-hop type %s\n", + yang_dnode_get_string(dnode, "./ipv6-next-hop-type")); + break; + case 9: /* metric */ + vty_out(vty, " match metric %s\n", + yang_dnode_get_string(dnode, "./metric")); + break; + case 10: /* tag */ + vty_out(vty, " match tag %s\n", + yang_dnode_get_string(dnode, "./tag")); + break; + case 100: + /* NOTHING: custom field, should be handled by daemon. */ + break; + } +} + +DEFPY( + set_ip_nexthop, set_ip_nexthop_cmd, + "set ip next-hop A.B.C.D$addr", + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv4-next-hop']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_set_ip_nexthop, no_set_ip_nexthop_cmd, + "no set ip next-hop [A.B.C.D]", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv4-next-hop']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd, + "set ipv6 next-hop local X:X::X:X$addr", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv6-next-hop']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd, + "no set ipv6 next-hop local [X:X::X:X]", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv6-next-hop']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + set_metric, set_metric_cmd, + "set metric <(0-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|+metric$ametric|-metric$smetric>", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n" + "Assign round trip time\n" + "Add round trip time\n" + "Subtract round trip time\n" + "Add metric\n" + "Subtract metric\n") +{ + const char *xpath = "./set-action[action='metric']"; + char xpath_value[XPATH_MAXLEN]; + char value[64]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (rtt) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/use-round-trip-time", xpath); + snprintf(value, sizeof(value), "true"); + } else if (artt) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/add-round-trip-time", xpath); + snprintf(value, sizeof(value), "true"); + } else if (srtt) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/subtract-round-trip-time", xpath); + snprintf(value, sizeof(value), "true"); + } else if (ametric) { + snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric", + xpath); + snprintf(value, sizeof(value), "true"); + } else if (smetric) { + snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric", + xpath); + snprintf(value, sizeof(value), "true"); + } else { + snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath); + snprintf(value, sizeof(value), "%lu", metric); + } + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_set_metric, no_set_metric_cmd, + "no set metric [(0-4294967295)]", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + const char *xpath = "./set-action[action='metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + set_tag, set_tag_cmd, + "set tag (1-4294967295)$tag", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + const char *xpath = "./set-action[action='tag']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_set_tag, no_set_tag_cmd, + "no set tag [(1-4294967295)]", + NO_STR + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + const char *xpath = "./set-action[action='tag']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_action_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int action = yang_dnode_get_enum(dnode, "./action"); + + switch (action) { + case 0: /* ipv4-next-hop */ + vty_out(vty, " set ip next-hop %s\n", + yang_dnode_get_string(dnode, "./ipv4-address")); + break; + case 1: /* ipv6-next-hop */ + vty_out(vty, " set ipv6 next-hop local %s\n", + yang_dnode_get_string(dnode, "./ipv6-address")); + break; + case 2: /* metric */ + if (yang_dnode_get(dnode, "./use-round-trip-time")) { + vty_out(vty, " set metric rtt\n"); + } else if (yang_dnode_get(dnode, "./add-round-trip-time")) { + vty_out(vty, " set metric +rtt\n"); + } else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) { + vty_out(vty, " set metric -rtt\n"); + } else if (yang_dnode_get(dnode, "./add-metric")) { + vty_out(vty, " set metric +metric\n"); + } else if (yang_dnode_get(dnode, "./subtract-metric")) { + vty_out(vty, " set metric -metric\n"); + } else { + vty_out(vty, " set metric %s\n", + yang_dnode_get_string(dnode, "./value")); + } + break; + case 3: /* tag */ + vty_out(vty, " set tag %s\n", + yang_dnode_get_string(dnode, "./tag")); + break; + case 100: + /* NOTHING: custom field, should be handled by daemon. */ + break; + } +} + +DEFPY( + rmap_onmatch_next, rmap_onmatch_next_cmd, + "on-match next", + "Exit policy on matches\n" + "Next clause\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "next"); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_rmap_onmatch_next, + no_rmap_onmatch_next_cmd, + "no on-match next", + NO_STR + "Exit policy on matches\n" + "Next clause\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + rmap_onmatch_goto, rmap_onmatch_goto_cmd, + "on-match goto (1-65535)$rm_num", + "Exit policy on matches\n" + "Goto Clause number\n" + "Number\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "goto"); + nb_cli_enqueue_change(vty, "./goto-value", NB_OP_MODIFY, rm_num_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd, + "no on-match goto", + NO_STR + "Exit policy on matches\n" + "Goto Clause number\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +/* Cisco/GNU Zebra compatibility aliases */ +ALIAS( + rmap_onmatch_goto, rmap_continue_cmd, + "continue (1-65535)$rm_num", + "Continue on a different entry within the route-map\n" + "Route-map entry sequence number\n") + +ALIAS( + no_rmap_onmatch_goto, no_rmap_continue_cmd, + "no continue [(1-65535)]", + NO_STR + "Continue on a different entry within the route-map\n" + "Route-map entry sequence number\n") + +void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int exit_policy = yang_dnode_get_enum(dnode, NULL); + + switch (exit_policy) { + case 0: /* permit-or-deny */ + /* NOTHING: default option. */ + break; + case 1: /* next */ + vty_out(vty, " on-match next\n"); + break; + case 2: /* goto */ + vty_out(vty, " on-match goto %s\n", + yang_dnode_get_string(dnode, "../goto-value")); + break; + } +} + +DEFPY( + rmap_call, rmap_call_cmd, + "call WORD$name", + "Jump to another Route-Map after match+set\n" + "Target route-map name\n") +{ + nb_cli_enqueue_change(vty, "./call", NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY( + no_rmap_call, no_rmap_call_cmd, + "no call", + NO_STR + "Jump to another Route-Map after match+set\n") +{ + nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_call_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL)); +} + +DEFPY( + rmap_description, rmap_description_cmd, + "description LINE...", + "Route-map comment\n" + "Comment describing this route-map rule\n") +{ + char *desc; + int rv; + + desc = argv_concat(argv, argc, 1); + nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc); + rv = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, desc); + + return rv; +} + +DEFUN (no_rmap_description, + no_rmap_description_cmd, + "no description", + NO_STR + "Route-map comment\n") +{ + nb_cli_enqueue_change(vty, "./description", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_description_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL)); +} + +static int route_map_config_write(struct vty *vty) +{ + struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, + "/frr-route-map:lib"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; +} + +/* Route map node structure. */ +static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1}; + +static void rmap_autocomplete(vector comps, struct cmd_token *token) +{ + struct route_map *map; + + for (map = route_map_master.head; map; map = map->next) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name)); +} + +static const struct cmd_variable_handler rmap_var_handlers[] = { + {.varname = "route_map", .completions = rmap_autocomplete}, + {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete}, + {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete}, + {.completions = NULL} +}; + +void route_map_cli_init(void) +{ + /* Auto complete handler. */ + cmd_variable_handler_register(rmap_var_handlers); + + /* CLI commands. */ + install_node(&rmap_node, route_map_config_write); + install_default(RMAP_NODE); + install_element(CONFIG_NODE, &route_map_cmd); + install_element(CONFIG_NODE, &no_route_map_cmd); + install_element(CONFIG_NODE, &no_route_map_all_cmd); + + /* Install the on-match stuff */ + install_element(RMAP_NODE, &route_map_cmd); + install_element(RMAP_NODE, &rmap_onmatch_next_cmd); + install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd); + install_element(RMAP_NODE, &rmap_onmatch_goto_cmd); + install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd); + install_element(RMAP_NODE, &rmap_continue_cmd); + install_element(RMAP_NODE, &no_rmap_continue_cmd); + + /* Install the call stuff. */ + install_element(RMAP_NODE, &rmap_call_cmd); + install_element(RMAP_NODE, &no_rmap_call_cmd); + + /* Install description commands. */ + install_element(RMAP_NODE, &rmap_description_cmd); + install_element(RMAP_NODE, &no_rmap_description_cmd); + + /* Install 'match' commands. */ + install_element(RMAP_NODE, &match_interface_cmd); + install_element(RMAP_NODE, &no_match_interface_cmd); + + install_element(RMAP_NODE, &match_ip_address_cmd); + install_element(RMAP_NODE, &no_match_ip_address_cmd); + + install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + + install_element(RMAP_NODE, &match_ip_next_hop_cmd); + install_element(RMAP_NODE, &no_match_ip_next_hop_cmd); + + install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + + install_element(RMAP_NODE, &match_ip_next_hop_type_cmd); + install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd); + + install_element(RMAP_NODE, &match_ipv6_address_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_cmd); + + install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); + + install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd); + install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd); + + install_element(RMAP_NODE, &match_metric_cmd); + install_element(RMAP_NODE, &no_match_metric_cmd); + + install_element(RMAP_NODE, &match_tag_cmd); + install_element(RMAP_NODE, &no_match_tag_cmd); + + /* Install 'set' commands. */ + install_element(RMAP_NODE, &set_ip_nexthop_cmd); + install_element(RMAP_NODE, &no_set_ip_nexthop_cmd); + + install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd); + install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); + + install_element(RMAP_NODE, &set_metric_cmd); + install_element(RMAP_NODE, &no_set_metric_cmd); + + install_element(RMAP_NODE, &set_tag_cmd); + install_element(RMAP_NODE, &no_set_tag_cmd); +} diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c new file mode 100644 index 000000000..68b112b09 --- /dev/null +++ b/lib/routemap_northbound.c @@ -0,0 +1,1446 @@ +/* + * Route map northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" + +/* + * Auxiliary functions to avoid code duplication: + * + * lib_route_map_entry_set_destroy: unset `set` commands. + * lib_route_map_entry_match_destroy: unset `match` commands. + */ +int lib_route_map_entry_match_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + rhc = nb_running_get_entry(dnode, NULL, true); + if (rhc->rhc_mhook == NULL) + return NB_OK; + + rv = rhc->rhc_mhook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL, + rhc->rhc_event); + if (rv != CMD_SUCCESS) + return NB_ERR_INCONSISTENCY; + + return NB_OK; +} + +int lib_route_map_entry_set_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + rhc = nb_running_get_entry(dnode, NULL, true); + if (rhc->rhc_shook == NULL) + return NB_OK; + + rv = rhc->rhc_shook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL); + if (rv != CMD_SUCCESS) + return NB_ERR_INCONSISTENCY; + + return NB_OK; +} + +/* + * Auxiliary hook context list manipulation functions. + */ +struct routemap_hook_context * +routemap_hook_context_insert(struct route_map_index *rmi) +{ + struct routemap_hook_context *rhc; + + rhc = XCALLOC(MTYPE_TMP, sizeof(*rhc)); + rhc->rhc_rmi = rmi; + TAILQ_INSERT_TAIL(&rmi->rhclist, rhc, rhc_entry); + + return rhc; +} + +void +routemap_hook_context_free(struct routemap_hook_context *rhc) +{ + struct route_map_index *rmi = rhc->rhc_rmi; + + TAILQ_REMOVE(&rmi->rhclist, rhc, rhc_entry); + XFREE(MTYPE_TMP, rhc); +} + +/* + * XPath: /frr-route-map:lib/route-map + */ +static int lib_route_map_create(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map *rm; + const char *rm_name; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rm_name = yang_dnode_get_string(dnode, "./name"); + rm = route_map_get(rm_name); + nb_running_set_entry(dnode, rm); + break; + } + + return NB_OK; +} + +static int lib_route_map_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct route_map *rm; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rm = nb_running_unset_entry(dnode); + route_map_delete(rm); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry + */ +static int lib_route_map_entry_create(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map_index *rmi; + struct route_map *rm; + uint16_t sequence; + int action; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + sequence = yang_dnode_get_uint16(dnode, "./sequence"); + action = yang_dnode_get_enum(dnode, "./action") == 0 + ? RMAP_PERMIT + : RMAP_DENY; + rm = nb_running_get_entry(dnode, NULL, true); + rmi = route_map_index_get(rm, action, sequence); + nb_running_set_entry(dnode, rmi); + break; + } + + return NB_OK; +} + +static int lib_route_map_entry_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct route_map_index *rmi; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_unset_entry(dnode); + route_map_index_delete(rmi, 1); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/description + */ +static int lib_route_map_entry_description_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map_index *rmi; + const char *description; + + switch (event) { + case NB_EV_VALIDATE: + /* NOTHING */ + break; + case NB_EV_PREPARE: + description = yang_dnode_get_string(dnode, NULL); + resource->ptr = XSTRDUP(MTYPE_TMP, description); + if (resource->ptr == NULL) + return NB_ERR_RESOURCE; + break; + case NB_EV_ABORT: + XFREE(MTYPE_TMP, resource->ptr); + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + if (rmi->description != NULL) + XFREE(MTYPE_TMP, rmi->description); + rmi->description = resource->ptr; + break; + } + + return NB_OK; +} + +static int lib_route_map_entry_description_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct route_map_index *rmi; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + if (rmi->description != NULL) + XFREE(MTYPE_TMP, rmi->description); + rmi->description = NULL; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/action + */ +static int lib_route_map_entry_action_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map_index *rmi; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + rmi->type = yang_dnode_get_enum(dnode, NULL); + /* TODO: notify? */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/call + */ +static int lib_route_map_entry_call_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map_index *rmi; + const char *rm_name, *rmn_name; + + switch (event) { + case NB_EV_VALIDATE: + rm_name = yang_dnode_get_string(dnode, "../../name"); + rmn_name = yang_dnode_get_string(dnode, NULL); + /* Don't allow to jump to the same route map instance. */ + if (strcmp(rm_name, rmn_name) == 0) + return NB_ERR_VALIDATION; + + /* TODO: detect circular route map sequences. */ + break; + case NB_EV_PREPARE: + rmn_name = yang_dnode_get_string(dnode, NULL); + resource->ptr = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmn_name); + break; + case NB_EV_ABORT: + XFREE(MTYPE_ROUTE_MAP_NAME, resource->ptr); + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + if (rmi->nextrm) { + route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, + rmi->nextrm, rmi->map->name); + XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm); + } + rmi->nextrm = resource->ptr; + route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, rmi->nextrm, + rmi->map->name); + break; + } + + return NB_OK; +} + +static int lib_route_map_entry_call_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct route_map_index *rmi; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, rmi->nextrm, + rmi->map->name); + XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm); + rmi->nextrm = NULL; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/exit-policy + */ +static int lib_route_map_entry_exit_policy_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map_index *rmi; + int rm_action; + int policy; + + switch (event) { + case NB_EV_VALIDATE: + policy = yang_dnode_get_enum(dnode, NULL); + switch (policy) { + case 0: /* permit-or-deny */ + break; + case 1: /* next */ + /* FALLTHROUGH */ + case 2: /* goto */ + rm_action = yang_dnode_get_enum(dnode, "../action"); + if (rm_action == 1 /* deny */) { + /* + * On deny it is not possible to 'goto' + * anywhere. + */ + return NB_ERR_VALIDATION; + } + break; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + policy = yang_dnode_get_enum(dnode, NULL); + + switch (policy) { + case 0: /* permit-or-deny */ + rmi->exitpolicy = RMAP_EXIT; + break; + case 1: /* next */ + rmi->exitpolicy = RMAP_NEXT; + break; + case 2: /* goto */ + rmi->exitpolicy = RMAP_GOTO; + break; + } + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/goto-value + */ +static int lib_route_map_entry_goto_value_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct route_map_index *rmi; + uint16_t rmi_index; + uint16_t rmi_next; + + switch (event) { + case NB_EV_VALIDATE: + rmi_index = yang_dnode_get_uint16(dnode, "../sequence"); + rmi_next = yang_dnode_get_uint16(dnode, NULL); + if (rmi_next <= rmi_index) { + /* Can't jump backwards on a route map. */ + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + rmi->nextpref = yang_dnode_get_uint16(dnode, NULL); + break; + } + + return NB_OK; +} + +static int lib_route_map_entry_goto_value_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct route_map_index *rmi; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + rmi->nextpref = 0; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition + */ +static int +lib_route_map_entry_match_condition_create(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + struct route_map_index *rmi; + + switch (event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(dnode, NULL, true); + rhc = routemap_hook_context_insert(rmi); + nb_running_set_entry(dnode, rhc); + break; + } + + return NB_OK; +} + +static int +lib_route_map_entry_match_condition_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + rv = lib_route_map_entry_match_destroy(event, dnode); + rhc = nb_running_unset_entry(dnode); + routemap_hook_context_free(rhc); + + return rv; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/interface + */ +static int lib_route_map_entry_match_condition_interface_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *ifname; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_interface == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + ifname = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_interface; + rhc->rhc_rule = "interface"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_interface(NULL, rhc->rhc_rmi, + "interface", ifname, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_interface_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/access-list-num + */ +static int lib_route_map_entry_match_condition_access_list_num_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *acl; + int condition, rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + rv = CMD_SUCCESS; + acl = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(dnode, NULL, true); + condition = yang_dnode_get_enum(dnode, "../condition"); + switch (condition) { + case 1: /* ipv4-address-list */ + if (rmap_match_set_hook.match_ip_address == NULL) + break; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address; + rhc->rhc_rule = "ip address"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ip_address( + NULL, rhc->rhc_rmi, "ip address", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 3: /* ipv4-next-hop-list */ + if (rmap_match_set_hook.match_ip_next_hop == NULL) + break; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop; + rhc->rhc_rule = "ip next-hop"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ip_next_hop( + NULL, rhc->rhc_rmi, "ip next-hop", acl, + RMAP_EVENT_FILTER_ADDED); + break; + } + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_access_list_num_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended + */ +static int lib_route_map_entry_match_condition_access_list_num_extended_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + return lib_route_map_entry_match_condition_access_list_num_modify( + event, dnode, resource); +} + +static int lib_route_map_entry_match_condition_access_list_num_extended_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_condition_access_list_num_destroy( + event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/list-name + */ +static int lib_route_map_entry_match_condition_list_name_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *acl; + int condition; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook installation, otherwise we can just stop. */ + acl = yang_dnode_get_string(dnode, NULL); + rhc = nb_running_get_entry(dnode, NULL, true); + condition = yang_dnode_get_enum(dnode, "../condition"); + switch (condition) { + case 1: /* ipv4-address-list */ + if (rmap_match_set_hook.match_ip_address == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address; + rhc->rhc_rule = "ip address"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ip_address( + NULL, rhc->rhc_rmi, "ip address", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 2: /* ipv4-prefix-list */ + if (rmap_match_set_hook.match_ip_address_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ip_address_prefix_list; + rhc->rhc_rule = "ip address prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ip_address_prefix_list( + NULL, rhc->rhc_rmi, "ip address prefix-list", acl, + RMAP_EVENT_PLIST_ADDED); + break; + case 3: /* ipv4-next-hop-list */ + if (rmap_match_set_hook.match_ip_next_hop == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop; + rhc->rhc_rule = "ip next-hop"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ip_next_hop( + NULL, rhc->rhc_rmi, "ip next-hop", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 4: /* ipv4-next-hop-prefix-list */ + if (rmap_match_set_hook.match_ip_next_hop_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ip_next_hop_prefix_list; + rhc->rhc_rule = "ip next-hop prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ip_next_hop_prefix_list( + NULL, rhc->rhc_rmi, "ip next-hop prefix-list", acl, + RMAP_EVENT_PLIST_ADDED); + break; + case 6: /* ipv6-address-list */ + if (rmap_match_set_hook.match_ipv6_address == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_address; + rhc->rhc_rule = "ipv6 address"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ipv6_address( + NULL, rhc->rhc_rmi, "ipv6 address", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 7: /* ipv6-prefix-list */ + if (rmap_match_set_hook.match_ipv6_address_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ipv6_address_prefix_list; + rhc->rhc_rule = "ipv6 address prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ipv6_address_prefix_list( + NULL, rhc->rhc_rmi, "ipv6 address prefix-list", acl, + RMAP_EVENT_PLIST_ADDED); + break; + default: + rv = CMD_ERR_NO_MATCH; + break; + } + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_list_name_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type + */ +static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_ip_next_hop_type == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + type = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop_type; + rhc->rhc_rule = "ip next-hop type"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_ip_next_hop_type( + NULL, rhc->rhc_rmi, "ip next-hop type", type, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type + */ +static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_ipv6_next_hop_type == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + type = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_next_hop_type; + rhc->rhc_rule = "ipv6 next-hop type"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_ipv6_next_hop_type( + NULL, rhc->rhc_rmi, "ipv6 next-hop type", type, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/metric + */ +static int +lib_route_map_entry_match_condition_metric_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_metric == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + type = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_metric; + rhc->rhc_rule = "metric"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_metric(NULL, rhc->rhc_rmi, "metric", + type, RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_match_condition_metric_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/tag + */ +static int +lib_route_map_entry_match_condition_tag_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *tag; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_tag == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + tag = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_tag; + rhc->rhc_rule = "tag"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_tag(NULL, rhc->rhc_rmi, "tag", tag, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_match_condition_tag_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + return lib_route_map_entry_match_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action + */ +static int lib_route_map_entry_set_action_create(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + return lib_route_map_entry_match_condition_create(event, dnode, + resource); +} + +static int lib_route_map_entry_set_action_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + rv = lib_route_map_entry_set_destroy(event, dnode); + rhc = nb_running_unset_entry(dnode); + routemap_hook_context_free(rhc); + + return rv; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv4-address + */ +static int +lib_route_map_entry_set_action_ipv4_address_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *address; + struct in_addr ia; + int rv; + + switch (event) { + case NB_EV_VALIDATE: + /* + * NOTE: validate if 'action' is 'ipv4-next-hop', + * currently it is not necessary because this is the + * only implemented action. + */ + yang_dnode_get_ipv4(&ia, dnode, NULL); + if (ia.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(ia.s_addr))) + return NB_ERR_VALIDATION; + /* FALLTHROUGH */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + break; + } + + /* Check for hook function. */ + if (rmap_match_set_hook.set_ip_nexthop == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + address = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_ip_nexthop; + rhc->rhc_rule = "ip next-hop"; + + rv = rmap_match_set_hook.set_ip_nexthop(NULL, rhc->rhc_rmi, + "ip next-hop", address); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_set_action_ipv4_address_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv6-address + */ +static int +lib_route_map_entry_set_action_ipv6_address_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *address; + struct in6_addr i6a; + int rv; + + switch (event) { + case NB_EV_VALIDATE: + /* + * NOTE: validate if 'action' is 'ipv6-next-hop', + * currently it is not necessary because this is the + * only implemented action. Other actions might have + * different validations. + */ + yang_dnode_get_ipv6(&i6a, dnode, NULL); + if (!IN6_IS_ADDR_LINKLOCAL(&i6a)) + return NB_ERR_VALIDATION; + /* FALLTHROUGH */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + break; + } + + /* Check for hook function. */ + if (rmap_match_set_hook.set_ipv6_nexthop_local == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + address = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_ipv6_nexthop_local; + rhc->rhc_rule = "ipv6 next-hop local"; + + rv = rmap_match_set_hook.set_ipv6_nexthop_local( + NULL, rhc->rhc_rmi, "ipv6 next-hop local", address); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_set_action_ipv6_address_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/value + */ +static int set_action_modify(enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource, const char *value) +{ + struct routemap_hook_context *rhc; + int rv; + + /* + * NOTE: validate if 'action' is 'metric', currently it is not + * necessary because this is the only implemented action. Other + * actions might have different validations. + */ + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_metric == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_metric; + rhc->rhc_rule = "metric"; + + rv = rmap_match_set_hook.set_metric(NULL, rhc->rhc_rmi, "metric", + value); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_value_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + const char *metric = yang_dnode_get_string(dnode, NULL); + + return set_action_modify(event, dnode, resource, metric); +} + +static int +lib_route_map_entry_set_action_value_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric + */ +static int +lib_route_map_entry_set_action_add_metric_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + return set_action_modify(event, dnode, resource, "+metric"); +} + +static int +lib_route_map_entry_set_action_add_metric_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_action_value_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-metric + */ +static int lib_route_map_entry_set_action_subtract_metric_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + return set_action_modify(event, dnode, resource, "-metric"); +} + +static int lib_route_map_entry_set_action_subtract_metric_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_action_value_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/use-round-trip-time + */ +static int lib_route_map_entry_set_action_use_round_trip_time_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + return set_action_modify(event, dnode, resource, "rtt"); +} + +static int lib_route_map_entry_set_action_use_round_trip_time_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_action_value_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time + */ +static int lib_route_map_entry_set_action_add_round_trip_time_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + return set_action_modify(event, dnode, resource, "+rtt"); +} + +static int lib_route_map_entry_set_action_add_round_trip_time_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_action_value_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time + */ +static int lib_route_map_entry_set_action_subtract_round_trip_time_modify( + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource) +{ + return set_action_modify(event, dnode, resource, "-rtt"); +} + +static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy( + enum nb_event event, const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_action_value_destroy(event, dnode); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/tag + */ +static int +lib_route_map_entry_set_action_tag_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource) +{ + struct routemap_hook_context *rhc; + const char *tag; + int rv; + + /* + * NOTE: validate if 'action' is 'tag', currently it is not + * necessary because this is the only implemented action. Other + * actions might have different validations. + */ + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_tag == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + tag = yang_dnode_get_string(dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_tag; + rhc->rhc_rule = "tag"; + + rv = rmap_match_set_hook.set_tag(NULL, rhc->rhc_rmi, "tag", tag); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_tag_destroy(enum nb_event event, + const struct lyd_node *dnode) +{ + return lib_route_map_entry_set_destroy(event, dnode); +} + +/* clang-format off */ +#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__) +/* + * gcc versions before 5.x miscalculate the size for structs with variable + * length arrays (they just count it as size 0) + */ +struct frr_yang_module_info_sizen { + /* YANG module name. */ + const char *name; + + /* Northbound callbacks. */ + const struct { + /* Data path of this YANG node. */ + const char *xpath; + + /* Callbacks implemented for this node. */ + struct nb_callbacks cbs; + + /* Priority - lower priorities are processed first. */ + uint32_t priority; + } nodes[28]; +}; + +const struct frr_yang_module_info_sizen frr_route_map_info_sizen asm("frr_route_map_info") = { +#else +const struct frr_yang_module_info frr_route_map_info = { +#endif + .name = "frr-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map", + .cbs = { + .create = lib_route_map_create, + .destroy = lib_route_map_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry", + .cbs = { + .create = lib_route_map_entry_create, + .destroy = lib_route_map_entry_destroy, + .cli_show = route_map_instance_show, + .cli_show_end = route_map_instance_show_end, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/description", + .cbs = { + .modify = lib_route_map_entry_description_modify, + .destroy = lib_route_map_entry_description_destroy, + .cli_show = route_map_description_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/action", + .cbs = { + .modify = lib_route_map_entry_action_modify, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/call", + .cbs = { + .modify = lib_route_map_entry_call_modify, + .destroy = lib_route_map_entry_call_destroy, + .cli_show = route_map_call_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/exit-policy", + .cbs = { + .modify = lib_route_map_entry_exit_policy_modify, + .cli_show = route_map_exit_policy_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/goto-value", + .cbs = { + .modify = lib_route_map_entry_goto_value_modify, + .destroy = lib_route_map_entry_goto_value_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition", + .cbs = { + .create = lib_route_map_entry_match_condition_create, + .destroy = lib_route_map_entry_match_condition_destroy, + .cli_show = route_map_condition_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/interface", + .cbs = { + .modify = lib_route_map_entry_match_condition_interface_modify, + .destroy = lib_route_map_entry_match_condition_interface_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num", + .cbs = { + .modify = lib_route_map_entry_match_condition_access_list_num_modify, + .destroy = lib_route_map_entry_match_condition_access_list_num_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/access-list-num-extended", + .cbs = { + .modify = lib_route_map_entry_match_condition_access_list_num_extended_modify, + .destroy = lib_route_map_entry_match_condition_access_list_num_extended_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name", + .cbs = { + .modify = lib_route_map_entry_match_condition_list_name_modify, + .destroy = lib_route_map_entry_match_condition_list_name_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type", + .cbs = { + .modify = lib_route_map_entry_match_condition_ipv4_next_hop_type_modify, + .destroy = lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type", + .cbs = { + .modify = lib_route_map_entry_match_condition_ipv6_next_hop_type_modify, + .destroy = lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/metric", + .cbs = { + .modify = lib_route_map_entry_match_condition_metric_modify, + .destroy = lib_route_map_entry_match_condition_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/tag", + .cbs = { + .modify = lib_route_map_entry_match_condition_tag_modify, + .destroy = lib_route_map_entry_match_condition_tag_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action", + .cbs = { + .create = lib_route_map_entry_set_action_create, + .destroy = lib_route_map_entry_set_action_destroy, + .cli_show = route_map_action_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv4-address", + .cbs = { + .modify = lib_route_map_entry_set_action_ipv4_address_modify, + .destroy = lib_route_map_entry_set_action_ipv4_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv6-address", + .cbs = { + .modify = lib_route_map_entry_set_action_ipv6_address_modify, + .destroy = lib_route_map_entry_set_action_ipv6_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/value", + .cbs = { + .modify = lib_route_map_entry_set_action_value_modify, + .destroy = lib_route_map_entry_set_action_value_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_add_metric_modify, + .destroy = lib_route_map_entry_set_action_add_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_subtract_metric_modify, + .destroy = lib_route_map_entry_set_action_subtract_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/use-round-trip-time", + .cbs = { + .modify = lib_route_map_entry_set_action_use_round_trip_time_modify, + .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-round-trip-time", + .cbs = { + .modify = lib_route_map_entry_set_action_add_round_trip_time_modify, + .destroy = lib_route_map_entry_set_action_add_round_trip_time_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time", + .cbs = { + .modify = lib_route_map_entry_set_action_subtract_round_trip_time_modify, + .destroy = lib_route_map_entry_set_action_subtract_round_trip_time_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/tag", + .cbs = { + .modify = lib_route_map_entry_set_action_tag_modify, + .destroy = lib_route_map_entry_set_action_tag_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/srv6.c b/lib/srv6.c new file mode 100644 index 000000000..be340f13f --- /dev/null +++ b/lib/srv6.c @@ -0,0 +1,116 @@ +/* + * SRv6 definitions + * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation + * + * 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 "srv6.h" +#include "log.h" + +const char *seg6local_action2str(uint32_t action) +{ + switch (action) { + case ZEBRA_SEG6_LOCAL_ACTION_END: + return "End"; + case ZEBRA_SEG6_LOCAL_ACTION_END_X: + return "End.X"; + case ZEBRA_SEG6_LOCAL_ACTION_END_T: + return "End.T"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: + return "End.DX2"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: + return "End.DX6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: + return "End.DX4"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: + return "End.DT6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: + return "End.DT4"; + case ZEBRA_SEG6_LOCAL_ACTION_END_B6: + return "End.B6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: + return "End.B6.Encap"; + case ZEBRA_SEG6_LOCAL_ACTION_END_BM: + return "End.BM"; + case ZEBRA_SEG6_LOCAL_ACTION_END_S: + return "End.S"; + case ZEBRA_SEG6_LOCAL_ACTION_END_AS: + return "End.AS"; + case ZEBRA_SEG6_LOCAL_ACTION_END_AM: + return "End.AM"; + case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: + return "unspec"; + default: + return "unknown"; + } +} + +int snprintf_seg6_segs(char *str, + size_t size, const struct seg6_segs *segs) +{ + str[0] = '\0'; + for (size_t i = 0; i < segs->num_segs; i++) { + char addr[INET6_ADDRSTRLEN]; + bool not_last = (i + 1) < segs->num_segs; + + inet_ntop(AF_INET6, &segs->segs[i], addr, sizeof(addr)); + strlcat(str, addr, size); + strlcat(str, not_last ? "," : "", size); + } + return strlen(str); +} + +const char *seg6local_context2str(char *str, size_t size, + struct seg6local_context *ctx, uint32_t action) +{ + char b0[128]; + + switch (action) { + + case ZEBRA_SEG6_LOCAL_ACTION_END: + snprintf(str, size, "USP"); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_X: + case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: + inet_ntop(AF_INET6, &ctx->nh6, b0, 128); + snprintf(str, size, "nh6 %s", b0); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: + inet_ntop(AF_INET, &ctx->nh4, b0, 128); + snprintf(str, size, "nh4 %s", b0); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_T: + case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: + case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: + snprintf(str, size, "table %u", ctx->table); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: + case ZEBRA_SEG6_LOCAL_ACTION_END_B6: + case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: + case ZEBRA_SEG6_LOCAL_ACTION_END_BM: + case ZEBRA_SEG6_LOCAL_ACTION_END_S: + case ZEBRA_SEG6_LOCAL_ACTION_END_AS: + case ZEBRA_SEG6_LOCAL_ACTION_END_AM: + case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: + default: + snprintf(str, size, "unknown(%s)", __func__); + return str; + } +} diff --git a/lib/srv6.h b/lib/srv6.h new file mode 100644 index 000000000..24c7ffc3a --- /dev/null +++ b/lib/srv6.h @@ -0,0 +1,133 @@ +/* + * SRv6 definitions + * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation + * + * 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_SRV6_H +#define _FRR_SRV6_H + +#include +#include +#include + +#define SRV6_MAX_SIDS 16 + +#ifdef __cplusplus +extern "C" { +#endif + +#define sid2str(sid, str, size) \ + inet_ntop(AF_INET6, sid, str, size) + +enum seg6_mode_t { + INLINE, + ENCAP, + L2ENCAP, +}; + +enum seg6local_action_t { + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC = 0, + ZEBRA_SEG6_LOCAL_ACTION_END = 1, + ZEBRA_SEG6_LOCAL_ACTION_END_X = 2, + ZEBRA_SEG6_LOCAL_ACTION_END_T = 3, + ZEBRA_SEG6_LOCAL_ACTION_END_DX2 = 4, + ZEBRA_SEG6_LOCAL_ACTION_END_DX6 = 5, + ZEBRA_SEG6_LOCAL_ACTION_END_DX4 = 6, + ZEBRA_SEG6_LOCAL_ACTION_END_DT6 = 7, + ZEBRA_SEG6_LOCAL_ACTION_END_DT4 = 8, + ZEBRA_SEG6_LOCAL_ACTION_END_B6 = 9, + ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + ZEBRA_SEG6_LOCAL_ACTION_END_BM = 11, + ZEBRA_SEG6_LOCAL_ACTION_END_S = 12, + ZEBRA_SEG6_LOCAL_ACTION_END_AS = 13, + ZEBRA_SEG6_LOCAL_ACTION_END_AM = 14, + ZEBRA_SEG6_LOCAL_ACTION_END_BPF = 15, +}; + +struct seg6_segs { + size_t num_segs; + struct in6_addr segs[256]; +}; + +struct seg6local_context { + struct in_addr nh4; + struct in6_addr nh6; + uint32_t table; +}; + +static inline const char *seg6_mode2str(enum seg6_mode_t mode) +{ + switch (mode) { + case INLINE: + return "INLINE"; + case ENCAP: + return "ENCAP"; + case L2ENCAP: + return "L2ENCAP"; + default: + return "unknown"; + } +} + +static inline bool sid_same( + const struct in6_addr *a, + const struct in6_addr *b) +{ + if (!a && !b) + return true; + else if (!(a && b)) + return false; + else + return memcmp(a, b, sizeof(struct in6_addr)) == 0; +} + +static inline bool sid_diff( + const struct in6_addr *a, + const struct in6_addr *b) +{ + return !sid_same(a, b); +} + +static inline bool sid_zero( + const struct in6_addr *a) +{ + struct in6_addr zero = {}; + + return sid_same(a, &zero); +} + +static inline void *sid_copy(struct in6_addr *dst, + const struct in6_addr *src) +{ + return memcpy(dst, src, sizeof(struct in6_addr)); +} + +const char * +seg6local_action2str(uint32_t action); + +const char * +seg6local_context2str(char *str, size_t size, + struct seg6local_context *ctx, uint32_t action); + +int snprintf_seg6_segs(char *str, + size_t size, const struct seg6_segs *segs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/subdir.am b/lib/subdir.am index cb6fa7a3b..4f62eb226 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -51,6 +51,7 @@ lib_libfrr_la_SOURCES = \ lib/mlag.c \ lib/module.c \ lib/mpls.c \ + lib/srv6.c \ lib/network.c \ lib/nexthop.c \ lib/netns_linux.c \ @@ -70,6 +71,8 @@ lib_libfrr_la_SOURCES = \ lib/qobj.c \ lib/ringbuf.c \ lib/routemap.c \ + lib/routemap_cli.c \ + lib/routemap_northbound.c \ lib/sbuf.c \ lib/seqlock.c \ lib/sha256.c \ @@ -103,7 +106,9 @@ lib_libfrr_la_SOURCES = \ # end nodist_lib_libfrr_la_SOURCES = \ + yang/frr-filter.yang.c \ yang/frr-interface.yang.c \ + yang/frr-route-map.yang.c \ yang/frr-route-types.yang.c \ yang/ietf/ietf-routing-types.yang.c \ yang/frr-module-translator.yang.c \ @@ -119,6 +124,7 @@ vtysh_scan += \ $(top_srcdir)/lib/nexthop_group.c \ $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/routemap.c \ + $(top_srcdir)/lib/routemap_cli.c \ $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c \ # end @@ -138,6 +144,8 @@ lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) lib/nexthop_group.lo: lib/nexthop_group_clippy.c lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) lib/northbound_cli.lo: lib/northbound_cli_clippy.c +lib/routemap_cli_clippy.c: $(CLIPPY_DEPS) +lib/routemap_cli.lo: lib/routemap_cli_clippy.c lib/vty_clippy.c: $(CLIPPY_DEPS) lib/vty.lo: lib/vty_clippy.c lib/log_vty_clippy.c: $(CLIPPY_DEPS) @@ -193,6 +201,7 @@ pkginclude_HEADERS += \ lib/module.h \ lib/monotime.h \ lib/mpls.h \ + lib/srv6.h \ lib/network.h \ lib/nexthop.h \ lib/nexthop_group.h \ diff --git a/lib/vty.c b/lib/vty.c index 40da8abcd..cf1613385 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -3011,15 +3011,8 @@ void vty_reset(void) vty_timeout_val = VTY_TIMEOUT_DEFAULT; - if (vty_accesslist_name) { - XFREE(MTYPE_VTY, vty_accesslist_name); - vty_accesslist_name = NULL; - } - - if (vty_ipv6_accesslist_name) { - XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); - vty_ipv6_accesslist_name = NULL; - } + XFREE(MTYPE_VTY, vty_accesslist_name); + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); } static void vty_save_cwd(void) diff --git a/lib/zclient.c b/lib/zclient.c index b2c74cd0b..d87906346 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3299,3 +3299,72 @@ void zclient_interface_set_master(struct zclient *client, stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(client); } + +/* + * Send capabilities message to zebra + */ +int32_t zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, + struct zapi_cap *api) +{ + + struct stream *s; + + if (zclient == NULL) + return -1; + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, cmd, 0); + stream_putl(s, api->cap); + + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_RIB_STALE_TIME: + stream_putl(s, api->stale_removal_time); + stream_putl(s, api->vrf_id); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + stream_putl(s, api->afi); + stream_putl(s, api->safi); + stream_putl(s, api->vrf_id); + break; + case ZEBRA_CLIENT_GR_DISABLE: + stream_putl(s, api->vrf_id); + break; + } + + /* Put length at the first point of the stream */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + +/* + * Process capabilities message from zebra + */ +int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) +{ + + memset(api, 0, sizeof(*api)); + + STREAM_GETL(s, api->cap); + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_RIB_STALE_TIME: + STREAM_GETL(s, api->stale_removal_time); + STREAM_GETL(s, api->vrf_id); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + STREAM_GETL(s, api->afi); + STREAM_GETL(s, api->safi); + STREAM_GETL(s, api->vrf_id); + break; + case ZEBRA_CLIENT_GR_DISABLE: + STREAM_GETL(s, api->vrf_id); + break; + } +stream_failure: + return 0; +} diff --git a/lib/zclient.h b/lib/zclient.h index d1aa42da6..9a230d3f3 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -73,6 +73,18 @@ typedef uint16_t zebra_size_t; #define ZEBRA_FEC_REGISTER_LABEL 0x1 #define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x2 +/* Client capabilities */ +enum zserv_client_capabilities { + ZEBRA_CLIENT_GR_CAPABILITIES = 1, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE = 2, + ZEBRA_CLIENT_ROUTE_UPDATE_PENDING = 3, + ZEBRA_CLIENT_GR_DISABLE = 4, + ZEBRA_CLIENT_RIB_STALE_TIME +}; + +/* Macro to check if there GR enabled. */ +#define ZEBRA_CLIENT_GR_ENABLED(X) (X == ZEBRA_CLIENT_GR_CAPABILITIES) + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; @@ -184,10 +196,11 @@ typedef enum { ZEBRA_MLAG_CLIENT_UNREGISTER, ZEBRA_MLAG_FORWARD_MSG, ZEBRA_ERROR, + ZEBRA_CLIENT_CAPABILITIES } zebra_message_types_t; enum zebra_error_types { - ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */ + ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */ ZEBRA_NO_VRF, /* Vrf in header was not found */ ZEBRA_INVALID_MSG_TYPE, /* No handler found for msg type */ }; @@ -222,6 +235,15 @@ struct zclient_capabilities { enum mlag_role role; }; +/* Graceful Restart Capabilities message */ +struct zapi_cap { + enum zserv_client_capabilities cap; + uint32_t stale_removal_time; + afi_t afi; + safi_t safi; + vrf_id_t vrf_id; +}; + /* Structure for the zebra client. */ struct zclient { /* The thread master we schedule ourselves on */ @@ -759,6 +781,11 @@ extern bool zapi_nexthop_update_decode(struct stream *s, /* Decode the zebra error message */ extern bool zapi_error_decode(struct stream *s, enum zebra_error_types *error); +/* Encode and decode restart capabilities */ +extern int32_t zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, + struct zapi_cap *api); +extern int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api); + static inline void zapi_route_set_blackhole(struct zapi_route *api, enum blackhole_type bh_type) { diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index 9f828a1c7..7cf8dfbde 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -135,7 +135,7 @@ static void nhrp_interface_update_nbma(struct interface *ifp) &nifp->linkidx, &saddr); debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr); - if (saddr.s_addr) + if (saddr.s_addr != INADDR_ANY) sockunion_set(&nbma, AF_INET, (uint8_t *)&saddr.s_addr, sizeof(saddr.s_addr)); else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL) diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index cfca86a9b..a23ac3474 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -58,7 +58,6 @@ static void nhrp_route_update_put(struct route_node *rn) if (!ri->ifp && !ri->nhrp_ifp && sockunion_family(&ri->via) == AF_UNSPEC) { XFREE(MTYPE_NHRP_ROUTE, rn->info); - rn->info = NULL; route_unlock_node(rn); } route_unlock_node(rn); diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 84053b4b5..b3fdecf0e 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -141,7 +141,6 @@ static void nhrp_shortcut_delete(struct nhrp_shortcut *s) rn = route_node_lookup(shortcut_rib[afi], s->p); if (rn) { XFREE(MTYPE_NHRP_SHORTCUT, rn->info); - rn->info = NULL; route_unlock_node(rn); route_unlock_node(rn); } diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 0aaefeb3c..e4bed7a79 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -167,6 +167,7 @@ struct quagga_signal_t ospf6_signals[] = { static const struct frr_yang_module_info *const ospf6d_yang_modules[] = { &frr_interface_info, + &frr_route_map_info, }; FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT, diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index da42a2425..d75fc39bb 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1516,15 +1516,8 @@ int ospf6_iobuf_size(unsigned int size) void ospf6_message_terminate(void) { - if (recvbuf) { - XFREE(MTYPE_OSPF6_MESSAGE, recvbuf); - recvbuf = NULL; - } - - if (sendbuf) { - XFREE(MTYPE_OSPF6_MESSAGE, sendbuf); - sendbuf = NULL; - } + XFREE(MTYPE_OSPF6_MESSAGE, recvbuf); + XFREE(MTYPE_OSPF6_MESSAGE, sendbuf); iobuflen = 0; } diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c index 50485cc7e..da390e3c7 100644 --- a/ospfclient/ospf_apiclient.c +++ b/ospfclient/ospf_apiclient.c @@ -451,7 +451,7 @@ int ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient, tmp = SET_OPAQUE_LSID(opaque_type, opaque_id); lsah->id.s_addr = htonl(tmp); - lsah->adv_router.s_addr = 0; + lsah->adv_router.s_addr = INADDR_ANY; lsah->ls_seqnum = 0; lsah->checksum = 0; lsah->length = htons(sizeof(struct lsa_header) + opaquelen); diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index c8b8b611e..b4690cfa4 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -310,7 +310,7 @@ int ospf_area_range_substitute_unset(struct ospf *ospf, struct in_addr area_id, ospf_schedule_abr_task(ospf); UNSET_FLAG(range->flags, OSPF_AREA_RANGE_SUBSTITUTE); - range->subst_addr.s_addr = 0; + range->subst_addr.s_addr = INADDR_ANY; range->subst_masklen = 0; return 1; diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index a60af3656..6d0093534 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -141,7 +141,6 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, ospf->vrf_id, inet_ntoa(p.prefix), p.prefixlen, inetbuf); XFREE(MTYPE_OSPF_EXTERNAL_INFO, rn->info); - rn->info = NULL; } /* Create new External info instance. */ diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 44244f651..2c80d485a 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -370,7 +370,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) external-LSA. This indicates the IP address to which packets for the destination should be forwarded. */ - if (al->e[0].fwd_addr.s_addr == 0) { + if (al->e[0].fwd_addr.s_addr == INADDR_ANY) { /* If the forwarding address is set to 0.0.0.0, packets should be sent to the ASBR itself. Among the multiple routing table entries for the ASBR, select the preferred entry as follows. @@ -470,7 +470,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) ospf_route_add(ospf->new_external_route, &p, new, asbr_route); - if (al->e[0].fwd_addr.s_addr) + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) ospf_ase_complete_direct_routes(new, al->e[0].fwd_addr); return 0; } else { @@ -512,7 +512,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) zlog_debug( "Route[External]: New route is better"); ospf_route_subst(rn, new, asbr_route); - if (al->e[0].fwd_addr.s_addr) + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) ospf_ase_complete_direct_routes( new, al->e[0].fwd_addr); or = new; @@ -530,7 +530,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) if (IS_DEBUG_OSPF(lsa, LSA)) zlog_debug("Route[External]: Routes are equal"); ospf_route_copy_nexthops(or, asbr_route->paths); - if (al->e[0].fwd_addr.s_addr) + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) ospf_ase_complete_direct_routes( or, al->e[0].fwd_addr); } diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 8efb32af3..f2efaf322 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -694,7 +694,6 @@ static int ospf_if_delete_hook(struct interface *ifp) ospf_del_if_params((struct ospf_if_params *)IF_DEF_PARAMS(ifp)); XFREE(MTYPE_OSPF_IF_INFO, ifp->info); - ifp->info = NULL; return rc; } @@ -863,7 +862,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, p = prefix_ipv4_new(); p->family = AF_INET; - p->prefix.s_addr = 0; + p->prefix.s_addr = INADDR_ANY; p->prefixlen = 0; co->address = (struct prefix *)p; @@ -886,7 +885,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, if (IS_DEBUG_OSPF_EVENT) zlog_debug("ospf_vl_new(): set if->name to %s", vi->name); - area_id.s_addr = 0; + area_id.s_addr = INADDR_ANY; area = ospf_area_get(ospf, area_id); voi->area = area; @@ -908,7 +907,7 @@ static void ospf_vl_if_delete(struct ospf_vl_data *vl_data) { struct interface *ifp = vl_data->vl_oi->ifp; - vl_data->vl_oi->address->u.prefix4.s_addr = 0; + vl_data->vl_oi->address->u.prefix4.s_addr = INADDR_ANY; vl_data->vl_oi->address->prefixlen = 0; ospf_if_free(vl_data->vl_oi); if_delete(&ifp); @@ -972,7 +971,7 @@ static void ospf_vl_shutdown(struct ospf_vl_data *vl_data) if ((oi = vl_data->vl_oi) == NULL) return; - oi->address->u.prefix4.s_addr = 0; + oi->address->u.prefix4.s_addr = INADDR_ANY; oi->address->prefixlen = 0; UNSET_FLAG(oi->ifp->flags, IFF_UP); diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index e394b6f47..f4e89da45 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -169,7 +169,7 @@ static void ospf_dr_eligible_routers(struct route_table *nbrs, for (rn = route_top(nbrs); rn; rn = route_next(rn)) if ((nbr = rn->info) != NULL) /* Ignore 0.0.0.0 node*/ - if (nbr->router_id.s_addr != 0) + if (nbr->router_id.s_addr != INADDR_ANY) /* Is neighbor eligible? */ if (nbr->priority > 0) /* Is neighbor upper 2-Way? */ @@ -186,7 +186,7 @@ static void ospf_dr_change(struct ospf *ospf, struct route_table *nbrs) for (rn = route_top(nbrs); rn; rn = route_next(rn)) if ((nbr = rn->info) != NULL) /* Ignore 0.0.0.0 node*/ - if (nbr->router_id.s_addr != 0) + if (nbr->router_id.s_addr != INADDR_ANY) /* Is neighbor upper 2-Way? */ if (nbr->state >= NSM_TwoWay) /* Ignore myself. */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 6eec87c93..35bbe06cd 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -820,7 +820,7 @@ static struct ospf_lsa *ospf_router_lsa_originate(struct ospf_area *area) } /* Sanity check. */ - if (new->data->adv_router.s_addr == 0) { + if (new->data->adv_router.s_addr == INADDR_ANY) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("LSA[Type1]: AdvRouter is 0, discard"); ospf_lsa_discard(new); @@ -1459,7 +1459,7 @@ struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *oi) { struct in_addr fwd; - fwd.s_addr = 0; + fwd.s_addr = INADDR_ANY; if (if_is_operative(oi->ifp)) return oi->address->u.prefix4; @@ -1931,7 +1931,7 @@ int is_prefix_default(struct prefix_ipv4 *p) struct prefix_ipv4 q; q.family = AF_INET; - q.prefix.s_addr = 0; + q.prefix.s_addr = INADDR_ANY; q.prefixlen = 0; return prefix_same((struct prefix *)p, (struct prefix *)&q); @@ -1979,10 +1979,11 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, */ - if (ospf->router_id.s_addr == 0) { + if (ospf->router_id.s_addr == INADDR_ANY) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("LSA[Type5:%pI4]: deferring AS-external-LSA origination, router ID is zero", - &ei->p.prefix); + zlog_debug( + "LSA[Type5:%pI4]: deferring AS-external-LSA origination, router ID is zero", + &ei->p.prefix); return NULL; } @@ -2197,7 +2198,7 @@ void ospf_external_lsa_refresh_default(struct ospf *ospf) p.family = AF_INET; p.prefixlen = 0; - p.prefix.s_addr = 0; + p.prefix.s_addr = INADDR_ANY; ei = ospf_default_external_info(ospf); lsa = ospf_external_info_find_lsa(ospf, &p); diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index d02ffe044..9cde64bc3 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -40,6 +40,7 @@ #include "zclient.h" #include "vrf.h" #include "libfrr.h" +#include "routemap.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -126,6 +127,7 @@ struct quagga_signal_t ospf_signals[] = { static const struct frr_yang_module_info *const ospfd_yang_modules[] = { &frr_interface_info, + &frr_route_map_info, }; FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, @@ -141,6 +143,7 @@ FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, int main(int argc, char **argv) { unsigned short instance = 0; + bool created = false; #ifdef SUPPORT_OSPF_API /* OSPF apiserver is disabled by default. */ @@ -217,6 +220,17 @@ int main(int argc, char **argv) /* OSPF errors init */ ospf_error_init(); + /* + * Need to initialize the default ospf structure, so the interface mode + * commands can be duly processed if they are received before 'router + * ospf', when ospfd is restarted + */ + if (instance && !ospf_get_instance(instance, &created)) { + flog_err(EC_OSPF_INIT_FAIL, "OSPF instance init failed: %s", + strerror(errno)); + exit(1); + } + frr_config_fork(); frr_run(master); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 95fb69492..1b47fde21 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1074,7 +1074,8 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, /* If neighbor itself declares DR and no BDR exists, cause event BackupSeen */ if (IPV4_ADDR_SAME(&nbr->address.u.prefix4, &hello->d_router)) - if (hello->bd_router.s_addr == 0 && oi->state == ISM_Waiting) + if (hello->bd_router.s_addr == INADDR_ANY + && oi->state == ISM_Waiting) OSPF_ISM_EVENT_SCHEDULE(oi, ISM_BackupSeen); /* neighbor itself declares BDR. */ @@ -3352,7 +3353,7 @@ static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) if ((nbr = rn->info)) if (nbr->router_id.s_addr - != 0) /* Ignore 0.0.0.0 node. */ + != INADDR_ANY) /* Ignore 0.0.0.0 node. */ if (nbr->state != NSM_Attempt) /* Ignore Down neighbor. */ if (nbr->state @@ -3364,17 +3365,17 @@ static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) /* Check neighbor is * sane? */ if (nbr->d_router.s_addr - != 0 + != INADDR_ANY && IPV4_ADDR_SAME( - &nbr->d_router, - &oi->address - ->u - .prefix4) + &nbr->d_router, + &oi->address + ->u + .prefix4) && IPV4_ADDR_SAME( - &nbr->bd_router, - &oi->address - ->u - .prefix4)) + &nbr->bd_router, + &oi->address + ->u + .prefix4)) flag = 1; /* Hello packet overflows interface MTU. */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 6cabc0c98..b6e8338ee 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -603,7 +603,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, IF_NAME(oi)); path = ospf_path_new(); - path->nexthop.s_addr = 0; + path->nexthop.s_addr = INADDR_ANY; path->ifindex = oi->ifp->ifindex; if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) @@ -962,7 +962,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, new_or = ospf_route_new(); new_or->type = OSPF_DESTINATION_DISCARD; - new_or->id.s_addr = 0; + new_or->id.s_addr = INADDR_ANY; new_or->cost = 0; new_or->u.std.area_id = area->area_id; new_or->u.std.external_routing = area->external_routing; diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 1542ef88f..fb4082e50 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3551,7 +3551,7 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, } /* Show DR information. */ - if (DR(oi).s_addr == 0) { + if (DR(oi).s_addr == INADDR_ANY) { if (!use_json) vty_out(vty, " No backup designated router on this network\n"); @@ -4199,7 +4199,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, } if (nbr->state == NSM_Attempt - && nbr->router_id.s_addr == 0) + && nbr->router_id.s_addr == INADDR_ANY) strlcpy(neigh_str, "neighbor", sizeof(neigh_str)); else @@ -4258,7 +4258,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, ospf_nbr_state_message(nbr, msgbuf, 16); if (nbr->state == NSM_Attempt - && nbr->router_id.s_addr == 0) + && nbr->router_id.s_addr == INADDR_ANY) vty_out(vty, "%-15s %3d %-15s ", "-", nbr->priority, msgbuf); else @@ -4908,7 +4908,8 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, json_neigh_array = NULL; } - if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0) + if (nbr->state == NSM_Attempt + && nbr->router_id.s_addr == INADDR_ANY) strlcpy(neigh_str, "noNbrId", sizeof(neigh_str)); else strlcpy(neigh_str, inet_ntoa(nbr->router_id), @@ -4926,7 +4927,8 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, } else { /* Show neighbor ID. */ - if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0) + if (nbr->state == NSM_Attempt + && nbr->router_id.s_addr == INADDR_ANY) vty_out(vty, " Neighbor %s,", "-"); else vty_out(vty, " Neighbor %s,", diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 68d9d3bf8..64013435f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -585,9 +585,9 @@ int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, int cur_originate = ospf->default_originate; const char *type_str = NULL; - nexthop.s_addr = 0; + nexthop.s_addr = INADDR_ANY; p.family = AF_INET; - p.prefix.s_addr = 0; + p.prefix.s_addr = INADDR_ANY; p.prefixlen = 0; ospf->default_originate = originate; @@ -854,7 +854,7 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) /* Nothing has changed, so nothing to do; return */ return 0; } - if (ospf->router_id.s_addr != 0) { + if (ospf->router_id.s_addr != INADDR_ANY) { if (ei) { if (is_prefix_default(&p)) ospf_external_lsa_refresh_default(ospf); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 6a4e63372..2a3f1329a 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -329,7 +329,7 @@ struct ospf *ospf_lookup_instance(unsigned short instance) static int ospf_is_ready(struct ospf *ospf) { /* OSPF must be on and Router-ID must be configured. */ - if (!ospf || ospf->router_id.s_addr == 0) + if (!ospf || ospf->router_id.s_addr == INADDR_ANY) return 0; return 1; @@ -379,7 +379,7 @@ struct ospf *ospf_get(unsigned short instance, const char *name, bool *created) ospf = ospf_new(instance, name); ospf_add(ospf); - if (ospf->router_id_static.s_addr == 0) + if (ospf->router_id_static.s_addr == INADDR_ANY) ospf_router_id_update(ospf); ospf_opaque_type11_lsa_init(ospf); @@ -398,7 +398,7 @@ struct ospf *ospf_get_instance(unsigned short instance, bool *created) ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/); ospf_add(ospf); - if (ospf->router_id_static.s_addr == 0) + if (ospf->router_id_static.s_addr == INADDR_ANY) ospf_router_id_update(ospf); ospf_opaque_type11_lsa_init(ospf); @@ -938,7 +938,8 @@ static void add_ospf_interface(struct connected *co, struct ospf_area *area) * ospf_router_id_update() will call ospf_if_update * whenever r-id is configured instead. */ - if ((area->ospf->router_id.s_addr != 0) && if_is_operative(co->ifp)) + if ((area->ospf->router_id.s_addr != INADDR_ANY) + && if_is_operative(co->ifp)) ospf_if_up(oi); } @@ -1267,7 +1268,7 @@ static void ospf_network_run(struct prefix *p, struct ospf_area *area) struct interface *ifp; /* Schedule Router ID Update. */ - if (area->ospf->router_id.s_addr == 0) + if (area->ospf->router_id.s_addr == INADDR_ANY) ospf_router_id_update(area->ospf); /* Get target interface. */ diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index dd9576275..2f10abcce 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -448,7 +448,7 @@ int main(int argc, char *const argv[]) exit(EXIT_FAILURE); } - mc_group.s_addr = 0; + mc_group.s_addr = INADDR_ANY; not_group = false; if (argc == 3) { diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index 4a69e4d1a..e14b7e058 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -86,8 +86,7 @@ static void pim_free_bsgrp_node(struct route_table *rt, struct prefix *grp) static void pim_bsm_node_free(struct bsm_info *bsm) { - if (bsm->bsm) - XFREE(MTYPE_PIM_BSM_PKT_VAR_MEM, bsm->bsm); + XFREE(MTYPE_PIM_BSM_PKT_VAR_MEM, bsm->bsm); XFREE(MTYPE_PIM_BSM_INFO, bsm); } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 01bebebd2..6508fb445 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1741,12 +1741,12 @@ static void pim_show_join(struct pim_instance *pim, struct vty *vty, continue; RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { - if (sg->grp.s_addr != 0 + if (sg->grp.s_addr != INADDR_ANY && sg->grp.s_addr != ch->sg.grp.s_addr) continue; - if (sg->src.s_addr != 0 + if (sg->src.s_addr != INADDR_ANY && sg->src.s_addr != ch->sg.src.s_addr) - continue; + continue; pim_show_join_helper(vty, pim_ifp, ch, json, now, uj); } /* scan interface channels */ } @@ -2448,9 +2448,11 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, char msdp_reg_timer[10]; char state_str[PIM_REG_STATE_STR_LEN]; - if (sg->grp.s_addr != 0 && sg->grp.s_addr != up->sg.grp.s_addr) + if (sg->grp.s_addr != INADDR_ANY + && sg->grp.s_addr != up->sg.grp.s_addr) continue; - if (sg->src.s_addr != 0 && sg->src.s_addr != up->sg.src.s_addr) + if (sg->src.s_addr != INADDR_ANY + && sg->src.s_addr != up->sg.src.s_addr) continue; pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 3a7eb45f2..d9c9bb37d 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -580,7 +580,7 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch); - ch->ifassert_winner.s_addr = 0; + ch->ifassert_winner.s_addr = INADDR_ANY; /* Assert state */ ch->t_ifassert_timer = NULL; diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 695d04c7c..309b8c149 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -68,7 +68,7 @@ static bool mtrace_fwd_info_weak(struct pim_instance *pim, struct in_addr nh_addr; char nexthop_str[INET_ADDRSTRLEN]; - nh_addr.s_addr = 0; + nh_addr.s_addr = INADDR_ANY; memset(&nexthop, 0, sizeof(nexthop)); @@ -123,7 +123,7 @@ static bool mtrace_fwd_info(struct pim_instance *pim, up = pim_upstream_find(pim, &sg); if (!up) { - sg.src.s_addr = 0; + sg.src.s_addr = INADDR_ANY; up = pim_upstream_find(pim, &sg); } @@ -160,7 +160,7 @@ static bool mtrace_fwd_info(struct pim_instance *pim, rspp->rtg_proto = MTRACE_RTG_PROTO_PIM; /* 6.2.2. 4. Fill in ... S, and Src Mask */ - if (sg.src.s_addr) { + if (sg.src.s_addr != INADDR_ANY) { rspp->s = 1; rspp->src_mask = MTRACE_SRC_MASK_SOURCE; } else { @@ -181,9 +181,9 @@ static void mtrace_rsp_set_fwd_code(struct igmp_mtrace_rsp *mtrace_rspp, static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp) { mtrace_rspp->arrival = 0; - mtrace_rspp->incoming.s_addr = 0; - mtrace_rspp->outgoing.s_addr = 0; - mtrace_rspp->prev_hop.s_addr = 0; + mtrace_rspp->incoming.s_addr = INADDR_ANY; + mtrace_rspp->outgoing.s_addr = INADDR_ANY; + mtrace_rspp->prev_hop.s_addr = INADDR_ANY; mtrace_rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); mtrace_rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT); mtrace_rspp->total = htonl(MTRACE_UNKNOWN_COUNT); @@ -779,7 +779,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, /* 6.2.2. 2. Attempt to determine the forwarding information... */ - if (mtracep->grp_addr.s_addr) + if (mtracep->grp_addr.s_addr != INADDR_ANY) fwd_info = mtrace_fwd_info(pim, mtracep, rspp, &out_ifp); else fwd_info = mtrace_fwd_info_weak(pim, mtracep, rspp, &out_ifp); @@ -797,7 +797,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, reached_source = false; - if (nh_addr.s_addr == 0) { + if (nh_addr.s_addr == INADDR_ANY) { /* no pim? i.e. 7.5.3. No Previous Hop */ if (!out_ifp->info) { if (PIM_DEBUG_MTRACE) diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 58ebc6ce6..27af9473b 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -473,7 +473,7 @@ static void pim_msdp_sa_local_add(struct pim_instance *pim, struct prefix_sg *sg) { struct in_addr rp; - rp.s_addr = 0; + rp.s_addr = INADDR_ANY; pim_msdp_sa_ref(pim, NULL /* mp */, sg, rp); } @@ -1175,9 +1175,7 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp) stream_fifo_free(mp->obuf); } - if (mp->mesh_group_name) { - XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); - } + XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); mp->pim = NULL; XFREE(MTYPE_PIM_MSDP_PEER, mp); diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 670bc6f4c..db465f2b0 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -63,6 +63,13 @@ %endif %endif +# Check for python version - use python2.7 on CentOS 6, otherwise python3 +%if 0%{?rhel} && 0%{?rhel} < 7 + %global use_python2 1 +%else + %global use_python2 0 +%endif + # If init system is systemd, then always enable watchfrr %if "%{initsystem}" == "systemd" %global with_watchfrr 1 @@ -169,15 +176,18 @@ BuildRequires: libyang-devel >= 0.16.74 BuildRequires: python27-devel BuildRequires: python27-sphinx %else -%if 0%{?rhel} && 0%{?rhel} > 7 -BuildRequires: python2-devel -#platform-python-devel is needed for /usr/bin/pathfix.py -BuildRequires: platform-python-devel -%else +%if %{use_python2} BuildRequires: python-devel >= 2.7 BuildRequires: python-sphinx +%else +BuildRequires: python3-devel +BuildRequires: python3-sphinx %endif %endif +%if 0%{?rhel} > 7 +#platform-python-devel is needed for /usr/bin/pathfix.py +BuildRequires: platform-python-devel +%endif Requires: initscripts %if %{with_pam} BuildRequires: pam-devel @@ -223,12 +233,17 @@ Contributed/3rd party tools which may be of use with frr. %package pythontools Summary: python tools for frr -%if 0%{?rhel} && 0%{?rhel} > 7 -BuildRequires: python2 -Requires: python2-ipaddress +%if 0%{?rhel} && 0%{?rhel} < 7 +#python27 is available from ius community repo for RedHat/CentOS 6 +BuildRequires: python27 +Requires: python27-ipaddress +%else +%if %{use_python2} +BuildRequires: python2 +Requires: python2-ipaddress %else -BuildRequires: python -Requires: python-ipaddress +BuildRequires: python3 +%endif %endif Group: System Environment/Daemons @@ -369,6 +384,16 @@ developing OSPF-API and frr applications. make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" +%if %{use_python2} +# Change frr-reload.py to use python2.7 +sed -e '1c #!/usr/bin/python2.7' -i %{zeb_src}/tools/frr-reload.py +sed -e '1c #!/usr/bin/python2.7' -i %{zeb_src}/tools/generate_support_bundle.py +%else +# Change frr-reload.py to use python3 +sed -e '1c #!/usr/bin/python3' -i %{zeb_src}/tools/frr-reload.py +sed -e '1c #!/usr/bin/python3' -i %{zeb_src}/tools/generate_support_bundle.py +%endif + pushd doc make info popd @@ -408,10 +433,10 @@ install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr install -d -m750 %{buildroot}%{rundir} -%if 0%{?rhel} && 0%{?rhel} > 7 +%if 0%{?rhel} > 7 || 0%{?fedora} > 29 # avoid `ERROR: ambiguous python shebang in` errors -pathfix.py -pni "%{__python2} %{py2_shbang_opts}" %{buildroot}/usr/lib/frr/*.py -%py_byte_compile %{__python2} %{buildroot}/usr/lib/frr/*.py +pathfix.py -pni "%{__python3} %{py3_shbang_opts}" %{buildroot}/usr/lib/frr/*.py +%py_byte_compile %{__python3} %{buildroot}/usr/lib/frr/*.py %endif %pre @@ -674,11 +699,16 @@ fi %files pythontools %{_sbindir}/generate_support_bundle.py +%{_sbindir}/frr-reload.py +%if 0%{?rhel} > 7 || 0%{?fedora} > 29 +%{_sbindir}/__pycache__/* +%else %{_sbindir}/generate_support_bundle.pyc %{_sbindir}/generate_support_bundle.pyo %{_sbindir}/frr-reload.py %{_sbindir}/frr-reload.pyc %{_sbindir}/frr-reload.pyo +%endif %files devel diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 4d4874060..d07cc894a 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -1210,7 +1210,6 @@ static int rip_interface_delete_hook(struct interface *ifp) { rip_interface_reset(ifp->info); XFREE(MTYPE_RIP_INTERFACE, ifp->info); - ifp->info = NULL; return 0; } diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 060bb7658..ca41afaea 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -35,6 +35,7 @@ #include "vrf.h" #include "if_rmap.h" #include "libfrr.h" +#include "routemap.h" #include "ripd/ripd.h" #include "ripd/rip_nb.h" @@ -115,6 +116,7 @@ static struct quagga_signal_t ripd_signals[] = { static const struct frr_yang_module_info *const ripd_yang_modules[] = { &frr_interface_info, &frr_ripd_info, + &frr_route_map_info, }; FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT, diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index 4e848766d..102b64df6 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -158,8 +158,9 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, if (type == RMAP_RIP) { rinfo = object; p.family = AF_INET; - p.prefix = (rinfo->nh.gate.ipv4.s_addr) ? rinfo->nh.gate.ipv4 - : rinfo->from; + p.prefix = (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY) + ? rinfo->nh.gate.ipv4 + : rinfo->from; p.prefixlen = IPV4_MAX_BITLEN; alist = access_list_lookup(AFI_IP, (char *)rule); @@ -207,8 +208,9 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, if (type == RMAP_RIP) { rinfo = object; p.family = AF_INET; - p.prefix = (rinfo->nh.gate.ipv4.s_addr) ? rinfo->nh.gate.ipv4 - : rinfo->from; + p.prefix = (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY) + ? rinfo->nh.gate.ipv4 + : rinfo->from; p.prefixlen = IPV4_MAX_BITLEN; plist = prefix_list_lookup(AFI_IP, (char *)rule); diff --git a/ripd/ripd.c b/ripd/ripd.c index ec0770ef3..5ccefac17 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1223,7 +1223,8 @@ static void rip_response_process(struct rip_packet *packet, int size, } /* RIPv1 does not have nexthop value. */ - if (packet->version == RIPv1 && rte->nexthop.s_addr != 0) { + if (packet->version == RIPv1 + && rte->nexthop.s_addr != INADDR_ANY) { zlog_info("RIPv1 packet with nexthop value %s", inet_ntoa(rte->nexthop)); rip_peer_bad_route(rip, from); @@ -1234,7 +1235,8 @@ static void rip_response_process(struct rip_packet *packet, int size, sub-optimal, but absolutely valid, route may be taken. If the received Next Hop is not directly reachable, it should be treated as 0.0.0.0. */ - if (packet->version == RIPv2 && rte->nexthop.s_addr != 0) { + if (packet->version == RIPv2 + && rte->nexthop.s_addr != INADDR_ANY) { uint32_t addrval; /* Multicast address check. */ @@ -1272,7 +1274,8 @@ static void rip_response_process(struct rip_packet *packet, int size, "Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa( rte->nexthop)); - rte->nexthop.s_addr = 0; + rte->nexthop.s_addr = + INADDR_ANY; } route_unlock_node(rn); @@ -1282,7 +1285,7 @@ static void rip_response_process(struct rip_packet *packet, int size, "Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa( rte->nexthop)); - rte->nexthop.s_addr = 0; + rte->nexthop.s_addr = INADDR_ANY; } } } @@ -1297,10 +1300,11 @@ static void rip_response_process(struct rip_packet *packet, int size, (/16 for class B's) except when the RIP packet does to inside the classful network in question. */ - if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) + if ((packet->version == RIPv1 + && rte->prefix.s_addr != INADDR_ANY) || (packet->version == RIPv2 - && (rte->prefix.s_addr != 0 - && rte->mask.s_addr == 0))) { + && (rte->prefix.s_addr != INADDR_ANY + && rte->mask.s_addr == INADDR_ANY))) { uint32_t destination; if (subnetted == -1) { @@ -1352,7 +1356,8 @@ static void rip_response_process(struct rip_packet *packet, int size, /* In case of RIPv2, if prefix in RTE is not netmask applied one ignore the entry. */ - if ((packet->version == RIPv2) && (rte->mask.s_addr != 0) + if ((packet->version == RIPv2) + && (rte->mask.s_addr != INADDR_ANY) && ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)) { zlog_warn( @@ -1363,12 +1368,13 @@ static void rip_response_process(struct rip_packet *packet, int size, } /* Default route's netmask is ignored. */ - if (packet->version == RIPv2 && (rte->prefix.s_addr == 0) - && (rte->mask.s_addr != 0)) { + if (packet->version == RIPv2 + && (rte->prefix.s_addr == INADDR_ANY) + && (rte->mask.s_addr != INADDR_ANY)) { if (IS_RIP_DEBUG_EVENT) zlog_debug( "Default route with non-zero netmask. Set zero to netmask"); - rte->mask.s_addr = 0; + rte->mask.s_addr = INADDR_ANY; } /* Routing table updates. */ @@ -3670,8 +3676,7 @@ static int rip_vrf_enable(struct vrf *vrf) running_config->version++; } } - if (old_vrf_name) - XFREE(MTYPE_RIP_VRF_NAME, old_vrf_name); + XFREE(MTYPE_RIP_VRF_NAME, old_vrf_name); } if (!rip || rip->enabled) return 0; diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 97113a180..250c7803f 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -913,7 +913,6 @@ static int ripng_if_new_hook(struct interface *ifp) static int ripng_if_delete_hook(struct interface *ifp) { XFREE(MTYPE_RIPNG_IF, ifp->info); - ifp->info = NULL; return 0; } diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 9daeeb958..99adb2cba 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -36,6 +36,7 @@ #include "vrf.h" #include "if_rmap.h" #include "libfrr.h" +#include "routemap.h" #include "ripngd/ripngd.h" #include "ripngd/ripng_nb.h" @@ -115,6 +116,7 @@ struct quagga_signal_t ripng_signals[] = { static const struct frr_yang_module_info *const ripngd_yang_modules[] = { &frr_interface_info, &frr_ripngd_info, + &frr_route_map_info, }; FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT, diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index ad2ddd0db..f8d7dc968 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2804,8 +2804,7 @@ static int ripng_vrf_enable(struct vrf *vrf) running_config->version++; } } - if (old_vrf_name) - XFREE(MTYPE_RIPNG_VRF_NAME, old_vrf_name); + XFREE(MTYPE_RIPNG_VRF_NAME, old_vrf_name); } if (ripng->enabled) diff --git a/scripts/coccinelle/s_addr_0_to_INADDR_ANY.cocci b/scripts/coccinelle/s_addr_0_to_INADDR_ANY.cocci new file mode 100644 index 000000000..bd7f4af4f --- /dev/null +++ b/scripts/coccinelle/s_addr_0_to_INADDR_ANY.cocci @@ -0,0 +1,14 @@ +@@ +expression e; +@@ + +( +- e.s_addr == 0 ++ e.s_addr == INADDR_ANY +| +- e.s_addr != 0 ++ e.s_addr != INADDR_ANY +| +- e.s_addr = 0 ++ e.s_addr = INADDR_ANY +) diff --git a/scripts/coccinelle/shorthand_operator.cocci b/scripts/coccinelle/shorthand_operator.cocci new file mode 100644 index 000000000..f7019d404 --- /dev/null +++ b/scripts/coccinelle/shorthand_operator.cocci @@ -0,0 +1,12 @@ +@@ +identifier data; +constant x; +@@ + +( +- data = data + x ++ data += x +| +- data = data - x ++ data -= x +) diff --git a/scripts/coccinelle/void_no_return.cocci b/scripts/coccinelle/void_no_return.cocci new file mode 100644 index 000000000..7da9e7393 --- /dev/null +++ b/scripts/coccinelle/void_no_return.cocci @@ -0,0 +1,9 @@ +@@ +identifier f; +expression e; +@@ +void f(...) { + ... +- return + e; +} diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 21f4b3877..3f63c0499 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -297,7 +297,25 @@ struct bgp_node test_rn; static int setup_bgp_path_info_mpath_update(testcase_t *t) { int i; + struct bgp *bgp; + struct bgp_table *rt; + struct route_node *rt_node; + as_t asn = 1; + + t->tmp_data = bgp_create_fake(&asn, NULL); + if (!t->tmp_data) + return -1; + + bgp = t->tmp_data; + rt = bgp->rib[AFI_IP][SAFI_UNICAST]; + + if (!rt) + return -1; + str2prefix("42.1.1.0/24", &test_rn.p); + rt_node = bgp_node_to_rnode(&test_rn); + memcpy((struct route_table *)&rt_node->table, &rt->route_table, + sizeof(struct route_table)); setup_bgp_mp_list(t); for (i = 0; i < test_mp_list_info_count; i++) bgp_path_info_add(&test_rn, &test_mp_list_info[i]); @@ -352,7 +370,7 @@ static int cleanup_bgp_path_info_mpath_update(testcase_t *t) for (i = 0; i < test_mp_list_peer_count; i++) sockunion_free(test_mp_list_peer[i].su_remote); - return 0; + return bgp_delete((struct bgp *)t->tmp_data); } testcase_t test_bgp_path_info_mpath_update = { diff --git a/tests/lib/test_heavy_wq.c b/tests/lib/test_heavy_wq.c index 442b8c838..cffd52ee0 100644 --- a/tests/lib/test_heavy_wq.c +++ b/tests/lib/test_heavy_wq.c @@ -81,7 +81,6 @@ static void slow_func_del(struct work_queue *wq, void *data) assert(hn && hn->str); printf("%s: %s\n", __func__, hn->str); XFREE(MTYPE_WQ_NODE_STR, hn->str); - hn->str = NULL; XFREE(MTYPE_WQ_NODE, hn); } diff --git a/tools/generate_support_bundle.py b/tools/generate_support_bundle.py index c9ca9c3d0..540b7a135 100755 --- a/tools/generate_support_bundle.py +++ b/tools/generate_support_bundle.py @@ -30,16 +30,16 @@ def createOutputFile(procName): oldFile = LOG_DIR + fileName cpFileCmd = "cp " + oldFile + " " + oldFile + ".prev" rmFileCmd = "rm -rf " + oldFile - print "Making backup of " + oldFile + print("Making backup of " + oldFile) os.system(cpFileCmd) - print "Removing " + oldFile + print("Removing " + oldFile) os.system(rmFileCmd) return fileName # Open the output file for this process def openOutputFile(fileName): crt_file_cmd = LOG_DIR + fileName - print crt_file_cmd + print(crt_file_cmd) try: outputFile = open(crt_file_cmd, "w") return outputFile @@ -67,14 +67,14 @@ def executeCommand(cmd, outputFile): outputFile.write("########################################################\n") outputFile.write('\n') except: - print "Writing to ouptut file Failed" + print("Writing to ouptut file Failed") except subprocess.CalledProcessError as e: dateTime = datetime.datetime.now() outputFile.write(">>[" + str(dateTime) + "]" + cmd + "\n") outputFile.write(e.output) outputFile.write("########################################################\n") outputFile.write('\n') - print "Error:" + e.output + print("Error:" + e.output) # Process the support bundle configuration file @@ -87,26 +87,26 @@ def processConfFile(lines): if cmd_line[0] == "PROC_NAME": outputFileName = createOutputFile(cmd_line[1]) if outputFileName: - print outputFileName, "created for", cmd_line[1] + print(outputFileName, "created for", cmd_line[1]) elif cmd_line[0] == "CMD_LIST_START": outputFile = openOutputFile(outputFileName) if outputFile: - print outputFileName, "opened" + print(outputFileName, "opened") else: - print outputFileName, "open failed" - return FAIL + print(outputFileName, "open failed") + return FAIL elif cmd_line[0] == "CMD_LIST_END": if closeOutputFile(outputFile): - print outputFileName, "closed" + print(outputFileName, "closed") else: - print outputFileName, "close failed" + print(outputFileName, "close failed") else: - print "Execute:" , cmd_line[0] + print("Execute:" , cmd_line[0]) executeCommand(cmd_line[0], outputFile) # Main Function lines = openConfFile(inputFile) if not lines: - print "File support_bundle_commands.conf not present in /etc/frr/ directory" + print("File support_bundle_commands.conf not present in /etc/frr/ directory") else: processConfFile(lines) diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 13413888b..b7ac0abe0 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -87,7 +87,7 @@ sub scan_file { if ($file =~ /lib\/keychain\.c$/) { $protocol = "VTYSH_RIPD|VTYSH_EIGRPD"; } - elsif ($file =~ /lib\/routemap\.c$/) { + elsif ($file =~ /lib\/routemap\.c$/ || $file =~ /lib\/routemap_cli\.c$/) { $protocol = "VTYSH_RMAP"; } elsif ($file =~ /lib\/vrf\.c$/) { diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index b7d35caa3..3a46835d1 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1153,7 +1153,6 @@ static char *command_generator(const char *text, int state) return matched[index++]; XFREE(MTYPE_TMP, matched); - matched = NULL; return NULL; } @@ -3058,6 +3057,24 @@ DEFUN (vtysh_copy_running_config, return vtysh_write_memory(self, vty, argc, argv); } +DEFUN (vtysh_copy_to_running, + vtysh_copy_to_running_cmd, + "copy FILENAME running-config", + "Apply a configuration file\n" + "Configuration file to read\n" + "Apply to current configuration\n") +{ + int ret; + const char *fname = argv[1]->arg; + + ret = vtysh_read_config(fname); + + /* Return to enable mode - the 'read_config' api leaves us up a level */ + vtysh_execute_no_pager("enable"); + + return ret; +} + DEFUN (vtysh_terminal_paginate, vtysh_terminal_paginate_cmd, "[no] terminal paginate", @@ -4024,6 +4041,7 @@ void vtysh_init_vty(void) install_element(INTERFACE_NODE, &vtysh_link_params_cmd); install_element(ENABLE_NODE, &vtysh_show_running_config_cmd); install_element(ENABLE_NODE, &vtysh_copy_running_config_cmd); + install_element(ENABLE_NODE, &vtysh_copy_to_running_cmd); install_element(CONFIG_NODE, &vtysh_vrf_cmd); install_element(VRF_NODE, &vtysh_vrf_netns_cmd); diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index 0ce40b474..34a7e28a7 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -9,6 +9,9 @@ module frr-route-map { import frr-filter { prefix filter; } + import frr-interface { + prefix frr-interface; + } organization "Free Range Routing"; contact @@ -38,367 +41,343 @@ module frr-route-map { /* * Operational data. */ - container route-map { - list instance { + container lib { + list route-map { description "Route map instance"; - key "name sequence"; + key "name"; leaf name { description "Route map instance name"; type route-map-name; } - leaf sequence { - description - "Route map instance priority (low number means higher priority)"; - type route-map-sequence; - } + list entry { + description "Route map entry"; - leaf description { - description "Route map description"; - type string; - } + key "sequence"; - leaf action { - description - "Route map actions: permit (executes action), deny (quits evaluation)"; - mandatory true; - type enumeration { - enum permit { - description - "Executes configured action and permits the prefix/route - if the conditions matched. An alternative exit action can - be configured to continue processing the route map list - or jump to process another route map."; - value 0; - } - enum deny { - description - "If all conditions are met the prefix/route is denied and - route map processing stops."; - value 1; - } + leaf sequence { + description + "Route map instance priority (low number means higher priority)"; + type route-map-sequence; } - } - - list match-condition { - description "Route map match conditions"; - key "condition"; + leaf description { + description "Route map description"; + type string; + } - leaf condition { - description "Match condition"; + leaf action { + description + "Route map actions: permit (executes action), deny (quits evaluation)"; + mandatory true; type enumeration { - enum interface { - description "Match interface"; + enum permit { + description + "Executes configured action and permits the prefix/route + if the conditions matched. An alternative exit action can + be configured to continue processing the route map list + or jump to process another route map."; value 0; } - enum ipv4-address-list { - description "Match an IPv4 access-list"; + enum deny { + description + "If all conditions are met the prefix/route is denied and + route map processing stops."; value 1; } - enum ipv4-prefix-list { - description "Match an IPv4 prefix-list"; - value 2; - } - enum ipv4-prefix-length { - description "Match an IPv4 prefix length"; - value 3; - } - enum ipv4-next-hop-list { - description "Match an IPv4 next-hop"; - value 4; - } - enum ipv4-next-hop-prefix-list { - description "Match an IPv4 next-hop prefix list"; - value 5; - } - enum ipv4-next-hop-prefix-length { - description "Match an IPv4 next-hop prefix length"; - value 6; - } - enum ipv4-next-hop-type { - description "Match an IPv4 next-hop type"; - value 7; - } - enum ipv6-address-list { - description "Match an IPv6 access-list"; - value 8; - } - enum ipv6-prefix-list { - description "Match an IPv6 prefix-list"; - value 9; - } - enum ipv6-prefix-length { - description "Match an IPv6 prefix length"; - value 10; - } - enum ipv6-next-hop { - description "Match an IPv6 next-hop"; - value 11; - } - enum ipv6-next-hop-type { - description "Match an IPv6 next-hop type"; - value 12; - } - enum metric { - description "Match a route metric"; - value 13; + } + } + + leaf call { + description + "Call another route map before calling `exit-policy`. If the + called route map returns deny then this route map will also + return deny"; + type route-map-name; + } + + leaf exit-policy { + description "What do to after route map successful match, set and call"; + type enumeration { + enum permit-or-deny { + description "End route map evaluation and return"; + value 0; } - enum tag { - description "Match a route tag"; - value 14; + enum next { + description + "Proceed evaluating next route map entry per sequence"; + value 1; } - - /* - * Protocol YANG models should augment the parent node to - * contain the routing protocol specific value. The protocol - * must also augment `condition-value` to include its specific - * values or expand the `when` statement on the existing cases. - */ - enum routing-protocol-specific { - description "Match a routing protocol specific type"; - value 100; + enum goto { + description + "Go to route map entry with the provided sequence number"; + value 2; } } + default "permit-or-deny"; } - choice condition-value { + leaf goto-value { + when "../exit-policy = 'goto'"; description - "Value to match (interpretation depends on condition type)"; - case access-list-num { - when "./condition = 'ipv4-address-list' or - ./condition = 'ipv4-next-hop-list'"; - leaf access-list-num { - type filter:access-list-standard; - } - } - case access-list-num-extended { - when "./condition = 'ipv4-address-list' or - ./condition = 'ipv4-next-hop-list'"; - leaf access-list-num-extended { - type filter:access-list-extended; + "Sequence number to jump (when using `goto` exit policy)"; + mandatory true; + type route-map-sequence; + } + + list match-condition { + description "Route map match conditions"; + + key "condition"; + + leaf condition { + description "Match condition"; + type enumeration { + enum interface { + description "Match interface"; + value 0; + } + enum ipv4-address-list { + description "Match an IPv4 access-list"; + value 1; + } + enum ipv4-prefix-list { + description "Match an IPv4 prefix-list"; + value 2; + } + enum ipv4-next-hop-list { + description "Match an IPv4 next-hop"; + value 3; + } + enum ipv4-next-hop-prefix-list { + description "Match an IPv4 next-hop prefix list"; + value 4; + } + enum ipv4-next-hop-type { + description "Match an IPv4 next-hop type"; + value 5; + } + enum ipv6-address-list { + description "Match an IPv6 access-list"; + value 6; + } + enum ipv6-prefix-list { + description "Match an IPv6 prefix-list"; + value 7; + } + enum ipv6-next-hop-type { + description "Match an IPv6 next-hop type"; + value 8; + } + enum metric { + description "Match a route metric"; + value 9; + } + enum tag { + description "Match a route tag"; + value 10; + } + + /* + * Protocol YANG models should augment the parent node to + * contain the routing protocol specific value. The protocol + * must also augment `condition-value` to include its specific + * values or expand the `when` statement on the existing cases. + */ + enum routing-protocol-specific { + description "Match a routing protocol specific type"; + value 100; + } } } - case list-name { - when "./condition = 'ipv4-address-list' or - ./condition = 'ipv4-prefix-list' or - ./condition = 'ipv4-next-hop-list' or - ./condition = 'ipv4-next-hop-prefix-list' or - ./condition = 'ipv6-address-list' or - ./condition = 'ipv6-prefix-list'"; - leaf list-name { - type filter:access-list-name; + + choice condition-value { + description + "Value to match (interpretation depends on condition type)"; + mandatory true; + case interface { + when "./condition = 'interface'"; + leaf interface { + type frr-interface:interface-ref; + } } - } - case ipv6-address { - when "./condition = 'ipv6-next-hop'"; - leaf ipv6-address { - type inet:ipv6-address; + case access-list-num { + when "./condition = 'ipv4-address-list' or + ./condition = 'ipv4-next-hop-list'"; + leaf access-list-num { + type filter:access-list-standard; + } } - } - case ipv4-prefix-length { - when "./condition = 'ipv4-prefix-length' or - ./condition = 'ipv4-next-hop-prefix-length'"; - leaf ipv4-prefix-length { - type uint8 { - range "0..32"; + case access-list-num-extended { + when "./condition = 'ipv4-address-list' or + ./condition = 'ipv4-next-hop-list'"; + leaf access-list-num-extended { + type filter:access-list-extended; } } - } - case ipv6-prefix-length { - when "./condition = 'ipv6-prefix-length'"; - leaf ipv6-prefix-length { - type uint8 { - range "0..128"; + case list-name { + when "./condition = 'ipv4-address-list' or + ./condition = 'ipv4-prefix-list' or + ./condition = 'ipv4-next-hop-list' or + ./condition = 'ipv4-next-hop-prefix-list' or + ./condition = 'ipv6-address-list' or + ./condition = 'ipv6-prefix-list'"; + leaf list-name { + type filter:access-list-name; } } - } - case ipv4-next-hop-type { - when "./condition = 'ipv4-next-hop-type'"; - leaf ipv4-next-hop-type { - type enumeration { - enum blackhole { - value 0; + case ipv4-next-hop-type { + when "./condition = 'ipv4-next-hop-type'"; + leaf ipv4-next-hop-type { + type enumeration { + enum blackhole { + value 0; + } } } } - } - case ipv6-next-hop-type { - when "./condition = 'ipv6-next-hop-type'"; - leaf ipv6-next-hop-type { - type enumeration { - enum blackhole { - value 0; + case ipv6-next-hop-type { + when "./condition = 'ipv6-next-hop-type'"; + leaf ipv6-next-hop-type { + type enumeration { + enum blackhole { + value 0; + } } } } - } - case metric { - when "./condition = 'metric'"; - leaf metric { - type uint32 { - range "1..4294967295"; + case metric { + when "./condition = 'metric'"; + leaf metric { + type uint32 { + range "1..4294967295"; + } } } - } - case tag { - when "./condition = 'tag'"; - leaf tag { - type uint32 { - range "1..4294967295"; + case tag { + when "./condition = 'tag'"; + leaf tag { + type uint32 { + range "1..4294967295"; + } } } } } - } - list set-action { - description "Route map set actions"; + list set-action { + description "Route map set actions"; - key "action"; + key "action"; - leaf action { - description "Action to do when the route map matches"; - type enumeration { - enum ipv4-next-hop { - description "Set IPv4 address of the next hop"; - value 0; - } - enum ipv6-next-hop { - description "Set IPv6 address of the next hop"; - value 1; - } - enum metric { - description "Set prefix/route metric"; - value 2; - } - enum tag { - description "Set tag"; - value 3; - } + leaf action { + description "Action to do when the route map matches"; + type enumeration { + enum ipv4-next-hop { + description "Set IPv4 address of the next hop"; + value 0; + } + enum ipv6-next-hop { + description "Set IPv6 address of the next hop"; + value 1; + } + enum metric { + description "Set prefix/route metric"; + value 2; + } + enum tag { + description "Set tag"; + value 3; + } - /* - * Protocol YANG models should augment the parent node to - * contain the routing protocol specific value. The protocol - * must also augment `action-value` to include its specific - * values or expand the `when` statement on the existing cases. - */ - enum routing-protocol-specific { - description "Set a routing protocol specific action"; - value 100; + /* + * Protocol YANG models should augment the parent node to + * contain the routing protocol specific value. The protocol + * must also augment `action-value` to include its specific + * values or expand the `when` statement on the existing cases. + */ + enum routing-protocol-specific { + description "Set a routing protocol specific action"; + value 100; + } } } - } - choice action-value { - description - "Value to set (interpretation depends on action-type)"; - case ipv4-address { - when "./action = 'ipv4-next-hop'"; - leaf ipv4-address { - description "IPv4 address"; - type inet:ipv4-address; + choice action-value { + description + "Value to set (interpretation depends on action-type)"; + case ipv4-address { + when "./action = 'ipv4-next-hop'"; + leaf ipv4-address { + description "IPv4 address"; + type inet:ipv4-address; + } } - } - case ipv6-address { - when "./action = 'ipv6-next-hop'"; - leaf ipv6-address { - description "IPv6 address"; - type inet:ipv6-address; + case ipv6-address { + when "./action = 'ipv6-next-hop'"; + leaf ipv6-address { + description "IPv6 address"; + type inet:ipv6-address; + } } - } - case metric { - when "./action = 'metric'"; - choice metric-value { - description "Metric to set or use"; - case value { - leaf value { - description "Use the following metric value"; - type uint32 { - range "0..4294967295"; + case metric { + when "./action = 'metric'"; + choice metric-value { + description "Metric to set or use"; + case value { + leaf value { + description "Use the following metric value"; + type uint32 { + range "0..4294967295"; + } } } - } - case add-metric { - leaf add-metric { - description "Add unit to metric"; - type boolean; + case add-metric { + leaf add-metric { + description "Add unit to metric"; + type boolean; + } } - } - case subtract-metric { - leaf subtract-metric { - description "Subtract unit from metric"; - type boolean; + case subtract-metric { + leaf subtract-metric { + description "Subtract unit from metric"; + type boolean; + } } - } - case use-round-trip-time { - leaf use-round-trip-time { - description "Use the round trip time as metric"; - type boolean; + case use-round-trip-time { + leaf use-round-trip-time { + description "Use the round trip time as metric"; + type boolean; + } } - } - case add-round-trip-time { - leaf add-round-trip-time { - description "Add round trip time to metric"; - type boolean; + case add-round-trip-time { + leaf add-round-trip-time { + description "Add round trip time to metric"; + type boolean; + } } - } - case subtract-round-trip-time { - leaf subtract-round-trip-time { - description "Subtract round trip time to metric"; - type boolean; + case subtract-round-trip-time { + leaf subtract-round-trip-time { + description "Subtract round trip time to metric"; + type boolean; + } } } } - } - case tag { - leaf tag { - description "Tag value"; - type uint32 { - range "0..4294967295"; + case tag { + when "./action = 'tag'"; + leaf tag { + description "Tag value"; + type uint32 { + range "0..4294967295"; + } } } } } } - - leaf call { - description - "Call another route map before calling `exit-policy`. If the - called route map returns deny then this route map will also - return deny"; - type string; - } - - leaf exit-policy { - description "What do to after route map successful match, set and call"; - type enumeration { - enum permit-or-deny { - description "End route map evaluation and return"; - value 0; - } - enum next { - description - "Proceed evaluating next route map entry per sequence"; - value 1; - } - enum goto { - description - "Go to route map entry with the provided sequence number"; - value 2; - } - } - default "permit-or-deny"; - } - - leaf goto-value { - when "../exit-policy = 'goto'"; - description - "Sequence number to jump (when using `goto` exit policy)"; - type route-map-sequence; - } } } } diff --git a/yang/subdir.am b/yang/subdir.am index cfaf1a640..c1297dafd 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -19,9 +19,11 @@ EXTRA_DIST += yang/embedmodel.py # global symbols :(. Just put it in the daemon. Dynamic libraries.so work # without problems, as seen in libfrr. +dist_yangmodels_DATA += yang/frr-filter.yang dist_yangmodels_DATA += yang/frr-module-translator.yang dist_yangmodels_DATA += yang/frr-test-module.yang dist_yangmodels_DATA += yang/frr-interface.yang +dist_yangmodels_DATA += yang/frr-route-map.yang dist_yangmodels_DATA += yang/frr-route-types.yang dist_yangmodels_DATA += yang/ietf/ietf-routing-types.yang diff --git a/zebra/debug.c b/zebra/debug.c index 681dfb875..16b1d0057 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -105,9 +105,9 @@ DEFUN_NOSH (show_debugging_zebra, if (IS_ZEBRA_DEBUG_MLAG) vty_out(vty, " Zebra mlag debugging is on\n"); if (IS_ZEBRA_DEBUG_NHG_DETAIL) - vty_out(vty, "Zebra detailed nexthop debugging is on\n"); + vty_out(vty, " Zebra detailed nexthop debugging is on\n"); else if (IS_ZEBRA_DEBUG_NHG) - vty_out(vty, "Zebra nexthop debugging is on\n"); + vty_out(vty, " Zebra nexthop debugging is on\n"); hook_call(zebra_debug_show_debugging, vty); return CMD_SUCCESS; @@ -557,6 +557,14 @@ static int config_write_debug(struct vty *vty) write++; } + if (CHECK_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG_DETAILED)) { + vty_out(vty, "debug zebra nexthop detail\n"); + write++; + } else if (CHECK_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG)) { + vty_out(vty, "debug zebra nexthop\n"); + write++; + } + return write; } diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index c2812aa47..7cdd6ef84 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -556,7 +556,7 @@ int ifm_read(struct if_msghdr *ifm) * is 12 bytes larger than the 32 bit version. */ if (((struct sockaddr *)cp)->sa_family == AF_UNSPEC) - cp = cp + 12; + cp += 12; #endif /* Look up for RTA_IFP and skip others. */ diff --git a/zebra/main.c b/zebra/main.c index 75f825e50..5951c7e28 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -151,6 +151,10 @@ static void sigint(void) zebra_dplane_pre_finish(); + /* Clean up GR related info. */ + zebra_gr_stale_client_cleanup(zrouter.stale_client_list); + list_delete_all_node(zrouter.stale_client_list); + for (ALL_LIST_ELEMENTS(zrouter.client_list, ln, nn, client)) zserv_close_client(client); @@ -233,6 +237,7 @@ struct quagga_signal_t zebra_signals[] = { static const struct frr_yang_module_info *const zebra_yang_modules[] = { &frr_interface_info, + &frr_route_map_info, }; FRR_DAEMON_INFO( diff --git a/zebra/router-id.c b/zebra/router-id.c index b37d4aea7..710f2f6c2 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -75,11 +75,11 @@ void router_id_get(struct prefix *p, vrf_id_t vrf_id) struct connected *c; struct zebra_vrf *zvrf = vrf_info_get(vrf_id); - p->u.prefix4.s_addr = 0; + p->u.prefix4.s_addr = INADDR_ANY; p->family = AF_INET; p->prefixlen = 32; - if (zvrf->rid_user_assigned.u.prefix4.s_addr) + if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) p->u.prefix4.s_addr = zvrf->rid_user_assigned.u.prefix4.s_addr; else if (!list_isempty(zvrf->rid_lo_sorted_list)) { node = listtail(zvrf->rid_lo_sorted_list); @@ -185,7 +185,8 @@ void router_id_write(struct vty *vty) RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) if ((zvrf = vrf->info) != NULL) - if (zvrf->rid_user_assigned.u.prefix4.s_addr) { + if (zvrf->rid_user_assigned.u.prefix4.s_addr + != INADDR_ANY) { if (zvrf_id(zvrf) == VRF_DEFAULT) vty_out(vty, "router-id %s\n", inet_ntoa( diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index dd6e62ee6..1d63db32e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -75,6 +75,10 @@ static vlanid_t filter_vlan = 0; +/* We capture whether the current kernel supports nexthop ids; by + * default, we'll use them if possible. There's also a configuration + * available to _disable_ use of kernel nexthops. + */ static bool supports_nh; struct gw_family_t { @@ -86,6 +90,12 @@ struct gw_family_t { static const char ipv4_ll_buf[16] = "169.254.0.1"; static struct in_addr ipv4_ll; +/* Helper to control use of kernel-level nexthop ids */ +static bool kernel_nexthops_supported(void) +{ + return (supports_nh && zebra_nhg_kernel_nexthops_enabled()); +} + /* * The ipv4_ll data structure is used for all 5549 * additions to the kernel. Let's figure out the @@ -1152,10 +1162,12 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, addattr_l(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4); addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex); - if (nexthop->rmap_src.ipv4.s_addr && (cmd == RTM_NEWROUTE)) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY + && (cmd == RTM_NEWROUTE)) addattr_l(nlmsg, req_size, RTA_PREFSRC, &nexthop->rmap_src.ipv4, bytelen); - else if (nexthop->src.ipv4.s_addr && (cmd == RTM_NEWROUTE)) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY + && (cmd == RTM_NEWROUTE)) addattr_l(nlmsg, req_size, RTA_PREFSRC, &nexthop->src.ipv4, bytelen); @@ -1177,10 +1189,10 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, bytelen, nexthop); if (cmd == RTM_NEWROUTE) { - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) addattr_l(nlmsg, req_size, RTA_PREFSRC, &nexthop->rmap_src.ipv4, bytelen); - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) addattr_l(nlmsg, req_size, RTA_PREFSRC, &nexthop->src.ipv4, bytelen); } @@ -1226,10 +1238,10 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { if (cmd == RTM_NEWROUTE) { - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) addattr_l(nlmsg, req_size, RTA_PREFSRC, &nexthop->rmap_src.ipv4, bytelen); - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) addattr_l(nlmsg, req_size, RTA_PREFSRC, &nexthop->src.ipv4, bytelen); } @@ -1326,9 +1338,9 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, rtnh->rtnh_len += sizeof(struct rtattr) + bytelen; rtnh->rtnh_ifindex = nexthop->ifindex; - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->rmap_src; - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) @@ -1345,9 +1357,9 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, _netlink_route_rta_add_gateway_info(rtmsg->rtm_family, AF_INET, rta, rtnh, NL_PKT_BUF_SIZE, bytelen, nexthop); - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->rmap_src; - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) @@ -1386,9 +1398,9 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, /* ifindex */ if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->rmap_src; - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) @@ -1628,7 +1640,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) RTA_PAYLOAD(rta)); } - if (supports_nh) { + if (kernel_nexthops_supported()) { /* Kernel supports nexthop objects */ addattr32(&req.n, sizeof(req), RTA_NH_ID, dplane_ctx_get_nhe_id(ctx)); @@ -1943,7 +1955,7 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) size_t req_size = sizeof(req); /* Nothing to do if the kernel doesn't support nexthop objects */ - if (!supports_nh) + if (!kernel_nexthops_supported()) return 0; label_buf[0] = '\0'; @@ -2364,6 +2376,9 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) nhm = NLMSG_DATA(h); + if (ns_id) + vrf_id = ns_id; + if (startup && h->nlmsg_type != RTM_NEWNEXTHOP) return 0; @@ -2443,7 +2458,7 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return -1; } else if (h->nlmsg_type == RTM_DELNEXTHOP) - zebra_nhg_kernel_del(id); + zebra_nhg_kernel_del(id, vrf_id); return 0; } @@ -2501,8 +2516,10 @@ int netlink_nexthop_read(struct zebra_ns *zns) * this kernel must support them. */ supports_nh = true; - else if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Nexthop objects not supported on this kernel"); + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG) + zlog_debug("Nexthop objects %ssupported on this kernel", + supports_nh ? "" : "not "); return ret; } diff --git a/zebra/subdir.am b/zebra/subdir.am index 77ed5a6ca..1d49de541 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -17,6 +17,7 @@ vtysh_scan += \ $(top_srcdir)/zebra/zebra_routemap.c \ $(top_srcdir)/zebra/zebra_vty.c \ $(top_srcdir)/zebra/zserv.c \ + $(top_srcdir)/zebra/zebra_gr.c \ # end # can be loaded as DSO - always include for vtysh @@ -101,6 +102,7 @@ zebra_zebra_SOURCES = \ zebra/table_manager.c \ zebra/zapi_msg.c \ zebra/zebra_errors.c \ + zebra/zebra_gr.c \ # end zebra/debug_clippy.c: $(CLIPPY_DEPS) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index c21d00bbe..4d0e34561 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1475,6 +1475,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) api_nh = &api.nexthops[i]; ifindex_t ifindex = 0; + nexthop = NULL; + if (IS_ZEBRA_DEBUG_RECV) zlog_debug("nh type %d", api_nh->type); @@ -1773,6 +1775,8 @@ static void zread_hello(ZAPI_HANDLER_ARGS) client->instance = instance; } + /* Graceful restart processing for client connect */ + zebra_gr_client_reconnect(client); zsend_capabilities(client, zvrf); zebra_vrf_update_all(client); stream_failure: @@ -2582,14 +2586,14 @@ static void zserv_error_no_vrf(ZAPI_HANDLER_ARGS) zlog_debug("ZAPI message specifies unknown VRF: %d", hdr->vrf_id); - return zsend_error_msg(client, ZEBRA_NO_VRF, hdr); + zsend_error_msg(client, ZEBRA_NO_VRF, hdr); } static void zserv_error_invalid_msg_type(ZAPI_HANDLER_ARGS) { zlog_info("Zebra received unknown command %d", hdr->command); - return zsend_error_msg(client, ZEBRA_INVALID_MSG_TYPE, hdr); + zsend_error_msg(client, ZEBRA_INVALID_MSG_TYPE, hdr); } void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { @@ -2666,6 +2670,7 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, + [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities }; #if defined(HANDLE_ZAPI_FUZZING) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index bf1ba522a..17b148178 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -549,7 +549,6 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) } XFREE(MTYPE_DP_CTX, *pctx); - *pctx = NULL; } /* diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 7786dc246..d6f55fdeb 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -218,7 +218,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri, if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { nhi.gateway = &nexthop->gate; - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } @@ -228,7 +228,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri, } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index c09fa1c65..d50981deb 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -86,7 +86,7 @@ static inline int add_nexthop(qpb_allocator_t *allocator, Fpm__AddRoute *msg, if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { gateway = &nexthop->gate; - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } @@ -96,7 +96,7 @@ static inline int add_nexthop(qpb_allocator_t *allocator, Fpm__AddRoute *msg, } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c new file mode 100644 index 000000000..e8c7304f4 --- /dev/null +++ b/zebra/zebra_gr.c @@ -0,0 +1,684 @@ +/* + * Zebra GR related helper functions. + * + * Portions: + * Copyright (C) 2019 VMware, Inc. + * et al. + * + * 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 +#include + +#include "lib/prefix.h" +#include "lib/command.h" +#include "lib/if.h" +#include "lib/thread.h" +#include "lib/stream.h" +#include "lib/memory.h" +#include "lib/table.h" +#include "lib/network.h" +#include "lib/sockunion.h" +#include "lib/log.h" +#include "lib/zclient.h" +#include "lib/privs.h" +#include "lib/network.h" +#include "lib/buffer.h" +#include "lib/nexthop.h" +#include "lib/vrf.h" +#include "lib/libfrr.h" +#include "lib/sockopt.h" + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/zapi_msg.h" + + +/* + * Forward declaration. + */ +static struct zserv *zebra_gr_find_stale_client(struct zserv *client); +static int32_t zebra_gr_route_stale_delete_timer_expiry(struct thread *thread); +static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info); +static void zebra_gr_process_client_stale_routes(struct zserv *client, + vrf_id_t vrf_id); + +/* + * Debug macros. + */ +#define LOG_GR(msg, ...) \ + do { \ + if (IS_ZEBRA_DEBUG_EVENT) \ + zlog_debug(msg, ##__VA_ARGS__); \ + } while (0) + + +/* + * Client connection functions + */ + +/* + * Function to clean all the stale clients, + * function will also clean up all per instance + * capabilities that are exchanged. + */ +void zebra_gr_stale_client_cleanup(struct list *client_list) +{ + struct listnode *node, *nnode; + struct zserv *s_client = NULL; + struct client_gr_info *info, *ninfo; + + /* Find the stale client */ + for (ALL_LIST_ELEMENTS(client_list, node, nnode, s_client)) { + + LOG_GR("%s: Stale client %s is being deleted", __func__, + zebra_route_string(s_client->proto)); + + TAILQ_FOREACH_SAFE (info, &s_client->gr_info_queue, gr_info, + ninfo) { + + /* Cancel the stale timer */ + if (info->t_stale_removal != NULL) { + THREAD_OFF(info->t_stale_removal); + info->t_stale_removal = NULL; + /* Process the stale routes */ + thread_execute( + zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, + info, 1); + } + } + } +} + +/* + * A helper function to create client info. + */ +static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) +{ + struct client_gr_info *info; + + info = XCALLOC(MTYPE_TMP, sizeof(struct client_gr_info)); + + TAILQ_INSERT_TAIL(&(client->gr_info_queue), info, gr_info); + return info; +} + +/* + * A helper function to delte and destory client info. + */ +static void zebra_gr_client_info_delte(struct zserv *client, + struct client_gr_info *info) +{ + TAILQ_REMOVE(&(client->gr_info_queue), info, gr_info); + + THREAD_OFF(info->t_stale_removal); + + if (info->current_prefix) + XFREE(MTYPE_TMP, info->current_prefix); + + LOG_GR("%s: Instance info is being deleted for client %s", __func__, + zebra_route_string(client->proto)); + + /* Delete all the stale routes. */ + info->delete = true; + zebra_gr_delete_stale_routes(info); + + XFREE(MTYPE_TMP, info); +} + +/* + * Function to handle client when it disconnect. + */ +int32_t zebra_gr_client_disconnect(struct zserv *client) +{ + struct zserv *stale_client; + struct timeval tv; + struct client_gr_info *info = NULL; + + /* Find the stale client */ + stale_client = zebra_gr_find_stale_client(client); + + /* + * We should never be here. + */ + if (stale_client) { + LOG_GR("%s: Stale client %s exist, we should not be here!", + __func__, zebra_route_string(client->proto)); + assert(0); + } + + client->restart_time = monotime(&tv); + + /* For all the GR instance start the starle removal timer. */ + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities) + && (info->t_stale_removal == NULL)) { + thread_add_timer( + zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, info, + info->stale_removal_time, + &info->t_stale_removal); + info->current_afi = AFI_IP; + info->stale_client_ptr = client; + info->stale_client = true; + LOG_GR("%s: Client %s Stale timer update to %d", + __func__, zebra_route_string(client->proto), + info->stale_removal_time); + } + } + + listnode_add(zrouter.stale_client_list, client); + + return 0; +} + +/* + * Function to delete stale client + */ +static void zebra_gr_delete_stale_client(struct client_gr_info *info) +{ + struct client_gr_info *bgp_info; + struct zserv *s_client = NULL; + + s_client = info->stale_client_ptr; + + if (!s_client || !info->stale_client) + return; + + /* + * If there are bgp instances with the stale delete timer pending + * then stale client is not deleted + */ + if ((s_client->gr_instance_count > 0) && info->gr_enable) + s_client->gr_instance_count--; + + TAILQ_REMOVE(&(s_client->gr_info_queue), info, gr_info); + + LOG_GR("%s: Client %s gr count %d", __func__, + zebra_route_string(s_client->proto), + s_client->gr_instance_count); + + TAILQ_FOREACH (bgp_info, &s_client->gr_info_queue, gr_info) { + if (bgp_info->t_stale_removal != NULL) + return; + } + + LOG_GR("%s: Client %s is being deleted", __func__, + zebra_route_string(s_client->proto)); + + TAILQ_INIT(&(s_client->gr_info_queue)); + listnode_delete(zrouter.stale_client_list, s_client); + if (info->stale_client) + XFREE(MTYPE_TMP, s_client); + XFREE(MTYPE_TMP, info); +} + +/* + * Function to find stale client. + */ +static struct zserv *zebra_gr_find_stale_client(struct zserv *client) +{ + struct listnode *node, *nnode; + struct zserv *stale_client; + + /* Find the stale client */ + for (ALL_LIST_ELEMENTS(zrouter.stale_client_list, node, nnode, + stale_client)) { + if (client->proto == stale_client->proto + && client->instance == stale_client->instance) { + return stale_client; + } + } + + return NULL; +} + +/* + * Function to handle reconnect of client post restart. + */ +void zebra_gr_client_reconnect(struct zserv *client) +{ + struct listnode *node, *nnode; + struct zserv *old_client = NULL; + struct client_gr_info *info = NULL; + + /* Find the stale client */ + for (ALL_LIST_ELEMENTS(zrouter.stale_client_list, node, nnode, + old_client)) { + if (client->proto == old_client->proto + && client->instance == old_client->instance) + break; + } + + /* Copy the timers */ + if (old_client) { + client->gr_instance_count = old_client->gr_instance_count; + client->restart_time = old_client->restart_time; + + LOG_GR("%s : old client %s, gr_instance_count %d", __func__, + zebra_route_string(old_client->proto), + old_client->gr_instance_count); + + if (TAILQ_FIRST(&old_client->gr_info_queue)) { + TAILQ_CONCAT(&client->gr_info_queue, + &old_client->gr_info_queue, gr_info); + TAILQ_INIT(&old_client->gr_info_queue); + } + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + info->stale_client_ptr = client; + info->stale_client = false; + } + + /* Delete the stale client */ + listnode_delete(zrouter.stale_client_list, old_client); + /* Delete old client */ + XFREE(MTYPE_TMP, old_client); + } +} + +/* + * Functions to deal with capabilities + */ + +/* + * Update the graceful restart information + * for the client instance. + * This function handles all the capabilties that are received. + */ +static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) +{ + struct client_gr_info *info = NULL; + + /* Find the bgp information for the specified vrf id */ + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == api->vrf_id) + break; + } + + + /* + * If the command is delete, then cancel the stale timer and + * delete the bgp info + */ + switch (api->cap) { + case ZEBRA_CLIENT_GR_DISABLE: + if (!info) + return; + + LOG_GR("%s: Client %s instance GR disabled count %d", __func__, + zebra_route_string(client->proto), + client->gr_instance_count); + + if ((info->gr_enable) && (client->gr_instance_count > 0)) + client->gr_instance_count--; + + zebra_gr_client_info_delte(client, info); + break; + case ZEBRA_CLIENT_GR_CAPABILITIES: + /* Allocate bgp info */ + if (!info) + info = zebra_gr_client_info_create(client); + + /* Udpate other parameters */ + if (!info->gr_enable) { + client->gr_instance_count++; + + LOG_GR("%s: Cient %s GR enabled count %d", __func__, + zebra_route_string(client->proto), + client->gr_instance_count); + + info->capabilities = api->cap; + info->stale_removal_time = api->stale_removal_time; + info->vrf_id = api->vrf_id; + info->gr_enable = true; + } + break; + case ZEBRA_CLIENT_RIB_STALE_TIME: + LOG_GR("%s: Client %s stale time update event", __func__, + zebra_route_string(client->proto)); + + /* Update the stale removal timer */ + if (info && info->t_stale_removal == NULL) { + + LOG_GR("%s: Stale time: %d is now update to: %d", + __func__, info->stale_removal_time, + api->stale_removal_time); + + info->stale_removal_time = api->stale_removal_time; + } + + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + LOG_GR( + "%s: Client %s route update complete for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), api->afi, + api->safi); + if (info) + info->route_sync[api->afi][api->safi] = true; + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + LOG_GR("%s: Client %s route update pending for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), api->afi, + api->safi); + if (info) + info->af_enabled[api->afi][api->safi] = true; + break; + } +} + +/* + * Handler for capabilities that are received from client. + */ +static void zebra_client_capabilities_handler(struct zserv *client, + struct zapi_cap *api) +{ + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + case ZEBRA_CLIENT_GR_DISABLE: + case ZEBRA_CLIENT_RIB_STALE_TIME: + /* + * For all the cases we need to update the client info. + */ + zebra_client_update_info(client, api); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + /* + * After client info has been updated delete all + * stale routes + */ + zebra_client_update_info(client, api); + zebra_gr_process_client_stale_routes(client, api->vrf_id); + break; + } +} + +/* + * Function to decode and call appropriate functions + * to handle client capabilities. + */ +void zread_client_capabilities(ZAPI_HANDLER_ARGS) +{ + struct zapi_cap api; + struct stream *s; + + s = msg; + + if (zapi_capabilities_decode(s, &api)) { + LOG_GR("%s: Error in reading capabilities for client %s", + __func__, zebra_route_string(client->proto)); + return; + } + + /* Call the capabilities handler */ + zebra_client_capabilities_handler(client, &api); +} + + +/* + * Stale route handling + */ + +/* + * Delete all the stale routes that have not been refreshed + * post restart. + */ +static int32_t zebra_gr_route_stale_delete_timer_expiry(struct thread *thread) +{ + struct client_gr_info *info; + int32_t cnt = 0; + struct zserv *client; + + info = THREAD_ARG(thread); + info->t_stale_removal = NULL; + client = (struct zserv *)info->stale_client_ptr; + + /* Set the flag to indicate all stale route deletion */ + if (thread->u.val == 1) + info->delete = true; + + cnt = zebra_gr_delete_stale_routes(info); + + /* Retsart the timer */ + if (cnt > 0) { + LOG_GR("%s: Client %s processed %d routes. Start timer again", + __func__, zebra_route_string(client->proto), cnt); + + thread_add_timer(zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, info, + ZEBRA_DEFAULT_STALE_UPDATE_DELAY, + &info->t_stale_removal); + } else { + /* No routes to delete for the VRF */ + LOG_GR("%s: Client %s all starle routes processed", __func__, + zebra_route_string(client->proto)); + + if (info->current_prefix != NULL) + XFREE(MTYPE_TMP, info->current_prefix); + info->current_prefix = NULL; + info->current_afi = 0; + zebra_gr_delete_stale_client(info); + } + return 0; +} + + +/* + * Function to process to check if route entry is stale + * or has been updated. + */ +static void zebra_gr_process_route_entry(struct zserv *client, + struct route_node *rn, + struct route_entry *re) +{ + char buf[PREFIX2STR_BUFFER]; + + if ((client == NULL) || (rn == NULL) || (re == NULL)) + return; + + /* If the route is not refreshed after restart, delete the entry */ + if (re->uptime < client->restart_time) { + if (IS_ZEBRA_DEBUG_RIB) { + prefix2str(&rn->p, buf, sizeof(buf)); + zlog_debug("%s: Client %s stale route %s is deleted", + __func__, zebra_route_string(client->proto), + buf); + } + rib_delnode(rn, re); + } +} + +/* + * This function walks through the route table for all vrf and deletes + * the stale routes for the restarted client specified by the protocol + * type + */ +static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, + struct zebra_vrf *zvrf) +{ + struct route_node *rn, *curr; + struct route_entry *re; + struct route_entry *next; + struct route_table *table; + int32_t n = 0; + struct prefix *p; + afi_t afi, curr_afi; + uint8_t proto; + uint16_t instance; + struct zserv *s_client; + + if ((info == NULL) || (zvrf == NULL)) + return -1; + + s_client = info->stale_client_ptr; + if (s_client == NULL) { + LOG_GR("%s: Stale client not present", __func__); + return -1; + } + + proto = s_client->proto; + instance = s_client->instance; + curr_afi = info->current_afi; + + LOG_GR("%s: Client %s stale routes are being deleted", __func__, + zebra_route_string(proto)); + + /* Process routes for all AFI */ + for (afi = curr_afi; afi < AFI_MAX; afi++) { + table = zvrf->table[afi][SAFI_UNICAST]; + p = info->current_prefix; + + if (table) { + /* + * If the current prefix is NULL then get the first + * route entry in the table + */ + if (p == NULL) { + rn = route_top(table); + if (rn == NULL) + continue; + p = XCALLOC(MTYPE_TMP, sizeof(struct prefix)); + if (p == NULL) + return -1; + curr = rn; + prefix_copy(p, &rn->p); + } else + /* Get the next route entry */ + curr = route_table_get_next(table, p); + + for (rn = curr; rn; rn = srcdest_route_next(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (CHECK_FLAG(re->status, + ROUTE_ENTRY_REMOVED)) + continue; + /* If the route refresh is received + * after restart then do not delete + * the route + */ + if (re->type == proto + && re->instance == instance) { + zebra_gr_process_route_entry( + s_client, rn, re); + n++; + } + + /* If the max route count is reached + * then timer thread will be restarted + * Store the current prefix and afi + */ + if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) + && (info->delete == false)) { + prefix_copy(p, &rn->p); + info->current_afi = afi; + info->current_prefix = p; + return n; + } + } + } + } + /* + * Reset the current prefix to indicate processing completion + * of the current AFI + */ + if (info->current_prefix) { + XFREE(MTYPE_TMP, info->current_prefix); + info->current_prefix = NULL; + } + continue; + } + return 0; +} + +/* + * Delete the stale routes when client is restarted and routes are not + * refreshed within the stale timeout + */ +static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + uint64_t cnt = 0; + + if (info == NULL) + return -1; + + /* Get the current VRF */ + vrf = vrf_lookup_by_id(info->vrf_id); + if (vrf == NULL) { + LOG_GR("%s: Invalid VRF %d", __func__, info->vrf_id); + return -1; + } + + zvrf = vrf->info; + if (zvrf == NULL) { + LOG_GR("%s: Invalid VRF entry %d", __func__, info->vrf_id); + return -1; + } + + cnt = zebra_gr_delete_stale_route(info, zvrf); + return cnt; +} + +/* + * This function checks if route update for all AFI, SAFI is completed + * and cancels the stale timer + */ +static void zebra_gr_process_client_stale_routes(struct zserv *client, + vrf_id_t vrf_id) +{ + struct client_gr_info *info = NULL; + afi_t afi; + safi_t safi; + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == vrf_id) + break; + } + + if (info == NULL) + return; + + /* Check if route update completed for all AFI, SAFI */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { + if (info->af_enabled[afi][safi]) { + if (!info->route_sync[afi][safi]) { + LOG_GR( + "%s: Client %s route update not completed for AFI %d, SAFI %d", + __func__, zebra_route_string( + client->proto), + afi, safi); + return; + } + } + } + + /* + * Route update completed for all AFI, SAFI + * Cancel the stale timer and process the routes + */ + if (info->t_stale_removal) { + LOG_GR("%s: Client %s cancled stale delete timer vrf %d", + __func__, zebra_route_string(client->proto), + info->vrf_id); + THREAD_OFF(info->t_stale_removal); + thread_execute(zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, info, + 0); + } +} diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index f6bd5f458..fc7804a40 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -797,13 +797,11 @@ int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) } for (i = 0; i < mlag_msg.msg_cnt; i++) { - if (pay_load[i]->vrf_name) - XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE && pay_load[i]->intf_name) XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name); - if (pay_load[i]) - XFREE(MTYPE_MLAG_PBUF, pay_load[i]); + XFREE(MTYPE_MLAG_PBUF, pay_load[i]); } XFREE(MTYPE_MLAG_PBUF, pay_load); if (cleanup == true) @@ -861,13 +859,11 @@ int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) } for (i = 0; i < mlag_msg.msg_cnt; i++) { - if (pay_load[i]->vrf_name) - XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE && pay_load[i]->intf_name) XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name); - if (pay_load[i]) - XFREE(MTYPE_MLAG_PBUF, pay_load[i]); + XFREE(MTYPE_MLAG_PBUF, pay_load[i]); } XFREE(MTYPE_MLAG_PBUF, pay_load); if (cleanup) @@ -914,8 +910,7 @@ int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, sizeof(buf)), len); - if (hdr.data.data) - XFREE(MTYPE_MLAG_PBUF, hdr.data.data); + XFREE(MTYPE_MLAG_PBUF, hdr.data.data); return len; } diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 743030732..bb95e7238 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -49,6 +49,9 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context"); /* id counter to keep in sync with kernel */ uint32_t id_counter; +/* */ +static bool g_nexthops_enabled = true; + static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi); static void depends_add(struct nhg_connected_tree_head *head, @@ -99,31 +102,60 @@ nhg_connected_tree_root(struct nhg_connected_tree_head *head) return nhg_connected_tree_first(head); } -void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, - struct nhg_hash_entry *depend) +struct nhg_hash_entry * +nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *depend) { struct nhg_connected lookup = {}; struct nhg_connected *remove = NULL; + struct nhg_hash_entry *removed_nhe; lookup.nhe = depend; /* Lookup to find the element, then remove it */ remove = nhg_connected_tree_find(head, &lookup); - remove = nhg_connected_tree_del(head, remove); - if (remove) + /* Re-returning here just in case this API changes.. + * the _del list api's are a bit undefined at the moment. + * + * So hopefully returning here will make it fail if the api + * changes to something different than currently expected. + */ + remove = nhg_connected_tree_del(head, remove); + + /* If the entry was sucessfully removed, free the 'connected` struct */ + if (remove) { + removed_nhe = remove->nhe; nhg_connected_free(remove); + return removed_nhe; + } + + return NULL; } -void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, - struct nhg_hash_entry *depend) +/* Assuming UNIQUE RB tree. If this changes, assumptions here about + * insertion need to change. + */ +struct nhg_hash_entry * +nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *depend) { struct nhg_connected *new = NULL; new = nhg_connected_new(depend); - if (new) - nhg_connected_tree_add(head, new); + /* On success, NULL will be returned from the + * RB code. + */ + if (new && (nhg_connected_tree_add(head, new) == NULL)) + return NULL; + + /* If it wasn't successful, it must be a duplicate. We enforce the + * unique property for the `nhg_connected` tree. + */ + nhg_connected_free(new); + + return depend; } static void @@ -288,15 +320,15 @@ zebra_nhg_connect_depends(struct nhg_hash_entry *nhe, struct interface *ifp = NULL; ifp = if_lookup_by_index(nhe->nhg->nexthop->ifindex, - nhe->vrf_id); + nhe->nhg->nexthop->vrf_id); if (ifp) zebra_nhg_set_if(nhe, ifp); else flog_err( EC_ZEBRA_IF_LOOKUP_FAILED, "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u", - nhe->nhg->nexthop->ifindex, nhe->vrf_id, - nhe->id); + nhe->nhg->nexthop->ifindex, + nhe->nhg->nexthop->vrf_id, nhe->id); } } @@ -481,7 +513,9 @@ static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, resolved_ng.nexthop = nh; depend = zebra_nhg_rib_find(0, &resolved_ng, afi); - depends_add(nhg_depends, depend); + + if (depend) + depends_add(nhg_depends, depend); } static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, @@ -504,10 +538,10 @@ static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, lookup.type = type ? type : ZEBRA_ROUTE_NHG; lookup.nhg = nhg; + lookup.vrf_id = vrf_id; if (lookup.nhg->nexthop->next) { /* Groups can have all vrfs and AF's in them */ lookup.afi = AFI_UNSPEC; - lookup.vrf_id = VRF_DEFAULT; } else { switch (lookup.nhg->nexthop->type) { case (NEXTHOP_TYPE_IFINDEX): @@ -531,8 +565,6 @@ static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, lookup.afi = AFI_IP6; break; } - - lookup.vrf_id = vrf_id; } if (id) @@ -591,10 +623,11 @@ zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type) { struct nhg_hash_entry *nhe = NULL; struct nexthop_group nhg = {}; + vrf_id_t vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nh->vrf_id; nexthop_group_add_sorted(&nhg, nh); - zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, type); + zebra_nhg_find(&nhe, id, &nhg, NULL, vrf_id, afi, type); return nhe; } @@ -681,7 +714,6 @@ static void nhg_ctx_free(struct nhg_ctx **ctx) done: XFREE(MTYPE_NHG_CTX, *ctx); - *ctx = NULL; } static struct nhg_ctx *nhg_ctx_init(uint32_t id, struct nexthop *nh, @@ -1031,11 +1063,11 @@ int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp, } /* Kernel-side, received delete message */ -int zebra_nhg_kernel_del(uint32_t id) +int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id) { struct nhg_ctx *ctx = NULL; - ctx = nhg_ctx_init(id, NULL, NULL, 0, 0, 0, 0); + ctx = nhg_ctx_init(id, NULL, NULL, vrf_id, 0, 0, 0); nhg_ctx_set_op(ctx, NHG_CTX_OP_DEL); @@ -1104,8 +1136,14 @@ done: static void depends_add(struct nhg_connected_tree_head *head, struct nhg_hash_entry *depend) { - nhg_connected_tree_add_nhe(head, depend); - zebra_nhg_increment_ref(depend); + /* If NULL is returned, it was successfully added and + * needs to have its refcnt incremented. + * + * Else the NHE is already present in the tree and doesn't + * need to increment the refcnt. + */ + if (nhg_connected_tree_add_nhe(head, depend) == NULL) + zebra_nhg_increment_ref(depend); } static struct nhg_hash_entry * @@ -1146,6 +1184,14 @@ struct nhg_hash_entry * zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi) { struct nhg_hash_entry *nhe = NULL; + vrf_id_t vrf_id; + + /* + * CLANG SA is complaining that nexthop may be NULL + * Make it happy but this is ridonc + */ + assert(nhg->nexthop); + vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nhg->nexthop->vrf_id; if (!(nhg && nhg->nexthop)) { flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, @@ -1153,7 +1199,7 @@ zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi) return NULL; } - zebra_nhg_find(&nhe, id, nhg, NULL, nhg->nexthop->vrf_id, rt_afi, 0); + zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, 0); return nhe; } @@ -1442,7 +1488,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, * resolved by a route NH1. The exception is if the route is a * host route. */ - if (top && rn == top) + if (rn == top) if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) @@ -1985,3 +2031,18 @@ void zebra_nhg_sweep_table(struct hash *hash) { hash_iterate(hash, zebra_nhg_sweep_entry, NULL); } + +/* Global control to disable use of kernel nexthops, if available. We can't + * force the kernel to support nexthop ids, of course, but we can disable + * zebra's use of them, for testing e.g. By default, if the kernel supports + * nexthop ids, zebra uses them. + */ +void zebra_nhg_enable_kernel_nexthops(bool set) +{ + g_nexthops_enabled = set; +} + +bool zebra_nhg_kernel_nexthops_enabled(void) +{ + return g_nexthops_enabled; +} diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 522ec1e9d..4d001944b 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -153,6 +153,13 @@ struct nhg_ctx { enum nhg_ctx_status status; }; +/* Global control to disable use of kernel nexthops, if available. We can't + * force the kernel to support nexthop ids, of course, but we can disable + * zebra's use of them, for testing e.g. By default, if the kernel supports + * nexthop ids, zebra uses them. + */ +void zebra_nhg_enable_kernel_nexthops(bool set); +bool zebra_nhg_kernel_nexthops_enabled(void); /** * NHE abstracted tree functions. @@ -195,7 +202,7 @@ extern int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, vrf_id_t vrf_id, afi_t afi, int type, int startup); /* Del via kernel */ -extern int zebra_nhg_kernel_del(uint32_t id); +extern int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id); /* Find via route creation */ extern struct nhg_hash_entry * @@ -227,4 +234,5 @@ extern void zebra_nhg_sweep_table(struct hash *hash); /* Nexthop resolution processing */ struct route_entry; /* Forward ref to avoid circular includes */ extern int nexthop_active_update(struct route_node *rn, struct route_entry *re); -#endif + +#endif /* __ZEBRA_NHG_H__ */ diff --git a/zebra/zebra_nhg_private.h b/zebra/zebra_nhg_private.h index 79107b047..92f438fce 100644 --- a/zebra/zebra_nhg_private.h +++ b/zebra/zebra_nhg_private.h @@ -52,9 +52,22 @@ extern bool nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head); extern struct nhg_connected * nhg_connected_tree_root(struct nhg_connected_tree_head *head); -extern void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, - struct nhg_hash_entry *nhe); -extern void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, - struct nhg_hash_entry *nhe); + +/* I realize _add/_del returns are backwords. + * + * Currently the list APIs are not standardized for what happens in + * the _del() function when the item isn't present. + * + * We are choosing to return NULL if not found in the _del case for now. + */ + +/* Delete NHE from the tree. On success, return the NHE, otherwise NULL. */ +extern struct nhg_hash_entry * +nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *nhe); +/* ADD NHE to the tree. On success, return NULL, otherwise return the NHE. */ +extern struct nhg_hash_entry * +nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *nhe); #endif /* __ZEBRA_NHG_PRIVATE_H__ */ diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index fe7a93a50..ff3907451 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -483,8 +483,10 @@ static void zebra_pbr_cleanup_ipset(struct hash_bucket *b, void *data) int *sock = data; if (ipset->sock == *sock) { - hook_call(zebra_pbr_ipset_update, 0, ipset); - hash_release(zrouter.ipset_hash, ipset); + if (hash_release(zrouter.ipset_hash, ipset)) + zebra_pbr_ipset_free(ipset); + else + hook_call(zebra_pbr_ipset_update, 0, ipset); } } @@ -494,8 +496,10 @@ static void zebra_pbr_cleanup_ipset_entry(struct hash_bucket *b, void *data) int *sock = data; if (ipset->sock == *sock) { - hook_call(zebra_pbr_ipset_entry_update, 0, ipset); - hash_release(zrouter.ipset_entry_hash, ipset); + if (hash_release(zrouter.ipset_entry_hash, ipset)) + zebra_pbr_ipset_entry_free(ipset); + else + hook_call(zebra_pbr_ipset_entry_update, 0, ipset); } } @@ -505,8 +509,10 @@ static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data) int *sock = data; if (iptable->sock == *sock) { - hook_call(zebra_pbr_iptable_update, 0, iptable); - hash_release(zrouter.iptable_hash, iptable); + if (hash_release(zrouter.iptable_hash, iptable)) + zebra_pbr_iptable_free(iptable); + else + hook_call(zebra_pbr_iptable_update, 0, iptable); } } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0200ef2a5..f3112cc9c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3115,8 +3115,6 @@ static struct rib_update_ctx *rib_update_ctx_init(vrf_id_t vrf_id, static void rib_update_ctx_fini(struct rib_update_ctx **ctx) { XFREE(MTYPE_RIB_UPDATE_CTX, *ctx); - - *ctx = NULL; } static int rib_update_handler(struct thread *thread) diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index d8ad8a686..59bd0e55f 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -117,6 +117,9 @@ struct zebra_router { /* Lists of clients who have connected to us */ struct list *client_list; + /* List of clients in GR */ + struct list *stale_client_list; + struct zebra_router_table_head tables; /* L3-VNI hash table (for EVPN). Only in default instance */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index c8b96011d..86ec2ffef 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1123,9 +1123,11 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) vty_out(vty, " RefCnt: %d\n", nhe->refcnt); if (nhe_vrf) - vty_out(vty, " VRF: %s\n", nhe_vrf->name); + vty_out(vty, " VRF: %s AFI: %s\n", nhe_vrf->name, + afi2str(nhe->afi)); else - vty_out(vty, " VRF: UNKNOWN\n"); + vty_out(vty, " VRF: UNKNOWN AFI: %s\n", + afi2str(nhe->afi)); if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_UNHASHABLE)) vty_out(vty, " Duplicate - from kernel not hashable\n"); @@ -1276,25 +1278,44 @@ static int show_nexthop_group_id_cmd_helper(struct vty *vty, uint32_t id) return CMD_SUCCESS; } -static void show_nexthop_group_cmd_helper(struct vty *vty, - struct zebra_vrf *zvrf, afi_t afi) +/* Helper function for iteration through the hash of nexthop-groups/nhe-s */ + +struct nhe_show_context { + struct vty *vty; + vrf_id_t vrf_id; + afi_t afi; +}; + +static int nhe_show_walker(struct hash_bucket *bucket, void *arg) { - struct list *list = hash_to_list(zrouter.nhgs); - struct nhg_hash_entry *nhe = NULL; - struct listnode *node = NULL; + struct nhe_show_context *ctx = arg; + struct nhg_hash_entry *nhe; - for (ALL_LIST_ELEMENTS_RO(list, node, nhe)) { + nhe = bucket->data; /* We won't be offered NULL buckets */ - if (afi && nhe->afi != afi) - continue; + if (ctx->afi && nhe->afi != ctx->afi) + goto done; - if (nhe->vrf_id != zvrf->vrf->vrf_id) - continue; + if (ctx->vrf_id && nhe->vrf_id != ctx->vrf_id) + goto done; - show_nexthop_group_out(vty, nhe); - } + show_nexthop_group_out(ctx->vty, nhe); + +done: + return HASHWALK_CONTINUE; +} + +static void show_nexthop_group_cmd_helper(struct vty *vty, + struct zebra_vrf *zvrf, + afi_t afi) +{ + struct nhe_show_context ctx; + + ctx.vty = vty; + ctx.afi = afi; + ctx.vrf_id = zvrf->vrf->vrf_id; - list_delete(&list); + hash_walk(zrouter.nhgs_id, nhe_show_walker, &ctx); } static void if_nexthop_group_dump_vty(struct vty *vty, struct interface *ifp) @@ -1351,18 +1372,19 @@ DEFPY (show_interface_nexthop_group, DEFPY (show_nexthop_group, show_nexthop_group_cmd, - "show nexthop-group rib <(0-4294967295)$id|[] [vrf ]>", + "show nexthop-group rib <(0-4294967295)$id|[singleton ] [vrf ]>", SHOW_STR "Show Nexthop Groups\n" "RIB information\n" "Nexthop Group ID\n" + "Show Singleton Nexthop-Groups\n" IP_STR IP6_STR VRF_FULL_CMD_HELP_STR) { struct zebra_vrf *zvrf = NULL; - afi_t afi = 0; + afi_t afi = AFI_UNSPEC; if (id) return show_nexthop_group_id_cmd_helper(vty, id); @@ -1372,6 +1394,11 @@ DEFPY (show_nexthop_group, else if (v6) afi = AFI_IP6; + if (vrf_is_backend_netns() && (vrf_name || vrf_all)) { + vty_out(vty, "VRF subcommand does not make any sense in l3mdev based vrf's"); + return CMD_WARNING; + } + if (vrf_all) { struct vrf *vrf; @@ -1395,7 +1422,8 @@ DEFPY (show_nexthop_group, zvrf = zebra_vrf_lookup_by_name(VRF_DEFAULT_NAME); if (!zvrf) { - vty_out(vty, "VRF %s specified does not exist", vrf_name); + vty_out(vty, "%% VRF '%s' specified does not exist\n", + vrf_name); return CMD_WARNING; } @@ -1404,6 +1432,19 @@ DEFPY (show_nexthop_group, return CMD_SUCCESS; } +DEFPY_HIDDEN(nexthop_group_use_enable, + nexthop_group_use_enable_cmd, + "[no] zebra nexthop kernel enable", + NO_STR + ZEBRA_STR + "Nexthop configuration \n" + "Configure use of kernel nexthops\n" + "Enable kernel nexthops\n") +{ + zebra_nhg_enable_kernel_nexthops(!no); + return CMD_SUCCESS; +} + DEFUN (no_ip_nht_default_route, no_ip_nht_default_route_cmd, "no ip nht resolve-via-default", @@ -3115,6 +3156,10 @@ static int config_write_protocol(struct vty *vty) /* Include dataplane info */ dplane_config_write_helper(vty); + /* Include nexthop-group config */ + if (!zebra_nhg_kernel_nexthops_enabled()) + vty_out(vty, "no zebra nexthop kernel enable\n"); + return 1; } @@ -3486,6 +3531,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_zebra_workqueue_timer_cmd); install_element(CONFIG_NODE, &zebra_packet_process_cmd); install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); + install_element(CONFIG_NODE, &nexthop_group_use_enable_cmd); install_element(VIEW_NODE, &show_nexthop_group_cmd); install_element(VIEW_NODE, &show_interface_nexthop_group_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index ffb2528a2..4b56581ca 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3212,7 +3212,7 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, vtep_ip = n->r_vtep_ip; /* Mark appropriately */ UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - n->r_vtep_ip.s_addr = 0; + n->r_vtep_ip.s_addr = INADDR_ANY; SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); n->ifindex = ifp->ifindex; } @@ -9985,7 +9985,7 @@ static zebra_vxlan_sg_t *zebra_vxlan_sg_add(struct zebra_vrf *zvrf, * 2. the XG entry is used by pimd to setup the * vxlan-termination-mroute */ - if (sg->src.s_addr) { + if (sg->src.s_addr != INADDR_ANY) { memset(&sip, 0, sizeof(sip)); parent = zebra_vxlan_sg_do_ref(zvrf, sip, sg->grp); if (!parent) @@ -10017,7 +10017,7 @@ static void zebra_vxlan_sg_del(zebra_vxlan_sg_t *vxlan_sg) /* On SG entry deletion remove the reference to its parent XG * entry */ - if (vxlan_sg->sg.src.s_addr) { + if (vxlan_sg->sg.src.s_addr != INADDR_ANY) { memset(&sip, 0, sizeof(sip)); zebra_vxlan_sg_do_deref(zvrf, sip, vxlan_sg->sg.grp); } @@ -10076,7 +10076,8 @@ static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, { struct zebra_vrf *zvrf; - if (!local_vtep_ip.s_addr || !mcast_grp.s_addr) + if (local_vtep_ip.s_addr == INADDR_ANY + || mcast_grp.s_addr == INADDR_ANY) return; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -10091,7 +10092,8 @@ static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, { struct zebra_vrf *zvrf; - if (!local_vtep_ip.s_addr || !mcast_grp.s_addr) + if (local_vtep_ip.s_addr == INADDR_ANY + || mcast_grp.s_addr == INADDR_ANY) return; zvrf = vrf_info_lookup(VRF_DEFAULT); diff --git a/zebra/zserv.c b/zebra/zserv.c index cca926f3b..2a5352a1d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -557,6 +557,9 @@ DEFINE_KOOH(zserv_client_close, (struct zserv *client), (client)); */ static void zserv_client_free(struct zserv *client) { + if (client == NULL) + return; + hook_call(zserv_client_close, client); /* Close file descriptor. */ @@ -565,11 +568,14 @@ static void zserv_client_free(struct zserv *client) close(client->sock); - nroutes = rib_score_proto(client->proto, client->instance); - zlog_notice( - "client %d disconnected. %lu %s routes removed from the rib", - client->sock, nroutes, - zebra_route_string(client->proto)); + if (!client->gr_instance_count) { + nroutes = rib_score_proto(client->proto, + client->instance); + zlog_notice( + "client %d disconnected %lu %s routes removed from the rib", + client->sock, nroutes, + zebra_route_string(client->proto)); + } client->sock = -1; } @@ -600,7 +606,25 @@ static void zserv_client_free(struct zserv *client) } vrf_bitmap_free(client->ridinfo); - XFREE(MTYPE_TMP, client); + /* + * If any instance are graceful restart enabled, + * client is not deleted + */ + if (!client->gr_instance_count) { + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: Deleting client %s", __func__, + zebra_route_string(client->proto)); + XFREE(MTYPE_TMP, client); + } else { + /* Handle cases where client has GR instance. */ + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: client %s restart enabled", __func__, + zebra_route_string(client->proto)); + if (zebra_gr_client_disconnect(client) < 0) + zlog_err( + "%s: GR enabled but could not handle disconnect event", + __func__); + } } void zserv_close_client(struct zserv *client) @@ -670,6 +694,7 @@ static struct zserv *zserv_client_create(int sock) pthread_mutex_init(&client->ibuf_mtx, NULL); pthread_mutex_init(&client->obuf_mtx, NULL); client->wb = buffer_new(0); + TAILQ_INIT(&(client->gr_info_queue)); atomic_store_explicit(&client->connect_time, (uint32_t) monotime(NULL), memory_order_relaxed); @@ -861,12 +886,14 @@ static char *zserv_time_buf(time_t *time1, char *buf, int buflen) return buf; } +/* Display client info details */ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) { char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF]; char wbuf[ZEBRA_TIME_BUF], nhbuf[ZEBRA_TIME_BUF], mbuf[ZEBRA_TIME_BUF]; time_t connect_time, last_read_time, last_write_time; uint32_t last_read_cmd, last_write_cmd; + struct client_gr_info *info = NULL; vty_out(vty, "Client: %s", zebra_route_string(client->proto)); if (client->instance) @@ -945,12 +972,100 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt); vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt); + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id)); + vty_out(vty, "Capabilities : "); + switch (info->capabilities) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + vty_out(vty, "Graceful Restart\n"); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + case ZEBRA_CLIENT_GR_DISABLE: + case ZEBRA_CLIENT_RIB_STALE_TIME: + vty_out(vty, "None\n"); + break; + } + } + #if defined DEV_BUILD vty_out(vty, "Input Fifo: %zu:%zu Output Fifo: %zu:%zu\n", client->ibuf_fifo->count, client->ibuf_fifo->max_count, client->obuf_fifo->count, client->obuf_fifo->max_count); #endif vty_out(vty, "\n"); +} + +/* Display stale client information */ +static void zebra_show_stale_client_detail(struct vty *vty, + struct zserv *client) +{ + char buf[PREFIX2STR_BUFFER]; + struct tm *tm; + struct timeval tv; + time_t uptime; + struct client_gr_info *info = NULL; + struct zserv *s = NULL; + + if (client->instance) + vty_out(vty, " Instance: %d", client->instance); + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id)); + vty_out(vty, "Capabilities : "); + switch (info->capabilities) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + vty_out(vty, "Graceful Restart\n"); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + case ZEBRA_CLIENT_GR_DISABLE: + case ZEBRA_CLIENT_RIB_STALE_TIME: + vty_out(vty, "None\n"); + break; + } + + if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities)) { + if (info->stale_client_ptr) { + s = (struct zserv *)(info->stale_client_ptr); + uptime = monotime(&tv); + uptime -= s->restart_time; + tm = gmtime(&uptime); + vty_out(vty, "Last restart time : "); + if (uptime < ONE_DAY_SECOND) + vty_out(vty, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, + tm->tm_sec); + else if (uptime < ONE_WEEK_SECOND) + vty_out(vty, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, + tm->tm_min); + else + vty_out(vty, "%02dw%dd%02dh", + tm->tm_yday / 7, + tm->tm_yday - ((tm->tm_yday / 7) + * 7), + tm->tm_hour); + vty_out(vty, " ago\n"); + + vty_out(vty, "Stalepath removal time: %d sec\n", + info->stale_removal_time); + if (info->t_stale_removal) { + vty_out(vty, + "Stale delete timer: %ld sec\n", + thread_timer_remain_second( + info->t_stale_removal)); + } + } + vty_out(vty, "Current AFI : %d\n", info->current_afi); + if (info->current_prefix) { + prefix2str(info->current_prefix, buf, + sizeof(buf)); + vty_out(vty, "Current prefix : %s\n", buf); + } + } + } + vty_out(vty, "\n"); return; } @@ -1002,8 +1117,12 @@ DEFUN (show_zebra_client, struct listnode *node; struct zserv *client; - for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { zebra_show_client_detail(vty, client); + vty_out(vty, "Stale Client Information\n"); + vty_out(vty, "------------------------\n"); + zebra_show_stale_client_detail(vty, client); + } return CMD_SUCCESS; } @@ -1047,6 +1166,7 @@ void zserv_init(void) { /* Client list init. */ zrouter.client_list = list_new(); + zrouter.stale_client_list = list_new(); /* Misc init. */ zsock = -1; diff --git a/zebra/zserv.h b/zebra/zserv.h index d8d82a52e..77ea19202 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -52,6 +52,42 @@ extern "C" { #define ZEBRA_RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */ + +/* Stale route marker timer */ +#define ZEBRA_DEFAULT_STALE_UPDATE_DELAY 1 + +/* Count of stale routes processed in timer context */ +#define ZEBRA_MAX_STALE_ROUTE_COUNT 50000 + +/* Graceful Restart information */ +struct client_gr_info { + /* VRF for which GR enabled */ + vrf_id_t vrf_id; + + /* AFI */ + afi_t current_afi; + + /* Stale time and GR cap */ + uint32_t stale_removal_time; + enum zserv_client_capabilities capabilities; + + /* GR commands */ + bool delete; + bool gr_enable; + bool stale_client; + + /* Route sync and enable flags for AFI/SAFI */ + bool af_enabled[AFI_MAX][SAFI_MAX]; + bool route_sync[AFI_MAX][SAFI_MAX]; + + /* Book keeping */ + struct prefix *current_prefix; + void *stale_client_ptr; + struct thread *t_stale_removal; + + TAILQ_ENTRY(client_gr_info) gr_info; +}; + /* Client structure. */ struct zserv { /* Client pthread */ @@ -170,6 +206,19 @@ struct zserv { _Atomic uint32_t last_read_cmd; /* command code of last message written */ _Atomic uint32_t last_write_cmd; + + /* + * Number of instances configured with + * graceful restart + */ + uint32_t gr_instance_count; + time_t restart_time; + + /* + * Graceful restart information for + * each instance + */ + TAILQ_HEAD(info_list, client_gr_info) gr_info_queue; }; #define ZAPI_HANDLER_ARGS \ @@ -230,7 +279,6 @@ extern int zserv_send_message(struct zserv *client, struct stream *msg); */ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance); - /* * Close a client. * @@ -242,7 +290,6 @@ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance); */ extern void zserv_close_client(struct zserv *client); - /* * Log a ZAPI message hexdump. * @@ -265,6 +312,16 @@ extern void zserv_read_file(char *input); /* TODO */ int zebra_finalize(struct thread *event); +/* + * Graceful restart functions. + */ +extern int zebra_gr_client_disconnect(struct zserv *client); +extern void zebra_gr_client_reconnect(struct zserv *client); +extern void zebra_gr_stale_client_cleanup(struct list *client_list); +extern void zread_client_capabilities(struct zserv *client, struct zmsghdr *hdr, + struct stream *msg, + struct zebra_vrf *zvrf); + #ifdef __cplusplus } #endif