diff options
43 files changed, 2738 insertions, 716 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 16de59b72c..47da8fcb88 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); } /* @@ -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 8bd527c0ff..2e91f56df5 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 29e668d179..7ca48a5bea 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 87b29ac3be..c5718aecac 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_fsm.c b/bgpd/bgp_fsm.c index c5b46487a5..da8e6a907b 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -646,8 +646,9 @@ static int bgp_graceful_deferral_timer_expire(struct thread *thread) bgp = info->bgp; if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("afi %d, safi %d : graceful restart deferral timer expired", - afi, safi); + zlog_debug( + "afi %d, safi %d : graceful restart deferral timer expired", + afi, safi); bgp->gr_info[afi][safi].t_select_deferral = NULL; @@ -1198,28 +1199,28 @@ int bgp_stop(struct peer *peer) */ 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)) { - gr_info = &bgp->gr_info[afi][safi]; - - if (gr_info && (gr_info->eor_required)) - gr_info->eor_required--; - - if (gr_info && 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 && gr_info->eor_required - == 0) { - BGP_TIMER_OFF( + 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; - } + gr_info->eor_received = 0; } } } @@ -1677,27 +1678,14 @@ static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi, */ if (gr_info->eor_required == 0) { thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); - if (thread_info == NULL) { - if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("%s : Error allocating thread info", - __func__); - return -1; - } 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); - if (gr_info->t_select_deferral == NULL) { - if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("Error starting deferral timer for %s", - get_afi_safi_str(afi, safi, false)); - return -1; - } + 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 */ @@ -2308,11 +2296,6 @@ int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, "%s [BGP_GR] GLOBAL_INVALID", __func__); return BGP_ERR_GR_OPERATION_FAILED; - default: - zlog_debug( - "%s [BGP_GR] Global unknown ERROR", - __func__); - return BGP_ERR_GR_OPERATION_FAILED; } } } diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 3e4dfb11ad..41c4108c0a 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 03715f5621..5428022551 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_open.c b/bgpd/bgp_open.c index 2d3dc13557..54d785e26b 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -658,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, @@ -1310,82 +1310,72 @@ static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer, 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))) { + 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 helper Capability for Peer :%s :", - peer->host); + zlog_debug("[BGP_GR] Sending R-Bit 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); + 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 R-Bit for Peer :%s :", - peer->host); - } + "[BGP_GR] Sending GR Capability for AFI :%d :, SAFI :%d:", + afi, safi); - 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]) { - 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); - } - } - } + /* 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); } + + /* 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. */ @@ -1435,7 +1425,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 */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 954f524ec7..983b0184a5 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -38,6 +38,7 @@ #include "workqueue.h" #include "queue.h" #include "memory.h" +#include "srv6.h" #include "lib/json.h" #include "lib_errors.h" #include "zclient.h" @@ -2760,12 +2761,6 @@ int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) } thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); - if (thread_info == NULL) { - if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("%s : error allocating thread info", - __func__); - return -1; - } thread_info->afi = afi; thread_info->safi = safi; @@ -2777,13 +2772,6 @@ int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) thread_add_timer(bm->master, bgp_route_select_timer_expire, thread_info, BGP_ROUTE_SELECT_DELAY, &bgp->gr_info[afi][safi].t_route_select); - if (bgp->gr_info[afi][safi].t_route_select == NULL) { - if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("%s : error starting selection thread for %s", - __func__, get_afi_safi_str(afi, - safi, false)); - return -1; - } return 0; } @@ -3656,6 +3644,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)) { @@ -3835,6 +3839,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, @@ -9216,6 +9232,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) @@ -10716,6 +10741,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); } diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index e335c39fb1..8f31cd38dc 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 { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index eff5b3b7bc..696233e7a0 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9454,20 +9454,19 @@ static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, } } -static void bgp_show_neighnor_graceful_restart_rbit( - struct vty *vty, - struct peer *p, - bool use_json, - json_object *json) +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_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; @@ -9477,62 +9476,57 @@ static void bgp_show_neighnor_graceful_restart_rbit( if (rbit_status) { if (use_json) - json_object_boolean_true_add( - json, "rBit"); + json_object_boolean_true_add(json, "rBit"); else vty_out(vty, "True\n"); } else { if (use_json) - json_object_boolean_false_add( - json, "rBit"); + 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) +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 (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)) { + 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)) { + } 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)) { + } 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); + 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) +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"; @@ -9557,16 +9551,14 @@ static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty, } if (use_json) { - json_object_string_add(json, - "localGrMode", mode); + 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) +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; @@ -9577,215 +9569,193 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { - if (peer->afc[afi][safi] - && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) - && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV) - ) { - 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->afc[afi][safi]) + continue; - if (peer->eor_stime[afi][safi] >= - peer->pkt_stime[afi][safi]) - eor_flag = true; - else - eor_flag = false; + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) + || !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) + continue; - if (!use_json) { - vty_out(vty, " %s :\n", - get_afi_safi_str(afi, safi, false)); + if (use_json) { + json_afi_safi = json_object_new_object(); + json_endofrib_status = json_object_new_object(); + json_timer = json_object_new_object(); + } - vty_out(vty, - " F bit : "); - } else - get_afi_safi_str(afi, safi, true); + if (peer->eor_stime[afi][safi] + >= peer->pkt_stime[afi][safi]) + eor_flag = true; + else + eor_flag = false; - if (peer->nsf[afi][safi] - && CHECK_FLAG( - peer->af_cap[afi][safi], - PEER_CAP_RESTART_AF_PRESERVE_RCV)) { + if (!use_json) { + vty_out(vty, " %s :\n", + get_afi_safi_str(afi, safi, false)); - if (use_json) { - json_object_boolean_true_add( - json_afi_safi, "fBit"); - } else { - vty_out(vty, - "True\n"); - } + vty_out(vty, " F bit : "); + } - } else { + if (peer->nsf[afi][safi] + && CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV)) { - if (use_json) { - json_object_boolean_false_add( + if (use_json) { + json_object_boolean_true_add( json_afi_safi, "fBit"); - } else { - vty_out(vty, - "False\n"); - } - - } - - if (!use_json) - vty_out(vty, - " End-of-RIB Received : "); + } 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 (CHECK_FLAG(peer->af_sflags[afi][safi], - PEER_STATUS_EOR_RECEIVED)) { + if (!use_json) + vty_out(vty, " End-of-RIB Received : "); - if (use_json) { - json_object_boolean_true_add( + 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( + "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"); - } - } + "endOfRibRecv"); + else + vty_out(vty, "No\n"); + } - if (!use_json) - vty_out(vty, - " End-of-RIB Send : "); + 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( + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + if (use_json) { + json_object_boolean_true_add( json_endofrib_status, - "endOfRibSend"); + "endOfRibSend"); - PRINT_EOR_JSON(eor_flag); - } else { - vty_out(vty, "Yes\n"); - vty_out(vty, + 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( + PRINT_EOR(eor_flag); + } + } else { + if (use_json) { + json_object_boolean_false_add( json_endofrib_status, - "endOfRibSend"); - json_object_boolean_false_add( + "endOfRibSend"); + json_object_boolean_false_add( json_endofrib_status, - "endOfRibSentAfterUpdate"); - } else { - vty_out(vty, "No\n"); - vty_out(vty, + "endOfRibSentAfterUpdate"); + } else { + vty_out(vty, "No\n"); + vty_out(vty, " EoRSentAfterUpdate : "); - vty_out(vty, "No\n"); - } + 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) { + if (use_json) { + json_object_int_add(json_timer, + "stalePathTimer", + peer->bgp->stalepath_time); - json_object_int_add( + 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)) { + peer->t_gr_stale)); + } - json_object_int_add( + /* 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) { + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral + != NULL) { - json_object_int_add( + json_object_int_add( json_timer, "selectionDeferralTimerRemaining", thread_timer_remain_second( - peer->bgp - ->gr_info[afi][safi] - .t_select_deferral)); - } - - } else { + peer->bgp + ->gr_info[afi] + [safi] + .t_select_deferral)); + } + } else { + vty_out(vty, " Timers:\n"); - 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, - "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, + 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) { + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral + != NULL) { - vty_out(vty, "%*s", 6, ""); - vty_out(vty, + 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); + 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); + } } } } @@ -9800,39 +9770,30 @@ static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, json_timer = json_object_new_object(); - json_object_int_add(json_timer, - "configuredRestartTimer", - p->bgp->restart_time); + json_object_int_add(json_timer, "configuredRestartTimer", + p->bgp->restart_time); - json_object_int_add(json_timer, - "receivedRestartTimer", - p->v_gr_restart); + 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) - ); - } + 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, " 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, " 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)); - } + " Restart Time Remaining(sec) : %ld\n", + thread_timer_remain_second(p->t_gr_restart)); } } @@ -9849,14 +9810,14 @@ static void bgp_show_peer_gr_status(struct vty *vty, struct peer *p, if (p->conf_if) { if (use_json) - json_object_string_add(json, "neighborAddr", + json_object_string_add( + json, "neighborAddr", BGP_PEER_SU_UNSPEC(p) - ? "none" - : sockunion2str(&p->su, buf, - SU_ADDRSTRLEN)); + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); else - vty_out(vty, "BGP neighbor on %s: %s\n", - p->conf_if, + vty_out(vty, "BGP neighbor on %s: %s\n", p->conf_if, BGP_PEER_SU_UNSPEC(p) ? "none" : sockunion2str(&p->su, buf, @@ -12194,35 +12155,34 @@ static int bgp_show_neighbor_graceful_restart(struct vty *vty, if (type == show_all) { bgp_show_peer_gr_status(vty, peer, use_json, - json_neighbor); + json_neighbor); if (use_json) - json_object_object_add(json, - peer->host, json_neighbor); + 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->conf_if, conf_if)) + || (peer->hostname && !strcmp(peer->hostname, conf_if))) { find = 1; - bgp_show_peer_gr_status(vty, - peer, use_json, - json_neighbor); + 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); + 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 (use_json && find) + json_object_object_add(json, peer->host, + json_neighbor); } if (find) @@ -12231,14 +12191,14 @@ static int bgp_show_neighbor_graceful_restart(struct vty *vty, if (type == show_peer && !find) { if (use_json) - json_object_boolean_true_add(json, - "bgpNoSuchNeighbor"); + 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)); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); } else { vty_out(vty, "\n"); } @@ -12363,32 +12323,27 @@ static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty, bgp = bgp_get_default(); - if (bgp) { - - if (!use_json) { - bgp_show_global_graceful_restart_mode_vty(vty, bgp, - use_json, NULL); - } + if (!bgp) + return; - 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); - } + 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, @@ -12722,27 +12677,23 @@ static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, vty_out(vty, "\n%s", SHOW_GR_HEADER); - int bgp_global_gr_mode = bgp_global_gr_mode_get(bgp); + 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"); + vty_out(vty, "Global BGP GR Mode : Helper\n"); break; case GLOBAL_GR: - vty_out(vty, - "Global BGP GR Mode : Restart\n"); + vty_out(vty, "Global BGP GR Mode : Restart\n"); break; case GLOBAL_DISABLE: - vty_out(vty, - "Global BGP GR Mode : Disable\n"); + vty_out(vty, "Global BGP GR Mode : Disable\n"); break; case GLOBAL_INVALID: - default: vty_out(vty, "Global BGP GR Mode Invalid\n"); break; @@ -14606,23 +14557,25 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, addr); if (!CHECK_FLAG(peer->peer_gr_new_status_flag, - PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { if (CHECK_FLAG(peer->peer_gr_new_status_flag, - PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) { + 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)) { + } 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); + } 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); } } } @@ -16753,21 +16706,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> <deny|permit> AA:NN...", + "bgp community-list <(1-99)|standard WORD> [seq (1-4294967295)] <deny|permit> 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; @@ -16776,8 +16737,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); @@ -16792,13 +16753,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> <deny|permit> AA:NN...", + "no bgp community-list <(1-99)|standard WORD> [seq (1-4294967295)] <deny|permit> 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) @@ -16807,9 +16770,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); @@ -16828,7 +16796,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); @@ -16851,22 +16819,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> <deny|permit> AA:NN...", + "bgp community-list <(100-500)|expanded WORD> [seq (1-4294967295)] <deny|permit> 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; @@ -16875,8 +16851,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); @@ -16891,24 +16867,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> <deny|permit> AA:NN...", + "no bgp community-list <(100-500)|expanded WORD> [seq (1-4294967295)] <deny|permit> 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); @@ -16927,7 +16910,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); @@ -17054,7 +17037,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; @@ -17078,7 +17067,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(). */ @@ -17098,7 +17087,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); @@ -17122,7 +17117,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 @@ -17143,10 +17138,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) <deny|permit> AA:BB:CC...", + "bgp large-community-list (1-99) [seq (1-4294967295)] <deny|permit> 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) @@ -17157,10 +17154,12 @@ DEFUN (lcommunity_list_standard, DEFUN (lcommunity_list_expanded, bgp_lcommunity_list_expanded_cmd, - "bgp large-community-list (100-500) <deny|permit> LINE...", + "bgp large-community-list (100-500) [seq (1-4294967295)] <deny|permit> 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") @@ -17171,11 +17170,13 @@ DEFUN (lcommunity_list_expanded, DEFUN (lcommunity_list_name_standard, bgp_lcommunity_list_name_standard_cmd, - "bgp large-community-list standard WORD <deny|permit> AA:BB:CC...", + "bgp large-community-list standard WORD [seq (1-4294967295)] <deny|permit> 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) @@ -17186,11 +17187,13 @@ DEFUN (lcommunity_list_name_standard, DEFUN (lcommunity_list_name_expanded, bgp_lcommunity_list_name_expanded_cmd, - "bgp large-community-list expanded WORD <deny|permit> LINE...", + "bgp large-community-list expanded WORD [seq (1-4294967295)] <deny|permit> 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") @@ -17199,8 +17202,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 @@ -17213,6 +17216,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", @@ -17228,11 +17244,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) <deny|permit> AA:AA:NN...", + "no bgp large-community-list (1-99) [seq (1-4294967295)] <deny|permit> 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) @@ -17243,11 +17261,13 @@ DEFUN (no_lcommunity_list_standard, DEFUN (no_lcommunity_list_expanded, no_bgp_lcommunity_list_expanded_cmd, - "no bgp large-community-list (100-500) <deny|permit> LINE...", + "no bgp large-community-list (100-500) [seq (1-4294967295)] <deny|permit> 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") @@ -17258,12 +17278,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 <deny|permit> AA:AA:NN...", + "no bgp large-community-list standard WORD [seq (1-4294967295)] <deny|permit> 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) @@ -17274,12 +17296,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 <deny|permit> LINE...", + "no bgp large-community-list expanded WORD [seq (1-4294967295)] <deny|permit> 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") @@ -17374,12 +17398,14 @@ DEFUN (show_lcommunity_list_arg, DEFUN (extcommunity_list_standard, bgp_extcommunity_list_standard_cmd, - "bgp extcommunity-list <(1-99)|standard WORD> <deny|permit> AA:NN...", + "bgp extcommunity-list <(1-99)|standard WORD> [seq (1-4294967295)] <deny|permit> 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) @@ -17387,18 +17413,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); @@ -17413,12 +17445,14 @@ DEFUN (extcommunity_list_standard, DEFUN (extcommunity_list_name_expanded, bgp_extcommunity_list_name_expanded_cmd, - "bgp extcommunity-list <(100-500)|expanded WORD> <deny|permit> LINE...", + "bgp extcommunity-list <(100-500)|expanded WORD> [seq (1-4294967295)] <deny|permit> 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") @@ -17426,17 +17460,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); @@ -17451,13 +17491,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> <deny|permit> AA:NN...", + "no bgp extcommunity-list <(1-99)|standard WORD> [seq (1-4294967295)] <deny|permit> 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) @@ -17466,11 +17508,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 @@ -17487,7 +17534,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); @@ -17509,13 +17556,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> <deny|permit> LINE...", + "no bgp extcommunity-list <(100-500)|expanded WORD> [seq (1-4294967295)] <deny|permit> 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") @@ -17524,8 +17573,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); @@ -17545,7 +17600,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); @@ -17656,18 +17711,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++; } @@ -17677,18 +17736,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++; } @@ -17700,18 +17764,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++; } @@ -17754,7 +17824,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_zebra.c b/bgpd/bgp_zebra.c index d2b8f5d790..0e7a361edc 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -3101,43 +3101,6 @@ int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type) } -int 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; - default: - break; - } - - /* Put length at the first point of the stream */ - stream_putw_at(s, 0, stream_get_endp(s)); - - return zclient_send_message(zclient); -} - /* Send RIB stale timer update */ int bgp_zebra_stale_timer_update(struct bgp *bgp) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 7c2d60c515..2117baf835 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3320,14 +3320,13 @@ int bgp_delete(struct bgp *bgp) /* Delete the graceful restart info */ FOREACH_AFI_SAFI (afi, safi) { gr_info = &bgp->gr_info[afi][safi]; - if (gr_info) { - BGP_TIMER_OFF(gr_info->t_select_deferral); - gr_info->t_select_deferral = NULL; - BGP_TIMER_OFF(gr_info->t_route_select); - gr_info->t_route_select = NULL; - if (gr_info->route_list) - list_delete(&gr_info->route_list); - } + 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)) { diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 5ad822211b..77fcf909c8 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/zebra.rst b/doc/developer/zebra.rst index 74a8605bf2..e3526d1843 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/include/linux/seg6.h b/include/linux/seg6.h new file mode 100644 index 0000000000..329163e4a0 --- /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 <david.lebrun@uclouvain.be> + * + * + * 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 <linux/types.h> +#include <linux/in6.h> /* 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 0000000000..0c230524e0 --- /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 0000000000..3fb3412e1e --- /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 <linux/types.h> +#include <linux/seg6.h> + +#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 0000000000..3004e982c2 --- /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 <david.lebrun@uclouvain.be> + * + * + * 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 <linux/seg6.h> /* 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 0000000000..5312de80bc --- /dev/null +++ b/include/linux/seg6_local.h @@ -0,0 +1,80 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun <david.lebrun@uclouvain.be> + * + * + * 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 <linux/seg6.h> + +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 b1ca1be54f..86129c4d68 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/lib/agentx.c b/lib/agentx.c index 2c6a43d1a7..b479b5ea4c 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(); @@ -1093,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 d1a31ae35f..4d4376250f 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/srv6.c b/lib/srv6.c new file mode 100644 index 0000000000..be340f13f5 --- /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 0000000000..24c7ffc3a2 --- /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 <zebra.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#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 cb6fa7a3b8..d804d839db 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 \ @@ -193,6 +194,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/zclient.c b/lib/zclient.c index 7ddf0085de..d879063460 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3300,31 +3300,71 @@ void zclient_interface_set_master(struct zclient *client, zclient_send_message(client); } -/* Process capabilities message from zebra */ -int zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) +/* + * 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; + 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; + 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; - default: - break; + STREAM_GETL(s, api->vrf_id); + break; } - stream_failure: return 0; } diff --git a/lib/zclient.h b/lib/zclient.h index bbc70c3835..9a230d3f34 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -73,13 +73,17 @@ typedef uint16_t zebra_size_t; #define ZEBRA_FEC_REGISTER_LABEL 0x1 #define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x2 -/* Client Graceful Restart */ -#define ZEBRA_CLIENT_GR_CAPABILITIES 0x1 -#define ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE 0x2 -#define ZEBRA_CLIENT_ROUTE_UPDATE_PENDING 0x3 -#define ZEBRA_CLIENT_GR_DISABLE 0x4 -#define ZEBRA_CLIENT_RIB_STALE_TIME 0x5 -#define ZEBRA_CLIENT_GR_ENABLED(X) (X & ZEBRA_CLIENT_GR_CAPABILITIES) +/* 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; @@ -196,7 +200,7 @@ typedef enum { } 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 */ }; @@ -233,11 +237,11 @@ struct zclient_capabilities { /* Graceful Restart Capabilities message */ struct zapi_cap { - uint32_t cap; - uint32_t stale_removal_time; - afi_t afi; - safi_t safi; - vrf_id_t vrf_id; + 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. */ @@ -777,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) { @@ -794,7 +803,4 @@ extern void zclient_send_mlag_deregister(struct zclient *client); extern void zclient_send_mlag_data(struct zclient *client, struct stream *client_s); -extern int zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, - struct zapi_cap *api); -extern int zapi_capabilities_decode(struct stream *s, struct zapi_cap *api); #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/scripts/coccinelle/shorthand_operator.cocci b/scripts/coccinelle/shorthand_operator.cocci new file mode 100644 index 0000000000..f7019d4040 --- /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 0000000000..7da9e73933 --- /dev/null +++ b/scripts/coccinelle/void_no_return.cocci @@ -0,0 +1,9 @@ +@@ +identifier f; +expression e; +@@ +void f(...) { + ... +- return + e; +} diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index c2812aa47b..7cdd6ef84e 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 75f825e507..f23702d878 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); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index fe1c26a34f..b1c679e066 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 @@ -1628,7 +1638,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 +1953,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'; @@ -2504,8 +2514,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 77ed5a6caa..1d49de5410 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 4fa7a3c164..4d0e34561a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1775,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: @@ -2584,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) = { @@ -2668,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_gr.c b/zebra/zebra_gr.c new file mode 100644 index 0000000000..e8c7304f44 --- /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 <zebra.h> +#include <libgen.h> + +#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_nhg.c b/zebra/zebra_nhg.c index e05db5ffae..bb95e72382 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, @@ -1485,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) @@ -2028,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 c2e173e094..4d001944b7 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. @@ -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_router.h b/zebra/zebra_router.h index d8ad8a6864..59bd0e55f0 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 78001da170..86ec2ffef3 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); - list_delete(&list); +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; + + hash_walk(zrouter.nhgs_id, nhe_show_walker, &ctx); } static void if_nexthop_group_dump_vty(struct vty *vty, struct interface *ifp) @@ -1401,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; } @@ -1410,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", @@ -3121,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; } @@ -3492,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/zserv.c b/zebra/zserv.c index cca926f3b0..2a5352a1da 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 d8d82a52ec..77ea19202f 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 |
