diff --git a/.clang-format b/.clang-format index 21fe9d7c5e57..cc5a95baf6a9 100644 --- a/.clang-format +++ b/.clang-format @@ -57,3 +57,5 @@ ForEachMacros: - SUBGRP_FOREACH_ADJ_SAFE - AF_FOREACH - FOREACH_AFI_SAFI + # ospfd + - LSDB_LOOP diff --git a/COMMUNITY.md b/COMMUNITY.md index 7605b03201ae..0eee524d585b 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -68,6 +68,16 @@ Italicized lists are private. | _Security_ | security@lists.frrouting.org | | _Technical Steering Committee_ | tsc@lists.frrouting.org | +The Development list is used to discuss and document general issues +related to project development and governance. The public Slack +instance, frrouting.slack.com, and weekly technical meetings provide a +higher bandwidth channel for discussions. The results of such +discussions must be reflected in updates, as appropriate, to code (i.e., +merges), [github](https://github.com/FRRouting/frr/issues) tracked +issues, and for governance or process changes, updates to the +Development list and either this file or information posted at +[https://frrouting.org/](https://frrouting.org/). + ### Changelog diff --git a/Makefile.am b/Makefile.am index 2468dc733d42..579a9d868cb1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -55,6 +55,7 @@ include babeld/subdir.am include eigrpd/subdir.am include sharpd/subdir.am include pimd/subdir.am +include pbrd/subdir.am SUBDIRS = . @LIBRFP@ @RFPTEST@ \ @BGPD@ \ diff --git a/README b/README index bbad60087ba1..af14795a6a44 100644 --- a/README +++ b/README @@ -1,12 +1,14 @@ -FRRouting is free software that manages various IPv4 and IPv6 routing -protocols. +FRRouting is free software that implements and manages various IPv4 and IPv6 +routing protocols. -Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, -RIPv2, RIPng, PIM-SM/MSDP and LDP as well as very early support for IS-IS, -EIGRP and NHRP. +Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, RIPv2, RIPng, +IS-IS, PIM-SM/MSDP, LDP and Babel as well as very early support for EIGRP and +NHRP. See the file REPORTING-BUGS to report bugs. +See COMMUNITY.md for information on contributing. + Free RRRouting is free software. See the file COPYING for copying conditions. Public email discussion can be found at https://lists.frrouting.org/listinfo diff --git a/babeld/babeld.c b/babeld/babeld.c index 00367612c624..20dd098f3302 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -229,7 +229,7 @@ babel_get_myid(void) int ifindex = if_nametoindex(ifp->name); if(ifindex > 0) { unsigned char eui[8]; - rc = if_eui64(ifp->name, ifindex, eui); + rc = if_eui64(ifindex, eui); if(rc < 0) continue; memcpy(myid, eui, 8); @@ -245,7 +245,7 @@ babel_get_myid(void) ifname = if_indextoname(i, buf); if(ifname == NULL) continue; - rc = if_eui64(ifname, i, eui); + rc = if_eui64(i, eui); if(rc < 0) continue; memcpy(myid, eui, 8); diff --git a/babeld/kernel.c b/babeld/kernel.c index 3343ca2e9538..6b673c487c53 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -166,6 +166,7 @@ zebra_route(int add, int family, const unsigned char *pref, unsigned short plen, api.type = ZEBRA_ROUTE_BABEL; api.safi = SAFI_UNICAST; api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.prefix = quagga_prefix; if(metric >= KERNEL_INFINITY) { @@ -203,7 +204,7 @@ zebra_route(int add, int family, const unsigned char *pref, unsigned short plen, } int -if_eui64(char *ifname, int ifindex, unsigned char *eui) +if_eui64(int ifindex, unsigned char *eui) { struct interface *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); if (ifp == NULL) { diff --git a/babeld/kernel.h b/babeld/kernel.h index 7ade26dc3655..eb1e793279c5 100644 --- a/babeld/kernel.h +++ b/babeld/kernel.h @@ -40,7 +40,7 @@ int kernel_route(int operation, const unsigned char *dest, unsigned short plen, const unsigned char *gate, int ifindex, unsigned int metric, const unsigned char *newgate, int newifindex, unsigned int newmetric); -int if_eui64(char *ifname, int ifindex, unsigned char *eui); +int if_eui64(int ifindex, unsigned char *eui); int gettime(struct timeval *tv); int read_random_bytes(void *buf, size_t len); diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index fa1dcbb7626c..5e08f82774df 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -85,7 +85,8 @@ libbgp_a_SOURCES = \ bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \ bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \ bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \ - bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c + bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \ + bgp_keepalives.c bgp_io.c noinst_HEADERS = \ bgp_memory.h \ @@ -97,7 +98,8 @@ noinst_HEADERS = \ bgp_advertise.h bgp_vty.h bgp_mpath.h bgp_nht.h \ bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \ $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \ - bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h + bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \ + bgp_io.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ @@ -131,6 +133,7 @@ dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 \ bgpd.conf.vnc.sample bgp_vty.o: bgp_vty_clippy.c +bgp_route.o: bgp_route_clippy.c EXTRA_DIST = BGP4-MIB.txt diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 840cc35751f0..29b6ca6bfa78 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -246,8 +246,6 @@ void bgp_sync_init(struct peer *peer) BGP_ADV_FIFO_INIT(&sync->withdraw); BGP_ADV_FIFO_INIT(&sync->withdraw_low); peer->sync[afi][safi] = sync; - peer->hash[afi][safi] = hash_create(baa_hash_key, baa_hash_cmp, - "BGP Sync Hash"); } } @@ -260,9 +258,5 @@ void bgp_sync_delete(struct peer *peer) if (peer->sync[afi][safi]) XFREE(MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]); peer->sync[afi][safi] = NULL; - - if (peer->hash[afi][safi]) - hash_free(peer->hash[afi][safi]); - peer->hash[afi][safi] = NULL; } } diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 6ddb2ec8a7de..206d91bfe911 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -74,6 +74,7 @@ static const struct message attr_str[] = { {BGP_ATTR_AS4_PATH, "AS4_PATH"}, {BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR"}, {BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"}, + {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"}, {BGP_ATTR_ENCAP, "ENCAP"}, #if ENABLE_BGP_VNC {BGP_ATTR_VNC, "VNC"}, @@ -492,19 +493,13 @@ unsigned int attrhash_key_make(void *p) const struct attr *attr = (struct attr *)p; uint32_t key = 0; #define MIX(val) key = jhash_1word(val, key) +#define MIX3(a, b, c) key = jhash_3words((a), (b), (c), key) - MIX(attr->origin); - MIX(attr->nexthop.s_addr); - MIX(attr->med); - MIX(attr->local_pref); - MIX(attr->aggregator_as); - MIX(attr->aggregator_addr.s_addr); - MIX(attr->weight); - MIX(attr->mp_nexthop_global_in.s_addr); - MIX(attr->originator_id.s_addr); - MIX(attr->tag); - MIX(attr->label); - MIX(attr->label_index); + MIX3(attr->origin, attr->nexthop.s_addr, attr->med); + MIX3(attr->local_pref, attr->aggregator_as, attr->aggregator_addr.s_addr); + MIX3(attr->weight, attr->mp_nexthop_global_in.s_addr, + attr->originator_id.s_addr); + MIX3(attr->tag, attr->label, attr->label_index); if (attr->aspath) MIX(aspath_key_make(attr->aspath)); @@ -550,12 +545,6 @@ int attrhash_cmp(const void *p1, const void *p2) && attr1->tag == attr2->tag && attr1->label_index == attr2->label_index && attr1->mp_nexthop_len == attr2->mp_nexthop_len - && IPV6_ADDR_SAME(&attr1->mp_nexthop_global, - &attr2->mp_nexthop_global) - && IPV6_ADDR_SAME(&attr1->mp_nexthop_local, - &attr2->mp_nexthop_local) - && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, - &attr2->mp_nexthop_global_in) && attr1->ecommunity == attr2->ecommunity && attr1->lcommunity == attr2->lcommunity && attr1->cluster == attr2->cluster @@ -565,6 +554,12 @@ int attrhash_cmp(const void *p1, const void *p2) #if ENABLE_BGP_VNC && encap_same(attr1->vnc_subtlvs, attr2->vnc_subtlvs) #endif + && IPV6_ADDR_SAME(&attr1->mp_nexthop_global, + &attr2->mp_nexthop_global) + && IPV6_ADDR_SAME(&attr1->mp_nexthop_local, + &attr2->mp_nexthop_local) + && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, + &attr2->mp_nexthop_global_in) && IPV4_ADDR_SAME(&attr1->originator_id, &attr2->originator_id) && overlay_index_same(attr1, attr2)) @@ -1040,6 +1035,8 @@ const u_int8_t attr_flags_values[] = { BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_AS4_AGGREGATOR] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_PMSI_TUNNEL] = + BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_PREFIX_SID] = @@ -1156,7 +1153,7 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) * peer with AS4 => will get 4Byte ASnums * otherwise, will get 16 Bit */ - attr->aspath = aspath_parse(peer->ibuf, length, + attr->aspath = aspath_parse(peer->curr, length, CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)); /* In case of IBGP, length will be zero. */ @@ -1230,7 +1227,7 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, struct attr *const attr = args->attr; const bgp_size_t length = args->length; - *as4_path = aspath_parse(peer->ibuf, length, 1); + *as4_path = aspath_parse(peer->curr, length, 1); /* In case of IBGP, length will be zero. */ if (!*as4_path) { @@ -1271,7 +1268,7 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) logged locally (this is implemented somewhere else). The UPDATE message gets ignored in any of these cases. */ - nexthop_n = stream_get_ipv4(peer->ibuf); + nexthop_n = stream_get_ipv4(peer->curr); nexthop_h = ntohl(nexthop_n); if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) || IPV4_CLASS_DE(nexthop_h)) @@ -1307,7 +1304,7 @@ static bgp_attr_parse_ret_t bgp_attr_med(struct bgp_attr_parser_args *args) args->total); } - attr->med = stream_getl(peer->ibuf); + attr->med = stream_getl(peer->curr); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); @@ -1333,11 +1330,11 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args) external peer, then this attribute MUST be ignored by the receiving speaker. */ if (peer->sort == BGP_PEER_EBGP) { - stream_forward_getp(peer->ibuf, length); + stream_forward_getp(peer->curr, length); return BGP_ATTR_PARSE_PROCEED; } - attr->local_pref = stream_getl(peer->ibuf); + attr->local_pref = stream_getl(peer->curr); /* Set the local-pref flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); @@ -1386,10 +1383,10 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) } if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) - attr->aggregator_as = stream_getl(peer->ibuf); + attr->aggregator_as = stream_getl(peer->curr); else - attr->aggregator_as = stream_getw(peer->ibuf); - attr->aggregator_addr.s_addr = stream_get_ipv4(peer->ibuf); + attr->aggregator_as = stream_getw(peer->curr); + attr->aggregator_addr.s_addr = stream_get_ipv4(peer->curr); /* Set atomic aggregate flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); @@ -1413,8 +1410,8 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, 0); } - *as4_aggregator_as = stream_getl(peer->ibuf); - as4_aggregator_addr->s_addr = stream_get_ipv4(peer->ibuf); + *as4_aggregator_as = stream_getl(peer->curr); + as4_aggregator_addr->s_addr = stream_get_ipv4(peer->curr); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR); @@ -1540,10 +1537,10 @@ bgp_attr_community(struct bgp_attr_parser_args *args) } attr->community = - community_parse((u_int32_t *)stream_pnt(peer->ibuf), length); + community_parse((u_int32_t *)stream_pnt(peer->curr), length); /* XXX: fix community_parse to use stream API and remove this */ - stream_forward_getp(peer->ibuf, length); + stream_forward_getp(peer->curr, length); if (!attr->community) return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, @@ -1570,7 +1567,7 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args) args->total); } - attr->originator_id.s_addr = stream_get_ipv4(peer->ibuf); + attr->originator_id.s_addr = stream_get_ipv4(peer->curr); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID); @@ -1594,10 +1591,10 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) } attr->cluster = - cluster_parse((struct in_addr *)stream_pnt(peer->ibuf), length); + cluster_parse((struct in_addr *)stream_pnt(peer->curr), length); /* XXX: Fix cluster_parse to use stream API and then remove this */ - stream_forward_getp(peer->ibuf, length); + stream_forward_getp(peer->curr, length); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST); @@ -1667,7 +1664,11 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, /* * NOTE: intentional fall through * - for consistency in rx processing + * + * The following comment is to signal GCC this intention + * and supress the warning */ + /* FALLTHRU */ case BGP_ATTR_NHLEN_IPV4: stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN); /* Probably needed for RFC 2283 */ @@ -1778,7 +1779,7 @@ int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args, struct attr *const attr = args->attr; const bgp_size_t length = args->length; - s = peer->ibuf; + s = peer->curr; #define BGP_MP_UNREACH_MIN_SIZE 3 if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE)) @@ -1832,9 +1833,9 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args) } attr->lcommunity = - lcommunity_parse((u_int8_t *)stream_pnt(peer->ibuf), length); + lcommunity_parse((u_int8_t *)stream_pnt(peer->curr), length); /* XXX: fix ecommunity_parse to use stream API */ - stream_forward_getp(peer->ibuf, length); + stream_forward_getp(peer->curr, length); if (!attr->lcommunity) return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, @@ -1861,9 +1862,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) } attr->ecommunity = - ecommunity_parse((u_int8_t *)stream_pnt(peer->ibuf), length); + ecommunity_parse((u_int8_t *)stream_pnt(peer->curr), length); /* XXX: fix ecommunity_parse to use stream API */ - stream_forward_getp(peer->ibuf, length); + stream_forward_getp(peer->curr, length); if (!attr->ecommunity) return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, @@ -1875,6 +1876,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky); attr->sticky = sticky; + /* Extract the Rmac, if any */ + bgp_attr_rmac(attr, &attr->rmac); + return BGP_ATTR_PARSE_PROCEED; } @@ -1957,7 +1961,7 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ + sublength); tlv->type = subtype; tlv->length = sublength; - stream_get(tlv->value, peer->ibuf, sublength); + stream_get(tlv->value, peer->curr, sublength); length -= sublength; /* attach tlv to encap chain */ @@ -2025,8 +2029,8 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); - type = stream_getc(peer->ibuf); - length = stream_getw(peer->ibuf); + type = stream_getc(peer->curr); + length = stream_getw(peer->curr); if (type == BGP_PREFIX_SID_LABEL_INDEX) { if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { @@ -2039,11 +2043,11 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, } /* Ignore flags and reserved */ - stream_getc(peer->ibuf); - stream_getw(peer->ibuf); + stream_getc(peer->curr); + stream_getw(peer->curr); /* Fetch the label index and see if it is valid. */ - label_index = stream_getl(peer->ibuf); + label_index = stream_getl(peer->curr); if (label_index == BGP_INVALID_LABEL_INDEX) return bgp_attr_malformed( args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, @@ -2074,16 +2078,16 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, } /* Ignore reserved */ - stream_getc(peer->ibuf); - stream_getw(peer->ibuf); + stream_getc(peer->curr); + stream_getw(peer->curr); - stream_get(&ipv6_sid, peer->ibuf, 16); + stream_get(&ipv6_sid, peer->curr, 16); } /* Placeholder code for the Originator SRGB type */ else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) { /* Ignore flags */ - stream_getw(peer->ibuf); + stream_getw(peer->curr); length -= 2; @@ -2099,8 +2103,8 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, srgb_count = length / BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH; for (int i = 0; i < srgb_count; i++) { - stream_get(&srgb_base, peer->ibuf, 3); - stream_get(&srgb_range, peer->ibuf, 3); + stream_get(&srgb_base, peer->curr, 3); + stream_get(&srgb_range, peer->curr, 3); } } @@ -2125,7 +2129,7 @@ static bgp_attr_parse_ret_t bgp_attr_unknown(struct bgp_attr_parser_args *args) peer->host, type, length); /* Forward read pointer of input stream. */ - stream_forward_getp(peer->ibuf, length); + stream_forward_getp(peer->curr, length); /* If any of the mandatory well-known attributes are not recognized, then the Error Subcode is set to Unrecognized Well-known @@ -2247,7 +2251,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, "%s: error BGP attribute length %lu is smaller than min len", peer->host, (unsigned long)(endp - - STREAM_PNT(BGP_INPUT(peer)))); + - stream_pnt(BGP_INPUT(peer)))); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); @@ -2269,7 +2273,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, "%s: Extended length set, but just %lu bytes of attr header", peer->host, (unsigned long)(endp - - STREAM_PNT(BGP_INPUT(peer)))); + - stream_pnt(BGP_INPUT(peer)))); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); @@ -3248,6 +3252,17 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, #endif } + /* PMSI Tunnel */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_PMSI_TUNNEL); + stream_putc(s, 9); // Length + stream_putc(s, 0); // Flags + stream_putc(s, 6); // Tunnel type: Ingress Replication (6) + stream_put(s, &(attr->label), BGP_LABEL_BYTES); // MPLS Label / VXLAN VNI + stream_put_ipv4(s, attr->nexthop.s_addr); // Unicast tunnel endpoint IP address + } + /* Unknown transit attribute. */ if (attr->transit) stream_put(s, attr->transit->val, attr->transit->length); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index f694f01adb10..1de1bee0f9d6 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -182,6 +182,9 @@ struct attr { /* EVPN MAC Mobility sequence number, if any. */ u_int32_t mm_seqnum; + + /* EVPN local router-mac */ + struct ethaddr rmac; }; /* rmap_change_flags definition */ diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 300c9ddb506a..eaa4e329d455 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -105,6 +105,35 @@ char *ecom_mac2str(char *ecom_mac) return prefix_mac2str((struct ethaddr *)en, NULL, 0); } +/* Fetch router-mac from extended community */ +void bgp_attr_rmac(struct attr *attr, + struct ethaddr *rmac) +{ + int i = 0; + struct ecommunity *ecom; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return; + + /* If there is a router mac extended community, set RMAC in attr */ + for (i = 0; i < ecom->size; i++) { + u_char *pnt = NULL; + u_char type = 0; + u_char sub_type = 0; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + + if (!(type == ECOMMUNITY_ENCODE_EVPN && + sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC)) + continue; + + memcpy(rmac, pnt, ETH_ALEN); + } +} + /* * Fetch and return the sequence number from MAC Mobility extended * community, if present, else 0. diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 15d9e126e404..8b55cb30020b 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -59,7 +59,7 @@ extern void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac); extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag, struct prefix *dst); - +extern void bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac); extern u_int32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, u_char *sticky); diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 2e277bfa5ff6..ce46b21f03d1 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -302,13 +302,13 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient, prefix2str(&dp, buf[0], sizeof(buf[0])); if (ifp) { zlog_debug( - "Zebra: vrf %d interface %s bfd destination %s %s", + "Zebra: vrf %u interface %s bfd destination %s %s", vrf_id, ifp->name, buf[0], bfd_get_status_str(status)); } else { prefix2str(&sp, buf[1], sizeof(buf[1])); zlog_debug( - "Zebra: vrf %d source %s bfd destination %s %s", + "Zebra: vrf %u source %s bfd destination %s %s", vrf_id, buf[1], buf[0], bfd_get_status_str(status)); } diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index e19f516505f0..9caf38d569b3 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -695,19 +695,19 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (type == ECOMMUNITY_ENCODE_EVPN) { if (filter == ECOMMUNITY_ROUTE_TARGET) continue; - if (*pnt == ECOMMUNITY_SITE_ORIGIN) { - char macaddr[6]; + if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) { + struct ethaddr rmac; pnt++; - memcpy(&macaddr, pnt, 6); + memcpy(&rmac, pnt, ETH_ALEN); len = sprintf( str_buf + str_pnt, - "EVPN:%02x:%02x:%02x:%02x:%02x:%02x", - (uint8_t)macaddr[0], - (uint8_t)macaddr[1], - (uint8_t)macaddr[2], - (uint8_t)macaddr[3], - (uint8_t)macaddr[4], - (uint8_t)macaddr[5]); + "Rmac:%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)rmac.octet[0], + (uint8_t)rmac.octet[1], + (uint8_t)rmac.octet[2], + (uint8_t)rmac.octet[3], + (uint8_t)rmac.octet[4], + (uint8_t)rmac.octet[5]); } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { u_int32_t seqnum; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 182a6c64f2b3..12cc425bd36e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -92,6 +92,131 @@ static int vni_hash_cmp(const void *p1, const void *p2) return (vpn1->vni == vpn2->vni); } +/* + * Make vrf import route target hash key. + */ +static unsigned int vrf_import_rt_hash_key_make(void *p) +{ + struct vrf_irt_node *irt = p; + char *pnt = irt->rt.val; + unsigned int key = 0; + int c = 0; + + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + key += pnt[c + 6]; + key += pnt[c + 7]; + + return key; +} + +/* + * Comparison function for vrf import rt hash + */ +static int vrf_import_rt_hash_cmp(const void *p1, const void *p2) +{ + const struct vrf_irt_node *irt1 = p1; + const struct vrf_irt_node *irt2 = p2; + + if (irt1 == NULL && irt2 == NULL) + return 1; + + if (irt1 == NULL || irt2 == NULL) + return 0; + + return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0); +} + +/* + * Create a new vrf import_rt in default instance + */ +static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt) +{ + struct bgp *bgp_def = NULL; + struct vrf_irt_node *irt; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt new - def instance not created yet"); + return NULL; + } + + irt = XCALLOC(MTYPE_BGP_EVPN_VRF_IMPORT_RT, + sizeof(struct vrf_irt_node)); + if (!irt) + return NULL; + + irt->rt = *rt; + irt->vrfs = list_new(); + + /* Add to hash */ + if (!hash_get(bgp_def->vrf_import_rt_hash, irt, hash_alloc_intern)) { + XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); + return NULL; + } + + return irt; +} + +/* + * Free the vrf import rt node + */ +static void vrf_import_rt_free(struct vrf_irt_node *irt) +{ + struct bgp *bgp_def = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt free - def instance not created yet"); + return; + } + + hash_release(bgp_def->vrf_import_rt_hash, irt); + XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); +} + +/* + * Function to lookup Import RT node - used to map a RT to set of + * VNIs importing routes with that RT. + */ +static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt) +{ + struct bgp *bgp_def = NULL; + struct vrf_irt_node *irt; + struct vrf_irt_node tmp; + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("vrf import rt lookup - def instance not created yet"); + return NULL; + } + + memset(&tmp, 0, sizeof(struct vrf_irt_node)); + memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE); + irt = hash_lookup(bgp_def->vrf_import_rt_hash, &tmp); + return irt; +} + +/* + * Is specified VRF present on the RT's list of "importing" VRFs? + */ +static int is_vrf_present_in_irt_vrfs(struct list *vrfs, + struct bgp *bgp_vrf) +{ + struct listnode *node = NULL, *nnode = NULL; + struct bgp *tmp_bgp_vrf = NULL; + + for (ALL_LIST_ELEMENTS(vrfs, node, nnode, tmp_bgp_vrf)) { + if (tmp_bgp_vrf == bgp_vrf) + return 1; + } + return 0; +} + /* * Make import route target hash key. */ @@ -246,6 +371,57 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst, } } +/* + * Map one RT to specified VRF. + * bgp_vrf = BGP vrf instance + */ +static void map_vrf_to_rt(struct bgp *bgp_vrf, + struct ecommunity_val *eval) +{ + struct vrf_irt_node *irt = NULL; + struct ecommunity_val eval_tmp; + + /* If using "automatic" RT, + * we only care about the local-admin sub-field. + * This is to facilitate using L3VNI(VRF-VNI) + * as the RT for EBGP peering too. + */ + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_IMPORT_RT_CFGD)) + mask_ecom_global_admin(&eval_tmp, eval); + + irt = lookup_vrf_import_rt(&eval_tmp); + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + /* Already mapped. */ + return; + + if (!irt) { + irt = vrf_import_rt_new(&eval_tmp); + assert(irt); + } + + /* Add VRF to the list for this RT. */ + listnode_add(irt->vrfs, bgp_vrf); +} + +/* + * Unmap specified VRF from specified RT. If there are no other + * VRFs for this RT, then the RT hash is deleted. + * bgp_vrf: BGP VRF specific instance + */ +static void unmap_vrf_from_rt(struct bgp *bgp_vrf, + struct vrf_irt_node *irt) +{ + /* Delete VRF from list for this RT. */ + listnode_delete(irt->vrfs, bgp_vrf); + if (!listnode_head(irt->vrfs)) { + list_delete_and_null(&irt->vrfs); + vrf_import_rt_free(irt); + } +} + /* * Map one RT to specified VNI. */ @@ -301,12 +477,12 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn, * VNIs but the same across routers (in the same AS) for a particular * VNI. */ -static void form_auto_rt(struct bgp *bgp, struct bgpevpn *vpn, struct list *rtl) +static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) { struct ecommunity_val eval; struct ecommunity *ecomadd; - encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval); + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecomadd = ecommunity_new(); ecommunity_add_val(ecomadd, &eval); @@ -425,20 +601,76 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, return zclient_send_message(zclient); } +/* + * Build extended communities for EVPN prefix route. + */ +static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, + struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity ecom_rmac; + struct ecommunity_val eval; + struct ecommunity_val eval_rmac; + bgp_encap_types tnl_type; + struct listnode *node, *nnode; + struct ecommunity *ecom; + struct list *vrf_export_rtl = NULL; + + /* Encap */ + tnl_type = BGP_ENCAP_TYPE_VXLAN; + memset(&ecom_encap, 0, sizeof(ecom_encap)); + encode_encap_extcomm(tnl_type, &eval); + ecom_encap.size = 1; + ecom_encap.val = (u_int8_t *)eval.val; + + /* Add Encap */ + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* Add the export RTs for L3VNI/VRF */ + vrf_export_rtl = bgp_vrf->vrf_export_rtl; + if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { + for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom)) + attr->ecommunity = ecommunity_merge(attr->ecommunity, + ecom); + } + + /* add the router mac extended community */ + if (!is_zero_mac(&attr->rmac)) { + memset(&ecom_rmac, 0, sizeof(ecom_rmac)); + encode_rmac_extcomm(&eval_rmac, &attr->rmac); + ecom_rmac.size = 1; + ecom_rmac.val = (uint8_t *)eval_rmac.val; + attr->ecommunity = ecommunity_merge(attr->ecommunity, + &ecom_rmac); + } + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + /* * Build extended communities for EVPN route. RT and ENCAP are * applicable to all routes. + * TODO: currently kernel doesnt support ipv6 routes with ipv4 nexthops. + * This means that we can't do symmetric routing for ipv6 hosts routes + * in the same way as ipv4 host routes. + * We wont attach l3-vni related RTs for ipv6 routes. + * For now, We will only adevrtise ipv4 host routes + * with L3-VNI related ext-comm. */ -static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr) +static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, + afi_t afi) { struct ecommunity ecom_encap; struct ecommunity ecom_sticky; + struct ecommunity ecom_rmac; struct ecommunity_val eval; struct ecommunity_val eval_sticky; + struct ecommunity_val eval_rmac; bgp_encap_types tnl_type; struct listnode *node, *nnode; struct ecommunity *ecom; u_int32_t seqnum; + struct list *vrf_export_rtl = NULL; /* Encap */ tnl_type = BGP_ENCAP_TYPE_VXLAN; @@ -450,10 +682,24 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr) /* Add Encap */ attr->ecommunity = ecommunity_dup(&ecom_encap); - /* Add the export RTs */ + /* Add the export RTs for L2VNI */ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom); + /* Add the export RTs for L3VNI - currently only supported for IPV4 host + * routes + */ + if (afi == AFI_IP) { + vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn); + if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) { + for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, + ecom)) + attr->ecommunity = + ecommunity_merge(attr->ecommunity, + ecom); + } + } + if (attr->sticky) { seqnum = 0; memset(&ecom_sticky, 0, sizeof(ecom_sticky)); @@ -464,6 +710,15 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr) ecommunity_merge(attr->ecommunity, &ecom_sticky); } + if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) { + memset(&ecom_rmac, 0, sizeof(ecom_rmac)); + encode_rmac_extcomm(&eval_rmac, &attr->rmac); + ecom_rmac.size = 1; + ecom_rmac.val = (uint8_t *)eval_rmac.val; + attr->ecommunity = ecommunity_merge(attr->ecommunity, + &ecom_rmac); + } + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); } @@ -699,6 +954,123 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn) return local_ri->attr->sticky; } +static int update_evpn_type5_route_entry(struct bgp *bgp_def, + struct bgp *bgp_vrf, afi_t afi, + safi_t safi, struct bgp_node *rn, + struct attr *attr, int *route_changed) +{ + struct attr *attr_new = NULL; + struct bgp_info *ri = NULL; + mpls_label_t label = MPLS_INVALID_LABEL; + struct bgp_info *local_ri = NULL; + struct bgp_info *tmp_ri = NULL; + + *route_changed = 0; + /* locate the local route entry if any */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) { + if (tmp_ri->peer == bgp_def->peer_self + && tmp_ri->type == ZEBRA_ROUTE_BGP + && tmp_ri->sub_type == BGP_ROUTE_STATIC) + local_ri = tmp_ri; + } + + /* create a new route entry if one doesnt exist. + Otherwise see if route attr has changed + */ + if (!local_ri) { + + /* route has changed as this is the first entry */ + *route_changed = 1; + + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); + + /* create the route info from attribute */ + ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, + bgp_def->peer_self, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + + /* L3-VNI goes in the label2 field */ + bgp_info_extra_get(ri); + vni2label(bgp_vrf->l3vni, &label); + memcpy(&ri->extra->label2, &label, BGP_LABEL_BYTES); + + /* add the route entry to route node*/ + bgp_info_add(rn, ri); + } else { + + tmp_ri = local_ri; + if (!attrhash_cmp(tmp_ri->attr, attr)) { + + /* attribute changed */ + *route_changed = 1; + + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); + bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED); + + /* Restore route, if needed. */ + if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, tmp_ri); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&tmp_ri->attr); + tmp_ri->attr = attr_new; + tmp_ri->uptime = bgp_clock(); + } + } + return 0; +} + +/* update evpn type-5 route entry */ +static int update_evpn_type5_route(struct bgp *bgp_vrf, + struct prefix_evpn *evp) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct attr attr; + struct bgp_node *rn = NULL; + struct bgp *bgp_def = NULL; + int route_changed = 0; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* build path attribute for this route */ + memset(&attr, 0, sizeof(struct attr)); + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = bgp_vrf->originator_ip; + attr.mp_nexthop_global_in = bgp_vrf->originator_ip; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); + + /* Setup RT and encap extended community */ + build_evpn_type5_route_extcomm(bgp_vrf, &attr); + + /* get the route node in global table */ + rn = bgp_afi_node_get(bgp_def->rib[afi][safi], afi, safi, + (struct prefix *)evp, + &bgp_vrf->vrf_prd); + assert(rn); + + /* create or update the route entry within the route node */ + update_evpn_type5_route_entry(bgp_def, bgp_vrf, + afi, safi, + rn, &attr, &route_changed); + + /* schedule for processing and unlock node */ + if (route_changed) { + bgp_process(bgp_def, rn, afi, safi); + bgp_unlock_node(rn); + } + + /* uninten temporary */ + aspath_unintern(&attr.aspath); + return 0; +} + /* * Create or update EVPN route entry. This could be in the VNI route table * or the global route table. @@ -834,9 +1206,14 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, attr.mp_nexthop_global_in = vpn->originator_ip; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; attr.sticky = CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? 1 : 0; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL); + bgpevpn_get_rmac(vpn, &attr.rmac); + vni2label(vpn->vni, &(attr.label)); /* Set up RT and ENCAP extended community. */ - build_evpn_route_extcomm(vpn, &attr); + build_evpn_route_extcomm(vpn, &attr, + IS_EVPN_PREFIX_IPADDR_V4(p) ? + AFI_IP : AFI_IP6); /* First, create (or fetch) route node within the VNI. */ /* NOTE: There is no RD here. */ @@ -878,6 +1255,58 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* Delete EVPN type5 route entry from global table */ +static void delete_evpn_type5_route_entry(struct bgp *bgp_def, + struct bgp *bgp_vrf, + afi_t afi, safi_t safi, + struct bgp_node *rn, + struct bgp_info **ri) +{ + struct bgp_info *tmp_ri = NULL; + + *ri = NULL; + + /* find the matching route entry */ + for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) + if (tmp_ri->peer == bgp_def->peer_self + && tmp_ri->type == ZEBRA_ROUTE_BGP + && tmp_ri->sub_type == BGP_ROUTE_STATIC) + break; + + *ri = tmp_ri; + + /* Mark route for delete. */ + if (tmp_ri) + bgp_info_delete(rn, tmp_ri); +} + +/* Delete EVPN type5 route */ +static int delete_evpn_type5_route(struct bgp *bgp_vrf, + struct prefix_evpn *evp) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_node *rn = NULL; + struct bgp_info *ri = NULL; + struct bgp *bgp_def = NULL; /* default bgp instance */ + + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* locate the global route entry for this type-5 prefix */ + rn = bgp_afi_node_lookup(bgp_def->rib[afi][safi], afi, safi, + (struct prefix *)evp, &bgp_vrf->vrf_prd); + if (!rn) + return 0; + + delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri); + if (ri) + bgp_process(bgp_def, rn, afi, safi); + bgp_unlock_node(rn); + return 0; +} + /* * Delete EVPN route entry. This could be in the VNI route table * or the global route table. @@ -965,12 +1394,16 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) struct bgp_info *ri; struct attr attr; struct attr attr_sticky; + struct attr attr_ip6; + struct attr attr_sticky_ip6; struct attr *attr_new; afi = AFI_L2VPN; safi = SAFI_EVPN; memset(&attr, 0, sizeof(struct attr)); memset(&attr_sticky, 0, sizeof(struct attr)); + memset(&attr_ip6, 0, sizeof(struct attr)); + memset(&attr_sticky_ip6, 0, sizeof(struct attr)); /* Build path-attribute - all type-2 routes for this VNI will share the * same path attribute. @@ -980,14 +1413,29 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) attr.nexthop = vpn->originator_ip; attr.mp_nexthop_global_in = vpn->originator_ip; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + bgpevpn_get_rmac(vpn, &attr.rmac); attr_sticky.nexthop = vpn->originator_ip; attr_sticky.mp_nexthop_global_in = vpn->originator_ip; attr_sticky.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; attr_sticky.sticky = 1; + bgpevpn_get_rmac(vpn, &attr_sticky.rmac); + bgp_attr_default_set(&attr_ip6, BGP_ORIGIN_IGP); + bgp_attr_default_set(&attr_sticky_ip6, BGP_ORIGIN_IGP); + attr_ip6.nexthop = vpn->originator_ip; + attr_ip6.mp_nexthop_global_in = vpn->originator_ip; + attr_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + bgpevpn_get_rmac(vpn, &attr_ip6.rmac); + attr_sticky_ip6.nexthop = vpn->originator_ip; + attr_sticky_ip6.mp_nexthop_global_in = vpn->originator_ip; + attr_sticky_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + attr_sticky_ip6.sticky = 1; + bgpevpn_get_rmac(vpn, &attr_sticky_ip6.rmac); /* Set up RT, ENCAP and sticky MAC extended community. */ - build_evpn_route_extcomm(vpn, &attr); - build_evpn_route_extcomm(vpn, &attr_sticky); + build_evpn_route_extcomm(vpn, &attr, AFI_IP); + build_evpn_route_extcomm(vpn, &attr_sticky, AFI_IP); + build_evpn_route_extcomm(vpn, &attr_ip6, AFI_IP6); + build_evpn_route_extcomm(vpn, &attr_sticky_ip6, AFI_IP6); /* Walk this VNI's route table and update local type-2 routes. For any * routes updated, update corresponding entry in the global table too. @@ -1001,12 +1449,24 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - if (evpn_route_is_sticky(bgp, rn)) - update_evpn_route_entry(bgp, vpn, afi, safi, rn, - &attr_sticky, 0, 1, &ri, 0); - else - update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr, - 0, 1, &ri, 0); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + if (evpn_route_is_sticky(bgp, rn)) + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr_sticky, 0, 1, + &ri, 0); + else + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr, 0, 1, &ri, 0); + } else { + if (evpn_route_is_sticky(bgp, rn)) + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr_sticky_ip6, 0, 1, + &ri, 0); + else + update_evpn_route_entry(bgp, vpn, afi, safi, rn, + &attr_ip6, 0, 1, + &ri, 0); + } /* If a local route exists for this prefix, we need to update * the global routing table too. @@ -1219,6 +1679,100 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return update_routes_for_vni(bgp, vpn); } +/* + * Install route entry into the VRF routing table and invoke route selection. + */ +static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, + struct prefix_evpn *evp, + struct bgp_info *parent_ri) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct attr *attr_new; + int ret = 0; + struct prefix p; + struct prefix *pp = &p; + afi_t afi = 0; + safi_t safi = 0; + char buf[PREFIX_STRLEN]; + char buf1[PREFIX_STRLEN]; + + memset(pp, 0, sizeof(struct prefix)); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + ip_prefix_from_type2_prefix(evp, pp); + else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) + ip_prefix_from_type5_prefix(evp, pp); + + if (bgp_debug_zebra(NULL)) { + zlog_debug("installing evpn prefix %s as ip prefix %s in vrf %s", + prefix2str(evp, buf, sizeof(buf)), + prefix2str(pp, buf1, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + /* Create (or fetch) route within the VRF. */ + /* NOTE: There is no RD here. */ + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + afi = AFI_IP; + safi = SAFI_UNICAST; + rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + afi = AFI_IP6; + safi = SAFI_UNICAST; + rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + } else + return 0; + + /* Check if route entry is already present. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->extra + && (struct bgp_info *)ri->extra->parent == parent_ri) + break; + + if (!ri) { + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_ri->attr); + + /* Create new route with its attribute. */ + ri = info_make(parent_ri->type, parent_ri->sub_type, 0, + parent_ri->peer, attr_new, rn); + SET_FLAG(ri->flags, BGP_INFO_VALID); + bgp_info_extra_get(ri); + ri->extra->parent = parent_ri; + if (parent_ri->extra) + memcpy(&ri->extra->label, &parent_ri->extra->label, + BGP_LABEL_BYTES); + bgp_info_add(rn, ri); + } else { + if (attrhash_cmp(ri->attr, parent_ri->attr) + && !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) { + bgp_unlock_node(rn); + return 0; + } + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_ri->attr); + + /* Restore route, if needed. */ + if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, ri); + + /* Mark if nexthop has changed. */ + if (!IPV4_ADDR_SAME(&ri->attr->nexthop, &attr_new->nexthop)) + SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&ri->attr); + ri->attr = attr_new; + ri->uptime = bgp_clock(); + } + + /* Perform route selection and update zebra, if required. */ + bgp_process(bgp_vrf, rn, afi, safi); + + return ret; +} + /* * Install route entry into the VNI routing table and invoke route selection. */ @@ -1285,6 +1839,73 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return ret; } +/* + * Uninstall route entry from the VRF routing table and send message + * to zebra, if appropriate. + */ +static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, + struct prefix_evpn *evp, + struct bgp_info *parent_ri) +{ + struct bgp_node *rn; + struct bgp_info *ri; + int ret = 0; + struct prefix p; + struct prefix *pp = &p; + afi_t afi = 0; + safi_t safi = 0; + char buf[PREFIX_STRLEN]; + char buf1[PREFIX_STRLEN]; + + memset(pp, 0, sizeof(struct prefix)); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + ip_prefix_from_type2_prefix(evp, pp); + else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) + ip_prefix_from_type5_prefix(evp, pp); + + if (bgp_debug_zebra(NULL)) { + zlog_debug("uninstalling evpn prefix %s as ip prefix %s in vrf %s", + prefix2str(evp, buf, sizeof(buf)), + prefix2str(pp, buf1, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } + + /* Locate route within the VRF. */ + /* NOTE: There is no RD here. */ + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + afi = AFI_IP; + safi = SAFI_UNICAST; + rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + } else { + afi = AFI_IP6; + safi = SAFI_UNICAST; + rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + } + + if (!rn) + return 0; + + /* Find matching route entry. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->extra + && (struct bgp_info *)ri->extra->parent == parent_ri) + break; + + if (!ri) + return 0; + + /* Mark entry for deletion */ + bgp_info_delete(rn, ri); + + /* Perform route selection and update zebra, if required. */ + bgp_process(bgp_vrf, rn, afi, safi); + + /* Unlock route node. */ + bgp_unlock_node(rn); + + return ret; +} + /* * Uninstall route entry from the VNI routing table and send message * to zebra, if appropriate. @@ -1324,6 +1945,73 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return ret; } +/* + * Given a route entry and a VRF, see if this route entry should be + * imported into the VRF i.e., RTs match. + */ +static int is_route_matching_for_vrf(struct bgp *bgp_vrf, + struct bgp_info *ri) +{ + struct attr *attr = ri->attr; + struct ecommunity *ecom; + int i; + + assert(attr); + /* Route should have valid RT to be even considered. */ + if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) + return 0; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return 0; + + /* For each extended community RT, see if it matches this VNI. If any RT + * matches, we're done. + */ + for (i = 0; i < ecom->size; i++) { + u_char *pnt; + u_char type, sub_type; + struct ecommunity_val *eval; + struct ecommunity_val eval_tmp; + struct vrf_irt_node *irt; + + /* Only deal with RTs */ + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + eval = (struct ecommunity_val *)(ecom->val + + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + if (sub_type != ECOMMUNITY_ROUTE_TARGET) + continue; + + /* See if this RT matches specified VNIs import RTs */ + irt = lookup_vrf_import_rt(eval); + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + return 1; + + /* Also check for non-exact match. In this, we mask out the AS + * and + * only check on the local-admin sub-field. This is to + * facilitate using + * VNI as the RT for EBGP peering too. + */ + irt = NULL; + if (type == ECOMMUNITY_ENCODE_AS + || type == ECOMMUNITY_ENCODE_AS4 + || type == ECOMMUNITY_ENCODE_IP) { + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + mask_ecom_global_admin(&eval_tmp, eval); + irt = lookup_vrf_import_rt(&eval_tmp); + } + if (irt && irt->vrfs) + if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) + return 1; + } + + return 0; +} + /* * Given a route entry and a VNI, see if this route entry should be * imported into the VNI i.e., RTs match. @@ -1391,6 +2079,88 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* + * Install or uninstall mac-ip routes are appropriate for this + * particular VRF. + */ +static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, + int install) +{ + afi_t afi; + safi_t safi; + struct bgp_node *rd_rn, *rn; + struct bgp_table *table; + struct bgp_info *ri; + int ret; + char buf[PREFIX_STRLEN]; + struct bgp *bgp_def = NULL; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + bgp_def = bgp_get_default(); + if (!bgp_def) + return -1; + + /* Walk entire global routing table and evaluate routes which could be + * imported into this VRF. Note that we need to loop through all global + * routes to determine which route matches the import rt on vrf + */ + for (rd_rn = bgp_table_top(bgp_def->rib[afi][safi]); rd_rn; + rd_rn = bgp_route_next(rd_rn)) { + table = (struct bgp_table *)(rd_rn->info); + if (!table) + continue; + + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + + /* if not mac-ip route skip this route */ + if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || + evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + continue; + + /* if not a mac+ip route skip this route */ + if (!(IS_EVPN_PREFIX_IPADDR_V4(evp) || + IS_EVPN_PREFIX_IPADDR_V6(evp))) + continue; + + for (ri = rn->info; ri; ri = ri->next) { + /* Consider "valid" remote routes applicable for + * this VRF. + */ + if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID) + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (is_route_matching_for_vrf(bgp_vrf, ri)) { + if (install) + ret = + install_evpn_route_entry_in_vrf( + bgp_vrf, evp, ri); + else + ret = + uninstall_evpn_route_entry_in_vrf( + bgp_vrf, evp, ri); + + if (ret) { + zlog_err( + "Failed to %s EVPN %s route in VRF %s", + install ? "install" + : "uninstall", + prefix2str(evp, buf, + sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + return ret; + } + } + } + } + } + + return 0; +} + /* * Install or uninstall routes of specified type that are appropriate for this * particular VNI. @@ -1465,6 +2235,15 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, return 0; } +/* Install any existing remote routes applicable for this VRF into VRF RIB. This + * is invoked upon l3vni-add or l3vni import rt change + */ +static int install_routes_for_vrf(struct bgp *bgp_vrf) +{ + install_uninstall_routes_for_vrf(bgp_vrf, 1); + return 0; +} + /* * Install any existing remote routes applicable for this VNI into its * routing table. This is invoked when a VNI becomes "live" or its Import @@ -1486,6 +2265,13 @@ static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) 1); } +/* uninstall routes from l3vni vrf. */ +static int uninstall_routes_for_vrf(struct bgp *bgp_vrf) +{ + install_uninstall_routes_for_vrf(bgp_vrf, 0); + return 0; +} + /* * Uninstall any existing remote routes for this VNI. One scenario in which * this is invoked is upon an import RT change. @@ -1507,6 +2293,51 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) 0); } +/* + * Install or uninstall route in matching VRFs (list). + */ +static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, + safi_t safi, struct prefix_evpn *evp, + struct bgp_info *ri, + struct list *vrfs, int install) +{ + char buf[PREFIX2STR_BUFFER]; + struct bgp *bgp_vrf; + struct listnode *node, *nnode; + + /* Only type-2/type-5 routes go into a VRF */ + if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || + evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + return 0; + + /* if it is type-2 route and not a mac+ip route skip this route */ + if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) && + !(IS_EVPN_PREFIX_IPADDR_V4(evp) || IS_EVPN_PREFIX_IPADDR_V6(evp))) + return 0; + + for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) { + int ret; + + if (install) + ret = install_evpn_route_entry_in_vrf(bgp_vrf, + evp, ri); + else + ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, + evp, ri); + + if (ret) { + zlog_err("%u: Failed to %s prefix %s in VRF %s", + bgp_def->vrf_id, + install ? "install" : "uninstall", + prefix2str(evp, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + return ret; + } + } + + return 0; +} + /* * Install or uninstall route in matching VNIs (list). */ @@ -1557,9 +2388,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, assert(attr); - /* Only type-2 and type-3 routes go into a L2 VNI. */ + /* Only type-2 and type-3 and type-5 are supported currently */ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE - || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE)) + || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE + || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) return 0; /* If we don't have Route Target, nothing much to do. */ @@ -1570,15 +2402,16 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (!ecom || !ecom->size) return -1; - /* For each extended community RT, see which VNIs match and import - * the route into matching VNIs. + /* For each extended community RT, see which VNIs/VRFs match and import + * the route into matching VNIs/VRFs. */ for (i = 0; i < ecom->size; i++) { u_char *pnt; u_char type, sub_type; struct ecommunity_val *eval; struct ecommunity_val eval_tmp; - struct irt_node *irt; + struct irt_node *irt; /* import rt for l2vni */ + struct vrf_irt_node *vrf_irt; /* import rt for l3vni */ /* Only deal with RTs */ pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); @@ -1589,32 +2422,104 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (sub_type != ECOMMUNITY_ROUTE_TARGET) continue; - /* Are we interested in this RT? */ + /* Import route into matching l2-vnis (type-2/type-3 routes go + * into l2vni table) + */ irt = lookup_import_rt(bgp, eval); if (irt && irt->vnis) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); - /* Also check for non-exact match. In this, we mask out the AS - * and - * only check on the local-admin sub-field. This is to - * facilitate using + /* Import route into matching l3-vnis (type-2/type-5 routes go + * into l3vni/vrf table) + */ + vrf_irt = lookup_vrf_import_rt(eval); + if (vrf_irt && vrf_irt->vrfs) + install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri, + vrf_irt->vrfs, import); + + /* Also check for non-exact match. In this, + * we mask out the AS and + * only check on the local-admin sub-field. + * This is to facilitate using * VNI as the RT for EBGP peering too. */ irt = NULL; + vrf_irt = NULL; if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); + vrf_irt = lookup_vrf_import_rt(&eval_tmp); } if (irt && irt->vnis) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); + if (vrf_irt && vrf_irt->vrfs) + install_uninstall_route_in_vrfs(bgp, afi, safi, evp, + ri, vrf_irt->vrfs, + import); } - return 0; + return 0; +} + +/* delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5 + * routes */ +static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf) +{ + /* delete all ipv4 routes and withdraw from peers */ + bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST); + + /* delete all ipv6 routes and withdraw from peers */ + bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST); +} + +/* update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5 + * routes */ +static void update_advertise_vrf_routes(struct bgp *bgp_vrf) +{ + /* update all ipv4 routes */ + bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST); + + /* update all ipv6 routes */ + bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST); +} + +/* + * update and advertise local routes for a VRF as type-5 routes. + * This is invoked upon RD change for a VRF. Note taht the processing is only + * done in the global route table using the routes which already exist in the + * VRF routing table + */ +static void update_router_id_vrf(struct bgp *bgp_vrf) +{ + /* skip if the RD is configured */ + if (is_vrf_rd_configured(bgp_vrf)) + return; + + /* derive the RD for the VRF based on new router-id */ + bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); + + /* update advertise ipv4|ipv6 routes as type-5 routes */ + update_advertise_vrf_routes(bgp_vrf); +} + +/* + * Delete and withdraw all type-5 routes for the RD corresponding to VRF. + * This is invoked upon VRF RD change. The processing is done only from global + * table. + */ +static void withdraw_router_id_vrf(struct bgp *bgp_vrf) +{ + /* skip if the RD is configured */ + if (is_vrf_rd_configured(bgp_vrf)) + return; + + /* delete/withdraw ipv4|ipv6 routes as type-5 routes */ + delete_withdraw_vrf_routes(bgp_vrf); } /* @@ -1971,6 +2876,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Make EVPN prefix. */ memset(&p, 0, sizeof(struct prefix_evpn)); p.family = AF_EVPN; + p.prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; /* Additional information outside of prefix - ESI and GW IP */ @@ -2005,14 +2911,12 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, pfx += 4; memcpy(&evpn.gw_ip.ipv4, pfx, 4); pfx += 4; - p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV4; } else { SET_IPADDR_V6(&p.prefix.ip); memcpy(&p.prefix.ip.ipaddr_v6, pfx, 16); pfx += 16; memcpy(&evpn.gw_ip.ipv6, pfx, 16); pfx += 16; - p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6; } label_pnt = (mpls_label_t *)pfx; @@ -2043,10 +2947,13 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, return; p_evpn_p = &(p->u.prefix_evpn); + /* len denites the total len of IP and GW-IP in the route + IP and GW-IP have to be both ipv4 or ipv6 + */ if (IS_IPADDR_V4(&p_evpn_p->ip)) - len = 8; /* ipv4 */ + len = 8; /* IP and GWIP are both ipv4 */ else - len = 32; /* ipv6 */ + len = 32; /* IP and GWIP are both ipv6 */ /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */ stream_putc(s, 8 + 10 + 4 + 1 + len + 3); stream_put(s, prd->val, 8); @@ -2107,30 +3014,311 @@ static void free_vni_entry(struct hash_backet *backet, struct bgp *bgp) bgp_evpn_free(bgp, vpn); } +/* + * Derive AUTO import RT for BGP VRF - L3VNI + */ +static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + + /* Map RT to VRF */ + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); +} + +/* + * Delete AUTO import RT from BGP VRF - L3VNI + */ +static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf) +{ + evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl); +} + +/* + * Derive AUTO export RT for BGP VRF - L3VNI + */ +static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf) +{ + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); + form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl); +} + +/* + * Delete AUTO export RT from BGP VRF - L3VNI + */ +static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf) +{ + evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl); +} + +static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + + /* update all type-5 routes */ + update_advertise_vrf_routes(bgp_vrf); + + /* update all type-2 routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); +} /* * Public functions. */ +/* withdraw type-5 route corresponding to ip prefix */ +void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn, + afi_t afi, safi_t safi) +{ + int ret = 0; + struct prefix_evpn evp; + char buf[PREFIX_STRLEN]; + + build_type5_prefix_from_ip_prefix(&evp, &rn->p); + ret = delete_evpn_type5_route(bgp_vrf, &evp); + if (ret) { + zlog_err( + "%u failed to delete type-5 route for prefix %s in vrf %s", + bgp_vrf->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } +} + +/* withdraw all type-5 routes for an address family */ +void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, + afi_t afi, safi_t safi) +{ + struct bgp_table *table = NULL; + struct bgp_node *rn = NULL; + + if (!advertise_type5_routes(bgp_vrf, afi)) + return; + + table = bgp_vrf->rib[afi][safi]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) + bgp_evpn_withdraw_type5_route(bgp_vrf, rn, afi, safi); + +} + +/* advertise ip prefix as type-5 route*/ +void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn, + afi_t afi, safi_t safi) +{ + int ret = 0; + struct prefix_evpn evp; + char buf[PREFIX_STRLEN]; + + if (!advertise_type5_routes(bgp_vrf, afi)) + return; + + if (!rn->info) + return; + + /* only advertise subnet routes as type-5 */ + if (is_host_route(&rn->p)) + return; + + build_type5_prefix_from_ip_prefix(&evp, &rn->p); + ret = update_evpn_type5_route(bgp_vrf, &evp); + if (ret) { + zlog_err( + "%u failed to create type-5 route for prefix %s in vrf %s", + bgp_vrf->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf)), + vrf_id_to_name(bgp_vrf->vrf_id)); + } +} + +/* advertise all type-5 routes for an address family */ +void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, + afi_t afi, safi_t safi) +{ + struct bgp_table *table = NULL; + struct bgp_node *rn = NULL; + + table = bgp_vrf->rib[afi][safi]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) + bgp_evpn_advertise_type5_route(bgp_vrf, rn, afi, safi); +} + +void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, + struct list *rtl) +{ + struct listnode *node, *nnode, *node_to_del; + struct ecommunity *ecom, *ecom_auto; + struct ecommunity_val eval; + + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); + + ecom_auto = ecommunity_new(); + ecommunity_add_val(ecom_auto, &eval); + node_to_del = NULL; + + for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecom_auto)) { + ecommunity_free(&ecom); + node_to_del = node; + } + } + + if (node_to_del) + list_delete_node(rtl, node_to_del); + + ecommunity_free(&ecom_auto); +} + +void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd) +{ + /* uninstall routes from vrf */ + uninstall_routes_for_vrf(bgp_vrf); + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + + /* Remove auto generated RT */ + evpn_auto_rt_import_delete_for_vrf(bgp_vrf); + + /* Add the newly configured RT to RT list */ + listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + + /* map VRF to its RTs */ + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + + /* install routes matching the new VRF */ + install_routes_for_vrf(bgp_vrf); +} + +void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct ecommunity *ecom = NULL; + + /* uninstall routes from vrf */ + uninstall_routes_for_vrf(bgp_vrf); + + /* Cleanup the RT to VRF mapping */ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + + /* remove the RT from the RT list */ + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomdel)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + + if (node_to_del) + list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del); + + /* fallback to auto import rt, if this was the last RT */ + if (list_isempty(bgp_vrf->vrf_import_rtl)) { + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); + evpn_auto_rt_import_add_for_vrf(bgp_vrf); + } + + /* map VRFs to its RTs */ + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + + /* install routes matching this new RT */ + install_routes_for_vrf(bgp_vrf); +} + +void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd) +{ + /* remove auto-generated RT */ + evpn_auto_rt_export_delete_for_vrf(bgp_vrf); + + /* Add the new RT to the RT list */ + listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); + + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); + +} + +void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct ecommunity *ecom = NULL; + + /* Remove the RT from the RT list */ + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) { + if (ecommunity_match(ecom, ecomdel)) { + ecommunity_free(&ecom); + node_to_del = node; + break; + } + } + + if (node_to_del) + list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del); + + /* fall back to auto-generated RT if this was the last RT */ + if (bgp_vrf->vrf_export_rtl && list_isempty(bgp_vrf->vrf_export_rtl)) { + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); + evpn_auto_rt_export_add_for_vrf(bgp_vrf); + } + + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); +} + /* * Handle change to BGP router id. This is invoked twice by the change * handler, first before the router id has been changed and then after * the router id has been changed. The first invocation will result in - * local routes for all VNIs being deleted and withdrawn and the next + * local routes for all VNIs/VRF being deleted and withdrawn and the next * will result in the routes being re-advertised. */ void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw) { - if (withdraw) + if (withdraw) { + + /* delete and withdraw all the type-5 routes + stored in the global table for this vrf + */ + withdraw_router_id_vrf(bgp); + + /* delete all the VNI routes (type-2/type-3) routes for all the + * L2-VNIs + */ hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))withdraw_router_id_vni, bgp); - else + } else { + + /* advertise all routes in the vrf as type-5 routes with the new + * RD + */ + update_router_id_vrf(bgp); + + /* advertise all the VNI routes (type-2/type-3) routes with the + * new RD + */ hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))update_router_id_vni, bgp); + } } /* @@ -2141,6 +3329,15 @@ int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn) return update_routes_for_vni(bgp, vpn); } +void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, + int withdraw) +{ + if (withdraw) + delete_withdraw_vrf_routes(bgp_vrf); + else + update_advertise_vrf_routes(bgp_vrf); +} + /* * Handle change to RD. This is invoked twice by the change handler, * first before the RD has been changed and then after the RD has @@ -2285,6 +3482,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) inet_ntop(family, &p->prefix.ip.ip.addr, buf2, PREFIX2STR_BUFFER)); } + } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { + snprintf(buf, len, "[%d]:[0]:[%d]:[%s]", + p->prefix.route_type, + p->prefix.ip_prefix_length, + IS_EVPN_PREFIX_IPADDR_V4(p) ? + inet_ntoa(p->prefix.ip.ipaddr_v4) : + inet6_ntoa(p->prefix.ip.ipaddr_v6)); } else { /* For EVPN route types not supported yet. */ snprintf(buf, len, "(unsupported route type %d)", @@ -2453,6 +3657,65 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, return 0; } +/* + * Map the RTs (configured or automatically derived) of a VRF to the VRF. + * The mapping will be used during route processing. + * bgp_def: default bgp instance + * bgp_vrf: specific bgp vrf instance on which RT is configured + */ +void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf) +{ + int i = 0; + struct ecommunity_val *eval = NULL; + struct listnode *node = NULL, *nnode = NULL; + struct ecommunity *ecom = NULL; + + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + for (i = 0; i < ecom->size; i++) { + eval = (struct ecommunity_val *)(ecom->val + + (i + * ECOMMUNITY_SIZE)); + map_vrf_to_rt(bgp_vrf, eval); + } + } +} + +/* + * Unmap the RTs (configured or automatically derived) of a VRF from the VRF. + */ +void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf) +{ + int i; + struct ecommunity_val *eval; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) { + for (i = 0; i < ecom->size; i++) { + struct vrf_irt_node *irt; + struct ecommunity_val eval_tmp; + + eval = (struct ecommunity_val *)(ecom->val + + (i + * ECOMMUNITY_SIZE)); + /* If using "automatic" RT, we only care about the + * local-admin sub-field. + * This is to facilitate using VNI as the RT for EBGP + * peering too. + */ + memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_IMPORT_RT_CFGD)) + mask_ecom_global_admin(&eval_tmp, eval); + + irt = lookup_vrf_import_rt(&eval_tmp); + if (irt) + unmap_vrf_from_rt(bgp_vrf, irt); + } + } +} + + /* * Map the RTs (configured or automatically derived) of a VNI to the VNI. @@ -2515,7 +3778,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn) */ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn) { - form_auto_rt(bgp, vpn, vpn->import_rtl); + form_auto_rt(bgp, vpn->vni, vpn->import_rtl); UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD); /* Map RT to VNI */ @@ -2527,10 +3790,24 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn) */ void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn) { - form_auto_rt(bgp, vpn, vpn->export_rtl); + form_auto_rt(bgp, vpn->vni, vpn->export_rtl); UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD); } +/* + * Derive RD automatically for VNI using passed information - it + * is of the form RouterId:unique-id-for-vni. + */ +void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp) +{ + char buf[100]; + + bgp->vrf_prd.family = AF_UNSPEC; + bgp->vrf_prd.prefixlen = 64; + sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), bgp->vrf_rd_id); + str2prefix_rd(buf, &bgp->vrf_prd); +} + /* * Derive RD automatically for VNI using passed information - it * is of the form RouterId:unique-id-for-vni. @@ -2564,7 +3841,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni) * Create a new vpn - invoked upon configuration or zebra notification. */ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip) + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id) { struct bgpevpn *vpn; @@ -2578,13 +3856,14 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* Set values - RD and RT set to defaults. */ vpn->vni = vni; vpn->originator_ip = originator_ip; + vpn->tenant_vrf_id = tenant_vrf_id; /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; vpn->export_rtl = list_new(); vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - bf_assign_index(bgp->rd_idspace, vpn->rd_id); + bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); /* Initialize EVPN route table. */ @@ -2595,6 +3874,10 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, XFREE(MTYPE_BGP_EVPN, vpn); return NULL; } + + /* add to l2vni list on corresponding vrf */ + bgpevpn_link_to_l3vni(vpn); + QOBJ_REG(vpn, bgpevpn); return vpn; } @@ -2607,11 +3890,12 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); bgp_evpn_unmap_vni_from_its_rts(bgp, vpn); list_delete_and_null(&vpn->import_rtl); list_delete_and_null(&vpn->export_rtl); - bf_release_index(bgp->rd_idspace, vpn->rd_id); + bf_release_index(bm->rd_idspace, vpn->rd_id); hash_release(bgp->vnihash, vpn); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); @@ -2770,6 +4054,160 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, return 0; } +static void link_l2vni_hash_to_l3vni(struct hash_backet *backet, + struct bgp *bgp_vrf) +{ + struct bgpevpn *vpn = NULL; + struct bgp *bgp_def = NULL; + + bgp_def = bgp_get_default(); + assert(bgp_def); + + vpn = (struct bgpevpn *)backet->data; + if (vpn->tenant_vrf_id == bgp_vrf->vrf_id) + bgpevpn_link_to_l3vni(vpn); +} + +int bgp_evpn_local_l3vni_add(vni_t l3vni, + vrf_id_t vrf_id, + struct ethaddr *rmac, + struct in_addr originator_ip) +{ + struct bgp *bgp_vrf = NULL; /* bgp VRF instance */ + struct bgp *bgp_def = NULL; /* default bgp instance */ + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + as_t as = 0; + + /* get the default instamce - required to get the AS number for VRF + * auto-creatio + */ + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("Cannot process L3VNI %u ADD - default BGP instance not yet created", + l3vni); + return -1; + } + as = bgp_def->as; + + /* if the BGP vrf instance doesnt exist - create one */ + bgp_vrf = bgp_lookup_by_name(vrf_id_to_name(vrf_id)); + if (!bgp_vrf) { + + int ret = 0; + + ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id), + BGP_INSTANCE_TYPE_VRF); + switch (ret) { + case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET: + zlog_err("'bgp multiple-instance' not present\n"); + return -1; + case BGP_ERR_AS_MISMATCH: + zlog_err("BGP is already running; AS is %u\n", as); + return -1; + case BGP_ERR_INSTANCE_MISMATCH: + zlog_err("BGP instance name and AS number mismatch\n"); + return -1; + } + + /* mark as auto created */ + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO); + } + + /* associate with l3vni */ + bgp_vrf->l3vni = l3vni; + + /* set the router mac - to be used in mac-ip routes for this vrf */ + memcpy(&bgp_vrf->rmac, rmac, sizeof(struct ethaddr)); + + /* set the originator ip */ + bgp_vrf->originator_ip = originator_ip; + + /* auto derive RD/RT */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) + evpn_auto_rt_import_add_for_vrf(bgp_vrf); + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) + evpn_auto_rt_export_add_for_vrf(bgp_vrf); + bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); + + /* link all corresponding l2vnis */ + hash_iterate(bgp_def->vnihash, + (void (*)(struct hash_backet *, void *)) + link_l2vni_hash_to_l3vni, + bgp_vrf); + + /* updates all corresponding local mac-ip routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); + + /* advertise type-5 routes if needed */ + update_advertise_vrf_routes(bgp_vrf); + + /* install all remote routes belonging to this l3vni into correspondng + * vrf */ + install_routes_for_vrf(bgp_vrf); + + return 0; +} + +int bgp_evpn_local_l3vni_del(vni_t l3vni, + vrf_id_t vrf_id) +{ + struct bgp *bgp_vrf = NULL; /* bgp vrf instance */ + struct bgp *bgp_def = NULL; /* default bgp instance */ + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp_vrf) { + zlog_err("Cannot process L3VNI %u Del - Could not find BGP instance", + l3vni); + return -1; + } + + bgp_def = bgp_get_default(); + if (!bgp_def) { + zlog_err("Cannot process L3VNI %u Del - Could not find default BGP instance", + l3vni); + return -1; + } + + /* unimport remote routes from VRF, if it is AUTO vrf bgp_delete will + * take care of uninstalling the routes from zebra + */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) + uninstall_routes_for_vrf(bgp_vrf); + + /* delete/withdraw all type-5 routes */ + delete_withdraw_vrf_routes(bgp_vrf); + + /* remove the l3vni from vrf instance */ + bgp_vrf->l3vni = 0; + + /* remove the Rmac from the BGP vrf */ + memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr)); + + /* delete RD/RT */ + if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl)) { + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + list_delete_all_node(bgp_vrf->vrf_import_rtl); + } + if (bgp_vrf->vrf_export_rtl && !list_isempty(bgp_vrf->vrf_export_rtl)) { + list_delete_all_node(bgp_vrf->vrf_export_rtl); + } + + /* update all corresponding local mac-ip routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) + update_routes_for_vni(bgp_def, vpn); + + + /* Delete the instance if it was autocreated */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) + bgp_delete(bgp_vrf); + + return 0; +} + /* * Handle del of a local VNI. */ @@ -2813,7 +4251,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) * about is change to local-tunnel-ip. */ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip) + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -2826,6 +4265,17 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Lookup VNI. If present and no change, exit. */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (vpn) { + + /* update tenant_vrf_id if required */ + if (vpn->tenant_vrf_id != tenant_vrf_id) { + bgpevpn_unlink_from_l3vni(vpn); + vpn->tenant_vrf_id = tenant_vrf_id; + bgpevpn_link_to_l3vni(vpn); + + /* update all routes with new export RT for VRFs */ + update_routes_for_vni(bgp, vpn); + } + if (is_vni_live(vpn) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)) /* Probably some other param has changed that we don't @@ -2838,7 +4288,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Create or update as appropriate. */ if (!vpn) { - vpn = bgp_evpn_new(bgp, vni, originator_ip); + vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id); if (!vpn) { zlog_err( "%u: Failed to allocate VNI entry for VNI %u - at Add", @@ -2905,10 +4355,19 @@ void bgp_evpn_cleanup(struct bgp *bgp) if (bgp->import_rt_hash) hash_free(bgp->import_rt_hash); bgp->import_rt_hash = NULL; + if (bgp->vrf_import_rt_hash) + hash_free(bgp->vrf_import_rt_hash); + bgp->vrf_import_rt_hash = NULL; if (bgp->vnihash) hash_free(bgp->vnihash); bgp->vnihash = NULL; - bf_free(bgp->rd_idspace); + if (bgp->vrf_import_rtl) + list_delete_and_null(&bgp->vrf_import_rtl); + if (bgp->vrf_export_rtl) + list_delete_and_null(&bgp->vrf_export_rtl); + if (bgp->l2vnis) + list_delete_and_null(&bgp->l2vnis); + bf_release_index(bm->rd_idspace, bgp->vrf_rd_id); } /* @@ -2916,7 +4375,7 @@ void bgp_evpn_cleanup(struct bgp *bgp) * Create * VNI hash table * hash for RT to VNI - * unique rd id space for auto derivation of RD for VNIs + * assign a unique rd id for auto derivation of vrf_prd */ void bgp_evpn_init(struct bgp *bgp) { @@ -2925,7 +4384,24 @@ void bgp_evpn_init(struct bgp *bgp) bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); - bf_init(bgp->rd_idspace, UINT16_MAX); - /*assign 0th index in the bitfield, so that we start with id 1*/ - bf_assign_zero_index(bgp->rd_idspace); + bgp->vrf_import_rt_hash = + hash_create(vrf_import_rt_hash_key_make, vrf_import_rt_hash_cmp, + "BGP VRF Import RT Hash"); + bgp->vrf_import_rtl = list_new(); + bgp->vrf_import_rtl->cmp = + (int (*)(void *, void *))evpn_route_target_cmp; + + bgp->vrf_export_rtl = list_new(); + bgp->vrf_export_rtl->cmp = + (int (*)(void *, void *))evpn_route_target_cmp; + bgp->l2vnis = list_new(); + bgp->l2vnis->cmp = + (int (*)(void *, void *))vni_hash_cmp; + bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id); + +} + +void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) +{ + bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); } diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 985f41f58675..9400916845b0 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -22,9 +22,29 @@ #define _QUAGGA_BGP_EVPN_H #include "vxlan.h" +#include "bgpd.h" #define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */ +static inline int is_evpn_enabled(void) +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_default(); + return bgp ? bgp->advertise_all_vni : 0; +} + +extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, + struct bgp_node *rn, + afi_t afi, safi_t safi); +extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, + struct bgp_node *rn, + afi_t afi, safi_t safi); +extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, + safi_t safi); +extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, + safi_t safi); +extern void bgp_evpn_vrf_delete(struct bgp *bgp_vrf); extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw); extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len); extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len); @@ -45,9 +65,14 @@ extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip, u_char flags); +extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id, + struct ethaddr *rmac, + struct in_addr originator_ip); +extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id); extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip); + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); extern void bgp_evpn_init(struct bgp *bgp); diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index a58f73f4bc92..2d52e1995d2b 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -30,9 +30,10 @@ #define RT_ADDRSTRLEN 28 -/* EVPN prefix lengths. */ +/* EVPN prefix lengths. This reprsent the sizeof struct prefix_evpn */ #define EVPN_TYPE_2_ROUTE_PREFIXLEN 224 #define EVPN_TYPE_3_ROUTE_PREFIXLEN 224 +#define EVPN_TYPE_5_ROUTE_PREFIXLEN 224 /* EVPN route types. */ typedef enum { @@ -53,6 +54,7 @@ typedef enum { */ struct bgpevpn { vni_t vni; + vrf_id_t tenant_vrf_id; u_int32_t flags; #define VNI_FLAG_CFGD 0x1 /* VNI is user configured */ #define VNI_FLAG_LIVE 0x2 /* VNI is "live" */ @@ -96,10 +98,99 @@ struct irt_node { struct list *vnis; }; +/* Mapping of Import RT to VRFs. + * The Import RTs of all VRFss are maintained in a hash table with each + * RT linking to all VRFs that will import routes matching this RT. + */ +struct vrf_irt_node { + /* RT */ + struct ecommunity_val rt; + + /* List of VNIs importing routes matching this RT. */ + struct list *vrfs; +}; + + #define RT_TYPE_IMPORT 1 #define RT_TYPE_EXPORT 2 #define RT_TYPE_BOTH 3 +static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) +{ + return (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_RD_CFGD)); +} + +static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf, + struct prefix_rd *prd) +{ + return (memcmp(&bgp_vrf->vrf_prd.val, prd->val, ECOMMUNITY_SIZE) == 0); +} + +static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return 0; + + return bgp_vrf->l3vni; +} + +static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac) +{ + struct bgp *bgp_vrf = NULL; + + memset(rmac, 0, sizeof(struct ethaddr)); + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return; + memcpy(rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); +} + +static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return NULL; + + return bgp_vrf->vrf_export_rtl; +} + +static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf) + return NULL; + + return bgp_vrf->vrf_import_rtl; +} + +static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf || !bgp_vrf->l2vnis) + return; + listnode_delete(bgp_vrf->l2vnis, vpn); +} + +static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) +{ + struct bgp *bgp_vrf = NULL; + + bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id); + if (!bgp_vrf || !bgp_vrf->l2vnis) + return; + listnode_add_sort(bgp_vrf->l2vnis, vpn); +} + static inline int is_vni_configured(struct bgpevpn *vpn) { return (CHECK_FLAG(vpn->flags, VNI_FLAG_CFGD)); @@ -157,6 +248,15 @@ static inline vni_t label2vni(mpls_label_t *label) return vni; } +static inline void encode_rmac_extcomm(struct ecommunity_val *eval, + struct ethaddr *rmac) +{ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_EVPN; + eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC; + memcpy(&eval->val[2], rmac, ETH_ALEN); +} + static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq, struct ecommunity_val *eval) { @@ -171,6 +271,44 @@ static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq, eval->val[7] = seq & 0xff; } +static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp, + struct prefix *ip) +{ + memset(ip, 0, sizeof(struct prefix)); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + ip->family = AF_INET; + ip->prefixlen = evp->prefix.ip_prefix_length; + memcpy(&(ip->u.prefix4), + &(evp->prefix.ip.ip), + IPV4_MAX_BYTELEN); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + ip->family = AF_INET6; + ip->prefixlen = evp->prefix.ip_prefix_length; + memcpy(&(ip->u.prefix6), + &(evp->prefix.ip.ip), + IPV6_MAX_BYTELEN); + } +} + +static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp, + struct prefix *ip) +{ + memset(ip, 0, sizeof(struct prefix)); + if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + ip->family = AF_INET; + ip->prefixlen = IPV4_MAX_BITLEN; + memcpy(&(ip->u.prefix4), + &(evp->prefix.ip.ip), + IPV4_MAX_BYTELEN); + } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + ip->family = AF_INET6; + ip->prefixlen = IPV6_MAX_BITLEN; + memcpy(&(ip->u.prefix6), + &(evp->prefix.ip.ip), + IPV6_MAX_BYTELEN); + } +} + static inline void build_evpn_type2_prefix(struct prefix_evpn *p, struct ethaddr *mac, struct ipaddr *ip) @@ -185,6 +323,31 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, memcpy(&p->prefix.ip, ip, sizeof(*ip)); } +static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, + struct prefix *ip_prefix) +{ + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (ip_prefix->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4, + sizeof(struct in_addr)); + } else { + ip.ipa_type = IPADDR_V6; + memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6, + sizeof(struct in6_addr)); + } + + memset(evp, 0, sizeof(struct prefix_evpn)); + evp->family = AF_EVPN; + evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; + evp->prefix.ip_prefix_length = ip_prefix->prefixlen; + evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; + evp->prefix.ip.ipa_type = ip.ipa_type; + memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr)); +} + static inline void build_evpn_type3_prefix(struct prefix_evpn *p, struct in_addr originator_ip) { @@ -196,13 +359,41 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p, p->prefix.ip.ipaddr_v4 = originator_ip; } +static inline int advertise_type5_routes(struct bgp *bgp_vrf, + afi_t afi) +{ + if (!bgp_vrf->l3vni) + return 0; + + if (afi == AFI_IP && + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) + return 1; + + if (afi == AFI_IP6 && + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) + return 1; + + return 0; +} +extern void evpn_rt_delete_auto(struct bgp*, vni_t, struct list*); +extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd); +extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel); +extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomadd); +extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, + struct ecommunity *ecomdel); extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw); extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn, int withdraw); extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn); extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf); +extern void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf); extern void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn); @@ -211,8 +402,10 @@ extern void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, extern void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn); +extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp); extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni); extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, - struct in_addr originator_ip); + struct in_addr originator_ip, + vrf_id_t tenant_vrf_id); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 70f7e78976b9..b463896c4933 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -53,6 +53,117 @@ struct vni_walk_ctx { }; #if defined(HAVE_CUMULUS) +static void display_vrf_import_rt(struct vty *vty, + struct vrf_irt_node *irt, + json_object *json) +{ + u_char *pnt; + u_char type, sub_type; + struct ecommunity_as eas; + struct ecommunity_ip eip; + struct listnode *node, *nnode; + struct bgp *tmp_bgp_vrf = NULL; + json_object *json_rt = NULL; + json_object *json_vrfs = NULL; + char rt_buf[RT_ADDRSTRLEN]; + + if (json) { + json_rt = json_object_new_object(); + json_vrfs = json_object_new_array(); + } + + pnt = (u_char *)&irt->rt.val; + type = *pnt++; + sub_type = *pnt++; + if (sub_type != ECOMMUNITY_ROUTE_TARGET) + return; + + memset(&eas, 0, sizeof(eas)); + switch (type) { + case ECOMMUNITY_ENCODE_AS: + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + pnt = ptr_get_be32(pnt, &eas.val); + + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + + break; + + case ECOMMUNITY_ENCODE_IP: + memcpy(&eip.ip, pnt, 4); + pnt += 4; + eip.val = (*pnt++ << 8); + eip.val |= (*pnt++); + + snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip), + eip.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + + break; + + case ECOMMUNITY_ENCODE_AS4: + pnt = ptr_get_be32(pnt, &eas.val); + eas.val = (*pnt++ << 8); + eas.val |= (*pnt++); + + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + + break; + + default: + return; + } + + if (!json) { + vty_out(vty, + "\nList of VRFs importing routes with this route-target:\n"); + } + + for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) { + if (json) + json_object_array_add( + json_vrfs, + json_object_new_string( + vrf_id_to_name( + tmp_bgp_vrf->vrf_id))); + else + vty_out(vty, " %s\n", + vrf_id_to_name(tmp_bgp_vrf->vrf_id)); + } + + if (json) { + json_object_object_add(json_rt, "vrfs", json_vrfs); + json_object_object_add(json, rt_buf, json_rt); + } +} + +static void show_vrf_import_rt_entry(struct hash_backet *backet, + void *args[]) +{ + json_object *json = NULL; + struct vty *vty = NULL; + struct vrf_irt_node *irt = (struct vrf_irt_node *)backet->data; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + display_vrf_import_rt(vty, irt, json); +} + static void display_import_rt(struct vty *vty, struct irt_node *irt, json_object *json) { @@ -226,7 +337,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) { - char buf1[INET6_ADDRSTRLEN]; + char buf1[RD_ADDRSTRLEN]; char *ecom_str; struct listnode *node, *nnode; struct ecommunity *ecom; @@ -241,7 +352,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) is_vni_live(vpn) ? "Yes" : "No"); json_object_string_add( json, "rd", - prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); json_object_string_add(json, "originatorIp", inet_ntoa(vpn->originator_ip)); json_object_string_add(json, "advertiseGatewayMacip", @@ -252,8 +363,10 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) vty_out(vty, " (known to the kernel)"); vty_out(vty, "\n"); + vty_out(vty, " Tenant-Vrf: %s\n", + vrf_id_to_name(vpn->tenant_vrf_id)); vty_out(vty, " RD: %s\n", - prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); vty_out(vty, " Originator IP: %s\n", inet_ntoa(vpn->originator_ip)); vty_out(vty, " Advertise-gw-macip : %s\n", @@ -298,6 +411,53 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_object_add(json, "exportRts", json_export_rtl); } +static void evpn_show_vrf_routes(struct vty *vty, + struct bgp *bgp_vrf) +{ + struct bgp *bgp_def = NULL; + struct bgp_node *rn; + struct bgp_info *ri; + int header = 1; + u_int32_t prefix_cnt, path_cnt; + struct bgp_table *table; + + prefix_cnt = path_cnt = 0; + bgp_def = bgp_get_default(); + if (!bgp_def) + return; + + table = (struct bgp_table *)bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN]; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + char prefix_str[BUFSIZ]; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); + + if (rn->info) { + /* Overall header/legend displayed once. */ + if (header) { + bgp_evpn_show_route_header(vty, bgp_def, NULL); + header = 0; + } + prefix_cnt++; + } + + /* For EVPN, the prefix is displayed for each path (to fit in + * with code that already exists). + */ + for (ri = rn->info; ri; ri = ri->next) { + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + path_cnt++; + } + } + + if (prefix_cnt == 0) + vty_out(vty, "No EVPN prefixes exist for this VRF"); + else + vty_out(vty, "\nDisplayed %u prefixes (%u paths)", + prefix_cnt, path_cnt); +} + static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json) @@ -414,12 +574,12 @@ static void show_vni_entry(struct hash_backet *backet, void *args[]) { struct vty *vty; json_object *json; - json_object *json_vni; - json_object *json_import_rtl; - json_object *json_export_rtl; + json_object *json_vni = NULL; + json_object *json_import_rtl = NULL; + json_object *json_export_rtl = NULL; struct bgpevpn *vpn = (struct bgpevpn *)backet->data; char buf1[10]; - char buf2[INET6_ADDRSTRLEN]; + char buf2[RD_ADDRSTRLEN]; char rt_buf[25]; char *ecom_str; struct listnode *node, *nnode; @@ -446,7 +606,7 @@ static void show_vni_entry(struct hash_backet *backet, void *args[]) inet_ntoa(vpn->originator_ip)); json_object_string_add( json_vni, "rd", - prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + prefix_rd2str(&vpn->prd, buf2, sizeof(buf2))); } else { vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni, inet_ntoa(vpn->originator_ip), @@ -502,6 +662,9 @@ static void show_vni_entry(struct hash_backet *backet, void *args[]) break; } + if (!json) + vty_out(vty, "%-37s", vrf_id_to_name(vpn->tenant_vrf_id)); + if (json) { char vni_str[VNI_STR_LEN]; @@ -1166,40 +1329,15 @@ DEFUN(no_evpnrt5_network, } #if defined(HAVE_CUMULUS) -static void evpn_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn, - struct list *rtl) -{ - struct listnode *node, *nnode, *node_to_del; - struct ecommunity *ecom, *ecom_auto; - struct ecommunity_val eval; - - encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval); - - ecom_auto = ecommunity_new(); - ecommunity_add_val(ecom_auto, &eval); - node_to_del = NULL; - - for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { - if (ecommunity_match(ecom, ecom_auto)) { - ecommunity_free(&ecom); - node_to_del = node; - } - } - - if (node_to_del) - list_delete_node(rtl, node_to_del); - - ecommunity_free(&ecom_auto); -} static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn, vpn->import_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl); } static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) { - evpn_rt_delete_auto(bgp, vpn, vpn->export_rtl); + evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl); } /* @@ -1351,6 +1489,46 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn, bgp_evpn_handle_export_rt_change(bgp, vpn); } +/* + * Configure RD for VRF + */ +static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, + struct prefix_rd *rd) +{ + /* If we have already advertise type-5 routes with a diffrent RD, we + * have to delete and withdraw them firs + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1); + + /* update RD */ + memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd)); + SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); + + /* We have a new RD for VRF. + * Advertise all type-5 routes again with the new RD + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0); +} + +/* + * Unconfigure RD for VRF + */ +static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) +{ + /* If we have already advertise type-5 routes with a diffrent RD, we + * have to delete and withdraw them firs + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1); + + /* fall back to default RD */ + bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); + + /* We have a new RD for VRF. + * Advertise all type-5 routes again with the new RD + */ + bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0); +} + /* * Configure RD for a VNI (vty handler) */ @@ -1403,7 +1581,10 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vpn = bgp_evpn_new(bgp, vni, bgp->router_id); + /* tenant vrf will be updated when we get local_vni_add from + * zebra + */ + vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0); if (!vpn) { zlog_err( "%u: Failed to allocate VNI entry for VNI %u - at Config", @@ -1452,6 +1633,25 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) return 0; } +/* + * Display import RT mapping to VRFs (vty handler) + * bgp_def: default bgp instance + */ +static void evpn_show_vrf_import_rts(struct vty *vty, + struct bgp *bgp_def, + json_object *json) +{ + void *args[2]; + + args[0] = vty; + args[1] = json; + + hash_iterate(bgp_def->vrf_import_rt_hash, + (void (*)(struct hash_backet *, void *)) + show_vrf_import_rt_entry, + args); +} + /* * Display import RT mapping to VNIs (vty handler) */ @@ -1778,11 +1978,11 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* RD header and legend - once overall. */ if (rd_header && !json) { vty_out(vty, - "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" - "[MAC]\n"); + "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n"); + vty_out(vty, + "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); vty_out(vty, - "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:" - "[OrigIP]\n\n"); + "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n"); rd_header = 0; } @@ -2014,8 +2214,9 @@ static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp, } else { vty_out(vty, "Number of VNIs: %u\n", num_vnis); vty_out(vty, "Flags: * - Kernel\n"); - vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI", - "Orig IP", "RD", "Import RT", "Export RT"); + vty_out(vty, " %-10s %-15s %-21s %-25s %-25s %-37s\n", "VNI", + "Orig IP", "RD", "Import RT", + "Export RT", "Tenant-Vrf"); } args[0] = vty; @@ -2093,7 +2294,7 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp) static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) { - char buf1[INET6_ADDRSTRLEN]; + char buf1[RD_ADDRSTRLEN]; char *ecom_str; struct listnode *node, *nnode; struct ecommunity *ecom; @@ -2102,7 +2303,7 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) vty_out(vty, " vni %d\n", vpn->vni); if (is_rd_configured(vpn)) vty_out(vty, " rd %s\n", - prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); if (is_import_rt_configured(vpn)) { for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, @@ -2239,6 +2440,115 @@ DEFUN (no_bgp_evpn_advertise_all_vni, return CMD_SUCCESS; } +DEFUN (bgp_evpn_advertise_type5, + bgp_evpn_advertise_type5_cmd, + "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + "Advertise prefix routes\n" + BGP_AFI_HELP_STR + BGP_SAFI_HELP_STR) +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + int idx_afi = 0; + int idx_safi = 0; + afi_t afi = 0; + safi_t safi = 0; + + argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); + argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + + if (!(afi == AFI_IP) || (afi == AFI_IP6)) { + vty_out(vty, + "%%only ipv4 or ipv6 address families are supported"); + return CMD_WARNING; + } + + if (safi != SAFI_UNICAST) { + vty_out(vty, + "%%only ipv4 unicast or ipv6 unicast are supported"); + return CMD_WARNING; + } + + if (afi == AFI_IP) { + + /* if we are already advertising ipv4 prefix as type-5 + * nothing to do + */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); + bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); + } + } else { + + /* if we are already advertising ipv6 prefix as type-5 + * nothing to do + */ + if (!CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { + SET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi); + } + } + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_advertise_type5, + no_bgp_evpn_advertise_type5_cmd, + "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR, + NO_STR + "Advertise prefix routes\n" + BGP_AFI_HELP_STR + BGP_SAFI_HELP_STR) +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + int idx_afi = 0; + int idx_safi = 0; + afi_t afi = 0; + safi_t safi = 0; + + argv_find_and_parse_afi(argv, argc, &idx_afi, &afi); + argv_find_and_parse_safi(argv, argc, &idx_safi, &safi); + + if (!(afi == AFI_IP) || (afi == AFI_IP6)) { + vty_out(vty, + "%%only ipv4 or ipv6 address families are supported"); + return CMD_WARNING; + } + + if (safi != SAFI_UNICAST) { + vty_out(vty, + "%%only ipv4 unicast or ipv6 unicast are supported"); + return CMD_WARNING; + } + + if (afi == AFI_IP) { + + /* if we are already advertising ipv4 prefix as type-5 + * nothing to do + */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV4_IN_EVPN); + } + } else { + + /* if we are already advertising ipv6 prefix as type-5 + * nothing to do + */ + if (CHECK_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) { + bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_ADVERTISE_IPV6_IN_EVPN); + } + } + return CMD_SUCCESS; +} + /* * Display VNI information - for all or a specific VNI */ @@ -2278,7 +2588,7 @@ DEFUN(show_bgp_l2vpn_evpn_vni, ? "Enabled" : "Disabled"); json_object_string_add(json, "advertiseAllVnis", - bgp->advertise_all_vni + is_evpn_enabled() ? "Enabled" : "Disabled"); } else { @@ -2288,8 +2598,7 @@ DEFUN(show_bgp_l2vpn_evpn_vni, /* Display all VNIs */ vty_out(vty, "Advertise All VNI flag: %s\n", - bgp->advertise_all_vni ? "Enabled" - : "Disabled"); + is_evpn_enabled() ? "Enabled" : "Disabled"); } evpn_show_all_vnis(vty, bgp, json); @@ -2529,6 +2838,33 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, return CMD_SUCCESS; } +/* + * Display per-VRF EVPN routing table. + */ +DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd, + "show bgp l2vpn evpn route vrf VRFNAME", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VRF\n" + "VRF Name\n") +{ + int vrf_idx = 6; + char *vrf_name = NULL; + struct bgp *bgp_vrf = NULL; + + vrf_name = argv[vrf_idx]->arg; + bgp_vrf = bgp_lookup_by_name(vrf_name); + if (!bgp_vrf) + return CMD_WARNING; + + evpn_show_vrf_routes(vty, bgp_vrf); + + return CMD_SUCCESS; +} + /* * Display per-VNI EVPN routing table. */ @@ -2784,6 +3120,42 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, return CMD_SUCCESS; } +/* + * Display EVPN import route-target hash table + */ +DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt, + show_bgp_l2vpn_evpn_vrf_import_rt_cmd, + "show bgp l2vpn evpn vrf-import-rt [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show vrf import route target\n" + JSON_STR) +{ + u_char uj = 0; + struct bgp *bgp_def = NULL; + json_object *json = NULL; + + bgp_def = bgp_get_default(); + if (!bgp_def) + return CMD_WARNING; + + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + + evpn_show_vrf_import_rts(vty, bgp_def, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + /* * Display EVPN import route-target hash table */ @@ -2978,6 +3350,91 @@ DEFUN_NOSH (exit_vni, return CMD_SUCCESS; } +DEFUN (bgp_evpn_vrf_rd, + bgp_evpn_vrf_rd_cmd, + "rd ASN:NN_OR_IP-ADDRESS:NN", + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") +{ + int ret; + struct prefix_rd prd; + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + ret = str2prefix_rd(argv[1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + + /* If same as existing value, there is nothing more to do. */ + if (bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) + return CMD_SUCCESS; + + /* Configure or update the RD. */ + evpn_configure_vrf_rd(bgp_vrf, &prd); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_vrf_rd, + no_bgp_evpn_vrf_rd_cmd, + "no rd ASN:NN_OR_IP-ADDRESS:NN", + NO_STR + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") +{ + int ret; + struct prefix_rd prd; + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + ret = str2prefix_rd(argv[2]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } + + /* Check if we should disallow. */ + if (!is_vrf_rd_configured(bgp_vrf)) { + vty_out(vty, "%% RD is not configured for this VRF\n"); + return CMD_WARNING; + } + + if (!bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) { + vty_out(vty, + "%% RD specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + + evpn_unconfigure_vrf_rd(bgp_vrf); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_vrf_rd_without_val, + no_bgp_evpn_vrf_rd_without_val_cmd, + "no rd", + NO_STR + "Route Distinguisher\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + /* Check if we should disallow. */ + if (!is_vrf_rd_configured(bgp_vrf)) { + vty_out(vty, "%% RD is not configured for this VRF\n"); + return CMD_WARNING; + } + + evpn_unconfigure_vrf_rd(bgp_vrf); + return CMD_SUCCESS; +} + DEFUN (bgp_evpn_vni_rd, bgp_evpn_vni_rd_cmd, "rd ASN:NN_OR_IP-ADDRESS:NN", @@ -3084,6 +3541,279 @@ static int bgp_evpn_rt_matches_existing(struct list *rtl, return 0; } +/* display L3VNI related info for a VRF instance */ +DEFUN (show_bgp_vrf_l3vni_info, + show_bgp_vrf_l3vni_info_cmd, + "show bgp vrf VRFNAME l3vni info [json]", + SHOW_STR + BGP_STR + "show bgp vrf\n" + "VRF Name\n" + "L3-VNI\n" + "L3-VNI info\n" + JSON_STR) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + int idx_vrf = 3; + const char *name = NULL; + struct bgp *bgp = NULL; + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + struct ecommunity *ecom = NULL; + json_object *json = NULL; + json_object *json_vnis = NULL; + json_object *json_export_rts = NULL; + json_object *json_import_rts = NULL; + u_char uj = use_json(argc, argv); + + if (uj) { + json = json_object_new_object(); + json_vnis = json_object_new_array(); + json_export_rts = json_object_new_array(); + json_import_rts = json_object_new_array(); + } + + name = argv[idx_vrf]->arg; + bgp = bgp_lookup_by_name(name); + if (!bgp) { + if (!uj) + vty_out(vty, "BGP instance for VRF %s not found", + name); + else { + json_object_string_add(json, "warning", + "BGP instance not found"); + vty_out(vty, "%s\n", + json_object_to_json_string(json)); + json_object_free(json); + } + return CMD_WARNING; + } + + if (!json) { + vty_out(vty, "BGP VRF: %s\n", name); + vty_out(vty, " Local-Ip: %s\n", + inet_ntoa(bgp->originator_ip)); + vty_out(vty, " L3-VNI: %u\n", bgp->l3vni); + vty_out(vty, " Rmac: %s\n", + prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); + vty_out(vty, " L2-VNI List:\n"); + vty_out(vty, " "); + for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn)) + vty_out(vty, "%u ", vpn->vni); + vty_out(vty, "\n"); + vty_out(vty, " Export-RTs:\n"); + vty_out(vty, " "); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) + vty_out(vty, "%s ", ecommunity_str(ecom)); + vty_out(vty, "\n"); + vty_out(vty, " Import-RTs:\n"); + vty_out(vty, " "); + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) + vty_out(vty, "%s ", ecommunity_str(ecom)); + vty_out(vty, "\n"); + vty_out(vty, " RD: %s\n", + prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN)); + } else { + json_object_string_add(json, "vrf", name); + json_object_string_add(json, "local-ip", + inet_ntoa(bgp->originator_ip)); + json_object_int_add(json, "l3vni", bgp->l3vni); + json_object_string_add(json, "rmac", + prefix_mac2str(&bgp->rmac, buf, + sizeof(buf))); + /* list of l2vnis */ + for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn)) + json_object_array_add(json_vnis, + json_object_new_int(vpn->vni)); + json_object_object_add(json, "l2vnis", json_vnis); + + /* export rts */ + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom)) + json_object_array_add(json_export_rts, + json_object_new_string( + ecommunity_str(ecom))); + json_object_object_add(json, "export-rts", json_export_rts); + + /* import rts */ + for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom)) + json_object_array_add(json_import_rts, + json_object_new_string( + ecommunity_str(ecom))); + json_object_object_add(json, "import-rts", json_import_rts); + json_object_string_add( + json, "rd", + prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN)); + + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; +} + +/* import/export rt for l3vni-vrf */ +DEFUN (bgp_evpn_vrf_rt, + bgp_evpn_vrf_rt_cmd, + "route-target RT", + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + int rt_type; + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct ecommunity *ecomadd = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!strcmp(argv[1]->arg, "import")) + rt_type = RT_TYPE_IMPORT; + else if (!strcmp(argv[1]->arg, "export")) + rt_type = RT_TYPE_EXPORT; + else if (!strcmp(argv[1]->arg, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING; + } + + /* Add/update the import route-target */ + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) { + ecomadd = ecommunity_str2com(argv[2]->arg, + ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomadd); + + /* Do nothing if we already have this import route-target */ + if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, + ecomadd)) + bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd); + } + + /* Add/update the export route-target */ + if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) { + ecomadd = ecommunity_str2com(argv[2]->arg, + ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomadd); + + /* Do nothing if we already have this export route-target */ + if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, + ecomadd)) + bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd); + } + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_vrf_rt, + no_bgp_evpn_vrf_rt_cmd, + "no route-target RT", + NO_STR + "Route Target\n" + "import and export\n" + "import\n" + "export\n" + "ASN:XX or A.B.C.D:XX\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + int rt_type, found_ecomdel; + struct ecommunity *ecomdel = NULL; + + if (!bgp) + return CMD_WARNING; + + if (!strcmp(argv[2]->arg, "import")) + rt_type = RT_TYPE_IMPORT; + else if (!strcmp(argv[2]->arg, "export")) + rt_type = RT_TYPE_EXPORT; + else if (!strcmp(argv[2]->arg, "both")) + rt_type = RT_TYPE_BOTH; + else { + vty_out(vty, "%% Invalid Route Target type\n"); + return CMD_WARNING; + } + + if (rt_type == RT_TYPE_IMPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { + vty_out(vty, + "%% Import RT is not configured for this VRF\n"); + return CMD_WARNING; + } + } else if (rt_type == RT_TYPE_EXPORT) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + vty_out(vty, + "%% Export RT is not configured for this VRF\n"); + return CMD_WARNING; + } + } else if (rt_type == RT_TYPE_BOTH) { + if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) + && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + vty_out(vty, + "%% Import/Export RT is not configured for this VRF\n"); + return CMD_WARNING; + } + } + + ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomdel) { + vty_out(vty, "%% Malformed Route Target list\n"); + return CMD_WARNING; + } + ecommunity_str(ecomdel); + + if (rt_type == RT_TYPE_IMPORT) { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, + ecomdel)) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); + } else if (rt_type == RT_TYPE_EXPORT) { + if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, + ecomdel)) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); + } else if (rt_type == RT_TYPE_BOTH) { + found_ecomdel = 0; + + if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, + ecomdel)) { + bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel); + found_ecomdel = 1; + } + + if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, + ecomdel)) { + bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel); + found_ecomdel = 1; + } + + if (!found_ecomdel) { + vty_out(vty, + "%% RT specified does not match configuration for this VRF\n"); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} DEFUN (bgp_evpn_vni_rt, bgp_evpn_vni_rt_cmd, @@ -3306,6 +4036,12 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->advertise_gw_macip) vty_out(vty, " advertise-default-gw\n"); + + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) + vty_out(vty, " advertise ipv4 unicast\n"); + + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) + vty_out(vty, " advertise ipv6 unicast\n"); } void bgp_ethernetvpn_init(void) @@ -3333,6 +4069,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd); /* "show bgp l2vpn evpn" commands. */ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); @@ -3341,11 +4079,13 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); /* "show bgp evpn" commands. */ install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd); @@ -3358,6 +4098,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd); install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd); + install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd); @@ -3368,6 +4109,11 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rd_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rd_cmd); + install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd); install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_default_gw_vni_cmd); install_element(BGP_EVPN_VNI_NODE, diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 8de7e970de0f..de453de0c826 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -27,6 +27,7 @@ #include "thread.h" #include "log.h" #include "stream.h" +#include "ringbuf.h" #include "memory.h" #include "plist.h" #include "workqueue.h" @@ -49,6 +50,8 @@ #include "bgpd/bgp_nht.h" #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_memory.h" +#include "bgpd/bgp_keepalives.h" +#include "bgpd/bgp_io.h" DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) DEFINE_HOOK(peer_established, (struct peer * peer), (peer)) @@ -86,7 +89,6 @@ int bgp_event(struct thread *); static int bgp_start_timer(struct thread *); static int bgp_connect_timer(struct thread *); static int bgp_holdtime_timer(struct thread *); -static int bgp_keepalive_timer(struct thread *); /* BGP FSM functions. */ static int bgp_start(struct peer *); @@ -125,20 +127,68 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) from_peer->host, from_peer, from_peer->fd, peer, peer->fd); - BGP_WRITE_OFF(peer->t_write); - BGP_READ_OFF(peer->t_read); - BGP_WRITE_OFF(from_peer->t_write); - BGP_READ_OFF(from_peer->t_read); + bgp_writes_off(peer); + bgp_reads_off(peer); + bgp_writes_off(from_peer); + bgp_reads_off(from_peer); BGP_TIMER_OFF(peer->t_routeadv); + BGP_TIMER_OFF(peer->t_connect); + BGP_TIMER_OFF(peer->t_connect_check_r); + BGP_TIMER_OFF(peer->t_connect_check_w); BGP_TIMER_OFF(from_peer->t_routeadv); + BGP_TIMER_OFF(from_peer->t_connect); + BGP_TIMER_OFF(from_peer->t_connect_check_r); + BGP_TIMER_OFF(from_peer->t_connect_check_w); + BGP_TIMER_OFF(from_peer->t_process_packet); + + /* + * At this point in time, it is possible that there are packets pending + * on various buffers. Those need to be transferred or dropped, + * otherwise we'll get spurious failures during session establishment. + */ + pthread_mutex_lock(&peer->io_mtx); + pthread_mutex_lock(&from_peer->io_mtx); + { + fd = peer->fd; + peer->fd = from_peer->fd; + from_peer->fd = fd; - fd = peer->fd; - peer->fd = from_peer->fd; - from_peer->fd = fd; - stream_reset(peer->ibuf); - stream_fifo_clean(peer->obuf); - stream_fifo_clean(from_peer->obuf); + stream_fifo_clean(peer->ibuf); + stream_fifo_clean(peer->obuf); + + /* + * this should never happen, since bgp_process_packet() is the + * only task that sets and unsets the current packet and it + * runs in our pthread. + */ + if (peer->curr) { + zlog_err( + "[%s] Dropping pending packet on connection transfer:", + peer->host); + u_int16_t type = stream_getc_from(peer->curr, + BGP_MARKER_SIZE + 2); + bgp_dump_packet(peer, type, peer->curr); + stream_free(peer->curr); + peer->curr = NULL; + } + + // copy each packet from old peer's output queue to new peer + while (from_peer->obuf->head) + stream_fifo_push(peer->obuf, + stream_fifo_pop(from_peer->obuf)); + + // copy each packet from old peer's input queue to new peer + while (from_peer->ibuf->head) + stream_fifo_push(peer->ibuf, + stream_fifo_pop(from_peer->ibuf)); + + ringbuf_wipe(peer->ibuf_work); + ringbuf_copy(peer->ibuf_work, from_peer->ibuf_work, + ringbuf_remain(from_peer->ibuf_work)); + } + pthread_mutex_unlock(&from_peer->io_mtx); + pthread_mutex_unlock(&peer->io_mtx); peer->as = from_peer->as; peer->v_holdtime = from_peer->v_holdtime; @@ -216,12 +266,16 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) } } - BGP_READ_ON(peer->t_read, bgp_read, peer->fd); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + // Note: peer_xfer_stats() must be called with I/O turned OFF if (from_peer) peer_xfer_stats(peer, from_peer); + bgp_reads_on(peer); + bgp_writes_on(peer); + thread_add_timer_msec(bm->master, bgp_process_packet, peer, 0, + &peer->t_process_packet); + return (peer); } @@ -243,7 +297,7 @@ void bgp_timer_set(struct peer *peer) } BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); break; @@ -255,7 +309,7 @@ void bgp_timer_set(struct peer *peer) BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, peer->v_connect); BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); break; @@ -272,7 +326,7 @@ void bgp_timer_set(struct peer *peer) peer->v_connect); } BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); break; @@ -286,7 +340,7 @@ void bgp_timer_set(struct peer *peer) } else { BGP_TIMER_OFF(peer->t_holdtime); } - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); break; @@ -299,12 +353,11 @@ void bgp_timer_set(struct peer *peer) timer and KeepAlive timers are not started. */ if (peer->v_holdtime == 0) { BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); } else { BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); - BGP_TIMER_ON(peer->t_keepalive, bgp_keepalive_timer, - peer->v_keepalive); + bgp_keepalives_on(peer); } BGP_TIMER_OFF(peer->t_routeadv); break; @@ -319,12 +372,11 @@ void bgp_timer_set(struct peer *peer) and keepalive must be turned off. */ if (peer->v_holdtime == 0) { BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); } else { BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); - BGP_TIMER_ON(peer->t_keepalive, bgp_keepalive_timer, - peer->v_keepalive); + bgp_keepalives_on(peer); } break; case Deleted: @@ -336,7 +388,7 @@ void bgp_timer_set(struct peer *peer) BGP_TIMER_OFF(peer->t_start); BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); + bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); break; } @@ -367,6 +419,10 @@ static int bgp_connect_timer(struct thread *thread) int ret; peer = THREAD_ARG(thread); + + assert(!peer->t_write); + assert(!peer->t_read); + peer->t_connect = NULL; if (bgp_debug_neighbor_events(peer)) @@ -402,24 +458,6 @@ static int bgp_holdtime_timer(struct thread *thread) return 0; } -/* BGP keepalive fire ! */ -static int bgp_keepalive_timer(struct thread *thread) -{ - struct peer *peer; - - peer = THREAD_ARG(thread); - peer->t_keepalive = NULL; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [FSM] Timer (keepalive timer expire)", - peer->host); - - THREAD_VAL(thread) = KeepAlive_timer_expired; - bgp_event(thread); /* bgp_event unlocks peer */ - - return 0; -} - int bgp_routeadv_timer(struct thread *thread) { struct peer *peer; @@ -433,7 +471,8 @@ int bgp_routeadv_timer(struct thread *thread) peer->synctime = bgp_clock(); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + thread_add_timer_msec(bm->master, bgp_generate_updgrp_packets, peer, 0, + &peer->t_generate_updgrp_packets); /* MRAI timer will be started again when FIFO is built, no need to * do it here. @@ -640,7 +679,9 @@ void bgp_adjust_routeadv(struct peer *peer) BGP_TIMER_OFF(peer->t_routeadv); peer->synctime = bgp_clock(); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + thread_add_timer_msec(bm->master, bgp_generate_updgrp_packets, + peer, 0, + &peer->t_generate_updgrp_packets); return; } @@ -1035,27 +1076,41 @@ int bgp_stop(struct peer *peer) bgp_bfd_deregister_peer(peer); } - /* Stop read and write threads when exists. */ - BGP_READ_OFF(peer->t_read); - BGP_WRITE_OFF(peer->t_write); + /* stop keepalives */ + bgp_keepalives_off(peer); + + /* Stop read and write threads. */ + bgp_writes_off(peer); + bgp_reads_off(peer); + + THREAD_OFF(peer->t_connect_check_r); + THREAD_OFF(peer->t_connect_check_w); /* Stop all timers. */ BGP_TIMER_OFF(peer->t_start); BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_holdtime); - BGP_TIMER_OFF(peer->t_keepalive); BGP_TIMER_OFF(peer->t_routeadv); - /* Stream reset. */ - peer->packet_size = 0; - /* Clear input and output buffer. */ - if (peer->ibuf) - stream_reset(peer->ibuf); - if (peer->work) - stream_reset(peer->work); - if (peer->obuf) - stream_fifo_clean(peer->obuf); + pthread_mutex_lock(&peer->io_mtx); + { + if (peer->ibuf) + stream_fifo_clean(peer->ibuf); + if (peer->obuf) + stream_fifo_clean(peer->obuf); + + if (peer->ibuf_work) + ringbuf_wipe(peer->ibuf_work); + if (peer->obuf_work) + stream_reset(peer->obuf_work); + + if (peer->curr) { + stream_free(peer->curr); + peer->curr = NULL; + } + } + pthread_mutex_unlock(&peer->io_mtx); /* Close of file descriptor. */ if (peer->fd >= 0) { @@ -1161,6 +1216,61 @@ static int bgp_stop_with_notify(struct peer *peer, u_char code, u_char sub_code) return (bgp_stop(peer)); } +/** + * Determines whether a TCP session has successfully established for a peer and + * events as appropriate. + * + * This function is called when setting up a new session. After connect() is + * called on the peer's socket (in bgp_start()), the fd is passed to poll() + * to wait for connection success or failure. When poll() returns, this + * function is called to evaluate the result. + * + * Due to differences in behavior of poll() on Linux and BSD - specifically, + * the value of .revents in the case of a closed connection - this function is + * scheduled both for a read and a write event. The write event is triggered + * when the connection is established. A read event is triggered when the + * connection is closed. Thus we need to cancel whichever one did not occur. + */ +static int bgp_connect_check(struct thread *thread) +{ + int status; + socklen_t slen; + int ret; + struct peer *peer; + + peer = THREAD_ARG(thread); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON)); + assert(!peer->t_read); + assert(!peer->t_write); + + THREAD_OFF(peer->t_connect_check_r); + THREAD_OFF(peer->t_connect_check_w); + + /* Check file descriptor. */ + slen = sizeof(status); + ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *)&status, + &slen); + + /* If getsockopt is fail, this is fatal error. */ + if (ret < 0) { + zlog_info("can't get sockopt for nonblocking connect"); + BGP_EVENT_ADD(peer, TCP_fatal_error); + return -1; + } + + /* When status is 0 then TCP connection is established. */ + if (status == 0) { + BGP_EVENT_ADD(peer, TCP_connection_open); + return 1; + } else { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [Event] Connect failed (%s)", peer->host, + safe_strerror(errno)); + BGP_EVENT_ADD(peer, TCP_connection_open_failed); + return 0; + } +} /* TCP connection open. Next we send open message to remote peer. And add read thread for reading open message. */ @@ -1178,10 +1288,11 @@ static int bgp_connect_success(struct peer *peer) __FUNCTION__, peer->host, peer->fd); bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); /* internal error */ + bgp_writes_on(peer); return -1; } - BGP_READ_ON(peer->t_read, bgp_read, peer->fd); + bgp_reads_on(peer); if (bgp_debug_neighbor_events(peer)) { char buf1[SU_ADDRSTRLEN]; @@ -1285,6 +1396,10 @@ int bgp_start(struct peer *peer) #endif } + assert(!peer->t_write); + assert(!peer->t_read); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON)); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); status = bgp_connect(peer); switch (status) { @@ -1312,8 +1427,19 @@ int bgp_start(struct peer *peer) peer->fd); return -1; } - BGP_READ_ON(peer->t_read, bgp_read, peer->fd); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + /* + * - when the socket becomes ready, poll() will signify POLLOUT + * - if it fails to connect, poll() will signify POLLHUP + * - POLLHUP is handled as a 'read' event by thread.c + * + * therefore, we schedule both a read and a write event with + * bgp_connect_check() as the handler for each and cancel the + * unused event in that function. + */ + thread_add_read(bm->master, bgp_connect_check, peer, peer->fd, + &peer->t_connect_check_r); + thread_add_write(bm->master, bgp_connect_check, peer, peer->fd, + &peer->t_connect_check_w); break; } return 0; @@ -1340,13 +1466,6 @@ static int bgp_fsm_open(struct peer *peer) return 0; } -/* Keepalive send to peer. */ -static int bgp_fsm_keepalive_expire(struct peer *peer) -{ - bgp_keepalive_send(peer); - return 0; -} - /* FSM error, unexpected event. This is error of BGP connection. So cut the peer and change to Idle status. */ static int bgp_fsm_event_error(struct peer *peer) @@ -1367,8 +1486,12 @@ static int bgp_fsm_holdtime_expire(struct peer *peer) return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0); } -/* Status goes to Established. Send keepalive packet then make first - update information. */ +/** + * Transition to Established state. + * + * Convert peer from stub to full fledged peer, set some timers, and generate + * initial updates. + */ static int bgp_establish(struct peer *peer) { afi_t afi; @@ -1458,7 +1581,10 @@ static int bgp_establish(struct peer *peer) hook_call(peer_established, peer); - /* Reset uptime, send keepalive, send current table. */ + /* Reset uptime, turn on keepalives, send current table. */ + if (!peer->v_holdtime) + bgp_keepalives_on(peer); + peer->uptime = bgp_clock(); /* Send route-refresh when ORF is enabled */ @@ -1523,11 +1649,6 @@ static int bgp_establish(struct peer *peer) /* Keepalive packet is received. */ static int bgp_fsm_keepalive(struct peer *peer) { - bgp_update_implicit_eors(peer); - - /* peer count update */ - peer->keepalive_in++; - BGP_TIMER_OFF(peer->t_holdtime); return 0; } @@ -1700,9 +1821,8 @@ static const struct { {bgp_stop, Clearing}, /* TCP_fatal_error */ {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ {bgp_fsm_holdtime_expire, Clearing}, /* Hold_Timer_expired */ - {bgp_fsm_keepalive_expire, - Established}, /* KeepAlive_timer_expired */ - {bgp_stop, Clearing}, /* Receive_OPEN_message */ + {bgp_ignore, Established}, /* KeepAlive_timer_expired */ + {bgp_stop, Clearing}, /* Receive_OPEN_message */ {bgp_fsm_keepalive, Established}, /* Receive_KEEPALIVE_message */ {bgp_fsm_update, Established}, /* Receive_UPDATE_message */ @@ -1769,6 +1889,9 @@ int bgp_event_update(struct peer *peer, int event) int passive_conn = 0; int dyn_nbr; + /* default return code */ + ret = FSM_PEER_NOOP; + other = peer->doppelganger; passive_conn = (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) ? 1 : 0; @@ -1790,37 +1913,56 @@ int bgp_event_update(struct peer *peer, int event) if (FSM[peer->status - 1][event - 1].func) ret = (*(FSM[peer->status - 1][event - 1].func))(peer); - /* When function do not want proceed next job return -1. */ if (ret >= 0) { if (ret == 1 && next == Established) { /* The case when doppelganger swap accurred in bgp_establish. Update the peer pointer accordingly */ + ret = FSM_PEER_TRANSFERRED; peer = other; } /* If status is changed. */ - if (next != peer->status) + if (next != peer->status) { bgp_fsm_change_status(peer, next); + /* + * If we're going to ESTABLISHED then we executed a + * peer transfer. In this case we can either return + * FSM_PEER_TRANSITIONED or FSM_PEER_TRANSFERRED. + * Opting for TRANSFERRED since transfer implies + * session establishment. + */ + if (ret != FSM_PEER_TRANSFERRED) + ret = FSM_PEER_TRANSITIONED; + } + /* Make sure timer is set. */ bgp_timer_set(peer); - } else if (!dyn_nbr && !passive_conn && peer->bgp) { - /* If we got a return value of -1, that means there was an - * error, restart - * the FSM. If the peer structure was deleted + } else { + /* + * If we got a return value of -1, that means there was an + * error, restart the FSM. Since bgp_stop() was called on the + * peer. only a few fields are safe to access here. In any case + * we need to indicate that the peer was stopped in the return + * code. */ - zlog_err( - "%s [FSM] Failure handling event %s in state %s, " - "prior events %s, %s, fd %d", - peer->host, bgp_event_str[peer->cur_event], - lookup_msg(bgp_status_msg, peer->status, NULL), - bgp_event_str[peer->last_event], - bgp_event_str[peer->last_major_event], peer->fd); - bgp_stop(peer); - bgp_fsm_change_status(peer, Idle); - bgp_timer_set(peer); + if (!dyn_nbr && !passive_conn && peer->bgp) { + zlog_err( + "%s [FSM] Failure handling event %s in state %s, " + "prior events %s, %s, fd %d", + peer->host, bgp_event_str[peer->cur_event], + lookup_msg(bgp_status_msg, peer->status, NULL), + bgp_event_str[peer->last_event], + bgp_event_str[peer->last_major_event], + peer->fd); + bgp_stop(peer); + bgp_fsm_change_status(peer, Idle); + bgp_timer_set(peer); + } + ret = FSM_PEER_STOPPED; } + return ret; } diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 51d5d7aaa842..d021c9884a55 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -23,36 +23,6 @@ #define _QUAGGA_BGP_FSM_H /* Macro for BGP read, write and timer thread. */ -#define BGP_READ_ON(T, F, V) \ - do { \ - if ((peer->status != Deleted)) \ - thread_add_read(bm->master, (F), peer, (V), &(T)); \ - } while (0) - -#define BGP_READ_OFF(T) \ - do { \ - if (T) \ - THREAD_READ_OFF(T); \ - } while (0) - -#define BGP_WRITE_ON(T, F, V) \ - do { \ - if ((peer)->status != Deleted) \ - thread_add_write(bm->master, (F), (peer), (V), &(T)); \ - } while (0) - -#define BGP_PEER_WRITE_ON(T, F, V, peer) \ - do { \ - if ((peer)->status != Deleted) \ - thread_add_write(bm->master, (F), (peer), (V), &(T)); \ - } while (0) - -#define BGP_WRITE_OFF(T) \ - do { \ - if (T) \ - THREAD_WRITE_OFF(T); \ - } while (0) - #define BGP_TIMER_ON(T, F, V) \ do { \ if ((peer->status != Deleted)) \ @@ -80,6 +50,12 @@ #define BGP_MSEC_JITTER 10 +/* Status codes for bgp_event_update() */ +#define FSM_PEER_NOOP 0 +#define FSM_PEER_STOPPED 1 +#define FSM_PEER_TRANSFERRED 2 +#define FSM_PEER_TRANSITIONED 3 + /* Prototypes. */ extern void bgp_fsm_nht_update(struct peer *, int valid); extern int bgp_event(struct thread *); diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c new file mode 100644 index 000000000000..f4bfc90b7e37 --- /dev/null +++ b/bgpd/bgp_io.c @@ -0,0 +1,609 @@ +/* BGP I/O. + * Implements packet I/O in a pthread. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* clang-format off */ +#include +#include // for pthread_mutex_unlock, pthread_mutex_lock + +#include "frr_pthread.h" // for frr_pthread_get, frr_pthread +#include "linklist.h" // for list_delete, list_delete_all_node, lis... +#include "log.h" // for zlog_debug, safe_strerror, zlog_err +#include "memory.h" // for MTYPE_TMP, XCALLOC, XFREE +#include "network.h" // for ERRNO_IO_RETRY +#include "stream.h" // for stream_get_endp, stream_getw_from, str... +#include "ringbuf.h" // for ringbuf_remain, ringbuf_peek, ringbuf_... +#include "thread.h" // for THREAD_OFF, THREAD_ARG, thread, thread... +#include "zassert.h" // for assert + +#include "bgpd/bgp_io.h" +#include "bgpd/bgp_debug.h" // for bgp_debug_neighbor_events, bgp_type_str +#include "bgpd/bgp_fsm.h" // for BGP_EVENT_ADD, bgp_event +#include "bgpd/bgp_packet.h" // for bgp_notify_send_with_data, bgp_notify... +#include "bgpd/bgpd.h" // for peer, BGP_MARKER_SIZE, bgp_master, bm +/* clang-format on */ + +/* forward declarations */ +static uint16_t bgp_write(struct peer *); +static uint16_t bgp_read(struct peer *); +static int bgp_process_writes(struct thread *); +static int bgp_process_reads(struct thread *); +static bool validate_header(struct peer *); + +/* generic i/o status codes */ +#define BGP_IO_TRANS_ERR (1 << 0) // EAGAIN or similar occurred +#define BGP_IO_FATAL_ERR (1 << 1) // some kind of fatal TCP error + +/* Plumbing & control variables for thread lifecycle + * ------------------------------------------------------------------------ */ +bool bgp_io_thread_run; +pthread_mutex_t *running_cond_mtx; +pthread_cond_t *running_cond; + +/* Unused callback for thread_add_read() */ +static int bgp_io_dummy(struct thread *thread) { return 0; } + +/* Poison pill task */ +static int bgp_io_finish(struct thread *thread) +{ + bgp_io_thread_run = false; + return 0; +} + +/* Extern lifecycle control functions. init -> start -> stop + * ------------------------------------------------------------------------ */ +void bgp_io_init() +{ + bgp_io_thread_run = false; + + running_cond_mtx = XCALLOC(MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t)); + running_cond = XCALLOC(MTYPE_PTHREAD_PRIM, sizeof(pthread_cond_t)); + + pthread_mutex_init(running_cond_mtx, NULL); + pthread_cond_init(running_cond, NULL); + + /* unlocked in bgp_io_wait_running() */ + pthread_mutex_lock(running_cond_mtx); +} + +void *bgp_io_start(void *arg) +{ + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + fpt->master->owner = pthread_self(); + + // fd so we can sleep in poll() + int sleeper[2]; + pipe(sleeper); + thread_add_read(fpt->master, &bgp_io_dummy, NULL, sleeper[0], NULL); + + // we definitely don't want to handle signals + fpt->master->handle_signals = false; + + struct thread task; + + pthread_mutex_lock(running_cond_mtx); + { + bgp_io_thread_run = true; + pthread_cond_signal(running_cond); + } + pthread_mutex_unlock(running_cond_mtx); + + while (bgp_io_thread_run) { + if (thread_fetch(fpt->master, &task)) { + thread_call(&task); + } + } + + close(sleeper[1]); + close(sleeper[0]); + + return NULL; +} + +void bgp_io_wait_running() +{ + while (!bgp_io_thread_run) + pthread_cond_wait(running_cond, running_cond_mtx); + + /* locked in bgp_io_init() */ + pthread_mutex_unlock(running_cond_mtx); +} + +int bgp_io_stop(void **result, struct frr_pthread *fpt) +{ + thread_add_event(fpt->master, &bgp_io_finish, NULL, 0, NULL); + pthread_join(fpt->thread, result); + + pthread_mutex_destroy(running_cond_mtx); + pthread_cond_destroy(running_cond); + + XFREE(MTYPE_PTHREAD_PRIM, running_cond_mtx); + XFREE(MTYPE_PTHREAD_PRIM, running_cond); + + return 0; +} + +/* Extern API -------------------------------------------------------------- */ + +void bgp_writes_on(struct peer *peer) +{ + assert(bgp_io_thread_run); + + assert(peer->status != Deleted); + assert(peer->obuf); + assert(peer->ibuf); + assert(peer->ibuf_work); + assert(!peer->t_connect_check_r); + assert(!peer->t_connect_check_w); + assert(peer->fd); + + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + + thread_add_write(fpt->master, bgp_process_writes, peer, peer->fd, + &peer->t_write); + SET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON); +} + +void bgp_writes_off(struct peer *peer) +{ + assert(bgp_io_thread_run); + + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + + thread_cancel_async(fpt->master, &peer->t_write, NULL); + THREAD_OFF(peer->t_generate_updgrp_packets); + + UNSET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON); +} + +void bgp_reads_on(struct peer *peer) +{ + assert(bgp_io_thread_run); + + assert(peer->status != Deleted); + assert(peer->ibuf); + assert(peer->fd); + assert(peer->ibuf_work); + assert(peer->obuf); + assert(!peer->t_connect_check_r); + assert(!peer->t_connect_check_w); + assert(peer->fd); + + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + + thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd, + &peer->t_read); + + SET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON); +} + +void bgp_reads_off(struct peer *peer) +{ + assert(bgp_io_thread_run); + + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + + thread_cancel_async(fpt->master, &peer->t_read, NULL); + THREAD_OFF(peer->t_process_packet); + + UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON); +} + +/* Internal functions ------------------------------------------------------- */ + +/** + * Called from I/O pthread when a file descriptor has become ready for writing. + */ +static int bgp_process_writes(struct thread *thread) +{ + static struct peer *peer; + peer = THREAD_ARG(thread); + uint16_t status; + bool reschedule; + bool fatal = false; + + if (peer->fd < 0) + return -1; + + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + + pthread_mutex_lock(&peer->io_mtx); + { + status = bgp_write(peer); + reschedule = (stream_fifo_head(peer->obuf) != NULL); + } + pthread_mutex_unlock(&peer->io_mtx); + + if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { /* no problem */ + } + + if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) { + reschedule = false; /* problem */ + fatal = true; + } + + if (reschedule) { + thread_add_write(fpt->master, bgp_process_writes, peer, + peer->fd, &peer->t_write); + } else if (!fatal) { + BGP_TIMER_ON(peer->t_generate_updgrp_packets, + bgp_generate_updgrp_packets, 0); + } + + return 0; +} + +/** + * Called from I/O pthread when a file descriptor has become ready for reading, + * or has hung up. + * + * We read as much data as possible, process as many packets as we can and + * place them on peer->ibuf for secondary processing by the main thread. + */ +static int bgp_process_reads(struct thread *thread) +{ + /* clang-format off */ + static struct peer *peer; // peer to read from + uint16_t status; // bgp_read status code + bool more = true; // whether we got more data + bool fatal = false; // whether fatal error occurred + bool added_pkt = false; // whether we pushed onto ->ibuf + bool header_valid = true; // whether header is valid + /* clang-format on */ + + peer = THREAD_ARG(thread); + + if (peer->fd < 0) + return -1; + + struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); + + pthread_mutex_lock(&peer->io_mtx); + { + status = bgp_read(peer); + } + pthread_mutex_unlock(&peer->io_mtx); + + /* error checking phase */ + if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { + /* no problem; just don't process packets */ + more = false; + } + + if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) { + /* problem; tear down session */ + more = false; + fatal = true; + } + + while (more) { + /* static buffer for transferring packets */ + static unsigned char pktbuf[BGP_MAX_PACKET_SIZE]; + /* shorter alias to peer's input buffer */ + struct ringbuf *ibw = peer->ibuf_work; + /* packet size as given by header */ + uint16_t pktsize = 0; + + /* check that we have enough data for a header */ + if (ringbuf_remain(ibw) < BGP_HEADER_SIZE) + break; + + /* validate header */ + header_valid = validate_header(peer); + + if (!header_valid) { + fatal = true; + break; + } + + /* header is valid; retrieve packet size */ + ringbuf_peek(ibw, BGP_MARKER_SIZE, &pktsize, sizeof(pktsize)); + + pktsize = ntohs(pktsize); + + /* if this fails we are seriously screwed */ + assert(pktsize <= BGP_MAX_PACKET_SIZE); + + /* If we have that much data, chuck it into its own + * stream and append to input queue for processing. */ + if (ringbuf_remain(ibw) >= pktsize) { + struct stream *pkt = stream_new(pktsize); + assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize); + stream_put(pkt, pktbuf, pktsize); + + pthread_mutex_lock(&peer->io_mtx); + { + stream_fifo_push(peer->ibuf, pkt); + } + pthread_mutex_unlock(&peer->io_mtx); + + added_pkt = true; + } else + break; + } + + assert(ringbuf_space(peer->ibuf_work) >= BGP_MAX_PACKET_SIZE); + + /* handle invalid header */ + if (fatal) { + /* wipe buffer just in case someone screwed up */ + ringbuf_wipe(peer->ibuf_work); + } else { + thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd, + &peer->t_read); + if (added_pkt) + thread_add_timer_msec(bm->master, bgp_process_packet, + peer, 0, &peer->t_process_packet); + } + + return 0; +} + +/** + * Flush peer output buffer. + * + * This function pops packets off of peer->obuf and writes them to peer->fd. + * The amount of packets written is equal to the minimum of peer->wpkt_quanta + * and the number of packets on the output buffer, unless an error occurs. + * + * If write() returns an error, the appropriate FSM event is generated. + * + * The return value is equal to the number of packets written + * (which may be zero). + */ +static uint16_t bgp_write(struct peer *peer) +{ + u_char type; + struct stream *s; + int num; + int update_last_write = 0; + unsigned int count = 0; + uint32_t uo = 0; + uint16_t status = 0; + uint32_t wpkt_quanta_old; + + // cache current write quanta + wpkt_quanta_old = + atomic_load_explicit(&peer->bgp->wpkt_quanta, memory_order_relaxed); + + while (count < wpkt_quanta_old && (s = stream_fifo_head(peer->obuf))) { + int writenum; + do { + writenum = stream_get_endp(s) - stream_get_getp(s); + num = write(peer->fd, STREAM_PNT(s), writenum); + + if (num < 0) { + if (!ERRNO_IO_RETRY(errno)) { + BGP_EVENT_ADD(peer, TCP_fatal_error); + SET_FLAG(status, BGP_IO_FATAL_ERR); + } else { + SET_FLAG(status, BGP_IO_TRANS_ERR); + } + + goto done; + } else if (num != writenum) // incomplete write + stream_forward_getp(s, num); + + } while (num != writenum); + + /* Retrieve BGP packet type. */ + stream_set_getp(s, BGP_MARKER_SIZE + 2); + type = stream_getc(s); + + switch (type) { + case BGP_MSG_OPEN: + atomic_fetch_add_explicit(&peer->open_out, 1, + memory_order_relaxed); + break; + case BGP_MSG_UPDATE: + atomic_fetch_add_explicit(&peer->update_out, 1, + memory_order_relaxed); + uo++; + break; + case BGP_MSG_NOTIFY: + atomic_fetch_add_explicit(&peer->notify_out, 1, + memory_order_relaxed); + /* Double start timer. */ + peer->v_start *= 2; + + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + + /* Handle Graceful Restart case where the state changes + * to Connect instead of Idle */ + BGP_EVENT_ADD(peer, BGP_Stop); + goto done; + + case BGP_MSG_KEEPALIVE: + atomic_fetch_add_explicit(&peer->keepalive_out, 1, + memory_order_relaxed); + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + atomic_fetch_add_explicit(&peer->refresh_out, 1, + memory_order_relaxed); + break; + case BGP_MSG_CAPABILITY: + atomic_fetch_add_explicit(&peer->dynamic_cap_out, 1, + memory_order_relaxed); + break; + } + + count++; + + stream_free(stream_fifo_pop(peer->obuf)); + update_last_write = 1; + } + +done : { + /* + * Update last_update if UPDATEs were written. + * Note: that these are only updated at end, + * not per message (i.e., per loop) + */ + if (uo) + atomic_store_explicit(&peer->last_update, bgp_clock(), + memory_order_relaxed); + + /* If we TXed any flavor of packet */ + if (update_last_write) + atomic_store_explicit(&peer->last_write, bgp_clock(), + memory_order_relaxed); +} + + return status; +} + +/** + * Reads a chunk of data from peer->fd into peer->ibuf_work. + * + * @return status flag (see top-of-file) + */ +static uint16_t bgp_read(struct peer *peer) +{ + size_t readsize; // how many bytes we want to read + ssize_t nbytes; // how many bytes we actually read + uint16_t status = 0; + static uint8_t ibw[BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX]; + + readsize = MIN(ringbuf_space(peer->ibuf_work), sizeof(ibw)); + nbytes = read(peer->fd, ibw, readsize); + + /* EAGAIN or EWOULDBLOCK; come back later */ + if (nbytes < 0 && ERRNO_IO_RETRY(errno)) { + SET_FLAG(status, BGP_IO_TRANS_ERR); + /* Fatal error; tear down session */ + } else if (nbytes < 0) { + zlog_err("%s [Error] bgp_read_packet error: %s", peer->host, + safe_strerror(errno)); + + if (peer->status == Established) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + } else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + BGP_EVENT_ADD(peer, TCP_fatal_error); + SET_FLAG(status, BGP_IO_FATAL_ERR); + /* Received EOF / TCP session closed */ + } else if (nbytes == 0) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [Event] BGP connection closed fd %d", + peer->host, peer->fd); + + if (peer->status == Established) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + } else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + BGP_EVENT_ADD(peer, TCP_connection_closed); + SET_FLAG(status, BGP_IO_FATAL_ERR); + } else { + assert(ringbuf_put(peer->ibuf_work, ibw, nbytes) + == (size_t)nbytes); + } + + return status; +} + +/* + * Called after we have read a BGP packet header. Validates marker, message + * type and packet length. If any of these aren't correct, sends a notify. + * + * Assumes that there are at least BGP_HEADER_SIZE readable bytes in the input + * buffer. + */ +static bool validate_header(struct peer *peer) +{ + uint16_t size; + uint8_t type; + struct ringbuf *pkt = peer->ibuf_work; + + static uint8_t m_correct[BGP_MARKER_SIZE] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + uint8_t m_rx[BGP_MARKER_SIZE] = {0x00}; + + if (ringbuf_peek(pkt, 0, m_rx, BGP_MARKER_SIZE) != BGP_MARKER_SIZE) + return false; + + if (memcmp(m_correct, m_rx, BGP_MARKER_SIZE) != 0) { + bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_NOT_SYNC); + return false; + } + + /* Get size and type in network byte order. */ + ringbuf_peek(pkt, BGP_MARKER_SIZE, &size, sizeof(size)); + ringbuf_peek(pkt, BGP_MARKER_SIZE + 2, &type, sizeof(type)); + + size = ntohs(size); + + /* BGP type check. */ + if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE + && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE + && type != BGP_MSG_ROUTE_REFRESH_NEW + && type != BGP_MSG_ROUTE_REFRESH_OLD + && type != BGP_MSG_CAPABILITY) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s unknown message type 0x%02x", peer->host, + type); + + bgp_notify_send_with_data(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE, + &type, 1); + return false; + } + + /* Minimum packet length check. */ + if ((size < BGP_HEADER_SIZE) || (size > BGP_MAX_PACKET_SIZE) + || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) + || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE) + || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE) + || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_NEW + && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_OLD + && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_CAPABILITY + && size < BGP_MSG_CAPABILITY_MIN_SIZE)) { + if (bgp_debug_neighbor_events(peer)) { + zlog_debug("%s bad message length - %d for %s", + peer->host, size, + type == 128 ? "ROUTE-REFRESH" + : bgp_type_str[(int) type]); + } + + uint16_t nsize = htons(size); + + bgp_notify_send_with_data(peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESLEN, + (unsigned char *) &nsize, 2); + return false; + } + + return true; +} diff --git a/bgpd/bgp_io.h b/bgpd/bgp_io.h new file mode 100644 index 000000000000..7cfd1db71069 --- /dev/null +++ b/bgpd/bgp_io.h @@ -0,0 +1,120 @@ +/* BGP I/O. + * Implements packet I/O in a pthread. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _FRR_BGP_IO_H +#define _FRR_BGP_IO_H + +#define BGP_WRITE_PACKET_MAX 10U +#define BGP_READ_PACKET_MAX 10U + +#include "bgpd/bgpd.h" +#include "frr_pthread.h" + +/** + * Initializes data structures and flags for the write thread. + * + * This function must be called from the main thread before + * bgp_writes_start() is invoked. + */ +extern void bgp_io_init(void); + +/** + * Start function for write thread. + * + * @param arg - unused + */ +extern void *bgp_io_start(void *arg); + +/** + * Wait until the IO thread is ready to accept jobs. + * + * This function must be called immediately after the thread has been created + * for running. Use of other functions before calling this one will result in + * undefined behavior. + */ +extern void bgp_io_wait_running(void); + +/** + * Start function for write thread. + * + * Uninitializes all resources and stops the thread. + * + * @param result - where to store data result, unused + */ +extern int bgp_io_stop(void **result, struct frr_pthread *fpt); + +/** + * Turns on packet writing for a peer. + * + * After this function is called, any packets placed on peer->obuf will be + * written to peer->fd until no more packets remain. + * + * Additionally, it becomes unsafe to perform socket actions on peer->fd. + * + * @param peer - peer to register + */ +extern void bgp_writes_on(struct peer *peer); + +/** + * Turns off packet writing for a peer. + * + * After this function returns, packets placed on peer->obuf will not be + * written to peer->fd by the I/O thread. + * + * After this function returns it becomes safe to perform socket actions on + * peer->fd. + * + * @param peer - peer to deregister + * @param flush - as described + */ +extern void bgp_writes_off(struct peer *peer); + +/** + * Turns on packet reading for a peer. + * + * After this function is called, any packets received on peer->fd will be read + * and copied into the FIFO queue peer->ibuf. + * + * Additionally, it becomes unsafe to perform socket actions on peer->fd. + * + * Whenever one or more packets are placed onto peer->ibuf, a task of type + * THREAD_EVENT will be placed on the main thread whose handler is + * + * bgp_packet.c:bgp_process_packet() + * + * @param peer - peer to register + */ +extern void bgp_reads_on(struct peer *peer); + +/** + * Turns off packet reading for a peer. + * + * After this function is called, any packets received on peer->fd will not be + * read by the I/O thread. + * + * After this function returns it becomes safe to perform socket actions on + * peer->fd. + * + * @param peer - peer to deregister + */ +extern void bgp_reads_off(struct peer *peer); + +#endif /* _FRR_BGP_IO_H */ diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c new file mode 100644 index 000000000000..afa280a799ff --- /dev/null +++ b/bgpd/bgp_keepalives.c @@ -0,0 +1,292 @@ +/* BGP Keepalives. + * Implements a producer thread to generate BGP keepalives for peers. + * Copyright (C) 2017 Cumulus Networks, Inc. + * Quentin Young + * + * This file is part of FRRouting. + * + * FRRouting is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any later + * version. + * + * FRRouting 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 + */ + +/* clang-format off */ +#include +#include // for pthread_mutex_lock, pthread_mutex_unlock + +#include "frr_pthread.h" // for frr_pthread +#include "hash.h" // for hash, hash_clean, hash_create_size... +#include "log.h" // for zlog_debug +#include "memory.h" // for MTYPE_TMP, XFREE, XCALLOC, XMALLOC +#include "monotime.h" // for monotime, monotime_since + +#include "bgpd/bgpd.h" // for peer, PEER_THREAD_KEEPALIVES_ON, peer... +#include "bgpd/bgp_debug.h" // for bgp_debug_neighbor_events +#include "bgpd/bgp_packet.h" // for bgp_keepalive_send +#include "bgpd/bgp_keepalives.h" +/* clang-format on */ + +/** + * Peer KeepAlive Timer. + * Associates a peer with the time of its last keepalive. + */ +struct pkat { + // the peer to send keepalives to + struct peer *peer; + // absolute time of last keepalive sent + struct timeval last; +}; + +/* List of peers we are sending keepalives for, and associated mutex. */ +static pthread_mutex_t *peerhash_mtx; +static pthread_cond_t *peerhash_cond; +static struct hash *peerhash; + +/* Thread control flag. */ +bool bgp_keepalives_thread_run = false; + +static struct pkat *pkat_new(struct peer *peer) +{ + struct pkat *pkat = XMALLOC(MTYPE_TMP, sizeof(struct pkat)); + pkat->peer = peer; + monotime(&pkat->last); + return pkat; +} + +static void pkat_del(void *pkat) +{ + XFREE(MTYPE_TMP, pkat); +} + + +/* + * Callback for hash_iterate. Determines if a peer needs a keepalive and if so, + * generates and sends it. + * + * For any given peer, if the elapsed time since its last keepalive exceeds its + * configured keepalive timer, a keepalive is sent to the peer and its + * last-sent time is reset. Additionally, If the elapsed time does not exceed + * the configured keepalive timer, but the time until the next keepalive is due + * is within a hardcoded tolerance, a keepalive is sent as if the configured + * timer was exceeded. Doing this helps alleviate nanosecond sleeps between + * ticks by grouping together peers who are due for keepalives at roughly the + * same time. This tolerance value is arbitrarily chosen to be 100ms. + * + * In addition, this function calculates the maximum amount of time that the + * keepalive thread can sleep before another tick needs to take place. This is + * equivalent to shortest time until a keepalive is due for any one peer. + * + * @return maximum time to wait until next update (0 if infinity) + */ +static void peer_process(struct hash_backet *hb, void *arg) +{ + struct pkat *pkat = hb->data; + + struct timeval *next_update = arg; + + static struct timeval elapsed; // elapsed time since keepalive + static struct timeval ka = {0}; // peer->v_keepalive as a timeval + static struct timeval diff; // ka - elapsed + + static struct timeval tolerance = {0, 100000}; + + // calculate elapsed time since last keepalive + monotime_since(&pkat->last, &elapsed); + + // calculate difference between elapsed time and configured time + ka.tv_sec = pkat->peer->v_keepalive; + timersub(&ka, &elapsed, &diff); + + int send_keepalive = + elapsed.tv_sec >= ka.tv_sec || timercmp(&diff, &tolerance, <); + + if (send_keepalive) { + if (bgp_debug_neighbor_events(pkat->peer)) + zlog_debug("%s [FSM] Timer (keepalive timer expire)", + pkat->peer->host); + + bgp_keepalive_send(pkat->peer); + monotime(&pkat->last); + memset(&elapsed, 0x00, sizeof(struct timeval)); + diff = ka; // time until next keepalive == peer keepalive time + } + + // if calculated next update for this peer < current delay, use it + if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <)) + *next_update = diff; +} + +static int peer_hash_cmp(const void *f, const void *s) +{ + const struct pkat *p1 = f; + const struct pkat *p2 = s; + return p1->peer == p2->peer; +} + +static unsigned int peer_hash_key(void *arg) +{ + struct pkat *pkat = arg; + return (uintptr_t)pkat->peer; +} + +void bgp_keepalives_init() +{ + peerhash_mtx = XCALLOC(MTYPE_TMP, sizeof(pthread_mutex_t)); + peerhash_cond = XCALLOC(MTYPE_TMP, sizeof(pthread_cond_t)); + + // initialize mutex + pthread_mutex_init(peerhash_mtx, NULL); + + // use monotonic clock with condition variable + pthread_condattr_t attrs; + pthread_condattr_init(&attrs); + pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC); + pthread_cond_init(peerhash_cond, &attrs); + pthread_condattr_destroy(&attrs); + + // initialize peer hashtable + peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp, NULL); +} + +static void bgp_keepalives_finish(void *arg) +{ + bgp_keepalives_thread_run = false; + + if (peerhash) { + hash_clean(peerhash, pkat_del); + hash_free(peerhash); + } + + peerhash = NULL; + + pthread_mutex_unlock(peerhash_mtx); + pthread_mutex_destroy(peerhash_mtx); + pthread_cond_destroy(peerhash_cond); + + XFREE(MTYPE_TMP, peerhash_mtx); + XFREE(MTYPE_TMP, peerhash_cond); +} + +/** + * Entry function for peer keepalive generation pthread. + * + * bgp_keepalives_init() must be called prior to this. + */ +void *bgp_keepalives_start(void *arg) +{ + struct timeval currtime = {0, 0}; + struct timeval aftertime = {0, 0}; + struct timeval next_update = {0, 0}; + struct timespec next_update_ts = {0, 0}; + + pthread_mutex_lock(peerhash_mtx); + + // register cleanup handler + pthread_cleanup_push(&bgp_keepalives_finish, NULL); + + bgp_keepalives_thread_run = true; + + while (bgp_keepalives_thread_run) { + if (peerhash->count > 0) + pthread_cond_timedwait(peerhash_cond, peerhash_mtx, + &next_update_ts); + else + while (peerhash->count == 0 + && bgp_keepalives_thread_run) + pthread_cond_wait(peerhash_cond, peerhash_mtx); + + monotime(&currtime); + + next_update.tv_sec = -1; + + hash_iterate(peerhash, peer_process, &next_update); + if (next_update.tv_sec == -1) + memset(&next_update, 0x00, sizeof(next_update)); + + monotime_since(&currtime, &aftertime); + + timeradd(&currtime, &next_update, &next_update); + TIMEVAL_TO_TIMESPEC(&next_update, &next_update_ts); + } + + // clean up + pthread_cleanup_pop(1); + + return NULL; +} + +/* --- thread external functions ------------------------------------------- */ + +void bgp_keepalives_on(struct peer *peer) +{ + /* placeholder bucket data to use for fast key lookups */ + static struct pkat holder = {0}; + + if (!peerhash_mtx) { + zlog_warn("%s: call bgp_keepalives_init() first", __func__); + return; + } + + pthread_mutex_lock(peerhash_mtx); + { + holder.peer = peer; + if (!hash_lookup(peerhash, &holder)) { + struct pkat *pkat = pkat_new(peer); + hash_get(peerhash, pkat, hash_alloc_intern); + peer_lock(peer); + } + SET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON); + } + pthread_mutex_unlock(peerhash_mtx); + bgp_keepalives_wake(); +} + +void bgp_keepalives_off(struct peer *peer) +{ + /* placeholder bucket data to use for fast key lookups */ + static struct pkat holder = {0}; + + if (!peerhash_mtx) { + zlog_warn("%s: call bgp_keepalives_init() first", __func__); + return; + } + + pthread_mutex_lock(peerhash_mtx); + { + holder.peer = peer; + struct pkat *res = hash_release(peerhash, &holder); + if (res) { + pkat_del(res); + peer_unlock(peer); + } + UNSET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON); + } + pthread_mutex_unlock(peerhash_mtx); +} + +void bgp_keepalives_wake() +{ + pthread_mutex_lock(peerhash_mtx); + { + pthread_cond_signal(peerhash_cond); + } + pthread_mutex_unlock(peerhash_mtx); +} + +int bgp_keepalives_stop(void **result, struct frr_pthread *fpt) +{ + bgp_keepalives_thread_run = false; + bgp_keepalives_wake(); + pthread_join(fpt->thread, result); + return 0; +} diff --git a/bgpd/bgp_keepalives.h b/bgpd/bgp_keepalives.h new file mode 100644 index 000000000000..1fbd035b9edd --- /dev/null +++ b/bgpd/bgp_keepalives.h @@ -0,0 +1,93 @@ +/* BGP Keepalives. + * Implements a producer thread to generate BGP keepalives for peers. + * Copyright (C) 2017 Cumulus Networks, Inc. + * Quentin Young + * + * This file is part of FRRouting. + * + * FRRouting is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any later + * version. + * + * FRRouting is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_BGP_KEEPALIVES_H +#define _FRR_BGP_KEEPALIVES_H + +#include "frr_pthread.h" +#include "bgpd.h" + +/** + * Turns on keepalives for a peer. + * + * This function adds the peer to an internal list of peers to generate + * keepalives for. + * + * At set intervals, a BGP KEEPALIVE packet is generated and placed on + * peer->obuf. This operation is thread-safe with respect to peer->obuf. + * + * peer->v_keepalive determines the interval. Changing this value before + * unregistering this peer with bgp_keepalives_off() results in undefined + * behavior. + * + * If the peer is already registered for keepalives via this function, nothing + * happens. + */ +extern void bgp_keepalives_on(struct peer *); + +/** + * Turns off keepalives for a peer. + * + * Removes the peer from the internal list of peers to generate keepalives for. + * + * If the peer is already unregistered for keepalives, nothing happens. + */ +extern void bgp_keepalives_off(struct peer *); + +/** + * Pre-run initialization function for keepalives pthread. + * + * Initializes synchronization primitives. This should be called before + * anything else to avoid race conditions. + */ +extern void bgp_keepalives_init(void); + +/** + * Entry function for keepalives pthread. + * + * This function loops over an internal list of peers, generating keepalives at + * regular intervals as determined by each peer's keepalive timer. + * + * See bgp_keepalives_on() for additional details. + * + * @param arg pthread arg, not used + */ +extern void *bgp_keepalives_start(void *arg); + +/** + * Poking function for keepalives pthread. + * + * Under normal circumstances the pthread will automatically wake itself + * whenever it is necessary to do work. This function may be used to force the + * thread to wake up and see if there is any work to do, or if it is time to + * die. + * + * It is not necessary to call this after bgp_keepalives_on(). + */ +extern void bgp_keepalives_wake(void); + +/** + * Stops the thread and blocks until it terminates. + */ +int bgp_keepalives_stop(void **result, struct frr_pthread *fpt); + +#endif /* _FRR_BGP_KEEPALIVES_H */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 1fac2936eb7d..0508f4846de4 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -20,6 +20,7 @@ #include +#include #include "vector.h" #include "command.h" #include "getopt.h" @@ -54,6 +55,8 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_filter.h" #include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_keepalives.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -191,6 +194,9 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) /* reverse bgp_attr_init */ bgp_attr_finish(); + /* stop pthreads */ + bgp_pthreads_finish(); + /* reverse access_list_init */ access_list_add_hook(NULL); access_list_delete_hook(NULL); @@ -215,6 +221,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) #endif bgp_zebra_destroy(); + bf_free(bm->rd_idspace); list_delete_and_null(&bm->bgp); memset(bm, 0, sizeof(*bm)); @@ -225,7 +232,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) static int bgp_vrf_new(struct vrf *vrf) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("VRF Created: %s(%d)", vrf->name, vrf->vrf_id); + zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); return 0; } @@ -233,7 +240,7 @@ static int bgp_vrf_new(struct vrf *vrf) static int bgp_vrf_delete(struct vrf *vrf) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("VRF Deletion: %s(%d)", vrf->name, vrf->vrf_id); + zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); return 0; } @@ -244,7 +251,7 @@ static int bgp_vrf_enable(struct vrf *vrf) vrf_id_t old_vrf_id; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("VRF enable add %s id %d", vrf->name, vrf->vrf_id); + zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id); bgp = bgp_lookup_by_name(vrf->name); if (bgp) { @@ -393,6 +400,8 @@ int main(int argc, char **argv) (bm->address ? bm->address : ""), bm->port); frr_config_fork(); + /* must be called after fork() */ + bgp_pthreads_run(); frr_run(bm->master); /* Not reached. */ diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 37054ce42583..64543ff01997 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -117,4 +117,5 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT") +DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 35b83a040153..fae98329c68c 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -113,5 +113,6 @@ DECLARE_MTYPE(LCOMMUNITY_VAL) DECLARE_MTYPE(BGP_EVPN) DECLARE_MTYPE(BGP_EVPN_IMPORT_RT) +DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT) DECLARE_MTYPE(BGP_EVPN_MACIP) #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 0d7680ea51d0..bf39cbe1fcf9 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -550,6 +550,8 @@ static int bgp_update_source(struct peer *peer) /* BGP try to connect to the peer. */ int bgp_connect(struct peer *peer) { + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON)); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); ifindex_t ifindex = 0; if (peer->conf_if && BGP_PEER_SU_UNSPEC(peer)) { diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 625d4f844260..247884d2948b 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -336,7 +336,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) { zlog_err( - "parse nexthop update: instance not found for vrf_id %d", + "parse nexthop update: instance not found for vrf_id %u", vrf_id); return; } @@ -389,7 +389,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) char buf[PREFIX2STR_BUFFER]; prefix2str(&p, buf, sizeof(buf)); zlog_debug( - "%d: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x", + "%u: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x", vrf_id, buf, metric, bnc->metric, nexthop_num, bnc->nexthop_num, bnc->flags); } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index a955b3512c9a..eed5fdc65d23 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1,4 +1,6 @@ /* BGP packet management routine. + * Contains utility functions for constructing and consuming BGP messages. + * Copyright (C) 2017 Cumulus Networks * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. @@ -19,6 +21,7 @@ */ #include +#include #include "thread.h" #include "stream.h" @@ -54,8 +57,16 @@ #include "bgpd/bgp_vty.h" #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_label.h" +#include "bgpd/bgp_io.h" +#include "bgpd/bgp_keepalives.h" -/* Set up BGP packet marker and packet type. */ +/** + * Sets marker and type fields for a BGP message. + * + * @param s the stream containing the packet + * @param type the packet type + * @return the size of the stream + */ int bgp_packet_set_marker(struct stream *s, u_char type) { int i; @@ -74,8 +85,14 @@ int bgp_packet_set_marker(struct stream *s, u_char type) return stream_get_endp(s); } -/* Set BGP packet header size entry. If size is zero then use current - stream size. */ +/** + * Sets size field for a BGP message. + * + * Size field is set to the size of the stream passed. + * + * @param s the stream containing the packet + * @return the size of the stream + */ int bgp_packet_set_size(struct stream *s) { int cp; @@ -87,54 +104,15 @@ int bgp_packet_set_size(struct stream *s) return cp; } -/* Add new packet to the peer. */ -void bgp_packet_add(struct peer *peer, struct stream *s) +/* + * Push a packet onto the beginning of the peer's output queue. + * This function acquires the peer's write mutex before proceeding. + */ +static void bgp_packet_add(struct peer *peer, struct stream *s) { - /* Add packet to the end of list. */ + pthread_mutex_lock(&peer->io_mtx); stream_fifo_push(peer->obuf, s); -} - -/* Free first packet. */ -static void bgp_packet_delete(struct peer *peer) -{ - stream_free(stream_fifo_pop(peer->obuf)); -} - -/* Check file descriptor whether connect is established. */ -int bgp_connect_check(struct peer *peer, int change_state) -{ - int status; - socklen_t slen; - int ret; - - /* Anyway I have to reset read and write thread. */ - BGP_READ_OFF(peer->t_read); - BGP_WRITE_OFF(peer->t_write); - - /* Check file descriptor. */ - slen = sizeof(status); - ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *)&status, - &slen); - - /* If getsockopt is fail, this is fatal error. */ - if (ret < 0) { - zlog_info("can't get sockopt for nonblocking connect"); - BGP_EVENT_ADD(peer, TCP_fatal_error); - return -1; - } - - /* When status is 0 then TCP connection is established. */ - if (status == 0) { - BGP_EVENT_ADD(peer, TCP_connection_open); - return 1; - } else { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [Event] Connect failed (%s)", peer->host, - safe_strerror(errno)); - if (change_state) - BGP_EVENT_ADD(peer, TCP_connection_open_failed); - return 0; - } + pthread_mutex_unlock(&peer->io_mtx); } static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi, @@ -176,106 +154,172 @@ static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi, } bgp_packet_set_size(s); - bgp_packet_add(peer, s); return s; } -/* Get next packet to be written. */ -static struct stream *bgp_write_packet(struct peer *peer) +/* Called when there is a change in the EOR(implicit or explicit) status of a + * peer. Ends the update-delay if all expected peers are done with EORs. */ +void bgp_check_update_delay(struct bgp *bgp) { - struct stream *s = NULL; - struct peer_af *paf; - struct bpacket *next_pkt; - afi_t afi; - safi_t safi; + struct listnode *node, *nnode; + struct peer *peer = NULL; - s = stream_fifo_head(peer->obuf); - if (s) - return s; + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Checking update delay, T: %d R: %d I:%d E: %d", + bgp->established, bgp->restarted_peers, + bgp->implicit_eors, bgp->explicit_eors); - /* - * The code beyond this part deals with update packets, proceed only - * if peer is Established and updates are not on hold (as part of - * update-delay post processing). - */ - if (peer->status != Established) - return NULL; + if (bgp->established + <= bgp->restarted_peers + bgp->implicit_eors + bgp->explicit_eors) { + /* + * This is an extra sanity check to make sure we wait for all + * the eligible configured peers. This check is performed if + * establish wait timer is on, or establish wait option is not + * given with the update-delay command + */ + if (bgp->t_establish_wait + || (bgp->v_establish_wait == bgp->v_update_delay)) + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (CHECK_FLAG(peer->flags, + PEER_FLAG_CONFIG_NODE) + && !CHECK_FLAG(peer->flags, + PEER_FLAG_SHUTDOWN) + && !peer->update_delay_over) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + " Peer %s pending, continuing read-only mode", + peer->host); + return; + } + } - if (peer->bgp && peer->bgp->main_peers_update_hold) - return NULL; + zlog_info( + "Update delay ended, restarted: %d, EORs implicit: %d, explicit: %d", + bgp->restarted_peers, bgp->implicit_eors, + bgp->explicit_eors); + bgp_update_delay_end(bgp); + } +} - FOREACH_AFI_SAFI (afi, safi) { - paf = peer_af_find(peer, afi, safi); - if (!paf || !PAF_SUBGRP(paf)) - continue; - next_pkt = paf->next_pkt_to_send; +/* + * Called if peer is known to have restarted. The restart-state bit in + * Graceful-Restart capability is used for that + */ +void bgp_update_restarted_peers(struct peer *peer) +{ + if (!bgp_update_delay_active(peer->bgp)) + return; /* BGP update delay has ended */ + if (peer->update_delay_over) + return; /* This peer has already been considered */ - /* Try to generate a packet for the peer if we are at - * the end of - * the list. Always try to push out WITHDRAWs first. */ - if (!next_pkt || !next_pkt->buffer) { - next_pkt = subgroup_withdraw_packet(PAF_SUBGRP(paf)); - if (!next_pkt || !next_pkt->buffer) - subgroup_update_packet(PAF_SUBGRP(paf)); - next_pkt = paf->next_pkt_to_send; - } + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Peer %s: Checking restarted", peer->host); - /* If we still don't have a packet to send to the peer, - * then - * try to find out out if we have to send eor or if not, - * skip to - * the next AFI, SAFI. - * Don't send the EOR prematurely... if the subgroup's - * coalesce - * timer is running, the adjacency-out structure is not - * created - * yet. - */ - if (!next_pkt || !next_pkt->buffer) { - if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { - if (!(PAF_SUBGRP(paf))->t_coalesce - && peer->afc_nego[afi][safi] - && peer->synctime - && !CHECK_FLAG(peer->af_sflags[afi][safi], - PEER_STATUS_EOR_SEND)) { - SET_FLAG(peer->af_sflags[afi][safi], - PEER_STATUS_EOR_SEND); - return bgp_update_packet_eor(peer, afi, - safi); - } + if (peer->status == Established) { + peer->update_delay_over = 1; + peer->bgp->restarted_peers++; + bgp_check_update_delay(peer->bgp); + } +} + +/* + * Called as peer receives a keep-alive. Determines if this occurence can be + * taken as an implicit EOR for this peer. + * NOTE: The very first keep-alive after the Established state of a peer is + * considered implicit EOR for the update-delay purposes + */ +void bgp_update_implicit_eors(struct peer *peer) +{ + if (!bgp_update_delay_active(peer->bgp)) + return; /* BGP update delay has ended */ + if (peer->update_delay_over) + return; /* This peer has already been considered */ + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Peer %s: Checking implicit EORs", peer->host); + + if (peer->status == Established) { + peer->update_delay_over = 1; + peer->bgp->implicit_eors++; + bgp_check_update_delay(peer->bgp); + } +} + +/* + * Should be called only when there is a change in the EOR_RECEIVED status + * for any afi/safi on a peer. + */ +static void bgp_update_explicit_eors(struct peer *peer) +{ + afi_t afi; + safi_t safi; + + if (!bgp_update_delay_active(peer->bgp)) + return; /* BGP update delay has ended */ + if (peer->update_delay_over) + return; /* This peer has already been considered */ + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("Peer %s: Checking explicit EORs", peer->host); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + if (peer->afc_nego[afi][safi] + && !CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + " afi %d safi %d didnt receive EOR", + afi, safi); + return; } - continue; } + peer->update_delay_over = 1; + peer->bgp->explicit_eors++; + bgp_check_update_delay(peer->bgp); +} - /* - * Found a packet template to send, overwrite packet - * with appropriate - * attributes from peer and advance peer - */ - s = bpacket_reformat_for_peer(next_pkt, paf); - bpacket_queue_advance_peer(paf); - return s; +/** + * Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers. + * + * mp_withdraw, if set, is used to nullify attr structure on most of the + * calling safi function and for evpn, passed as parameter + */ +int bgp_nlri_parse(struct peer *peer, struct attr *attr, + struct bgp_nlri *packet, int mp_withdraw) +{ + switch (packet->safi) { + case SAFI_UNICAST: + case SAFI_MULTICAST: + return bgp_nlri_parse_ip(peer, mp_withdraw ? NULL : attr, + packet); + case SAFI_LABELED_UNICAST: + return bgp_nlri_parse_label(peer, mp_withdraw ? NULL : attr, + packet); + case SAFI_MPLS_VPN: + return bgp_nlri_parse_vpn(peer, mp_withdraw ? NULL : attr, + packet); + case SAFI_EVPN: + return bgp_nlri_parse_evpn(peer, attr, packet, mp_withdraw); } - - return NULL; + return -1; } -/* The next action for the peer from a write perspective */ +/* + * Checks a variety of conditions to determine whether the peer needs to be + * rescheduled for packet generation again, and does so if necessary. + * + * @param peer to check for rescheduling + */ static void bgp_write_proceed_actions(struct peer *peer) { afi_t afi; safi_t safi; struct peer_af *paf; struct bpacket *next_pkt; - int fullq_found = 0; struct update_subgroup *subgrp; - if (stream_fifo_head(peer->obuf)) { - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); - return; - } - FOREACH_AFI_SAFI (afi, safi) { paf = peer_af_find(peer, afi, safi); if (!paf) @@ -286,7 +330,8 @@ static void bgp_write_proceed_actions(struct peer *peer) next_pkt = paf->next_pkt_to_send; if (next_pkt && next_pkt->buffer) { - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + BGP_TIMER_ON(peer->t_generate_updgrp_packets, + bgp_generate_updgrp_packets, 0); return; } @@ -294,10 +339,10 @@ static void bgp_write_proceed_actions(struct peer *peer) * subgroup packets * that need to be generated? */ if (bpacket_queue_is_full(SUBGRP_INST(subgrp), - SUBGRP_PKTQ(subgrp))) - fullq_found = 1; - else if (subgroup_packets_to_build(subgrp)) { - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + SUBGRP_PKTQ(subgrp)) + || subgroup_packets_to_build(subgrp)) { + BGP_TIMER_ON(peer->t_generate_updgrp_packets, + bgp_generate_updgrp_packets, 0); return; } @@ -308,186 +353,119 @@ static void bgp_write_proceed_actions(struct peer *peer) && !CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND) && safi != SAFI_MPLS_VPN) { - BGP_WRITE_ON(peer->t_write, bgp_write, - peer->fd); + BGP_TIMER_ON(peer->t_generate_updgrp_packets, + bgp_generate_updgrp_packets, 0); return; } } } - if (fullq_found) { - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); - return; - } } -/* Write packet to the peer. */ -int bgp_write(struct thread *thread) +/* + * Generate advertisement information (withdraws, updates, EOR) from each + * update group a peer belongs to, encode this information into packets, and + * enqueue the packets onto the peer's output buffer. + */ +int bgp_generate_updgrp_packets(struct thread *thread) { - struct peer *peer; - u_char type; + struct peer *peer = THREAD_ARG(thread); + struct stream *s; - int num; - int update_last_write = 0; - unsigned int count = 0; - unsigned int oc = 0; + struct peer_af *paf; + struct bpacket *next_pkt; + uint32_t wpq; + uint32_t generated = 0; + afi_t afi; + safi_t safi; - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG(thread); - peer->t_write = NULL; + wpq = atomic_load_explicit(&peer->bgp->wpkt_quanta, + memory_order_relaxed); - /* For non-blocking IO check. */ - if (peer->status == Connect) { - bgp_connect_check(peer, 1); + /* + * The code beyond this part deals with update packets, proceed only + * if peer is Established and updates are not on hold (as part of + * update-delay post processing). + */ + if (peer->status != Established) return 0; - } - s = bgp_write_packet(peer); - if (!s) { - bgp_write_proceed_actions(peer); + if (peer->bgp && peer->bgp->main_peers_update_hold) return 0; - } - - sockopt_cork(peer->fd, 1); - oc = peer->update_out; - - /* Nonblocking write until TCP output buffer is full. */ do { - int writenum; - - /* Number of bytes to be sent. */ - writenum = stream_get_endp(s) - stream_get_getp(s); - - /* Call write() system call. */ - num = write(peer->fd, STREAM_PNT(s), writenum); - if (num < 0) { - /* write failed either retry needed or error */ - if (ERRNO_IO_RETRY(errno)) - break; - - BGP_EVENT_ADD(peer, TCP_fatal_error); - return 0; - } - - if (num != writenum) { - /* Partial write */ - stream_forward_getp(s, num); - break; - } - - /* Retrieve BGP packet type. */ - stream_set_getp(s, BGP_MARKER_SIZE + 2); - type = stream_getc(s); + s = NULL; + FOREACH_AFI_SAFI (afi, safi) { + paf = peer_af_find(peer, afi, safi); + if (!paf || !PAF_SUBGRP(paf)) + continue; + next_pkt = paf->next_pkt_to_send; - switch (type) { - case BGP_MSG_OPEN: - peer->open_out++; - break; - case BGP_MSG_UPDATE: - peer->update_out++; - break; - case BGP_MSG_NOTIFY: - peer->notify_out++; - /* Double start timer. */ - peer->v_start *= 2; + /* + * Try to generate a packet for the peer if we are at + * the end of the list. Always try to push out + * WITHDRAWs first. + */ + if (!next_pkt || !next_pkt->buffer) { + next_pkt = subgroup_withdraw_packet( + PAF_SUBGRP(paf)); + if (!next_pkt || !next_pkt->buffer) + subgroup_update_packet(PAF_SUBGRP(paf)); + next_pkt = paf->next_pkt_to_send; + } - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); + /* + * If we still don't have a packet to send to the peer, + * then try to find out out if we have to send eor or + * if not, skip to the next AFI, SAFI. Don't send the + * EOR prematurely; if the subgroup's coalesce timer is + * running, the adjacency-out structure is not created + * yet. + */ + if (!next_pkt || !next_pkt->buffer) { + if (CHECK_FLAG(peer->cap, + PEER_CAP_RESTART_RCV)) { + if (!(PAF_SUBGRP(paf))->t_coalesce + && peer->afc_nego[afi][safi] + && peer->synctime + && !CHECK_FLAG( + peer->af_sflags[afi] + [safi], + PEER_STATUS_EOR_SEND)) { + SET_FLAG(peer->af_sflags[afi] + [safi], + PEER_STATUS_EOR_SEND); + + if ((s = bgp_update_packet_eor( + peer, afi, + safi))) { + bgp_packet_add(peer, s); + } + } + } + continue; + } - /* Flush any existing events */ - BGP_EVENT_ADD(peer, BGP_Stop); - goto done; - case BGP_MSG_KEEPALIVE: - peer->keepalive_out++; - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - peer->refresh_out++; - break; - case BGP_MSG_CAPABILITY: - peer->dynamic_cap_out++; - break; + /* Found a packet template to send, overwrite + * packet with appropriate attributes from peer + * and advance peer */ + s = bpacket_reformat_for_peer(next_pkt, paf); + bgp_packet_add(peer, s); + bpacket_queue_advance_peer(paf); } + } while (s && (++generated < wpq)); - /* OK we send packet so delete it. */ - bgp_packet_delete(peer); - update_last_write = 1; - } while (++count < peer->bgp->wpkt_quanta - && (s = bgp_write_packet(peer)) != NULL); + if (generated) + bgp_writes_on(peer); bgp_write_proceed_actions(peer); -done: - /* Update last_update if UPDATEs were written. */ - if (peer->update_out > oc) - peer->last_update = bgp_clock(); - - /* If we TXed any flavor of packet update last_write */ - if (update_last_write) - peer->last_write = bgp_clock(); - - sockopt_cork(peer->fd, 0); - return 0; -} - -/* This is only for sending NOTIFICATION message to neighbor. */ -static int bgp_write_notify(struct peer *peer) -{ - int ret, val; - u_char type; - struct stream *s; - - /* There should be at least one packet. */ - s = stream_fifo_head(peer->obuf); - if (!s) - return 0; - assert(stream_get_endp(s) >= BGP_HEADER_SIZE); - - /* Stop collecting data within the socket */ - sockopt_cork(peer->fd, 0); - - /* socket is in nonblocking mode, if we can't deliver the NOTIFY, well, - * we only care about getting a clean shutdown at this point. */ - ret = write(peer->fd, STREAM_DATA(s), stream_get_endp(s)); - - /* only connection reset/close gets counted as TCP_fatal_error, failure - * to write the entire NOTIFY doesn't get different FSM treatment */ - if (ret <= 0) { - BGP_EVENT_ADD(peer, TCP_fatal_error); - return 0; - } - - /* Disable Nagle, make NOTIFY packet go out right away */ - val = 1; - (void)setsockopt(peer->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, - sizeof(val)); - - /* Retrieve BGP packet type. */ - stream_set_getp(s, BGP_MARKER_SIZE + 2); - type = stream_getc(s); - - assert(type == BGP_MSG_NOTIFY); - - /* Type should be notify. */ - peer->notify_out++; - - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - /* Handle Graceful Restart case where the state changes to - Connect instead of Idle */ - BGP_EVENT_ADD(peer, BGP_Stop); - return 0; } -/* Make keepalive packet and send it to the peer. */ +/* + * Creates a BGP Keepalive packet and appends it to the peer's output queue. + */ void bgp_keepalive_send(struct peer *peer) { struct stream *s; @@ -509,10 +487,13 @@ void bgp_keepalive_send(struct peer *peer) /* Add packet to the peer. */ bgp_packet_add(peer, s); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + bgp_writes_on(peer); } -/* Make open packet and send it to the peer. */ +/* + * Creates a BGP Open packet and appends it to the peer's output queue. + * Sets capabilities as necessary. + */ void bgp_open_send(struct peer *peer) { struct stream *s; @@ -555,16 +536,98 @@ void bgp_open_send(struct peer *peer) peer->host, BGP_VERSION_4, local_as, send_holdtime, inet_ntoa(peer->local_id)); - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + /* Add packet to the peer. */ + bgp_packet_add(peer, s); + + bgp_writes_on(peer); +} + +/* This is only for sending NOTIFICATION message to neighbor. */ +static int bgp_write_notify(struct peer *peer) +{ + int ret, val; + u_char type; + struct stream *s; + + pthread_mutex_lock(&peer->io_mtx); + { + /* There should be at least one packet. */ + s = stream_fifo_pop(peer->obuf); + } + pthread_mutex_unlock(&peer->io_mtx); + + if (!s) + return 0; + + assert(stream_get_endp(s) >= BGP_HEADER_SIZE); + + /* Stop collecting data within the socket */ + sockopt_cork(peer->fd, 0); + + /* + * socket is in nonblocking mode, if we can't deliver the NOTIFY, well, + * we only care about getting a clean shutdown at this point. + */ + ret = write(peer->fd, STREAM_DATA(s), stream_get_endp(s)); + + /* + * only connection reset/close gets counted as TCP_fatal_error, failure + * to write the entire NOTIFY doesn't get different FSM treatment + */ + if (ret <= 0) { + stream_free(s); + BGP_EVENT_ADD(peer, TCP_fatal_error); + return 0; + } + + /* Disable Nagle, make NOTIFY packet go out right away */ + val = 1; + (void)setsockopt(peer->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, + sizeof(val)); + + /* Retrieve BGP packet type. */ + stream_set_getp(s, BGP_MARKER_SIZE + 2); + type = stream_getc(s); + + assert(type == BGP_MSG_NOTIFY); + + /* Type should be notify. */ + atomic_fetch_add_explicit(&peer->notify_out, 1, memory_order_relaxed); + peer->notify_out++; + + /* Double start timer. */ + peer->v_start *= 2; - /* Add packet to the peer. */ - bgp_packet_add(peer, s); + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + + /* + * Handle Graceful Restart case where the state changes to + * Connect instead of Idle + */ + BGP_EVENT_ADD(peer, BGP_Stop); + + stream_free(s); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + return 0; } -/* Send BGP notify packet with data potion. */ +/* + * Creates a BGP Notify and appends it to the peer's output queue. + * + * This function attempts to write the packet from the thread it is called + * from, to ensure the packet gets out ASAP. + * + * @param peer + * @param code BGP error code + * @param sub_code BGP error subcode + * @param data Data portion + * @param datalen length of data portion + */ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code, u_char *data, size_t datalen) { @@ -574,7 +637,7 @@ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code, /* Allocate new stream. */ s = stream_new(BGP_MAX_PACKET_SIZE); - /* Make nitify packet. */ + /* Make notify packet. */ bgp_packet_set_marker(s, BGP_MSG_NOTIFY); /* Set notify packet values. */ @@ -588,9 +651,32 @@ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code, /* Set BGP packet length. */ length = bgp_packet_set_size(s); - /* Add packet to the peer. */ - stream_fifo_clean(peer->obuf); - bgp_packet_add(peer, s); + /* + * Turn off keepalive generation for peer. This is necessary because + * otherwise between the time we wipe the output buffer and the time we + * push the NOTIFY onto it, the KA generation thread could have pushed + * a KEEPALIVE in the middle. + */ + bgp_keepalives_off(peer); + + /* wipe output buffer */ + pthread_mutex_lock(&peer->io_mtx); + { + stream_fifo_clean(peer->obuf); + } + pthread_mutex_unlock(&peer->io_mtx); + + /* + * If possible, store last packet for debugging purposes. This check is + * in place because we are sometimes called with a doppelganger peer, + * who tends to have a plethora of fields nulled out. + */ + if (peer->curr && peer->last_reset_cause_size) { + size_t packetsize = stream_get_endp(peer->curr); + assert(packetsize <= peer->last_reset_cause_size); + memcpy(peer->last_reset_cause, peer->curr->data, packetsize); + peer->last_reset_cause_size = packetsize; + } /* For debug */ { @@ -641,19 +727,37 @@ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code, } else peer->last_reset = PEER_DOWN_NOTIFY_SEND; - /* Call immediately. */ - BGP_WRITE_OFF(peer->t_write); + /* Add packet to peer's output queue */ + bgp_packet_add(peer, s); bgp_write_notify(peer); } -/* Send BGP notify packet. */ +/* + * Creates a BGP Notify and appends it to the peer's output queue. + * + * This function attempts to write the packet from the thread it is called + * from, to ensure the packet gets out ASAP. + * + * @param peer + * @param code BGP error code + * @param sub_code BGP error subcode + */ void bgp_notify_send(struct peer *peer, u_char code, u_char sub_code) { bgp_notify_send_with_data(peer, code, sub_code, NULL, 0); } -/* Send route refresh message to the peer. */ +/* + * Creates BGP Route Refresh packet and appends it to the peer's output queue. + * + * @param peer + * @param afi Address Family Identifier + * @param safi Subsequent Address Family Identifier + * @param orf_type Outbound Route Filtering type + * @param when_to_refresh Whether to refresh immediately or defer + * @param remove Whether to remove ORF for specified AFI/SAFI + */ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, u_char orf_type, u_char when_to_refresh, int remove) { @@ -742,10 +846,18 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, /* Add packet to the peer. */ bgp_packet_add(peer, s); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + bgp_writes_on(peer); } -/* Send capability message to the peer. */ +/* + * Create a BGP Capability packet and append it to the peer's output queue. + * + * @param peer + * @param afi Address Family Identifier + * @param safi Subsequent Address Family Identifier + * @param capability_code BGP Capability Code + * @param action Set or Remove capability + */ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, int capability_code, int action) { @@ -785,7 +897,7 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, /* Add packet to the peer. */ bgp_packet_add(peer, s); - BGP_WRITE_ON(peer->t_write, bgp_write, peer->fd); + bgp_writes_on(peer); } /* RFC1771 6.8 Connection collision detection. */ @@ -872,6 +984,42 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) return 0; } +/* Packet processing routines ---------------------------------------------- */ +/* + * This is a family of functions designed to be called from + * bgp_process_packet(). These functions all share similar behavior and should + * adhere to the following invariants and restrictions: + * + * Return codes + * ------------ + * The return code of any one of those functions should be one of the FSM event + * codes specified in bgpd.h. If a NOTIFY was sent, this event code MUST be + * BGP_Stop. Otherwise, the code SHOULD correspond to the function's expected + * packet type. For example, bgp_open_receive() should return BGP_Stop upon + * error and Receive_OPEN_message otherwise. + * + * If no action is necessary, the correct return code is BGP_PACKET_NOOP as + * defined below. + * + * Side effects + * ------------ + * - May send NOTIFY messages + * - May not modify peer->status + * - May not call bgp_event_update() + */ + +#define BGP_PACKET_NOOP 0 + +/** + * Process BGP OPEN message for peer. + * + * If any errors are encountered in the OPEN message, immediately sends NOTIFY + * and returns BGP_Stop. + * + * @param peer + * @param size size of the packet + * @return as in summary + */ static int bgp_open_receive(struct peer *peer, bgp_size_t size) { int ret; @@ -889,13 +1037,13 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) u_int16_t *holdtime_ptr; /* Parse open packet. */ - version = stream_getc(peer->ibuf); - memcpy(notify_data_remote_as, stream_pnt(peer->ibuf), 2); - remote_as = stream_getw(peer->ibuf); - holdtime_ptr = (u_int16_t *)stream_pnt(peer->ibuf); - holdtime = stream_getw(peer->ibuf); - memcpy(notify_data_remote_id, stream_pnt(peer->ibuf), 4); - remote_id.s_addr = stream_get_ipv4(peer->ibuf); + version = stream_getc(peer->curr); + memcpy(notify_data_remote_as, stream_pnt(peer->curr), 2); + remote_as = stream_getw(peer->curr); + holdtime_ptr = (u_int16_t *)stream_pnt(peer->curr); + holdtime = stream_getw(peer->curr); + memcpy(notify_data_remote_id, stream_pnt(peer->curr), 4); + remote_id.s_addr = stream_get_ipv4(peer->curr); /* Receive OPEN message log */ if (bgp_debug_neighbor_events(peer)) @@ -907,14 +1055,14 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) /* BEGIN to read the capability here, but dont do it yet */ mp_capability = 0; - optlen = stream_getc(peer->ibuf); + optlen = stream_getc(peer->curr); if (optlen != 0) { /* If not enough bytes, it is an error. */ - if (STREAM_READABLE(peer->ibuf) < optlen) { + if (STREAM_READABLE(peer->curr) < optlen) { bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_MALFORMED_ATTR); - return -1; + return BGP_Stop; } /* We need the as4 capability value *right now* because @@ -934,7 +1082,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as4, 4); - return -1; + return BGP_Stop; } if (remote_as == BGP_AS_TRANS) { @@ -949,7 +1097,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as4, 4); - return -1; + return BGP_Stop; } if (!as4 && BGP_DEBUG(as4, AS4)) @@ -979,7 +1127,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as4, 4); - return -1; + return BGP_Stop; } } @@ -992,7 +1140,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_BGP_IDENT, notify_data_remote_id, 4); - return -1; + return BGP_Stop; } /* Set remote router-id */ @@ -1010,7 +1158,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSUP_VERSION, (u_int8_t *)&maxver, 2); - return -1; + return BGP_Stop; } /* Check neighbor as number. */ @@ -1022,7 +1170,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); - return -1; + return BGP_Stop; } else if (peer->as_type == AS_INTERNAL) { if (remote_as != peer->bgp->as) { if (bgp_debug_neighbor_events(peer)) @@ -1032,7 +1180,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); - return -1; + return BGP_Stop; } peer->as = peer->local_as; } else if (peer->as_type == AS_EXTERNAL) { @@ -1044,7 +1192,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); - return -1; + return BGP_Stop; } peer->as = remote_as; } else if ((peer->as_type == AS_SPECIFIED) && (remote_as != peer->as)) { @@ -1054,7 +1202,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, notify_data_remote_as, 2); - return -1; + return BGP_Stop; } /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST @@ -1068,7 +1216,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, (u_char *)holdtime_ptr, 2); - return -1; + return BGP_Stop; } /* From the rfc: A reasonable maximum time between KEEPALIVE messages @@ -1097,7 +1245,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) if (optlen != 0) { if ((ret = bgp_open_option_parse(peer, optlen, &mp_capability)) < 0) - return ret; + return BGP_Stop; } else { if (bgp_debug_neighbor_events(peer)) zlog_debug("%s rcvd OPEN w/ OPTION parameter len: 0", @@ -1131,13 +1279,13 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) immidiately. */ ret = bgp_collision_detect(peer, remote_id); if (ret < 0) - return ret; + return BGP_Stop; /* Get sockname. */ if ((ret = bgp_getsockname(peer)) < 0) { zlog_err("%s: bgp_getsockname() failed for peer: %s", __FUNCTION__, peer->host); - return (ret); + return BGP_Stop; } /* Verify valid local address present based on negotiated @@ -1154,7 +1302,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) peer->host, peer->fd); bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return -1; + return BGP_Stop; #endif } } @@ -1170,170 +1318,42 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) peer->host, peer->fd); bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_SUBCODE_UNSPECIFIC); - return -1; + return BGP_Stop; #endif } } peer->rtt = sockopt_tcp_rtt(peer->fd); - if ((ret = bgp_event_update(peer, Receive_OPEN_message)) < 0) { - zlog_err("%s: BGP event update failed for peer: %s", - __FUNCTION__, peer->host); - /* DD: bgp send notify and reset state */ - return (ret); - } - - peer->packet_size = 0; - if (peer->ibuf) - stream_reset(peer->ibuf); - - return 0; -} - -/* Called when there is a change in the EOR(implicit or explicit) status of a - peer. - Ends the update-delay if all expected peers are done with EORs. */ -void bgp_check_update_delay(struct bgp *bgp) -{ - struct listnode *node, *nnode; - struct peer *peer = NULL; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("Checking update delay, T: %d R: %d I:%d E: %d", - bgp->established, bgp->restarted_peers, - bgp->implicit_eors, bgp->explicit_eors); - - if (bgp->established - <= bgp->restarted_peers + bgp->implicit_eors + bgp->explicit_eors) { - /* This is an extra sanity check to make sure we wait for all - the - eligible configured peers. This check is performed if - establish wait - timer is on, or establish wait option is not given with the - update-delay command */ - if (bgp->t_establish_wait - || (bgp->v_establish_wait == bgp->v_update_delay)) - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (CHECK_FLAG(peer->flags, - PEER_FLAG_CONFIG_NODE) - && !CHECK_FLAG(peer->flags, - PEER_FLAG_SHUTDOWN) - && !peer->update_delay_over) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug( - " Peer %s pending, continuing read-only mode", - peer->host); - return; - } - } - - zlog_info( - "Update delay ended, restarted: %d, EORs implicit: %d, explicit: %d", - bgp->restarted_peers, bgp->implicit_eors, - bgp->explicit_eors); - bgp_update_delay_end(bgp); - } -} - -/* Called if peer is known to have restarted. The restart-state bit in - Graceful-Restart capability is used for that */ -void bgp_update_restarted_peers(struct peer *peer) -{ - if (!bgp_update_delay_active(peer->bgp)) - return; /* BGP update delay has ended */ - if (peer->update_delay_over) - return; /* This peer has already been considered */ - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("Peer %s: Checking restarted", peer->host); - - if (peer->status == Established) { - peer->update_delay_over = 1; - peer->bgp->restarted_peers++; - bgp_check_update_delay(peer->bgp); - } + return Receive_OPEN_message; } -/* Called as peer receives a keep-alive. Determines if this occurence can be - taken as an implicit EOR for this peer. - NOTE: The very first keep-alive after the Established state of a peer is - considered implicit EOR for the update-delay purposes */ -void bgp_update_implicit_eors(struct peer *peer) +/** + * Process BGP KEEPALIVE message for peer. + * + * @param peer + * @param size size of the packet + * @return as in summary + */ +static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size) { - if (!bgp_update_delay_active(peer->bgp)) - return; /* BGP update delay has ended */ - if (peer->update_delay_over) - return; /* This peer has already been considered */ + if (bgp_debug_keepalive(peer)) + zlog_debug("%s KEEPALIVE rcvd", peer->host); - if (bgp_debug_neighbor_events(peer)) - zlog_debug("Peer %s: Checking implicit EORs", peer->host); + bgp_update_implicit_eors(peer); - if (peer->status == Established) { - peer->update_delay_over = 1; - peer->bgp->implicit_eors++; - bgp_check_update_delay(peer->bgp); - } + return Receive_KEEPALIVE_message; } -/* Should be called only when there is a change in the EOR_RECEIVED status - for any afi/safi on a peer */ -static void bgp_update_explicit_eors(struct peer *peer) -{ - afi_t afi; - safi_t safi; - - if (!bgp_update_delay_active(peer->bgp)) - return; /* BGP update delay has ended */ - if (peer->update_delay_over) - return; /* This peer has already been considered */ - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("Peer %s: Checking explicit EORs", peer->host); - - FOREACH_AFI_SAFI (afi, safi) { - if (peer->afc_nego[afi][safi] - && !CHECK_FLAG(peer->af_sflags[afi][safi], - PEER_STATUS_EOR_RECEIVED)) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug( - " afi %d safi %d didnt receive EOR", - afi, safi); - return; - } - } - - peer->update_delay_over = 1; - peer->bgp->explicit_eors++; - bgp_check_update_delay(peer->bgp); -} -/* Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers - * mp_withdraw, if set, is used to nullify attr structure on most of the calling - * safi function - * and for evpn, passed as parameter +/** + * Process BGP UPDATE message for peer. + * + * Parses UPDATE and creates attribute object. + * + * @param peer + * @param size size of the packet + * @return as in summary */ -int bgp_nlri_parse(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int mp_withdraw) -{ - switch (packet->safi) { - case SAFI_UNICAST: - case SAFI_MULTICAST: - return bgp_nlri_parse_ip(peer, mp_withdraw ? NULL : attr, - packet); - case SAFI_LABELED_UNICAST: - return bgp_nlri_parse_label(peer, mp_withdraw ? NULL : attr, - packet); - case SAFI_MPLS_VPN: - return bgp_nlri_parse_vpn(peer, mp_withdraw ? NULL : attr, - packet); - case SAFI_EVPN: - return bgp_nlri_parse_evpn(peer, attr, packet, mp_withdraw); - default: - return -1; - } -} - -/* Parse BGP Update packet and make attribute object. */ static int bgp_update_receive(struct peer *peer, bgp_size_t size) { int ret, nlri_ret; @@ -1359,7 +1379,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); - return -1; + return BGP_Stop; } /* Set initial values. */ @@ -1370,7 +1390,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) memset(peer->rcvd_attr_str, 0, BUFSIZ); peer->rcvd_attr_printed = 0; - s = peer->ibuf; + s = peer->curr; end = stream_pnt(s) + size; /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute @@ -1384,7 +1404,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->host); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; + return BGP_Stop; } /* Unfeasible Route Length. */ @@ -1398,7 +1418,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->host, withdraw_len); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; + return BGP_Stop; } /* Unfeasible Route packet format check. */ @@ -1418,7 +1438,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->host); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; + return BGP_Stop; } /* Fetch attribute total length. */ @@ -1432,7 +1452,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->host, attribute_len); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; + return BGP_Stop; } /* Certain attribute parsing errors should not be considered bad enough @@ -1455,7 +1475,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) &nlris[NLRI_MP_WITHDRAW]); if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) { bgp_attr_unintern_sub(&attr); - return -1; + return BGP_Stop; } } @@ -1534,7 +1554,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) ? BGP_NOTIFY_UPDATE_INVAL_NETWORK : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR); bgp_attr_unintern_sub(&attr); - return -1; + return BGP_Stop; } } @@ -1590,24 +1610,23 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) interned in bgp_attr_parse(). */ bgp_attr_unintern_sub(&attr); - /* If peering is stopped due to some reason, do not generate BGP - event. */ - if (peer->status != Established) - return 0; - - /* Increment packet counter. */ - peer->update_in++; peer->update_time = bgp_clock(); /* Rearm holdtime timer */ BGP_TIMER_OFF(peer->t_holdtime); bgp_timer_set(peer); - return 0; + return Receive_UPDATE_message; } -/* Notify message treatment function. */ -static void bgp_notify_receive(struct peer *peer, bgp_size_t size) +/** + * Process BGP NOTIFY message for peer. + * + * @param peer + * @param size size of the packet + * @return as in summary + */ +static int bgp_notify_receive(struct peer *peer, bgp_size_t size) { struct bgp_notify bgp_notify; @@ -1617,8 +1636,8 @@ static void bgp_notify_receive(struct peer *peer, bgp_size_t size) peer->notify.length = 0; } - bgp_notify.code = stream_getc(peer->ibuf); - bgp_notify.subcode = stream_getc(peer->ibuf); + bgp_notify.code = stream_getc(peer->curr); + bgp_notify.subcode = stream_getc(peer->curr); bgp_notify.length = size - 2; bgp_notify.data = NULL; @@ -1629,7 +1648,7 @@ static void bgp_notify_receive(struct peer *peer, bgp_size_t size) if (bgp_notify.length) { peer->notify.length = size - 2; peer->notify.data = XMALLOC(MTYPE_TMP, size - 2); - memcpy(peer->notify.data, stream_pnt(peer->ibuf), size - 2); + memcpy(peer->notify.data, stream_pnt(peer->curr), size - 2); } /* For debug */ @@ -1644,12 +1663,12 @@ static void bgp_notify_receive(struct peer *peer, bgp_size_t size) for (i = 0; i < bgp_notify.length; i++) if (first) { sprintf(c, " %02x", - stream_getc(peer->ibuf)); + stream_getc(peer->curr)); strcat(bgp_notify.data, c); } else { first = 1; sprintf(c, "%02x", - stream_getc(peer->ibuf)); + stream_getc(peer->curr)); strcpy(bgp_notify.data, c); } bgp_notify.raw_data = (u_char *)peer->notify.data; @@ -1664,7 +1683,7 @@ static void bgp_notify_receive(struct peer *peer, bgp_size_t size) } /* peer count update */ - peer->notify_in++; + atomic_fetch_add_explicit(&peer->notify_in, 1, memory_order_relaxed); peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED; @@ -1676,20 +1695,17 @@ static void bgp_notify_receive(struct peer *peer, bgp_size_t size) && bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM) UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - BGP_EVENT_ADD(peer, Receive_NOTIFICATION_message); -} - -/* Keepalive treatment function -- get keepalive send keepalive */ -static void bgp_keepalive_receive(struct peer *peer, bgp_size_t size) -{ - if (bgp_debug_keepalive(peer)) - zlog_debug("%s KEEPALIVE rcvd", peer->host); - - BGP_EVENT_ADD(peer, Receive_KEEPALIVE_message); + return Receive_NOTIFICATION_message; } -/* Route refresh message is received. */ -static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) +/** + * Process BGP ROUTEREFRESH message for peer. + * + * @param peer + * @param size size of the packet + * @return as in summary + */ +static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) { iana_afi_t pkt_afi; afi_t afi; @@ -1706,7 +1722,7 @@ static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) peer->host); bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, BGP_NOTIFY_HEADER_BAD_MESTYPE); - return; + return BGP_Stop; } /* Status must be Established. */ @@ -1716,10 +1732,10 @@ static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); - return; + return BGP_Stop; } - s = peer->ibuf; + s = peer->curr; /* Parse packet. */ pkt_afi = stream_getw(s); @@ -1735,7 +1751,7 @@ static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) zlog_info( "%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", peer->host, pkt_afi, pkt_safi); - return; + return BGP_PACKET_NOOP; } if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) { @@ -1749,7 +1765,7 @@ static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) zlog_info("%s ORF route refresh length error", peer->host); bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); - return; + return BGP_Stop; } when_to_refresh = stream_getc(s); @@ -1920,7 +1936,7 @@ static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) ? "Defer" : "Immediate"); if (when_to_refresh == REFRESH_DEFER) - return; + return BGP_PACKET_NOOP; } /* First update is deferred until ORF or ROUTE-REFRESH is received */ @@ -1951,8 +1967,18 @@ static void bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) /* Perform route refreshment to the peer */ bgp_announce_route(peer, afi, safi); + + /* No FSM action necessary */ + return BGP_PACKET_NOOP; } +/** + * Parse BGP CAPABILITY message for peer. + * + * @param peer + * @param size size of the packet + * @return as in summary + */ static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, bgp_size_t length) { @@ -1973,7 +1999,7 @@ static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, if (pnt + 3 > end) { zlog_info("%s Capability length error", peer->host); bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); - return -1; + return BGP_Stop; } action = *pnt; hdr = (struct capability_header *)(pnt + 1); @@ -1984,7 +2010,7 @@ static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, zlog_info("%s Capability Action Value error %d", peer->host, action); bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); - return -1; + return BGP_Stop; } if (bgp_debug_neighbor_events(peer)) @@ -1996,7 +2022,7 @@ static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, if ((pnt + hdr->length + 3) > end) { zlog_info("%s Capability length error", peer->host); bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); - return -1; + return BGP_Stop; } /* Fetch structure to the byte stream. */ @@ -2047,7 +2073,7 @@ static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, if (peer_active_nego(peer)) bgp_clear_route(peer, afi, safi); else - BGP_EVENT_ADD(peer, BGP_Stop); + return BGP_Stop; } } else { zlog_warn( @@ -2055,19 +2081,26 @@ static int bgp_capability_msg_parse(struct peer *peer, u_char *pnt, peer->host, hdr->code); } } - return 0; + + /* No FSM action necessary */ + return BGP_PACKET_NOOP; } -/* Dynamic Capability is received. +/** + * Parse BGP CAPABILITY message for peer. * - * This is exported for unit-test purposes + * Exported for unit testing. + * + * @param peer + * @param size size of the packet + * @return as in summary */ int bgp_capability_receive(struct peer *peer, bgp_size_t size) { u_char *pnt; /* Fetch pointer. */ - pnt = stream_pnt(peer->ibuf); + pnt = stream_pnt(peer->curr); if (bgp_debug_neighbor_events(peer)) zlog_debug("%s rcv CAPABILITY", peer->host); @@ -2078,7 +2111,7 @@ int bgp_capability_receive(struct peer *peer, bgp_size_t size) peer->host); bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, BGP_NOTIFY_HEADER_BAD_MESTYPE); - return -1; + return BGP_Stop; } /* Status must be Established. */ @@ -2088,254 +2121,175 @@ int bgp_capability_receive(struct peer *peer, bgp_size_t size) peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); - return -1; + return BGP_Stop; } /* Parse packet. */ return bgp_capability_msg_parse(peer, pnt, size); } -/* BGP read utility function. */ -static int bgp_read_packet(struct peer *peer) +/** + * Processes a peer's input buffer. + * + * This function sidesteps the event loop and directly calls bgp_event_update() + * after processing each BGP message. This is necessary to ensure proper + * ordering of FSM events and unifies the behavior that was present previously, + * whereby some of the packet handling functions would update the FSM and some + * would not, making event flow difficult to understand. Please think twice + * before hacking this. + * + * Thread type: THREAD_EVENT + * @param thread + * @return 0 + */ +int bgp_process_packet(struct thread *thread) { - int nbytes; - int readsize; + /* Yes first of all get peer pointer. */ + struct peer *peer; // peer + uint32_t rpkt_quanta_old; // how many packets to read + int fsm_update_result; // return code of bgp_event_update() + int mprc; // message processing return code - readsize = peer->packet_size - stream_get_endp(peer->ibuf); + peer = THREAD_ARG(thread); + rpkt_quanta_old = atomic_load_explicit(&peer->bgp->rpkt_quanta, + memory_order_relaxed); + fsm_update_result = 0; - /* If size is zero then return. */ - if (!readsize) + /* Guard against scheduled events that occur after peer deletion. */ + if (peer->status == Deleted || peer->status == Clearing) return 0; - /* Read packet from fd. */ - nbytes = stream_read_try(peer->ibuf, peer->fd, readsize); - - /* If read byte is smaller than zero then error occured. */ - if (nbytes < 0) { - /* Transient error should retry */ - if (nbytes == -2) - return -1; - - zlog_err("%s [Error] bgp_read_packet error: %s", peer->host, - safe_strerror(errno)); - - if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); - } else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } + unsigned int processed = 0; - BGP_EVENT_ADD(peer, TCP_fatal_error); - return -1; - } + while (processed < rpkt_quanta_old) { + u_char type = 0; + bgp_size_t size; + char notify_data_length[2]; - /* When read byte is zero : clear bgp peer and return */ - if (nbytes == 0) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [Event] BGP connection closed fd %d", - peer->host, peer->fd); - - if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); - } else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; + pthread_mutex_lock(&peer->io_mtx); + { + peer->curr = stream_fifo_pop(peer->ibuf); } + pthread_mutex_unlock(&peer->io_mtx); - BGP_EVENT_ADD(peer, TCP_connection_closed); - return -1; - } - - /* We read partial packet. */ - if (stream_get_endp(peer->ibuf) != peer->packet_size) - return -1; - - return 0; -} - -/* Marker check. */ -static int bgp_marker_all_one(struct stream *s, int length) -{ - int i; - - for (i = 0; i < length; i++) - if (s->data[i] != 0xff) + if (peer->curr == NULL) // no packets to process, hmm... return 0; - return 1; -} - -/* Starting point of packet process function. */ -int bgp_read(struct thread *thread) -{ - int ret; - u_char type = 0; - struct peer *peer; - bgp_size_t size; - char notify_data_length[2]; - u_int32_t notify_out; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG(thread); - peer->t_read = NULL; + /* skip the marker and copy the packet length */ + stream_forward_getp(peer->curr, BGP_MARKER_SIZE); + memcpy(notify_data_length, stream_pnt(peer->curr), 2); - /* Note notify_out so we can check later to see if we sent another one - */ - notify_out = peer->notify_out; + /* read in the packet length and type */ + size = stream_getw(peer->curr); + type = stream_getc(peer->curr); - /* For non-blocking IO check. */ - if (peer->status == Connect) { - bgp_connect_check(peer, 1); - goto done; - } else { - if (peer->fd < 0) { - zlog_err("bgp_read peer's fd is negative value %d", - peer->fd); - return -1; - } - BGP_READ_ON(peer->t_read, bgp_read, peer->fd); - } + /* BGP packet dump function. */ + bgp_dump_packet(peer, type, peer->curr); - /* Read packet header to determine type of the packet */ - if (peer->packet_size == 0) - peer->packet_size = BGP_HEADER_SIZE; - - if (stream_get_endp(peer->ibuf) < BGP_HEADER_SIZE) { - ret = bgp_read_packet(peer); - - /* Header read error or partial read packet. */ - if (ret < 0) - goto done; - - /* Get size and type. */ - stream_forward_getp(peer->ibuf, BGP_MARKER_SIZE); - memcpy(notify_data_length, stream_pnt(peer->ibuf), 2); - size = stream_getw(peer->ibuf); - type = stream_getc(peer->ibuf); - - /* Marker check */ - if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) - && !bgp_marker_all_one(peer->ibuf, BGP_MARKER_SIZE)) { - bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_NOT_SYNC); - goto done; - } + /* adjust size to exclude the marker + length + type */ + size -= BGP_HEADER_SIZE; - /* BGP type check. */ - if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE - && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE - && type != BGP_MSG_ROUTE_REFRESH_NEW - && type != BGP_MSG_ROUTE_REFRESH_OLD - && type != BGP_MSG_CAPABILITY) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s unknown message type 0x%02x", - peer->host, type); - bgp_notify_send_with_data(peer, BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE, - &type, 1); - goto done; - } - /* Mimimum packet length check. */ - if ((size < BGP_HEADER_SIZE) || (size > BGP_MAX_PACKET_SIZE) - || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) - || (type == BGP_MSG_UPDATE - && size < BGP_MSG_UPDATE_MIN_SIZE) - || (type == BGP_MSG_NOTIFY - && size < BGP_MSG_NOTIFY_MIN_SIZE) - || (type == BGP_MSG_KEEPALIVE - && size != BGP_MSG_KEEPALIVE_MIN_SIZE) - || (type == BGP_MSG_ROUTE_REFRESH_NEW - && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) - || (type == BGP_MSG_ROUTE_REFRESH_OLD - && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) - || (type == BGP_MSG_CAPABILITY - && size < BGP_MSG_CAPABILITY_MIN_SIZE)) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s bad message length - %d for %s", - peer->host, size, - type == 128 - ? "ROUTE-REFRESH" - : bgp_type_str[(int)type]); - bgp_notify_send_with_data(peer, BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESLEN, - (u_char *)notify_data_length, - 2); - goto done; + /* Read rest of the packet and call each sort of packet routine + */ + switch (type) { + case BGP_MSG_OPEN: + atomic_fetch_add_explicit(&peer->open_in, 1, + memory_order_relaxed); + mprc = bgp_open_receive(peer, size); + if (mprc == BGP_Stop) + zlog_err( + "%s: BGP OPEN receipt failed for peer: %s", + __FUNCTION__, peer->host); + break; + case BGP_MSG_UPDATE: + atomic_fetch_add_explicit(&peer->update_in, 1, + memory_order_relaxed); + peer->readtime = monotime(NULL); + mprc = bgp_update_receive(peer, size); + if (mprc == BGP_Stop) + zlog_err( + "%s: BGP UPDATE receipt failed for peer: %s", + __FUNCTION__, peer->host); + break; + case BGP_MSG_NOTIFY: + atomic_fetch_add_explicit(&peer->notify_in, 1, + memory_order_relaxed); + mprc = bgp_notify_receive(peer, size); + if (mprc == BGP_Stop) + zlog_err( + "%s: BGP NOTIFY receipt failed for peer: %s", + __FUNCTION__, peer->host); + break; + case BGP_MSG_KEEPALIVE: + peer->readtime = monotime(NULL); + atomic_fetch_add_explicit(&peer->keepalive_in, 1, + memory_order_relaxed); + mprc = bgp_keepalive_receive(peer, size); + if (mprc == BGP_Stop) + zlog_err( + "%s: BGP KEEPALIVE receipt failed for peer: %s", + __FUNCTION__, peer->host); + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + atomic_fetch_add_explicit(&peer->refresh_in, 1, + memory_order_relaxed); + mprc = bgp_route_refresh_receive(peer, size); + if (mprc == BGP_Stop) + zlog_err( + "%s: BGP ROUTEREFRESH receipt failed for peer: %s", + __FUNCTION__, peer->host); + break; + case BGP_MSG_CAPABILITY: + atomic_fetch_add_explicit(&peer->dynamic_cap_in, 1, + memory_order_relaxed); + mprc = bgp_capability_receive(peer, size); + if (mprc == BGP_Stop) + zlog_err( + "%s: BGP CAPABILITY receipt failed for peer: %s", + __FUNCTION__, peer->host); + break; + default: + /* + * The message type should have been sanitized before + * we ever got here. Receipt of a message with an + * invalid header at this point is indicative of a + * security issue. + */ + assert (!"Message of invalid type received during input processing"); } - /* Adjust size to message length. */ - peer->packet_size = size; - } + /* delete processed packet */ + stream_free(peer->curr); + peer->curr = NULL; + processed++; - ret = bgp_read_packet(peer); - if (ret < 0) - goto done; - - /* Get size and type again. */ - (void)stream_getw_from(peer->ibuf, BGP_MARKER_SIZE); - type = stream_getc_from(peer->ibuf, BGP_MARKER_SIZE + 2); - - /* BGP packet dump function. */ - bgp_dump_packet(peer, type, peer->ibuf); - - size = (peer->packet_size - BGP_HEADER_SIZE); - - /* Read rest of the packet and call each sort of packet routine */ - switch (type) { - case BGP_MSG_OPEN: - peer->open_in++; - bgp_open_receive(peer, size); /* XXX return value ignored! */ - break; - case BGP_MSG_UPDATE: - peer->readtime = monotime(NULL); - bgp_update_receive(peer, size); - break; - case BGP_MSG_NOTIFY: - bgp_notify_receive(peer, size); - break; - case BGP_MSG_KEEPALIVE: - peer->readtime = monotime(NULL); - bgp_keepalive_receive(peer, size); - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - peer->refresh_in++; - bgp_route_refresh_receive(peer, size); - break; - case BGP_MSG_CAPABILITY: - peer->dynamic_cap_in++; - bgp_capability_receive(peer, size); - break; - } + /* Update FSM */ + if (mprc != BGP_PACKET_NOOP) + fsm_update_result = bgp_event_update(peer, mprc); + else + continue; - /* If reading this packet caused us to send a NOTIFICATION then store a - * copy - * of the packet for troubleshooting purposes - */ - if (notify_out < peer->notify_out) { - memcpy(peer->last_reset_cause, peer->ibuf->data, - peer->packet_size); - peer->last_reset_cause_size = peer->packet_size; - notify_out = peer->notify_out; + /* + * If peer was deleted, do not process any more packets. This + * is usually due to executing BGP_Stop or a stub deletion. + */ + if (fsm_update_result == FSM_PEER_TRANSFERRED + || fsm_update_result == FSM_PEER_STOPPED) + break; } - /* Clear input buffer. */ - peer->packet_size = 0; - if (peer->ibuf) - stream_reset(peer->ibuf); - -done: - /* If reading this packet caused us to send a NOTIFICATION then store a - * copy - * of the packet for troubleshooting purposes - */ - if (notify_out < peer->notify_out) { - memcpy(peer->last_reset_cause, peer->ibuf->data, - peer->packet_size); - peer->last_reset_cause_size = peer->packet_size; + if (fsm_update_result != FSM_PEER_TRANSFERRED + && fsm_update_result != FSM_PEER_STOPPED) { + pthread_mutex_lock(&peer->io_mtx); + { + // more work to do, come back later + if (peer->ibuf->count > 0) + thread_add_timer_msec( + bm->master, bgp_process_packet, peer, 0, + &peer->t_process_packet); + } + pthread_mutex_unlock(&peer->io_mtx); } return 0; diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 7bf498c37cfc..008f2b814b8c 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -24,7 +24,6 @@ #define BGP_NLRI_LENGTH 1U #define BGP_TOTAL_ATTR_LEN 2U #define BGP_UNFEASIBLE_LEN 2U -#define BGP_WRITE_PACKET_MAX 10U /* When to refresh */ #define REFRESH_IMMEDIATE 1 @@ -38,10 +37,6 @@ #define ORF_COMMON_PART_DENY 0x20 /* Packet send and receive function prototypes. */ -extern int bgp_read(struct thread *); -extern int bgp_write(struct thread *); -extern int bgp_connect_check(struct peer *, int change_state); - extern void bgp_keepalive_send(struct peer *); extern void bgp_open_send(struct peer *); extern void bgp_notify_send(struct peer *, u_int8_t, u_int8_t); @@ -65,6 +60,8 @@ extern void bgp_check_update_delay(struct bgp *); extern int bgp_packet_set_marker(struct stream *s, u_char type); extern int bgp_packet_set_size(struct stream *s); -extern void bgp_packet_add(struct peer *peer, struct stream *s); + +extern int bgp_generate_updgrp_packets(struct thread *); +extern int bgp_process_packet(struct thread *); #endif /* _QUAGGA_BGP_PACKET_H */ diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c index 2b676e052b34..ae1ec7b84525 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -166,8 +166,7 @@ char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size) struct rd_as rd_as; struct rd_ip rd_ip; - if (size < RD_ADDRSTRLEN) - return NULL; + assert(size >= RD_ADDRSTRLEN); pnt = prd->val; @@ -197,5 +196,7 @@ char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size) return buf; } #endif - return NULL; + + snprintf(buf, size, "Unknown Type: %d", type); + return buf; } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a655bd0b6f4f..fdc7f22ae859 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -74,6 +74,9 @@ #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_vty.h" +#ifndef VTYSH_EXTRACT_PL +#include "bgpd/bgp_route_clippy.c" +#endif /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; @@ -2219,6 +2222,14 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } } + /* advertise/withdraw type-5 routes */ + if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { + if (new_select) + bgp_evpn_advertise_type5_route(bgp, rn, afi, safi); + else if (old_select) + bgp_evpn_withdraw_type5_route(bgp, rn, afi, safi); + } + /* Clear any route change flags. */ bgp_zebra_clear_route_change_flags(rn); @@ -2708,7 +2719,9 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id, /* AS path local-as loop check. */ if (peer->change_local_as) { - if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) + if (peer->allowas_in[afi][safi]) + aspath_loop_count = peer->allowas_in[afi][safi]; + else if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) aspath_loop_count = 1; if (aspath_loop_check(attr->aspath, peer->change_local_as) @@ -4480,9 +4493,9 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, /* Configure static BGP network. When user don't run zebra, static route should be installed as valid. */ -static int bgp_static_set(struct vty *vty, const char *ip_str, afi_t afi, - safi_t safi, const char *rmap, int backdoor, - u_int32_t label_index) +static int bgp_static_set(struct vty *vty, const char *negate, + const char *ip_str, afi_t afi, safi_t safi, + const char *rmap, int backdoor, u_int32_t label_index) { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; @@ -4504,112 +4517,109 @@ static int bgp_static_set(struct vty *vty, const char *ip_str, afi_t afi, apply_mask(&p); - /* Set BGP static route configuration. */ - rn = bgp_node_get(bgp->route[afi][safi], &p); + if (negate) { - if (rn->info) { - /* Configuration change. */ - bgp_static = rn->info; + /* Set BGP static route configuration. */ + rn = bgp_node_lookup(bgp->route[afi][safi], &p); - /* Label index cannot be changed. */ - if (bgp_static->label_index != label_index) { - vty_out(vty, "%% Label index cannot be changed\n"); + if (!rn) { + vty_out(vty, + "%% Can't find static route specified\n"); return CMD_WARNING_CONFIG_FAILED; } - /* Check previous routes are installed into BGP. */ - if (bgp_static->valid && bgp_static->backdoor != backdoor) - need_update = 1; - - bgp_static->backdoor = backdoor; + bgp_static = rn->info; - if (rmap) { - if (bgp_static->rmap.name) - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp_static->rmap.name); - bgp_static->rmap.name = - XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); - bgp_static->rmap.map = route_map_lookup_by_name(rmap); - } else { - if (bgp_static->rmap.name) - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp_static->rmap.name); - bgp_static->rmap.name = NULL; - bgp_static->rmap.map = NULL; - bgp_static->valid = 0; + if ((label_index != BGP_INVALID_LABEL_INDEX) + && (label_index != bgp_static->label_index)) { + vty_out(vty, + "%% label-index doesn't match static route\n"); + return CMD_WARNING_CONFIG_FAILED; } - bgp_unlock_node(rn); - } else { - /* New configuration. */ - bgp_static = bgp_static_new(); - bgp_static->backdoor = backdoor; - bgp_static->valid = 0; - bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = 0; - bgp_static->label_index = label_index; - if (rmap) { - if (bgp_static->rmap.name) - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp_static->rmap.name); - bgp_static->rmap.name = - XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); - bgp_static->rmap.map = route_map_lookup_by_name(rmap); + if ((rmap && bgp_static->rmap.name) + && strcmp(rmap, bgp_static->rmap.name)) { + vty_out(vty, + "%% route-map name doesn't match static route\n"); + return CMD_WARNING_CONFIG_FAILED; } - rn->info = bgp_static; - } - bgp_static->valid = 1; - if (need_update) - bgp_static_withdraw(bgp, &p, afi, safi); + /* Update BGP RIB. */ + if (!bgp_static->backdoor) + bgp_static_withdraw(bgp, &p, afi, safi); - if (!bgp_static->backdoor) - bgp_static_update(bgp, &p, bgp_static, afi, safi); + /* Clear configuration. */ + bgp_static_free(bgp_static); + rn->info = NULL; + bgp_unlock_node(rn); + bgp_unlock_node(rn); + } else { - return CMD_SUCCESS; -} + /* Set BGP static route configuration. */ + rn = bgp_node_get(bgp->route[afi][safi], &p); -/* Configure static BGP network. */ -static int bgp_static_unset(struct vty *vty, const char *ip_str, afi_t afi, - safi_t safi) -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - int ret; - struct prefix p; - struct bgp_static *bgp_static; - struct bgp_node *rn; + if (rn->info) { + /* Configuration change. */ + bgp_static = rn->info; - /* Convert IP prefix string to struct prefix. */ - ret = str2prefix(ip_str, &p); - if (!ret) { - vty_out(vty, "%% Malformed prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) { - vty_out(vty, "%% Malformed prefix (link-local address)\n"); - return CMD_WARNING_CONFIG_FAILED; - } + /* Label index cannot be changed. */ + if (bgp_static->label_index != label_index) { + vty_out(vty, "%% cannot change label-index\n"); + return CMD_WARNING_CONFIG_FAILED; + } - apply_mask(&p); + /* Check previous routes are installed into BGP. */ + if (bgp_static->valid && + bgp_static->backdoor != backdoor) + need_update = 1; - rn = bgp_node_lookup(bgp->route[afi][safi], &p); - if (!rn) { - vty_out(vty, - "%% Can't find specified static route configuration.\n"); - return CMD_WARNING_CONFIG_FAILED; - } + bgp_static->backdoor = backdoor; + + if (rmap) { + if (bgp_static->rmap.name) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_static->rmap.name); + bgp_static->rmap.name = + XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); + bgp_static->rmap.map = + route_map_lookup_by_name(rmap); + } else { + if (bgp_static->rmap.name) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_static->rmap.name); + bgp_static->rmap.name = NULL; + bgp_static->rmap.map = NULL; + bgp_static->valid = 0; + } + bgp_unlock_node(rn); + } else { + /* New configuration. */ + bgp_static = bgp_static_new(); + bgp_static->backdoor = backdoor; + bgp_static->valid = 0; + bgp_static->igpmetric = 0; + bgp_static->igpnexthop.s_addr = 0; + bgp_static->label_index = label_index; - bgp_static = rn->info; + if (rmap) { + if (bgp_static->rmap.name) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp_static->rmap.name); + bgp_static->rmap.name = + XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); + bgp_static->rmap.map = + route_map_lookup_by_name(rmap); + } + rn->info = bgp_static; + } - /* Update BGP RIB. */ - if (!bgp_static->backdoor) - bgp_static_withdraw(bgp, &p, afi, safi); + bgp_static->valid = 1; + if (need_update) + bgp_static_withdraw(bgp, &p, afi, safi); - /* Clear configuration. */ - bgp_static_free(bgp_static); - rn->info = NULL; - bgp_unlock_node(rn); - bgp_unlock_node(rn); + if (!bgp_static->backdoor) + bgp_static_update(bgp, &p, bgp_static, afi, safi); + } return CMD_SUCCESS; } @@ -5036,387 +5046,62 @@ DEFUN (no_bgp_table_map, argv[idx_word]->arg); } -DEFUN (bgp_network, - bgp_network_cmd, - "network A.B.C.D/M", - "Specify a network to announce via BGP\n" - "IPv4 prefix\n") -{ - int idx_ipv4_prefixlen = 1; - return bgp_static_set(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP, - bgp_node_safi(vty), NULL, 0, - BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_route_map, - bgp_network_route_map_cmd, - "network A.B.C.D/M route-map WORD", - "Specify a network to announce via BGP\n" - "IPv4 prefix\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv4_prefixlen = 1; - int idx_word = 3; - return bgp_static_set(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP, - bgp_node_safi(vty), argv[idx_word]->arg, 0, - BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_backdoor, - bgp_network_backdoor_cmd, - "network A.B.C.D/M backdoor", - "Specify a network to announce via BGP\n" - "IPv4 prefix\n" - "Specify a BGP backdoor route\n") -{ - int idx_ipv4_prefixlen = 1; - return bgp_static_set(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP, - SAFI_UNICAST, NULL, 1, BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_mask, - bgp_network_mask_cmd, - "network A.B.C.D mask A.B.C.D", - "Specify a network to announce via BGP\n" - "Network number\n" - "Network mask\n" - "Network mask\n") -{ - int idx_ipv4 = 1; - int idx_ipv4_2 = 3; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, - prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), NULL, - 0, BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_mask_route_map, - bgp_network_mask_route_map_cmd, - "network A.B.C.D mask A.B.C.D route-map WORD", - "Specify a network to announce via BGP\n" - "Network number\n" - "Network mask\n" - "Network mask\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv4 = 1; - int idx_ipv4_2 = 3; - int idx_word = 5; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, - prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), - argv[idx_word]->arg, 0, BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_mask_backdoor, - bgp_network_mask_backdoor_cmd, - "network A.B.C.D mask A.B.C.D backdoor", - "Specify a network to announce via BGP\n" - "Network number\n" - "Network mask\n" - "Network mask\n" - "Specify a BGP backdoor route\n") -{ - int idx_ipv4 = 1; - int idx_ipv4_2 = 3; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, - prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_set(vty, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 1, - BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_mask_natural, - bgp_network_mask_natural_cmd, - "network A.B.C.D", - "Specify a network to announce via BGP\n" - "Network number\n") -{ - int idx_ipv4 = 1; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), NULL, - 0, BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_mask_natural_route_map, - bgp_network_mask_natural_route_map_cmd, - "network A.B.C.D route-map WORD", - "Specify a network to announce via BGP\n" - "Network number\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv4 = 1; - int idx_word = 3; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), - argv[idx_word]->arg, 0, BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_mask_natural_backdoor, - bgp_network_mask_natural_backdoor_cmd, - "network A.B.C.D backdoor", - "Specify a network to announce via BGP\n" - "Network number\n" - "Specify a BGP backdoor route\n") -{ - int idx_ipv4 = 1; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_set(vty, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 1, - BGP_INVALID_LABEL_INDEX); -} - -DEFUN (bgp_network_label_index, - bgp_network_label_index_cmd, - "network A.B.C.D/M label-index (0-1048560)", - "Specify a network to announce via BGP\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Label index to associate with the prefix\n" - "Label index value\n") -{ - u_int32_t label_index; - - label_index = strtoul(argv[3]->arg, NULL, 10); - return bgp_static_set(vty, argv[1]->arg, AFI_IP, bgp_node_safi(vty), - NULL, 0, label_index); -} - -DEFUN (bgp_network_label_index_route_map, - bgp_network_label_index_route_map_cmd, - "network A.B.C.D/M label-index (0-1048560) route-map WORD", - "Specify a network to announce via BGP\n" - "IP prefix\n" - "Label index to associate with the prefix\n" - "Label index value\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - u_int32_t label_index; - - label_index = strtoul(argv[3]->arg, NULL, 10); - return bgp_static_set(vty, argv[1]->arg, AFI_IP, bgp_node_safi(vty), - argv[5]->arg, 0, label_index); -} - -DEFUN (no_bgp_network, - no_bgp_network_cmd, - "no network A.B.C.D/M []", - NO_STR - "Specify a network to announce via BGP\n" - "IPv4 prefix\n" - "Specify a BGP backdoor route\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv4_prefixlen = 2; - return bgp_static_unset(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP, - bgp_node_safi(vty)); -} - -DEFUN (no_bgp_network_mask, - no_bgp_network_mask_cmd, - "no network A.B.C.D mask A.B.C.D []", - NO_STR - "Specify a network to announce via BGP\n" - "Network number\n" - "Network mask\n" - "Network mask\n" - "Specify a BGP backdoor route\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv4 = 2; - int idx_ipv4_2 = 4; - int ret; - char prefix_str[BUFSIZ]; - - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, - prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return bgp_static_unset(vty, prefix_str, AFI_IP, bgp_node_safi(vty)); -} - -DEFUN (no_bgp_network_mask_natural, - no_bgp_network_mask_natural_cmd, - "no network A.B.C.D []", - NO_STR - "Specify a network to announce via BGP\n" - "Network number\n" - "Specify a BGP backdoor route\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv4 = 2; - int ret; - char prefix_str[BUFSIZ]; +DEFPY(bgp_network, + bgp_network_cmd, + "[no] network \ + \ + [{route-map WORD$map_name|label-index (0-1048560)$label_index| \ + backdoor$backdoor}]", + NO_STR + "Specify a network to announce via BGP\n" + "IPv4 prefix\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Route-map to modify the attributes\n" + "Name of the route map\n" + "Label index to associate with the prefix\n" + "Label index value\n" + "Specify a BGP backdoor route\n") +{ + char addr_prefix_str[BUFSIZ]; + + if (address_str) { + int ret; - ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str); - if (!ret) { - vty_out(vty, "%% Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; + ret = netmask_str2prefix_str(address_str, netmask_str, + addr_prefix_str); + if (!ret) { + vty_out(vty, "%% Inconsistent address and mask\n"); + return CMD_WARNING_CONFIG_FAILED; + } } - return bgp_static_unset(vty, prefix_str, AFI_IP, bgp_node_safi(vty)); -} - -ALIAS(no_bgp_network, no_bgp_network_label_index_cmd, - "no network A.B.C.D/M label-index (0-1048560)", NO_STR - "Specify a network to announce via BGP\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Label index to associate with the prefix\n" - "Label index value\n") - -ALIAS(no_bgp_network, no_bgp_network_label_index_route_map_cmd, - "no network A.B.C.D/M label-index (0-1048560) route-map WORD", NO_STR - "Specify a network to announce via BGP\n" - "IP prefix\n" - "Label index to associate with the prefix\n" - "Label index value\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") - -DEFUN (ipv6_bgp_network, - ipv6_bgp_network_cmd, - "network X:X::X:X/M", - "Specify a network to announce via BGP\n" - "IPv6 prefix\n") -{ - int idx_ipv6_prefixlen = 1; - return bgp_static_set(vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6, - bgp_node_safi(vty), NULL, 0, - BGP_INVALID_LABEL_INDEX); -} - -DEFUN (ipv6_bgp_network_route_map, - ipv6_bgp_network_route_map_cmd, - "network X:X::X:X/M route-map WORD", - "Specify a network to announce via BGP\n" - "IPv6 prefix\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - int idx_ipv6_prefixlen = 1; - int idx_word = 3; - return bgp_static_set(vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6, - bgp_node_safi(vty), argv[idx_word]->arg, 0, - BGP_INVALID_LABEL_INDEX); -} - -DEFUN (ipv6_bgp_network_label_index, - ipv6_bgp_network_label_index_cmd, - "network X:X::X:X/M label-index (0-1048560)", - "Specify a network to announce via BGP\n" - "IPv6 prefix /\n" - "Label index to associate with the prefix\n" - "Label index value\n") -{ - u_int32_t label_index; - - label_index = strtoul(argv[3]->arg, NULL, 10); - return bgp_static_set(vty, argv[1]->arg, AFI_IP6, bgp_node_safi(vty), - NULL, 0, label_index); -} - -DEFUN (ipv6_bgp_network_label_index_route_map, - ipv6_bgp_network_label_index_route_map_cmd, - "network X:X::X:X/M label-index (0-1048560) route-map WORD", - "Specify a network to announce via BGP\n" - "IPv6 prefix\n" - "Label index to associate with the prefix\n" - "Label index value\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") -{ - u_int32_t label_index; - - label_index = strtoul(argv[3]->arg, NULL, 10); - return bgp_static_set(vty, argv[1]->arg, AFI_IP6, bgp_node_safi(vty), - argv[5]->arg, 0, label_index); + return bgp_static_set(vty, no, address_str ? addr_prefix_str:prefix_str, + AFI_IP, bgp_node_safi(vty), + map_name, backdoor?1:0, + label_index ? + (uint32_t)label_index : BGP_INVALID_LABEL_INDEX); } -DEFUN (no_ipv6_bgp_network, - no_ipv6_bgp_network_cmd, - "no network X:X::X:X/M [route-map WORD]", - NO_STR - "Specify a network to announce via BGP\n" - "IPv6 prefix\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") +DEFPY(ipv6_bgp_network, + ipv6_bgp_network_cmd, + "[no] network X:X::X:X/M$prefix \ + [{route-map WORD$map_name|label-index (0-1048560)$label_index}]", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix\n" + "Route-map to modify the attributes\n" + "Name of the route map\n" + "Label index to associate with the prefix\n" + "Label index value\n") { - int idx_ipv6_prefixlen = 2; - return bgp_static_unset(vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6, - bgp_node_safi(vty)); + return bgp_static_set(vty, no, prefix_str, AFI_IP6, + bgp_node_safi(vty), map_name, 0, + label_index ? + (uint32_t)label_index : BGP_INVALID_LABEL_INDEX); } -ALIAS(no_ipv6_bgp_network, no_ipv6_bgp_network_label_index_cmd, - "no network X:X::X:X/M label-index (0-1048560)", NO_STR - "Specify a network to announce via BGP\n" - "IPv6 prefix /\n" - "Label index to associate with the prefix\n" - "Label index value\n") - -ALIAS(no_ipv6_bgp_network, no_ipv6_bgp_network_label_index_route_map_cmd, - "no network X:X::X:X/M label-index (0-1048560) route-map WORD", NO_STR - "Specify a network to announce via BGP\n" - "IPv6 prefix\n" - "Label index to associate with the prefix\n" - "Label index value\n" - "Route-map to modify the attributes\n" - "Name of the route map\n") - /* Aggreagete address: advertise-map Set condition to advertise attribute @@ -7388,7 +7073,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, vty_out(vty, " Imported from %s:%s\n", prefix_rd2str( (struct prefix_rd *)&prn->p, - buf1, RD_ADDRSTRLEN), + buf1, sizeof(buf1)), buf2); } } @@ -7601,7 +7286,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_peer, "routerId", inet_ntop(AF_INET, &binfo->peer->remote_id, buf1, - BUFSIZ)); + sizeof(buf1))); if (binfo->peer->hostname) json_object_string_add( @@ -7655,7 +7340,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, inet_ntop( AF_INET, &binfo->peer->remote_id, - buf1, BUFSIZ)); + buf1, sizeof(buf1))); } } @@ -8174,7 +7859,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, "{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64 ",\n \"routerId\": \"%s\",\n \"routes\": { ", - bgp->vrf_id == VRF_UNKNOWN ? -1 : bgp->vrf_id, + bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id, bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ? "Default" : bgp->name, table->version, inet_ntoa(bgp->router_id)); @@ -8394,8 +8079,9 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, ",\"%s\": ", buf2); vty_out(vty, "%s", - json_object_to_json_string_ext(json_paths, JSON_C_TO_STRING_PRETTY)); + json_object_to_json_string(json_paths)); json_object_free(json_paths); + json_paths = NULL; first = 0; } } @@ -8409,7 +8095,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, *total_cum = total_count; } if (use_json) { - json_object_free(json_paths); + if (json_paths) + json_object_free(json_paths); if (is_last) vty_out(vty, " } }\n"); else @@ -8440,6 +8127,9 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_node *rn, *next; unsigned long output_cum = 0; unsigned long total_cum = 0; + bool show_msg; + + show_msg = (!use_json && type == bgp_show_type_normal); for (rn = bgp_table_top(table); rn; rn = next) { next = bgp_route_next(rn); @@ -8447,19 +8137,27 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, continue; if (rn->info != NULL) { struct prefix_rd prd; - char rd[BUFSIZ]; + char rd[RD_ADDRSTRLEN]; memcpy(&prd, &(rn->p), sizeof(struct prefix_rd)); - if (prefix_rd2str(&prd, rd, BUFSIZ) == NULL) - sprintf(rd, - "Unknown Type: %u", - decode_rd_type(prd.val)); + prefix_rd2str(&prd, rd, sizeof(rd)); bgp_show_table(vty, bgp, safi, rn->info, type, output_arg, use_json, rd, next == NULL, &output_cum, &total_cum); + if (next == NULL) + show_msg = false; } } + if (show_msg) { + if (output_cum == 0) + vty_out(vty, "No BGP prefixes displayed, %ld exist\n", + total_cum); + else + vty_out(vty, + "\nDisplayed %ld routes and %ld total paths\n", + output_cum, total_cum); + } if (use_json) vty_out(vty, " } }"); return CMD_SUCCESS; @@ -8539,7 +8237,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, struct prefix *p; struct peer *peer; struct listnode *node, *nnode; - char buf1[INET6_ADDRSTRLEN]; + char buf1[RD_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; #if defined(HAVE_CUMULUS) char buf3[EVPN_ROUTE_STRLEN]; @@ -8573,7 +8271,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, #if defined(HAVE_CUMULUS) if (safi == SAFI_EVPN) vty_out(vty, "BGP routing table entry for %s%s%s\n", - prd ? prefix_rd2str(prd, buf1, RD_ADDRSTRLEN) + prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) : "", prd ? ":" : "", bgp_evpn_route2str((struct prefix_evpn *)p, @@ -8582,7 +8280,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "BGP routing table entry for %s%s%s/%d\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ? prefix_rd2str(prd, buf1, - RD_ADDRSTRLEN) + sizeof(buf1)) : ""), safi == SAFI_MPLS_VPN ? ":" : "", inet_ntop(p->family, &p->u.prefix, buf2, @@ -8597,8 +8295,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "BGP routing table entry for %s%s%s/%d\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) - ? prefix_rd2str(prd, buf1, RD_ADDRSTRLEN) - : ""), + ? prefix_rd2str(prd, buf1, sizeof(buf1)) + : ""), ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":" : "", buf2, p->prefixlen); @@ -10569,12 +10267,17 @@ DEFUN (show_bgp_afi_vpn_rd_route, afi_t afi = AFI_MAX; int idx = 0; - (void)argv_find_and_parse_afi(argv, argc, &idx, &afi); + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + vty_out(vty, "%% Malformed Address Family\n"); + return CMD_WARNING; + } + ret = str2prefix_rd(argv[5]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } + return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL, use_json(argc, argv)); } @@ -11139,7 +10842,7 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, prd = (struct prefix_rd *)&prn->p; /* "network" configuration display. */ - prefix_rd2str(prd, rdbuf, RD_ADDRSTRLEN); + prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); label = decode_label(&bgp_static->label); vty_out(vty, " network %s/%d rd %s", @@ -11152,10 +10855,10 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, if (bgp_static->rmap.name) vty_out(vty, " route-map %s", bgp_static->rmap.name); - else { - if (bgp_static->backdoor) - vty_out(vty, " backdoor"); - } + + if (bgp_static->backdoor) + vty_out(vty, " backdoor"); + vty_out(vty, "\n"); } } @@ -11196,7 +10899,7 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, prd = (struct prefix_rd *)&prn->p; /* "network" configuration display. */ - prefix_rd2str(prd, rdbuf, RD_ADDRSTRLEN); + prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); if (p->u.prefix_evpn.route_type == 5) { char local_buf[PREFIX_STRLEN]; uint8_t family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p) @@ -11215,7 +10918,7 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, &bgp_static->gatewayIp.u.prefix, buf2, sizeof(buf2)); vty_out(vty, - " network %s rd %s ethtag %u tag %u esi %s gwip %s routermac %s\n", + " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n", buf, rdbuf, p->u.prefix_evpn.eth_tag, decode_label(&bgp_static->label), esi, buf2, macrouter); @@ -11288,10 +10991,9 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp_static->rmap.name) vty_out(vty, " route-map %s", bgp_static->rmap.name); - else { - if (bgp_static->backdoor) - vty_out(vty, " backdoor"); - } + + if (bgp_static->backdoor) + vty_out(vty, " backdoor"); vty_out(vty, "\n"); } @@ -11374,18 +11076,7 @@ void bgp_route_init(void) /* IPv4 BGP commands. */ install_element(BGP_NODE, &bgp_table_map_cmd); install_element(BGP_NODE, &bgp_network_cmd); - install_element(BGP_NODE, &bgp_network_mask_cmd); - install_element(BGP_NODE, &bgp_network_mask_natural_cmd); - install_element(BGP_NODE, &bgp_network_route_map_cmd); - install_element(BGP_NODE, &bgp_network_mask_route_map_cmd); - install_element(BGP_NODE, &bgp_network_mask_natural_route_map_cmd); - install_element(BGP_NODE, &bgp_network_backdoor_cmd); - install_element(BGP_NODE, &bgp_network_mask_backdoor_cmd); - install_element(BGP_NODE, &bgp_network_mask_natural_backdoor_cmd); install_element(BGP_NODE, &no_bgp_table_map_cmd); - install_element(BGP_NODE, &no_bgp_network_cmd); - install_element(BGP_NODE, &no_bgp_network_mask_cmd); - install_element(BGP_NODE, &no_bgp_network_mask_natural_cmd); install_element(BGP_NODE, &aggregate_address_cmd); install_element(BGP_NODE, &aggregate_address_mask_cmd); @@ -11395,20 +11086,7 @@ void bgp_route_init(void) /* IPv4 unicast configuration. */ install_element(BGP_IPV4_NODE, &bgp_table_map_cmd); install_element(BGP_IPV4_NODE, &bgp_network_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_mask_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_mask_natural_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_route_map_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_mask_route_map_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_mask_natural_route_map_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_label_index_cmd); - install_element(BGP_IPV4_NODE, &bgp_network_label_index_route_map_cmd); - install_element(BGP_IPV4_NODE, &no_bgp_network_label_index_cmd); - install_element(BGP_IPV4_NODE, - &no_bgp_network_label_index_route_map_cmd); install_element(BGP_IPV4_NODE, &no_bgp_table_map_cmd); - install_element(BGP_IPV4_NODE, &no_bgp_network_cmd); - install_element(BGP_IPV4_NODE, &no_bgp_network_mask_cmd); - install_element(BGP_IPV4_NODE, &no_bgp_network_mask_natural_cmd); install_element(BGP_IPV4_NODE, &aggregate_address_cmd); install_element(BGP_IPV4_NODE, &aggregate_address_mask_cmd); @@ -11418,16 +11096,7 @@ void bgp_route_init(void) /* IPv4 multicast configuration. */ install_element(BGP_IPV4M_NODE, &bgp_table_map_cmd); install_element(BGP_IPV4M_NODE, &bgp_network_cmd); - install_element(BGP_IPV4M_NODE, &bgp_network_mask_cmd); - install_element(BGP_IPV4M_NODE, &bgp_network_mask_natural_cmd); - install_element(BGP_IPV4M_NODE, &bgp_network_route_map_cmd); - install_element(BGP_IPV4M_NODE, &bgp_network_mask_route_map_cmd); - install_element(BGP_IPV4M_NODE, - &bgp_network_mask_natural_route_map_cmd); install_element(BGP_IPV4M_NODE, &no_bgp_table_map_cmd); - install_element(BGP_IPV4M_NODE, &no_bgp_network_cmd); - install_element(BGP_IPV4M_NODE, &no_bgp_network_mask_cmd); - install_element(BGP_IPV4M_NODE, &no_bgp_network_mask_natural_cmd); install_element(BGP_IPV4M_NODE, &aggregate_address_cmd); install_element(BGP_IPV4M_NODE, &aggregate_address_mask_cmd); install_element(BGP_IPV4M_NODE, &no_aggregate_address_cmd); @@ -11470,21 +11139,12 @@ void bgp_route_init(void) /* New config IPv6 BGP commands. */ install_element(BGP_IPV6_NODE, &bgp_table_map_cmd); install_element(BGP_IPV6_NODE, &ipv6_bgp_network_cmd); - install_element(BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd); install_element(BGP_IPV6_NODE, &no_bgp_table_map_cmd); - install_element(BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd); - install_element(BGP_IPV6_NODE, &ipv6_bgp_network_label_index_cmd); - install_element(BGP_IPV6_NODE, &no_ipv6_bgp_network_label_index_cmd); - install_element(BGP_IPV6_NODE, - &ipv6_bgp_network_label_index_route_map_cmd); - install_element(BGP_IPV6_NODE, - &no_ipv6_bgp_network_label_index_route_map_cmd); install_element(BGP_IPV6_NODE, &ipv6_aggregate_address_cmd); install_element(BGP_IPV6_NODE, &no_ipv6_aggregate_address_cmd); install_element(BGP_IPV6M_NODE, &ipv6_bgp_network_cmd); - install_element(BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd); install_element(BGP_NODE, &bgp_distance_cmd); install_element(BGP_NODE, &no_bgp_distance_cmd); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 085de3fabb0e..ae4759aad0db 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -73,9 +73,12 @@ struct bgp_info_extra { /* Nexthop reachability check. */ u_int32_t igpmetric; - /* MPLS label. */ + /* MPLS label - L2VNI */ mpls_label_t label; + /* MPLS label - L3-VNI */ + mpls_label_t label2; + #if ENABLE_BGP_VNC union { diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 30397f84877d..8c9f9f65ca4e 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2082,7 +2082,10 @@ static void *route_set_aggregator_as_compile(const char *arg) aggregator = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct aggregator)); - sscanf(arg, "%s %s", as, address); + if (sscanf(arg, "%s %s", as, address) != 2) { + XFREE(MTYPE_ROUTE_MAP_COMPILED, aggregator); + return NULL; + } aggregator->as = strtoul(as, NULL, 10); ret = inet_aton(address, &aggregator->address); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 1a42478513f8..19d4769cd3fe 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -52,7 +52,9 @@ #include "libfrr.h" #include "version.h" -#include "bgp_rpki_clippy.c" +#ifndef VTYSH_EXTRACT_PL +#include "bgpd/bgp_rpki_clippy.c" +#endif DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server") DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group") diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 484ea7c433b6..8317a252e167 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -512,6 +512,7 @@ static u_char *bgpPeerTable(struct variable *v, oid name[], size_t *length, { static struct in_addr addr; struct peer *peer; + uint32_t ui, uo; if (smux_header_table(v, name, length, exact, var_len, write_method) == MATCH_FAILED) @@ -571,21 +572,20 @@ static u_char *bgpPeerTable(struct variable *v, oid name[], size_t *length, return SNMP_INTEGER(peer->as); break; case BGPPEERINUPDATES: - return SNMP_INTEGER(peer->update_in); + ui = atomic_load_explicit(&peer->update_in, + memory_order_relaxed); + return SNMP_INTEGER(ui); break; case BGPPEEROUTUPDATES: - return SNMP_INTEGER(peer->update_out); + uo = atomic_load_explicit(&peer->update_out, + memory_order_relaxed); + return SNMP_INTEGER(uo); break; case BGPPEERINTOTALMESSAGES: - return SNMP_INTEGER(peer->open_in + peer->update_in - + peer->keepalive_in + peer->notify_in - + peer->refresh_in + peer->dynamic_cap_in); + return SNMP_INTEGER(PEER_TOTAL_RX(peer)); break; case BGPPEEROUTTOTALMESSAGES: - return SNMP_INTEGER(peer->open_out + peer->update_out - + peer->keepalive_out + peer->notify_out - + peer->refresh_out - + peer->dynamic_cap_out); + return SNMP_INTEGER(PEER_TOTAL_TX(peer)); break; case BGPPEERLASTERROR: { static u_char lasterror[2]; diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 8f6729060098..1c589f796029 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -53,6 +53,7 @@ #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_filter.h" +#include "bgpd/bgp_io.h" /******************** * PRIVATE FUNCTIONS @@ -1871,17 +1872,16 @@ void subgroup_trigger_write(struct update_subgroup *subgrp) { struct peer_af *paf; -#if 0 - if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug("u%llu:s%llu scheduling write thread for peers", - subgrp->update_group->id, subgrp->id); -#endif - SUBGRP_FOREACH_PEER (subgrp, paf) { - if (paf->peer->status == Established) { - BGP_PEER_WRITE_ON(paf->peer->t_write, bgp_write, - paf->peer->fd, paf->peer); - } - } + /* + * For each peer in the subgroup, schedule a job to pull packets from + * the subgroup output queue into their own output queue. This action + * will trigger a write job on the I/O thread. + */ + SUBGRP_FOREACH_PEER(subgrp, paf) + if (paf->peer->status == Established) + thread_add_timer_msec(bm->master, bgp_generate_updgrp_packets, + paf->peer, 0, + &paf->peer->t_generate_updgrp_packets); } int update_group_clear_update_dbg(struct update_group *updgrp, void *arg) diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 52a21679b895..e941fecb6181 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -29,7 +29,27 @@ #include "bgp_advertise.h" -#define BGP_DEFAULT_SUBGROUP_COALESCE_TIME 200 +/* + * The following three heuristic constants determine how long advertisement to + * a subgroup will be delayed after it is created. The intent is to allow + * transient changes in peer state (primarily session establishment) to settle, + * so that more peers can be grouped together and benefit from sharing + * advertisement computations with the subgroup. + * + * These values have a very large impact on initial convergence time; any + * changes should be accompanied by careful performance testing at all scales. + * + * The coalesce time 'C' for a new subgroup within a particular BGP instance + * 'B' with total number of known peers 'P', established or not, is computed as + * follows: + * + * C = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME, + * BGP_DEFAULT_SUBGROUP_COALESCE_TIME + + * (P*BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME)) + */ +#define BGP_DEFAULT_SUBGROUP_COALESCE_TIME 1000 +#define BGP_MAX_SUBGROUP_COALESCE_TIME 10000 +#define BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME 50 #define PEER_UPDGRP_FLAGS \ (PEER_FLAG_LOCAL_AS_NO_PREPEND | PEER_FLAG_LOCAL_AS_REPLACE_AS) @@ -179,7 +199,7 @@ struct update_subgroup { struct stream *work; /* We use a separate stream to encode MP_REACH_NLRI for efficient - * NLRI packing. peer->work stores all the other attributes. The + * NLRI packing. peer->obuf_work stores all the other attributes. The * actual packet is then constructed by concatenating the two. */ struct stream *scratch; diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index b4f18c9f5eb0..705cb152f0c1 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -483,7 +483,7 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, { struct bgp_adj_out *adj; struct bgp_advertise *adv; - char trigger_write; + bool trigger_write; if (DISABLE_BGP_ANNOUNCE) return; @@ -502,17 +502,13 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, adv->adj = adj; /* Note if we need to trigger a packet write */ - if (BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw)) - trigger_write = 1; - else - trigger_write = 0; + trigger_write = + BGP_ADV_FIFO_EMPTY(&subgrp->sync->withdraw); /* Add to synchronization entry for withdraw * announcement. */ BGP_ADV_FIFO_ADD(&subgrp->sync->withdraw, &adv->fifo); - /* Schedule packet write, if FIFO is getting its first - * entry. */ if (trigger_write) subgroup_trigger_write(subgrp); } else { diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index a35d814e477c..b63dfbed0ac2 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -633,7 +633,6 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, } } - bgp_packet_add(peer, s); return s; } @@ -963,7 +962,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) addpath_tx_id = adj->addpath_tx_id; space_remaining = - STREAM_REMAIN(s) - BGP_MAX_PACKET_SIZE_OVERFLOW; + STREAM_WRITEABLE(s) - BGP_MAX_PACKET_SIZE_OVERFLOW; space_needed = BGP_NLRI_LENGTH + addpath_overhead + BGP_TOTAL_ATTR_LEN + bgp_packet_mpattr_prefix_size(afi, safi, &rn->p); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 9159bc683d8f..c98cf9c327d7 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -57,6 +57,8 @@ #include "bgpd/bgp_packet.h" #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_bfd.h" +#include "bgpd/bgp_io.h" +#include "bgpd/bgp_evpn.h" static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); @@ -720,7 +722,7 @@ static void bgp_clear_star_soft_out(struct vty *vty, const char *name) #ifndef VTYSH_EXTRACT_PL -#include "bgp_vty_clippy.c" +#include "bgpd/bgp_vty_clippy.c" #endif /* BGP global configuration. */ @@ -872,6 +874,8 @@ DEFUN_NOSH (router_bgp, */ } + /* unset the auto created flag as the user config is now present */ + UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO); VTY_PUSH_CONTEXT(BGP_NODE, bgp); return CMD_SUCCESS; @@ -908,6 +912,12 @@ DEFUN (no_router_bgp, "%% Multiple BGP processes are configured\n"); return CMD_WARNING_CONFIG_FAILED; } + + if (bgp->l3vni) { + vty_out(vty, "%% Please unconfigure l3vni %u", + bgp->l3vni); + return CMD_WARNING_CONFIG_FAILED; + } } else { as = strtoul(argv[idx_asn]->arg, NULL, 10); @@ -920,6 +930,12 @@ DEFUN (no_router_bgp, vty_out(vty, "%% Can't find BGP instance\n"); return CMD_WARNING_CONFIG_FAILED; } + + if (bgp->l3vni) { + vty_out(vty, "%% Please unconfigure l3vni %u", + bgp->l3vni); + return CMD_WARNING_CONFIG_FAILED; + } } bgp_delete(bgp); @@ -1332,25 +1348,55 @@ static int bgp_wpkt_quanta_config_vty(struct vty *vty, const char *num, { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (set) - bgp->wpkt_quanta = strtoul(num, NULL, 10); - else - bgp->wpkt_quanta = BGP_WRITE_PACKET_MAX; + if (set) { + uint32_t quanta = strtoul(num, NULL, 10); + atomic_store_explicit(&bgp->wpkt_quanta, quanta, + memory_order_relaxed); + } else { + atomic_store_explicit(&bgp->wpkt_quanta, BGP_WRITE_PACKET_MAX, + memory_order_relaxed); + } + + return CMD_SUCCESS; +} + +static int bgp_rpkt_quanta_config_vty(struct vty *vty, const char *num, + char set) +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (set) { + uint32_t quanta = strtoul(num, NULL, 10); + atomic_store_explicit(&bgp->rpkt_quanta, quanta, + memory_order_relaxed); + } else { + atomic_store_explicit(&bgp->rpkt_quanta, BGP_READ_PACKET_MAX, + memory_order_relaxed); + } return CMD_SUCCESS; } void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp) { - if (bgp->wpkt_quanta != BGP_WRITE_PACKET_MAX) - vty_out(vty, " write-quanta %d\n", bgp->wpkt_quanta); + uint32_t quanta = + atomic_load_explicit(&bgp->wpkt_quanta, memory_order_relaxed); + if (quanta != BGP_WRITE_PACKET_MAX) + vty_out(vty, " write-quanta %d\n", quanta); } +void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp) +{ + uint32_t quanta = + atomic_load_explicit(&bgp->rpkt_quanta, memory_order_relaxed); + if (quanta != BGP_READ_PACKET_MAX) + vty_out(vty, " read-quanta %d\n", quanta); +} -/* Update-delay configuration */ +/* Packet quanta configuration */ DEFUN (bgp_wpkt_quanta, bgp_wpkt_quanta_cmd, - "write-quanta (1-10000)", + "write-quanta (1-10)", "How many packets to write to peer socket per run\n" "Number of packets\n") { @@ -1358,21 +1404,41 @@ DEFUN (bgp_wpkt_quanta, return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 1); } -/* Update-delay deconfiguration */ DEFUN (no_bgp_wpkt_quanta, no_bgp_wpkt_quanta_cmd, - "no write-quanta (1-10000)", + "no write-quanta (1-10)", NO_STR - "How many packets to write to peer socket per run\n" + "How many packets to write to peer socket per I/O cycle\n" "Number of packets\n") { int idx_number = 2; return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); } +DEFUN (bgp_rpkt_quanta, + bgp_rpkt_quanta_cmd, + "read-quanta (1-10)", + "How many packets to read from peer socket per I/O cycle\n" + "Number of packets\n") +{ + int idx_number = 1; + return bgp_rpkt_quanta_config_vty(vty, argv[idx_number]->arg, 1); +} + +DEFUN (no_bgp_rpkt_quanta, + no_bgp_rpkt_quanta_cmd, + "no read-quanta (1-10)", + NO_STR + "How many packets to read from peer socket per I/O cycle\n" + "Number of packets\n") +{ + int idx_number = 2; + return bgp_rpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); +} + void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp) { - if (bgp->coalesce_time != BGP_DEFAULT_SUBGROUP_COALESCE_TIME) + if (!bgp->heuristic_coalesce) vty_out(vty, " coalesce-time %u\n", bgp->coalesce_time); } @@ -1387,6 +1453,7 @@ DEFUN (bgp_coalesce_time, int idx = 0; argv_find(argv, argc, "(0-4294967295)", &idx); + bgp->heuristic_coalesce = false; bgp->coalesce_time = strtoul(argv[idx]->arg, NULL, 10); return CMD_SUCCESS; } @@ -1400,6 +1467,7 @@ DEFUN (no_bgp_coalesce_time, { VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp->heuristic_coalesce = true; bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME; return CMD_SUCCESS; } @@ -2624,6 +2692,19 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str, return bgp_vty_return(vty, ret); } +DEFUN (bgp_default_shutdown, + bgp_default_shutdown_cmd, + "[no] bgp default shutdown", + NO_STR + BGP_STR + "Configure BGP defaults\n" + "Do not automatically activate peers upon configuration\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp->autoshutdown = !strmatch(argv[0]->text, "no"); + return CMD_SUCCESS; +} + DEFUN (neighbor_remote_as, neighbor_remote_as_cmd, "neighbor remote-as <(1-4294967295)|internal|external>", @@ -3170,7 +3251,6 @@ DEFUN (no_neighbor_password, return bgp_vty_return(vty, ret); } - DEFUN (neighbor_activate, neighbor_activate_cmd, "neighbor activate", @@ -3335,18 +3415,6 @@ static int peer_flag_modify_vty(struct vty *vty, const char *ip_str, if (!peer) return CMD_WARNING_CONFIG_FAILED; - /* - * If 'neighbor ', then this is for directly connected peers, - * we should not accept disable-connected-check. - */ - if (peer->conf_if && (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)) { - vty_out(vty, - "%s is directly connected peer, cannot accept disable-" - "connected-check\n", - ip_str); - return CMD_WARNING_CONFIG_FAILED; - } - if (!set && flag == PEER_FLAG_SHUTDOWN) peer_tx_shutdown_message_unset(peer); @@ -4437,9 +4505,9 @@ DEFUN (no_neighbor_ebgp_multihop, /* disable-connected-check */ DEFUN (neighbor_disable_connected_check, neighbor_disable_connected_check_cmd, - "neighbor ", + "neighbor ", NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 + NEIGHBOR_ADDR_STR "one-hop away EBGP peer using loopback address\n" "Enforce EBGP neighbors perform multihop\n") { @@ -4450,10 +4518,10 @@ DEFUN (neighbor_disable_connected_check, DEFUN (no_neighbor_disable_connected_check, no_neighbor_disable_connected_check_cmd, - "no neighbor ", + "no neighbor ", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 + NEIGHBOR_ADDR_STR "one-hop away EBGP peer using loopback address\n" "Enforce EBGP neighbors perform multihop\n") { @@ -5892,9 +5960,9 @@ ALIAS_HIDDEN( DEFUN (neighbor_ttl_security, neighbor_ttl_security_cmd, - "neighbor ttl-security hops (1-254)", + "neighbor ttl-security hops (1-254)", NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 + NEIGHBOR_ADDR_STR "BGP ttl-security parameters\n" "Specify the maximum number of hops to the BGP peer\n" "Number of hops to BGP peer\n") @@ -5910,26 +5978,15 @@ DEFUN (neighbor_ttl_security, gtsm_hops = strtoul(argv[idx_number]->arg, NULL, 10); - /* - * If 'neighbor swpX', then this is for directly connected peers, - * we should not accept a ttl-security hops value greater than 1. - */ - if (peer->conf_if && (gtsm_hops > 1)) { - vty_out(vty, - "%s is directly connected peer, hops cannot exceed 1\n", - argv[idx_peer]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - return bgp_vty_return(vty, peer_ttl_security_hops_set(peer, gtsm_hops)); } DEFUN (no_neighbor_ttl_security, no_neighbor_ttl_security_cmd, - "no neighbor ttl-security hops (1-254)", + "no neighbor ttl-security hops (1-254)", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 + NEIGHBOR_ADDR_STR "BGP ttl-security parameters\n" "Specify the maximum number of hops to the BGP peer\n" "Number of hops to BGP peer\n") @@ -6397,6 +6454,7 @@ DEFUN (show_bgp_vrfs, "Show BGP VRFs\n" JSON_STR) { + char buf[ETHER_ADDR_STRLEN]; struct list *inst = bm->bgp; struct listnode *node; struct bgp *bgp; @@ -6404,8 +6462,6 @@ DEFUN (show_bgp_vrfs, json_object *json = NULL; json_object *json_vrfs = NULL; int count = 0; - static char header[] = - "Type Id RouterId #PeersCfg #PeersEstb Name"; if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { vty_out(vty, "BGP Multiple Instance is not enabled\n"); @@ -6423,7 +6479,6 @@ DEFUN (show_bgp_vrfs, struct listnode *node, *nnode; int peers_cfg, peers_estb; json_object *json_vrf = NULL; - int vrf_id_ui; /* Skip Views. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) @@ -6431,7 +6486,10 @@ DEFUN (show_bgp_vrfs, count++; if (!uj && count == 1) - vty_out(vty, "%s\n", header); + vty_out(vty, + "%4s %-5s %-16s %9s %10s %-37s %-10s %-15s\n", + "Type", "Id", "routerId", "#PeersVfg", + "#PeersEstb", "Name", "L3-VNI", "Rmac"); peers_cfg = peers_estb = 0; if (uj) @@ -6454,8 +6512,10 @@ DEFUN (show_bgp_vrfs, type = "VRF"; } - vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) ? -1 : bgp->vrf_id; + if (uj) { + int64_t vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) ? -1 : + (int64_t)bgp->vrf_id; json_object_string_add(json_vrf, "type", type); json_object_int_add(json_vrf, "vrfId", vrf_id_ui); json_object_string_add(json_vrf, "routerId", @@ -6465,11 +6525,19 @@ DEFUN (show_bgp_vrfs, json_object_int_add(json_vrf, "numEstablishedPeers", peers_estb); + json_object_int_add(json_vrf, "l3vni", bgp->l3vni); + json_object_string_add(json_vrf, "rmac", + prefix_mac2str(&bgp->rmac, buf, + sizeof(buf))); json_object_object_add(json_vrfs, name, json_vrf); } else - vty_out(vty, "%4s %-5d %-16s %9u %10u %s\n", type, - vrf_id_ui, inet_ntoa(bgp->router_id), peers_cfg, - peers_estb, name); + vty_out(vty, + "%4s %-5d %-16s %9u %10u %-37s %-10u %-15s\n", + type, bgp->vrf_id == VRF_UNKNOWN ? + -1 : (int)bgp->vrf_id, + inet_ntoa(bgp->router_id), + peers_cfg, peers_estb, name, bgp->l3vni, + prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); } if (uj) { @@ -6787,10 +6855,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (!count) { unsigned long ents; char memstrbuf[MTYPE_MEMSTR_LEN]; - int vrf_id_ui; + int64_t vrf_id_ui; vrf_id_ui = - (bgp->vrf_id == VRF_UNKNOWN) ? -1 : bgp->vrf_id; + (bgp->vrf_id == VRF_UNKNOWN) ? -1 : + (int64_t)bgp->vrf_id; /* Usage summary and header */ if (use_json) { @@ -6809,7 +6878,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, "BGP router identifier %s, local AS number %u vrf-id %d", inet_ntoa(bgp->router_id), bgp->as, - vrf_id_ui); + bgp->vrf_id == VRF_UNKNOWN ? -1 : + (int)bgp->vrf_id); vty_out(vty, "\n"); } @@ -7000,17 +7070,9 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_int_add(json_peer, "remoteAs", peer->as); json_object_int_add(json_peer, "version", 4); json_object_int_add(json_peer, "msgRcvd", - peer->open_in + peer->update_in - + peer->keepalive_in - + peer->notify_in - + peer->refresh_in - + peer->dynamic_cap_in); + PEER_TOTAL_RX(peer)); json_object_int_add(json_peer, "msgSent", - peer->open_out + peer->update_out - + peer->keepalive_out - + peer->notify_out - + peer->refresh_out - + peer->dynamic_cap_out); + PEER_TOTAL_TX(peer)); json_object_int_add(json_peer, "tableVersion", peer->version[afi][safi]); @@ -7067,22 +7129,18 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, " "); vty_out(vty, "4 %10u %7u %7u %8" PRIu64 " %4d %4zd %8s", - peer->as, - peer->open_in + peer->update_in - + peer->keepalive_in + peer->notify_in - + peer->refresh_in - + peer->dynamic_cap_in, - peer->open_out + peer->update_out - + peer->keepalive_out + peer->notify_out - + peer->refresh_out - + peer->dynamic_cap_out, - peer->version[afi][safi], 0, peer->obuf->count, + peer->as, PEER_TOTAL_RX(peer), + PEER_TOTAL_TX(peer), peer->version[afi][safi], + 0, peer->obuf->count, peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL)); if (peer->status == Established) - vty_out(vty, " %12ld", - peer->pcount[afi][pfx_rcd_safi]); + if (peer->afc_recv[afi][pfx_rcd_safi]) + vty_out(vty, " %12ld", + peer->pcount[afi][pfx_rcd_safi]); + else + vty_out(vty, " NoNeg"); else { if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) vty_out(vty, " Idle (Admin)"); @@ -7644,7 +7702,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, } if (afi == AFI_L2VPN && safi == SAFI_EVPN) { - if (p->bgp->advertise_all_vni) + if (is_evpn_enabled()) json_object_boolean_true_add( json_addr, "advertiseAllVnis"); } @@ -7916,7 +7974,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, /* advertise-vni-all */ if (afi == AFI_L2VPN && safi == SAFI_EVPN) { - if (p->bgp->advertise_all_vni) + if (is_evpn_enabled()) vty_out(vty, " advertise-all-vni\n"); } @@ -8209,17 +8267,29 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, if (p->status == Established) { time_t uptime; - struct tm *tm; uptime = bgp_clock(); uptime -= p->uptime; - tm = gmtime(&uptime); epoch_tbuf = time(NULL) - uptime; +#if CONFDATE > 20200101 + CPP_NOTICE("bgpTimerUp should be deprecated and can be removed now"); +#endif + /* + * bgpTimerUp was miliseconds that was accurate + * up to 1 day, then the value returned + * became garbage. So in order to provide + * some level of backwards compatability, + * we still provde the data, but now + * we are returning the correct value + * and also adding a new bgpTimerUpMsec + * which will allow us to deprecate + * this eventually + */ json_object_int_add(json_neigh, "bgpTimerUp", - (tm->tm_sec * 1000) - + (tm->tm_min * 60000) - + (tm->tm_hour * 3600000)); + uptime * 1000); + json_object_int_add(json_neigh, "bgpTimerUpMsec", + uptime * 1000); json_object_string_add(json_neigh, "bgpTimerUpString", peer_uptime(p->uptime, timebuf, BGP_UPTIME_LEN, 0, @@ -9250,34 +9320,44 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, json_object_int_add(json_stat, "depthInq", 0); json_object_int_add(json_stat, "depthOutq", (unsigned long)p->obuf->count); - json_object_int_add(json_stat, "opensSent", p->open_out); - json_object_int_add(json_stat, "opensRecv", p->open_in); + json_object_int_add(json_stat, "opensSent", + atomic_load_explicit(&p->open_out, + memory_order_relaxed)); + json_object_int_add(json_stat, "opensRecv", + atomic_load_explicit(&p->open_in, + memory_order_relaxed)); json_object_int_add(json_stat, "notificationsSent", - p->notify_out); + atomic_load_explicit(&p->notify_out, + memory_order_relaxed)); json_object_int_add(json_stat, "notificationsRecv", - p->notify_in); - json_object_int_add(json_stat, "updatesSent", p->update_out); - json_object_int_add(json_stat, "updatesRecv", p->update_in); + atomic_load_explicit(&p->notify_in, + memory_order_relaxed)); + json_object_int_add(json_stat, "updatesSent", + atomic_load_explicit(&p->update_out, + memory_order_relaxed)); + json_object_int_add(json_stat, "updatesRecv", + atomic_load_explicit(&p->update_in, + memory_order_relaxed)); json_object_int_add(json_stat, "keepalivesSent", - p->keepalive_out); + atomic_load_explicit(&p->keepalive_out, + memory_order_relaxed)); json_object_int_add(json_stat, "keepalivesRecv", - p->keepalive_in); + atomic_load_explicit(&p->keepalive_in, + memory_order_relaxed)); json_object_int_add(json_stat, "routeRefreshSent", - p->refresh_out); + atomic_load_explicit(&p->refresh_out, + memory_order_relaxed)); json_object_int_add(json_stat, "routeRefreshRecv", - p->refresh_in); + atomic_load_explicit(&p->refresh_in, + memory_order_relaxed)); json_object_int_add(json_stat, "capabilitySent", - p->dynamic_cap_out); + atomic_load_explicit(&p->dynamic_cap_out, + memory_order_relaxed)); json_object_int_add(json_stat, "capabilityRecv", - p->dynamic_cap_in); - json_object_int_add(json_stat, "totalSent", - p->open_out + p->notify_out + p->update_out - + p->keepalive_out + p->refresh_out - + p->dynamic_cap_out); - json_object_int_add(json_stat, "totalRecv", - p->open_in + p->notify_in + p->update_in - + p->keepalive_in + p->refresh_in - + p->dynamic_cap_in); + atomic_load_explicit(&p->dynamic_cap_in, + memory_order_relaxed)); + json_object_int_add(json_stat, "totalSent", PEER_TOTAL_TX(p)); + json_object_int_add(json_stat, "totalRecv", PEER_TOTAL_RX(p)); json_object_object_add(json_neigh, "messageStats", json_stat); } else { /* Packet counts. */ @@ -9286,25 +9366,38 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, vty_out(vty, " Outq depth is %lu\n", (unsigned long)p->obuf->count); vty_out(vty, " Sent Rcvd\n"); - vty_out(vty, " Opens: %10d %10d\n", p->open_out, - p->open_in); - vty_out(vty, " Notifications: %10d %10d\n", p->notify_out, - p->notify_in); - vty_out(vty, " Updates: %10d %10d\n", p->update_out, - p->update_in); - vty_out(vty, " Keepalives: %10d %10d\n", p->keepalive_out, - p->keepalive_in); - vty_out(vty, " Route Refresh: %10d %10d\n", p->refresh_out, - p->refresh_in); + vty_out(vty, " Opens: %10d %10d\n", + atomic_load_explicit(&p->open_out, + memory_order_relaxed), + atomic_load_explicit(&p->open_in, + memory_order_relaxed)); + vty_out(vty, " Notifications: %10d %10d\n", + atomic_load_explicit(&p->notify_out, + memory_order_relaxed), + atomic_load_explicit(&p->notify_in, + memory_order_relaxed)); + vty_out(vty, " Updates: %10d %10d\n", + atomic_load_explicit(&p->update_out, + memory_order_relaxed), + atomic_load_explicit(&p->update_in, + memory_order_relaxed)); + vty_out(vty, " Keepalives: %10d %10d\n", + atomic_load_explicit(&p->keepalive_out, + memory_order_relaxed), + atomic_load_explicit(&p->keepalive_in, + memory_order_relaxed)); + vty_out(vty, " Route Refresh: %10d %10d\n", + atomic_load_explicit(&p->refresh_out, + memory_order_relaxed), + atomic_load_explicit(&p->refresh_in, + memory_order_relaxed)); vty_out(vty, " Capability: %10d %10d\n", - p->dynamic_cap_out, p->dynamic_cap_in); - vty_out(vty, " Total: %10d %10d\n", - p->open_out + p->notify_out + p->update_out - + p->keepalive_out + p->refresh_out - + p->dynamic_cap_out, - p->open_in + p->notify_in + p->update_in - + p->keepalive_in + p->refresh_in - + p->dynamic_cap_in); + atomic_load_explicit(&p->dynamic_cap_out, + memory_order_relaxed), + atomic_load_explicit(&p->dynamic_cap_in, + memory_order_relaxed)); + vty_out(vty, " Total: %10d %10d\n", PEER_TOTAL_TX(p), + PEER_TOTAL_RX(p)); } if (use_json) { @@ -9657,7 +9750,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, json_object_string_add(json_neigh, "readThread", "on"); else json_object_string_add(json_neigh, "readThread", "off"); - if (p->t_write) + + if (CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON)) json_object_string_add(json_neigh, "writeThread", "on"); else json_object_string_add(json_neigh, "writeThread", @@ -9683,7 +9777,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, vty_out(vty, "Peer Authentication Enabled\n"); vty_out(vty, "Read thread: %s Write thread: %s\n", - p->t_read ? "on" : "off", p->t_write ? "on" : "off"); + p->t_read ? "on" : "off", + CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON) + ? "on" + : "off"); } if (p->notify.code == BGP_NOTIFY_OPEN_ERR @@ -9784,8 +9881,7 @@ static void bgp_show_all_instances_neighbors_vty(struct vty *vty, json_object_int_add(json, "vrfId", (bgp->vrf_id == VRF_UNKNOWN) - ? -1 - : bgp->vrf_id); + ? -1 : (int64_t) bgp->vrf_id); json_object_string_add( json, "vrfName", (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) @@ -11345,6 +11441,8 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_wpkt_quanta_cmd); install_element(BGP_NODE, &no_bgp_wpkt_quanta_cmd); + install_element(BGP_NODE, &bgp_rpkt_quanta_cmd); + install_element(BGP_NODE, &no_bgp_rpkt_quanta_cmd); install_element(BGP_NODE, &bgp_coalesce_time_cmd); install_element(BGP_NODE, &no_bgp_coalesce_time_cmd); @@ -11473,6 +11571,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_listen_range_cmd); install_element(BGP_NODE, &no_bgp_listen_range_cmd); + /* "neighbors auto-shutdown" command */ + install_element(BGP_NODE, &bgp_default_shutdown_cmd); + /* "neighbor remote-as" commands. */ install_element(BGP_NODE, &neighbor_remote_as_cmd); install_element(BGP_NODE, &neighbor_interface_config_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 59bc01266154..e456f7caed06 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -48,6 +48,7 @@ extern const char *afi_safi_print(afi_t, safi_t); extern const char *afi_safi_json(afi_t, safi_t); extern void bgp_config_write_update_delay(struct vty *, struct bgp *); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); +extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp); extern int bgp_vty_return(struct vty *vty, int ret); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 1cf04abfce69..de170fdd0135 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -999,7 +999,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, /* Make Zebra API structure. */ memset(&api, 0, sizeof(api)); + memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); api.vrf_id = bgp->vrf_id; + api.nh_vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; api.prefix = *p; @@ -1015,6 +1017,13 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (info->sub_type == BGP_ROUTE_AGGREGATE) zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + /* If it is an EVPN route mark as such. + * Currently presence of rmac in attr denotes + * this is an EVPN type-2 route + */ + if (!is_zero_mac(&(info->attr->rmac))) + SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); + if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || info->sub_type == BGP_ROUTE_AGGREGATE) { SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); @@ -1072,7 +1081,14 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh = &api.nexthops[valid_nh_count]; api_nh->gate.ipv4 = *nexthop; - api_nh->type = NEXTHOP_TYPE_IPV4; + + /* EVPN type-2 routes are + programmed as onlink on l3-vni SVI + */ + if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + else + api_nh->type = NEXTHOP_TYPE_IPV4; } else { ifindex_t ifindex; struct in6_addr *nexthop; @@ -1126,8 +1142,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; } - if (mpinfo->extra - && bgp_is_valid_label(&mpinfo->extra->label)) { + if (mpinfo->extra && bgp_is_valid_label(&mpinfo->extra->label) + && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) { has_valid_label = 1; label = label_pton(&mpinfo->extra->label); @@ -1137,7 +1153,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, valid_nh_count++; } - if (has_valid_label) + /* if this is a evpn route we don't have to include the label */ + if (has_valid_label && + !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))) SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); if (info->sub_type != BGP_ROUTE_AGGREGATE) @@ -1179,7 +1197,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, sizeof(nh_buf)); label_buf[0] = '\0'; - if (has_valid_label) + if (has_valid_label && + !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) sprintf(label_buf, "label %u", api_nh->labels[0]); zlog_debug(" nhop [%d]: %s %s", i + 1, nh_buf, @@ -1233,11 +1252,20 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi) return; memset(&api, 0, sizeof(api)); + memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); api.vrf_id = peer->bgp->vrf_id; + api.nh_vrf_id = peer->bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; api.prefix = *p; + /* If it is an EVPN route mark as such. + * Currently presence of rmac in attr denotes + * this is an EVPN type-2 route + */ + if (!is_zero_mac(&(info->attr->rmac))) + SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); + if (peer->sort == BGP_PEER_IBGP) { SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); @@ -1516,8 +1544,9 @@ void bgp_update_redist_vrf_bitmaps(struct bgp *bgp, vrf_id_t old_vrf_id) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (vrf_bitmap_check(zclient->redist[afi][i], - old_vrf_id)) { + if ((old_vrf_id == VRF_UNKNOWN) + || vrf_bitmap_check(zclient->redist[afi][i], + old_vrf_id)) { vrf_bitmap_unset(zclient->redist[afi][i], old_vrf_id); vrf_bitmap_set(zclient->redist[afi][i], @@ -1550,7 +1579,7 @@ void bgp_zebra_instance_register(struct bgp *bgp) /* For default instance, register to learn about VNIs, if appropriate. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT - && bgp->advertise_all_vni) + && is_evpn_enabled()) bgp_zebra_advertise_all_vni(bgp, 1); } @@ -1569,7 +1598,7 @@ void bgp_zebra_instance_deregister(struct bgp *bgp) /* For default instance, unregister learning about VNIs, if appropriate. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT - && bgp->advertise_all_vni) + && is_evpn_enabled()) bgp_zebra_advertise_all_vni(bgp, 0); /* Deregister for router-id, interfaces, redistributed routes. */ @@ -1675,6 +1704,39 @@ static void bgp_zebra_connected(struct zclient *zclient) */ } +static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + char buf[ETHER_ADDR_STRLEN]; + vni_t l3vni = 0; + struct ethaddr rmac; + struct in_addr originator_ip; + struct stream *s; + + memset(&rmac, 0, sizeof(struct ethaddr)); + memset(&originator_ip, 0, sizeof(struct in_addr)); + s = zclient->ibuf; + l3vni = stream_getl(s); + if (cmd == ZEBRA_L3VNI_ADD) { + stream_get(&rmac, s, sizeof(struct ethaddr)); + originator_ip.s_addr = stream_get_ipv4(s); + } + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s", + (cmd == ZEBRA_L3VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), + l3vni, + prefix_mac2str(&rmac, buf, sizeof(buf))); + + if (cmd == ZEBRA_L3VNI_ADD) + bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip); + else + bgp_evpn_local_l3vni_del(l3vni, vrf_id); + + return 0; +} + static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { @@ -1682,23 +1744,29 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, vni_t vni; struct bgp *bgp; struct in_addr vtep_ip; + vrf_id_t tenant_vrf_id = VRF_DEFAULT; s = zclient->ibuf; vni = stream_getl(s); - if (command == ZEBRA_VNI_ADD) + if (command == ZEBRA_VNI_ADD) { vtep_ip.s_addr = stream_get_ipv4(s); + stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); + } + bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx VNI %s VRF %u VNI %u", - (command == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id, - vni); + zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", + (command == ZEBRA_VNI_ADD) ? "add" : "del", + vrf_id_to_name(vrf_id), + vni, vrf_id_to_name(tenant_vrf_id)); if (command == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( - bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id); + bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, + tenant_vrf_id); else return bgp_evpn_local_vni_del(bgp, vni); } @@ -1782,6 +1850,8 @@ void bgp_zebra_init(struct thread_master *master) zclient->local_vni_del = bgp_zebra_process_local_vni; zclient->local_macip_add = bgp_zebra_process_local_macip; zclient->local_macip_del = bgp_zebra_process_local_macip; + zclient->local_l3vni_add = bgp_zebra_process_local_l3vni; + zclient->local_l3vni_del = bgp_zebra_process_local_l3vni; } void bgp_zebra_destroy(void) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a4952be8a67a..7db73043ccb3 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -24,6 +24,7 @@ #include "thread.h" #include "buffer.h" #include "stream.h" +#include "ringbuf.h" #include "command.h" #include "sockunion.h" #include "sockopt.h" @@ -42,6 +43,7 @@ #include "jhash.h" #include "table.h" #include "lib/json.h" +#include "frr_pthread.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -75,7 +77,9 @@ #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_memory.h" #include "bgpd/bgp_evpn_vty.h" - +#include "bgpd/bgp_keepalives.h" +#include "bgpd/bgp_io.h" +#include "bgpd/bgp_ecommunity.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_QOBJ_TYPE(bgp_master) @@ -214,7 +218,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) return 0; /* EVPN uses router id in RD, withdraw them */ - if (bgp->advertise_all_vni) + if (is_evpn_enabled()) bgp_evpn_handle_router_id_update(bgp, TRUE); IPV4_ADDR_COPY(&bgp->router_id, id); @@ -231,7 +235,7 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) } /* EVPN uses router id in RD, update them */ - if (bgp->advertise_all_vni) + if (is_evpn_enabled()) bgp_evpn_handle_router_id_update(bgp, FALSE); return 0; @@ -862,8 +866,7 @@ static void peer_af_flag_reset(struct peer *peer, afi_t afi, safi_t safi) /* peer global config reset */ static void peer_global_config_reset(struct peer *peer) { - - int v6only; + int saved_flags = 0; peer->change_local_as = 0; peer->ttl = (peer_sort(peer) == BGP_PEER_IBGP ? MAXTTL : 1); @@ -881,13 +884,11 @@ static void peer_global_config_reset(struct peer *peer) else peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; - /* This is a per-peer specific flag and so we must preserve it */ - v6only = CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); - + /* These are per-peer specific flags and so we must preserve them */ + saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); + saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN); peer->flags = 0; - - if (v6only) - SET_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); + SET_FLAG(peer->flags, saved_flags); peer->config = 0; peer->holdtime = 0; @@ -909,7 +910,7 @@ static void peer_global_config_reset(struct peer *peer) } /* Check peer's AS number and determines if this peer is IBGP or EBGP */ -static bgp_peer_sort_t peer_calc_sort(struct peer *peer) +static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer) { struct bgp *bgp; @@ -989,10 +990,14 @@ static void peer_free(struct peer *peer) * but just to be sure.. */ bgp_timer_set(peer); - BGP_READ_OFF(peer->t_read); - BGP_WRITE_OFF(peer->t_write); + bgp_reads_off(peer); + bgp_writes_off(peer); + assert(!peer->t_write); + assert(!peer->t_read); BGP_EVENT_FLUSH(peer); + pthread_mutex_destroy(&peer->io_mtx); + /* Free connected nexthop, if present */ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE) && !peer_dynamic_neighbor(peer)) @@ -1135,27 +1140,28 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); /* Create buffers. */ - peer->ibuf = stream_new(BGP_MAX_PACKET_SIZE); + peer->ibuf = stream_fifo_new(); peer->obuf = stream_fifo_new(); + pthread_mutex_init(&peer->io_mtx, NULL); - /* We use a larger buffer for peer->work in the event that: + /* We use a larger buffer for peer->obuf_work in the event that: * - We RX a BGP_UPDATE where the attributes alone are just * under BGP_MAX_PACKET_SIZE * - The user configures an outbound route-map that does many as-path - * prepends or adds many communities. At most they can have - * CMD_ARGC_MAX - * args in a route-map so there is a finite limit on how large they - * can - * make the attributes. + * prepends or adds many communities. At most they can have + * CMD_ARGC_MAX args in a route-map so there is a finite limit on how + * large they can make the attributes. * * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid - * bounds - * checking for every single attribute as we construct an UPDATE. + * bounds checking for every single attribute as we construct an + * UPDATE. */ - peer->work = + peer->obuf_work = stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW); - peer->scratch = stream_new(BGP_MAX_PACKET_SIZE); + peer->ibuf_work = + ringbuf_new(BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX); + peer->scratch = stream_new(BGP_MAX_PACKET_SIZE); bgp_sync_init(peer); @@ -1196,7 +1202,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->local_as = peer_src->local_as; peer_dst->ifindex = peer_src->ifindex; peer_dst->port = peer_src->port; - peer_sort(peer_dst); + (void)peer_sort(peer_dst); peer_dst->rmap_type = peer_src->rmap_type; /* Timers */ @@ -1466,6 +1472,14 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, listnode_add_sort(bgp->peer, peer); hash_get(bgp->peerhash, peer, hash_alloc_intern); + /* Adjust update-group coalesce timer heuristics for # peers. */ + if (bgp->heuristic_coalesce) { + long ct = BGP_DEFAULT_SUBGROUP_COALESCE_TIME + + (bgp->peer->count + * BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME); + bgp->coalesce_time = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME, ct); + } + active = peer_active(peer); /* Last read and reset time set */ @@ -1481,8 +1495,11 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, peer_af_create(peer, afi, safi); } + /* auto shutdown if configured */ + if (bgp->autoshutdown) + peer_flag_set(peer, PEER_FLAG_SHUTDOWN); /* Set up peer's events and timers. */ - if (!active && peer_active(peer)) + else if (!active && peer_active(peer)) bgp_timer_set(peer); return peer; @@ -1869,6 +1886,11 @@ static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi) BGP_NOTIFY_CEASE_CONFIG_CHANGE); } } + if (peer->status == OpenSent || peer->status == OpenConfirm) { + peer->last_reset = PEER_DOWN_AF_ACTIVATE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } } return 0; @@ -2082,6 +2104,11 @@ int peer_delete(struct peer *peer) bgp = peer->bgp; accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); + bgp_reads_off(peer); + bgp_writes_off(peer); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON)); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) peer_nsf_stop(peer); @@ -2143,7 +2170,7 @@ int peer_delete(struct peer *peer) /* Buffers. */ if (peer->ibuf) { - stream_free(peer->ibuf); + stream_fifo_free(peer->ibuf); peer->ibuf = NULL; } @@ -2152,9 +2179,14 @@ int peer_delete(struct peer *peer) peer->obuf = NULL; } - if (peer->work) { - stream_free(peer->work); - peer->work = NULL; + if (peer->ibuf_work) { + ringbuf_del(peer->ibuf_work); + peer->ibuf_work = NULL; + } + + if (peer->obuf_work) { + stream_free(peer->obuf_work); + peer->obuf_work = NULL; } if (peer->scratch) { @@ -2308,7 +2340,7 @@ static void peer_group2peer_config_copy(struct peer_group *group, struct peer *peer) { struct peer *conf; - int v6only; + int saved_flags = 0; conf = group->conf; @@ -2326,14 +2358,11 @@ static void peer_group2peer_config_copy(struct peer_group *group, /* GTSM hops */ peer->gtsm_hops = conf->gtsm_hops; - /* this flag is per-neighbor and so has to be preserved */ - v6only = CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); - - /* peer flags apply */ + /* These are per-peer specific flags and so we must preserve them */ + saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); + saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN); peer->flags = conf->flags; - - if (v6only) - SET_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); + SET_FLAG(peer->flags, saved_flags); /* peer config apply */ peer->config = conf->config; @@ -2890,7 +2919,10 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->restart_time, &bgp->t_startup); } - bgp->wpkt_quanta = BGP_WRITE_PACKET_MAX; + atomic_store_explicit(&bgp->wpkt_quanta, BGP_WRITE_PACKET_MAX, + memory_order_relaxed); + atomic_store_explicit(&bgp->rpkt_quanta, BGP_READ_PACKET_MAX, + memory_order_relaxed); bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME; QOBJ_REG(bgp, bgp); @@ -3108,6 +3140,9 @@ int bgp_delete(struct bgp *bgp) bgp->name); } + /* unmap from RT list */ + bgp_evpn_vrf_delete(bgp); + /* Stop timers. */ if (bgp->t_rmap_def_originate_eval) { BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval); @@ -7046,6 +7081,11 @@ int bgp_config_write(struct vty *vty) /* BGP configuration. */ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + + /* skip all auto created vrf as they dont have user config */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) + continue; + /* Router bgp ASN */ vty_out(vty, "router bgp %u", bgp->as); @@ -7109,6 +7149,10 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", bgp->default_subgroup_pkt_queue_max); + /* BGP default autoshutdown neighbors */ + if (bgp->autoshutdown) + vty_out(vty, " bgp default auto-shutdown\n"); + /* BGP client-to-client reflection. */ if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) vty_out(vty, " no bgp client-to-client reflection\n"); @@ -7174,6 +7218,8 @@ int bgp_config_write(struct vty *vty) /* write quanta */ bgp_config_write_wpkt_quanta(vty, bgp); + /* read quanta */ + bgp_config_write_rpkt_quanta(vty, bgp); /* coalesce time */ bgp_config_write_coalesce_time(vty, bgp); @@ -7268,6 +7314,38 @@ int bgp_config_write(struct vty *vty) if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) vty_out(vty, " no auto-summary\n"); + /* import route-target */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, + ecom)) { + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " route-target import %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + + /* export route-target */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) { + char *ecom_str; + struct listnode *node, *nnode; + struct ecommunity *ecom; + + for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, + ecom)) { + ecom_str = ecommunity_ecom2str( + ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " route-target export %s\n", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } + } + /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); @@ -7328,6 +7406,13 @@ void bgp_master_init(struct thread_master *master) bgp_process_queue_init(); + /* init the rd id space. + assign 0th index in the bitfield, + so that we start with id 1 + */ + bf_init(bm->rd_idspace, UINT16_MAX); + bf_assign_zero_index(bm->rd_idspace); + /* Enable multiple instances by default. */ bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); @@ -7381,12 +7466,51 @@ static const struct cmd_variable_handler bgp_viewvrf_var_handlers[] = { {.completions = NULL}, }; +static void bgp_pthreads_init() +{ + frr_pthread_init(); + + frr_pthread_new("BGP i/o thread", PTHREAD_IO, bgp_io_start, + bgp_io_stop); + frr_pthread_new("BGP keepalives thread", PTHREAD_KEEPALIVES, + bgp_keepalives_start, bgp_keepalives_stop); + + /* pre-run initialization */ + bgp_keepalives_init(); + bgp_io_init(); +} + +void bgp_pthreads_run() +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + + /* + * I/O related code assumes the thread is ready for work at all times, + * so we wait until it is. + */ + frr_pthread_run(PTHREAD_IO, &attr, NULL); + bgp_io_wait_running(); + + frr_pthread_run(PTHREAD_KEEPALIVES, &attr, NULL); +} + +void bgp_pthreads_finish() +{ + frr_pthread_stop_all(); + frr_pthread_finish(); +} + void bgp_init(void) { /* allocates some vital data structures used by peer commands in * vty_init */ + /* pre-init pthreads */ + bgp_pthreads_init(); + /* Init zebra. */ bgp_zebra_init(bm->master); @@ -7451,6 +7575,7 @@ void bgp_terminate(void) */ /* reverse bgp_master_init */ bgp_close(); + if (bm->listen_sockets) list_delete_and_null(&bm->listen_sockets); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 36bdaf0125f4..c4ac4b0adb05 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -22,6 +22,8 @@ #define _QUAGGA_BGPD_H #include "qobj.h" +#include + #include "lib/json.h" #include "vrf.h" #include "vty.h" @@ -34,6 +36,7 @@ #include "defaults.h" #include "bgp_memory.h" #include "bitfield.h" +#include "vxlan.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -98,6 +101,10 @@ struct bgp_master { /* BGP thread master. */ struct thread_master *master; +/* BGP pthreads. */ +#define PTHREAD_IO (1 << 1) +#define PTHREAD_KEEPALIVES (1 << 2) + /* work queues */ struct work_queue *process_main_queue; @@ -130,6 +137,9 @@ struct bgp_master { /* clang-format off */ #define RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */ + /* Id space for automatic RD derivation for an EVI/VRF */ + bitfield_t rd_idspace; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp_master) @@ -372,8 +382,16 @@ struct bgp { #define BGP_FLAG_IBGP_MULTIPATH_SAME_CLUSTERLEN (1 << 0) } maxpaths[AFI_MAX][SAFI_MAX]; - u_int32_t wpkt_quanta; /* per peer packet quanta to write */ - u_int32_t coalesce_time; + _Atomic uint32_t wpkt_quanta; // max # packets to write per i/o cycle + _Atomic uint32_t rpkt_quanta; // max # packets to read per i/o cycle + + /* Automatic coalesce adjust on/off */ + bool heuristic_coalesce; + /* Actual coalesce time */ + uint32_t coalesce_time; + + /* Auto-shutdown new peers */ + bool autoshutdown; u_int32_t addpath_tx_id; int addpath_tx_used[AFI_MAX][SAFI_MAX]; @@ -397,8 +415,41 @@ struct bgp { /* Hash table of Import RTs to EVIs */ struct hash *import_rt_hash; - /* Id space for automatic RD derivation for an EVI */ - bitfield_t rd_idspace; + /* Hash table of VRF import RTs to VRFs */ + struct hash *vrf_import_rt_hash; + + /* L3-VNI corresponding to this vrf */ + vni_t l3vni; + + /* router-mac to be used in mac-ip routes for this vrf */ + struct ethaddr rmac; + + /* originator ip - to be used as NH for type-5 routes */ + struct in_addr originator_ip; + + /* vrf flags */ + uint32_t vrf_flags; +#define BGP_VRF_AUTO (1 << 0) +#define BGP_VRF_ADVERTISE_IPV4_IN_EVPN (1 << 1) +#define BGP_VRF_ADVERTISE_IPV6_IN_EVPN (1 << 2) +#define BGP_VRF_IMPORT_RT_CFGD (1 << 3) +#define BGP_VRF_EXPORT_RT_CFGD (1 << 4) +#define BGP_VRF_RD_CFGD (1 << 5) + + /* unique ID for auto derivation of RD for this vrf */ + uint16_t vrf_rd_id; + + /* RD for this VRF */ + struct prefix_rd vrf_prd; + + /* import rt list for the vrf instance */ + struct list *vrf_import_rtl; + + /* export rt list for the vrf instance */ + struct list *vrf_export_rtl; + + /* list of corresponding l2vnis (struct bgpevpn) */ + struct list *l2vnis; QOBJ_FIELDS }; @@ -583,12 +634,17 @@ struct peer { struct in_addr local_id; /* Packet receive and send buffer. */ - struct stream *ibuf; - struct stream_fifo *obuf; - struct stream *work; + pthread_mutex_t io_mtx; // guards ibuf, obuf + struct stream_fifo *ibuf; // packets waiting to be processed + struct stream_fifo *obuf; // packets waiting to be written + + struct ringbuf *ibuf_work; // WiP buffer used by bgp_read() only + struct stream *obuf_work; // WiP buffer used to construct packets + + struct stream *curr; // the current packet being parsed /* We use a separate stream to encode MP_REACH_NLRI for efficient - * NLRI packing. peer->work stores all the other attributes. The + * NLRI packing. peer->obuf_work stores all the other attributes. The * actual packet is then constructed by concatenating the two. */ struct stream *scratch; @@ -776,49 +832,73 @@ struct peer { (CHECK_FLAG(peer->config, PEER_CONFIG_TIMER) \ || CHECK_FLAG(peer->config, PEER_GROUP_CONFIG_TIMER)) - u_int32_t holdtime; - u_int32_t keepalive; - u_int32_t connect; - u_int32_t routeadv; + _Atomic uint32_t holdtime; + _Atomic uint32_t keepalive; + _Atomic uint32_t connect; + _Atomic uint32_t routeadv; /* Timer values. */ - u_int32_t v_start; - u_int32_t v_connect; - u_int32_t v_holdtime; - u_int32_t v_keepalive; - u_int32_t v_routeadv; - u_int32_t v_pmax_restart; - u_int32_t v_gr_restart; + _Atomic uint32_t v_start; + _Atomic uint32_t v_connect; + _Atomic uint32_t v_holdtime; + _Atomic uint32_t v_keepalive; + _Atomic uint32_t v_routeadv; + _Atomic uint32_t v_pmax_restart; + _Atomic uint32_t v_gr_restart; /* Threads. */ struct thread *t_read; struct thread *t_write; struct thread *t_start; + struct thread *t_connect_check_r; + struct thread *t_connect_check_w; struct thread *t_connect; struct thread *t_holdtime; - struct thread *t_keepalive; struct thread *t_routeadv; struct thread *t_pmax_restart; struct thread *t_gr_restart; struct thread *t_gr_stale; - + struct thread *t_generate_updgrp_packets; + struct thread *t_process_packet; + + /* Thread flags. */ + _Atomic uint16_t thread_flags; +#define PEER_THREAD_WRITES_ON (1 << 0) +#define PEER_THREAD_READS_ON (1 << 1) +#define PEER_THREAD_KEEPALIVES_ON (1 << 2) /* workqueues */ struct work_queue *clear_node_queue; +#define PEER_TOTAL_RX(peer) \ + atomic_load_explicit(&peer->open_in, memory_order_relaxed) + \ + atomic_load_explicit(&peer->update_in, memory_order_relaxed) + \ + atomic_load_explicit(&peer->notify_in, memory_order_relaxed) + \ + atomic_load_explicit(&peer->refresh_in, memory_order_relaxed) + \ + atomic_load_explicit(&peer->keepalive_in, memory_order_relaxed) + \ + atomic_load_explicit(&peer->dynamic_cap_in, memory_order_relaxed) + +#define PEER_TOTAL_TX(peer) \ + atomic_load_explicit(&peer->open_out, memory_order_relaxed) + \ + atomic_load_explicit(&peer->update_out, memory_order_relaxed) + \ + atomic_load_explicit(&peer->notify_out, memory_order_relaxed) + \ + atomic_load_explicit(&peer->refresh_out, memory_order_relaxed) + \ + atomic_load_explicit(&peer->keepalive_out, memory_order_relaxed) + \ + atomic_load_explicit(&peer->dynamic_cap_out, memory_order_relaxed) + /* Statistics field */ - u_int32_t open_in; /* Open message input count */ - u_int32_t open_out; /* Open message output count */ - u_int32_t update_in; /* Update message input count */ - u_int32_t update_out; /* Update message ouput count */ - time_t update_time; /* Update message received time. */ - u_int32_t keepalive_in; /* Keepalive input count */ - u_int32_t keepalive_out; /* Keepalive output count */ - u_int32_t notify_in; /* Notify input count */ - u_int32_t notify_out; /* Notify output count */ - u_int32_t refresh_in; /* Route Refresh input count */ - u_int32_t refresh_out; /* Route Refresh output count */ - u_int32_t dynamic_cap_in; /* Dynamic Capability input count. */ - u_int32_t dynamic_cap_out; /* Dynamic Capability output count. */ + _Atomic uint32_t open_in; /* Open message input count */ + _Atomic uint32_t open_out; /* Open message output count */ + _Atomic uint32_t update_in; /* Update message input count */ + _Atomic uint32_t update_out; /* Update message ouput count */ + _Atomic time_t update_time; /* Update message received time. */ + _Atomic uint32_t keepalive_in; /* Keepalive input count */ + _Atomic uint32_t keepalive_out; /* Keepalive output count */ + _Atomic uint32_t notify_in; /* Notify input count */ + _Atomic uint32_t notify_out; /* Notify output count */ + _Atomic uint32_t refresh_in; /* Route Refresh input count */ + _Atomic uint32_t refresh_out; /* Route Refresh output count */ + _Atomic uint32_t dynamic_cap_in; /* Dynamic Capability input count. */ + _Atomic uint32_t dynamic_cap_out; /* Dynamic Capability output count. */ /* BGP state count */ u_int32_t established; /* Established */ @@ -831,21 +911,17 @@ struct peer { /* Syncronization list and time. */ struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX]; time_t synctime; - time_t last_write; /* timestamp when the last msg was written */ - time_t last_update; /* timestamp when the last UPDATE msg was written */ + /* timestamp when the last UPDATE msg was written */ + _Atomic time_t last_write; + /* timestamp when the last msg was written */ + _Atomic time_t last_update; /* Send prefix count. */ unsigned long scount[AFI_MAX][SAFI_MAX]; - /* Announcement attribute hash. */ - struct hash *hash[AFI_MAX][SAFI_MAX]; - /* Notify data. */ struct bgp_notify notify; - /* Whole packet size to be read. */ - unsigned long packet_size; - /* Filter structure. */ struct bgp_filter filter[AFI_MAX][SAFI_MAX]; @@ -997,6 +1073,7 @@ struct bgp_nlri { #define BGP_ATTR_AS4_PATH 17 #define BGP_ATTR_AS4_AGGREGATOR 18 #define BGP_ATTR_AS_PATHLIMIT 21 +#define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_ENCAP 23 #define BGP_ATTR_LARGE_COMMUNITIES 32 #define BGP_ATTR_PREFIX_SID 40 @@ -1139,8 +1216,8 @@ enum bgp_clear_type { }; /* Macros. */ -#define BGP_INPUT(P) ((P)->ibuf) -#define BGP_INPUT_PNT(P) (STREAM_PNT(BGP_INPUT(P))) +#define BGP_INPUT(P) ((P)->curr) +#define BGP_INPUT_PNT(P) (stream_pnt(BGP_INPUT(P))) #define BGP_IS_VALID_STATE_FOR_NOTIF(S) \ (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established)) @@ -1251,6 +1328,8 @@ extern int bgp_config_write(struct vty *); extern void bgp_master_init(struct thread_master *master); extern void bgp_init(void); +extern void bgp_pthreads_run(void); +extern void bgp_pthreads_finish(void); extern void bgp_route_map_init(void); extern void bgp_session_reset(struct peer *); diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 3ecc4be94eba..e3cb73946474 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -170,12 +170,12 @@ struct rfapi_nve_group_cfg *bgp_rfapi_cfg_match_group(struct rfapi_cfg *hc, #if BGP_VNC_DEBUG_MATCH_GROUP { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(vn, buf, BUFSIZ); + prefix2str(vn, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: vn prefix: %s", __func__, buf); - prefix2str(un, buf, BUFSIZ); + prefix2str(un, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: un prefix: %s", __func__, buf); vnc_zlog_debug_verbose( @@ -1626,7 +1626,11 @@ DEFUN (vnc_nve_group_export_no_prefixlist, return CMD_WARNING_CONFIG_FAILED; } - argv_find_and_parse_afi(argv, argc, &idx, &afi); + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + vty_out(vty, "%% Malformed Address Family\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (argv[idx-1]->text[0] == 'z') is_bgp = 0; idx += 2; /* skip afi and keyword */ @@ -1691,7 +1695,11 @@ DEFUN (vnc_nve_group_export_prefixlist, return CMD_WARNING_CONFIG_FAILED; } - argv_find_and_parse_afi(argv, argc, &idx, &afi); + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + vty_out(vty, "%% Malformed Address Family\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (argv[idx-1]->text[0] == 'z') is_bgp = 0; idx = argc - 1; @@ -3884,8 +3892,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) } if (rfg->rd.prefixlen) { - char buf[BUFSIZ]; - buf[0] = buf[BUFSIZ - 1] = 0; + char buf[RD_ADDRSTRLEN]; if (AF_UNIX == rfg->rd.family) { @@ -3898,18 +3905,10 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty_out(vty, " rd auto:nh:%d\n", value); - } else { - - if (!prefix_rd2str(&rfg->rd, buf, - BUFSIZ) - || !buf[0] || buf[BUFSIZ - 1]) { - - vty_out(vty, - "!Error: Can't convert rd\n"); - } else { - vty_out(vty, " rd %s\n", buf); - } - } + } else + vty_out(vty, " rd %s\n", + prefix_rd2str(&rfg->rd, buf, + sizeof(buf))); } if (rfg->rt_import_list && rfg->rt_export_list @@ -4098,7 +4097,8 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) } } - if (hc->default_rd.prefixlen || hc->default_response_lifetime + if (hc->default_rd.prefixlen + || hc->default_response_lifetime != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT || hc->default_rt_import_list || hc->default_rt_export_list || hc->nve_groups_sequential->count) { @@ -4107,8 +4107,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty_out(vty, " vnc defaults\n"); if (hc->default_rd.prefixlen) { - char buf[BUFSIZ]; - buf[0] = buf[BUFSIZ - 1] = 0; + char buf[RD_ADDRSTRLEN]; if (AF_UNIX == hc->default_rd.family) { uint16_t value = 0; @@ -4121,20 +4120,14 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty_out(vty, " rd auto:vn:%d\n", value); - } else { - - if (!prefix_rd2str(&hc->default_rd, buf, - BUFSIZ) - || !buf[0] || buf[BUFSIZ - 1]) { - - vty_out(vty, - "!Error: Can't convert rd\n"); - } else { - vty_out(vty, " rd %s\n", buf); - } - } + } else + vty_out(vty, " rd %s\n", + prefix_rd2str(&hc->default_rd, + buf, + sizeof(buf))); } - if (hc->default_response_lifetime) { + if (hc->default_response_lifetime + != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT) { vty_out(vty, " response-lifetime "); if (hc->default_response_lifetime != UINT32_MAX) vty_out(vty, "%d", @@ -4187,38 +4180,26 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) vty_out(vty, " vnc nve-group %s\n", rfg->name); if (rfg->vn_prefix.family && rfg->vn_node) { - char buf[BUFSIZ]; - buf[0] = buf[BUFSIZ - 1] = 0; + char buf[PREFIX_STRLEN]; prefix2str(&rfg->vn_prefix, buf, - BUFSIZ); - if (!buf[0] || buf[BUFSIZ - 1]) { - vty_out(vty, - "!Error: Can't convert prefix\n"); - } else { - vty_out(vty, " prefix %s %s\n", - "vn", buf); - } + sizeof(buf)); + vty_out(vty, " prefix %s %s\n", + "vn", buf); } if (rfg->un_prefix.family && rfg->un_node) { - char buf[BUFSIZ]; - buf[0] = buf[BUFSIZ - 1] = 0; + char buf[PREFIX_STRLEN]; + prefix2str(&rfg->un_prefix, buf, - BUFSIZ); - if (!buf[0] || buf[BUFSIZ - 1]) { - vty_out(vty, - "!Error: Can't convert prefix\n"); - } else { - vty_out(vty, " prefix %s %s\n", - "un", buf); - } + sizeof(buf)); + vty_out(vty, " prefix %s %s\n", + "un", buf); } if (rfg->rd.prefixlen) { - char buf[BUFSIZ]; - buf[0] = buf[BUFSIZ - 1] = 0; + char buf[RD_ADDRSTRLEN]; if (AF_UNIX == rfg->rd.family) { @@ -4233,21 +4214,12 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) " rd auto:vn:%d\n", value); - } else { - - if (!prefix_rd2str(&rfg->rd, - buf, BUFSIZ) - || !buf[0] - || buf[BUFSIZ - 1]) { - - vty_out(vty, - "!Error: Can't convert rd\n"); - } else { - vty_out(vty, - " rd %s\n", - buf); - } - } + } else + vty_out(vty, + " rd %s\n", + prefix_rd2str(&rfg->rd, + buf, + sizeof(buf))); } if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) { vty_out(vty, " response-lifetime "); diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 15a29442f470..95666143a5c5 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -31,6 +31,7 @@ #include "lib/linklist.h" #include "lib/command.h" #include "lib/stream.h" +#include "lib/ringbuf.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" @@ -363,15 +364,11 @@ void del_vnc_route(struct rfapi_descriptor *rfd, afi_t afi; /* of the VN address */ struct bgp_node *bn; struct bgp_info *bi; - char buf[BUFSIZ]; - char buf2[BUFSIZ]; + char buf[PREFIX_STRLEN]; + char buf2[RD_ADDRSTRLEN]; struct prefix_rd prd0; - prefix2str(p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ - - prefix_rd2str(prd, buf2, BUFSIZ); - buf2[BUFSIZ - 1] = 0; + prefix2str(p, buf, sizeof(buf)); afi = family2afi(p->family); assert(afi == AFI_IP || afi == AFI_IP6); @@ -386,7 +383,8 @@ void del_vnc_route(struct rfapi_descriptor *rfd, vnc_zlog_debug_verbose( "%s: peer=%p, prefix=%s, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p", - __func__, peer, buf, buf2, afi, safi, bn, + __func__, peer, buf, + prefix_rd2str(prd, buf2, sizeof(buf2)), afi, safi, bn, (bn ? bn->info : NULL)); for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) { @@ -464,11 +462,9 @@ void del_vnc_route(struct rfapi_descriptor *rfd, rfapiProcessWithdraw(peer, rfd, p, prd, NULL, afi, safi, type, kill); if (bi) { - char buf[BUFSIZ]; - - prefix2str(p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + char buf[PREFIX_STRLEN]; + prefix2str(p, buf, sizeof(buf)); vnc_zlog_debug_verbose( "%s: Found route (safi=%d) to delete at prefix %s", __func__, safi, buf); @@ -593,8 +589,8 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ uint32_t label_val; struct bgp_attr_encap_subtlv *encaptlv; - char buf[BUFSIZ]; - char buf2[BUFSIZ]; + char buf[PREFIX_STRLEN]; + char buf2[RD_ADDRSTRLEN]; #if 0 /* unused? */ struct prefix pfx_buf; #endif @@ -650,9 +646,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ else label_val = MPLS_LABEL_IMPLICIT_NULL; - prefix_rd2str(prd, buf2, BUFSIZ); - buf2[BUFSIZ - 1] = 0; - + prefix_rd2str(prd, buf2, sizeof(buf2)); afi = family2afi(p->family); assert(afi == AFI_IP || afi == AFI_IP6); @@ -907,8 +901,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ } - prefix2str(p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + prefix2str(p, buf, sizeof(buf)); /* * At this point: @@ -920,7 +913,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ * aspath: points to interned hash from aspath hash table */ - red = bgp_redist_lookup(bgp, afi, type, VRF_DEFAULT); + red = bgp_redist_lookup(bgp, afi, type, 0); if (red && red->redist_metric_flag) { attr.med = red->redist_metric; @@ -1304,18 +1297,31 @@ static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp, rfd->peer = peer_new(bgp); rfd->peer->status = Established; /* keep bgp core happy */ bgp_sync_delete(rfd->peer); /* don't need these */ - if (rfd->peer->ibuf) { - stream_free(rfd->peer->ibuf); /* don't need it */ + + /* + * since this peer is not on the I/O thread, this lock is not strictly + * necessary, but serves as a reminder to those who may meddle... + */ + pthread_mutex_lock(&rfd->peer->io_mtx); + { + // we don't need any I/O related facilities + if (rfd->peer->ibuf) + stream_fifo_free(rfd->peer->ibuf); + if (rfd->peer->obuf) + stream_fifo_free(rfd->peer->obuf); + + if (rfd->peer->ibuf_work) + ringbuf_del(rfd->peer->ibuf_work); + if (rfd->peer->obuf_work) + stream_free(rfd->peer->obuf_work); + rfd->peer->ibuf = NULL; - } - if (rfd->peer->obuf) { - stream_fifo_free(rfd->peer->obuf); /* don't need it */ rfd->peer->obuf = NULL; + rfd->peer->obuf_work = NULL; + rfd->peer->ibuf_work = NULL; } - if (rfd->peer->work) { - stream_free(rfd->peer->work); /* don't need it */ - rfd->peer->work = NULL; - } + pthread_mutex_unlock(&rfd->peer->io_mtx); + { /* base code assumes have valid host pointer */ char buf[BUFSIZ]; buf[0] = 0; @@ -1598,11 +1604,10 @@ rfapi_query_inner(void *handle, struct rfapi_ip_addr *target, } { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; char *s; - prefix2str(&p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + prefix2str(&p, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s(rfd=%p, target=%s, ppNextHop=%p)", __func__, rfd, buf, ppNextHopEntry); @@ -2421,10 +2426,9 @@ int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix, { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(&p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + prefix2str(&p, buf, sizeof(buf)); vnc_zlog_debug_verbose( "%s(rfd=%p, pfx=%s, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)", __func__, rfd, buf, lifetime, options_un, options_vn, @@ -3828,12 +3832,10 @@ int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn) 4); /* low order 4 bytes */ } { - char buf[BUFSIZ]; - buf[0] = 0; - prefix_rd2str(rd, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + char buf[RD_ADDRSTRLEN]; + vnc_zlog_debug_verbose("%s: auto-RD is set to %s", __func__, - buf); + prefix_rd2str(rd, buf, sizeof(buf))); } return 0; } diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index cdbc88c967ad..8727c5d5ea5c 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -41,6 +41,7 @@ #include "bgpd/bgp_route.h" #include "bgpd/bgp_mplsvpn.h" /* prefix_rd2str() */ #include "bgpd/bgp_vnc_types.h" +#include "bgpd/bgp_rd.h" #include "bgpd/rfapi/rfapi.h" #include "bgpd/rfapi/bgp_rfapi_cfg.h" @@ -645,10 +646,9 @@ rfapiMonitorMoveShorter(struct route_node *original_vpn_node, int lockoffset) #if DEBUG_MONITOR_MOVE_SHORTER { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(&original_vpn_node->p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(&original_vpn_node->p, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: called with node pfx=%s", __func__, buf); } @@ -779,10 +779,9 @@ rfapiMonitorMoveShorter(struct route_node *original_vpn_node, int lockoffset) #if DEBUG_MONITOR_MOVE_SHORTER { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(&par->p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(&par->p, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: moved to node pfx=%s", __func__, buf); } @@ -1452,7 +1451,7 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, __func__, __LINE__, have_vnc_tunnel_un); #endif - if (!have_vnc_tunnel_un && bi && bi->extra) { + if (!have_vnc_tunnel_un && bi->extra) { /* * use cached UN address from ENCAP route */ @@ -1594,10 +1593,9 @@ static int rfapiNhlAddNodeRoutes( } if (!skiplist_search(seen_nexthops, &pfx_vn, NULL)) { #if DEBUG_RETURNED_NHL - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(&pfx_vn, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + prefix2str(&pfx_vn, buf, sizeof(buf)); vnc_zlog_debug_verbose( "%s: already put VN/nexthop %s, skip", __func__, buf); @@ -1760,10 +1758,9 @@ struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( #if DEBUG_RETURNED_NHL { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(&rn->p, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(&rn->p, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: called with node pfx=%s", __func__, buf); } @@ -2136,10 +2133,12 @@ static void rfapiItBiIndexAdd(struct route_node *rn, /* Import table VPN node */ assert(bi->extra); { - char buf[BUFSIZ]; - prefix_rd2str(&bi->extra->vnc.import.rd, buf, BUFSIZ); + char buf[RD_ADDRSTRLEN]; + vnc_zlog_debug_verbose("%s: bi %p, peer %p, rd %s", __func__, - bi, bi->peer, buf); + bi, bi->peer, + prefix_rd2str(&bi->extra->vnc.import.rd, + buf, sizeof(buf))); } sl = RFAPI_RDINDEX_W_ALLOC(rn); @@ -2173,18 +2172,15 @@ static void rfapiItBiIndexDump(struct route_node *rn) for (rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor); !rc; rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor)) { - char buf[BUFSIZ]; - char buf_aux_pfx[BUFSIZ]; + char buf[RD_ADDRSTRLEN]; + char buf_aux_pfx[PREFIX_STRLEN]; - prefix_rd2str(&k->extra->vnc.import.rd, buf, BUFSIZ); - buf_aux_pfx[0] = 0; + prefix_rd2str(&k->extra->vnc.import.rd, buf, sizeof(buf)); if (k->extra->vnc.import.aux_prefix.family) { prefix2str(&k->extra->vnc.import.aux_prefix, - buf_aux_pfx, BUFSIZ); - } else { - strncpy(buf_aux_pfx, "(none)", BUFSIZ); - buf_aux_pfx[BUFSIZ - 1] = 0; - } + buf_aux_pfx, sizeof(buf_aux_pfx)); + } else + strncpy(buf_aux_pfx, "(none)", PREFIX_STRLEN); vnc_zlog_debug_verbose("bi %p, peer %p, rd %s, aux_prefix %s", k, k->peer, buf, buf_aux_pfx); @@ -2208,19 +2204,19 @@ static struct bgp_info *rfapiItBiIndexSearch( #if DEBUG_BI_SEARCH { - char buf[BUFSIZ]; - char buf_aux_pfx[BUFSIZ]; + char buf[RD_ADDRSTRLEN]; + char buf_aux_pfx[PREFIX_STRLEN]; - prefix_rd2str(prd, buf, BUFSIZ); if (aux_prefix) { - prefix2str(aux_prefix, buf_aux_pfx, BUFSIZ); - } else { - strncpy(buf_aux_pfx, "(nil)", BUFSIZ - 1); - buf_aux_pfx[BUFSIZ - 1] = 0; - } + prefix2str(aux_prefix, buf_aux_pfx, + sizeof(buf_aux_pfx)); + } else + strncpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx)); vnc_zlog_debug_verbose("%s want prd=%s, peer=%p, aux_prefix=%s", - __func__, buf, peer, buf_aux_pfx); + __func__, + prefix_rd2str(prd, buf, sizeof(buf)), + peer, buf_aux_pfx); rfapiItBiIndexDump(rn); } #endif @@ -2235,12 +2231,14 @@ static struct bgp_info *rfapiItBiIndexSearch( bi_result = bi_result->next) { #if DEBUG_BI_SEARCH { - char buf[BUFSIZ]; - prefix_rd2str(&bi_result->extra->vnc.import.rd, - buf, BUFSIZ); + char buf[RD_ADDRSTRLEN]; + vnc_zlog_debug_verbose( "%s: bi has prd=%s, peer=%p", __func__, - buf, bi_result->peer); + prefix_rd2str(&bi_result->extra->vnc.import.rd, + buf, + sizeof(buf)), + bi_result->peer); } #endif if (peer == bi_result->peer @@ -2303,10 +2301,12 @@ static void rfapiItBiIndexDel(struct route_node *rn, /* Import table VPN node */ int rc; { - char buf[BUFSIZ]; - prefix_rd2str(&bi->extra->vnc.import.rd, buf, BUFSIZ); + char buf[RD_ADDRSTRLEN]; + vnc_zlog_debug_verbose("%s: bi %p, peer %p, rd %s", __func__, - bi, bi->peer, buf); + bi, bi->peer, + prefix_rd2str(&bi->extra->vnc.import.rd, + buf, sizeof(buf))); } sl = RFAPI_RDINDEX(rn); @@ -3671,9 +3671,9 @@ void rfapiBgpInfoFilteredImportVPN( rfapiCopyUnEncap2VPN(ern->info, info_new); route_unlock_node(ern); /* undo lock in route_note_match */ } else { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; + prefix2str(&vn_prefix, buf, sizeof(buf)); - buf[BUFSIZ - 1] = 0; /* Not a big deal, just means VPN route got here first */ vnc_zlog_debug_verbose("%s: no encap route for vn addr %s", __func__, buf); @@ -3947,7 +3947,7 @@ void rfapiProcessUpdate(struct peer *peer, vnc_zlog_debug_verbose( "%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p", __func__, rc, lni, attr); - if (attr && !rc) { + if (!rc) { it = rfapiMacImportTableGet(bgp, lni); rfapiBgpInfoFilteredImportVPN( @@ -4433,10 +4433,10 @@ static void rfapiDeleteRemotePrefixesIt( #if DEBUG_L2_EXTRA { - char buf_pfx[BUFSIZ]; + char buf_pfx[PREFIX_STRLEN]; if (p) { - prefix2str(p, buf_pfx, BUFSIZ); + prefix2str(p, buf_pfx, sizeof(buf_pfx)); } else { buf_pfx[0] = '*'; buf_pfx[1] = 0; @@ -4469,11 +4469,11 @@ static void rfapiDeleteRemotePrefixesIt( struct bgp_info *next; if (VNC_DEBUG(IMPORT_DEL_REMOTE)) { - char p1line[BUFSIZ]; - char p2line[BUFSIZ]; + char p1line[PREFIX_STRLEN]; + char p2line[PREFIX_STRLEN]; - prefix2str(p, p1line, BUFSIZ); - prefix2str(&rn->p, p2line, BUFSIZ); + prefix2str(p, p1line, sizeof(p1line)); + prefix2str(&rn->p, p2line, sizeof(p2line)); vnc_zlog_debug_any("%s: want %s, have %s", __func__, p1line, p2line); } @@ -4482,8 +4482,9 @@ static void rfapiDeleteRemotePrefixesIt( continue; { - char buf_pfx[BUFSIZ]; - prefix2str(&rn->p, buf_pfx, BUFSIZ); + char buf_pfx[PREFIX_STRLEN]; + + prefix2str(&rn->p, buf_pfx, sizeof(buf_pfx)); vnc_zlog_debug_verbose("%s: rn pfx=%s", __func__, buf_pfx); } diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index 47a72d75fd40..5c222b6dda82 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -839,7 +839,7 @@ void rfapiMonitorItNodeChanged( struct bgp *bgp = bgp_get_default(); afi_t afi = family2afi(rn->p.family); #if DEBUG_L2_EXTRA - char buf_prefix[BUFSIZ]; + char buf_prefix[PREFIX_STRLEN]; #endif assert(bgp); @@ -848,7 +848,7 @@ void rfapiMonitorItNodeChanged( nves_seen = skiplist_new(0, NULL, NULL); #if DEBUG_L2_EXTRA - prefix2str(&it_node->p, buf_prefix, BUFSIZ); + prefix2str(&it_node->p, buf_prefix, sizeof(buf_prefix)); vnc_zlog_debug_verbose("%s: it=%p, it_node=%p, it_node->prefix=%s", __func__, import_table, it_node, buf_prefix); #endif @@ -926,22 +926,20 @@ void rfapiMonitorItNodeChanged( assert(!skiplist_insert(nves_seen, m->rfd, NULL)); - { - char buf_attach_pfx[BUFSIZ]; - char buf_target_pfx[BUFSIZ]; - - prefix2str(&m->node->p, - buf_attach_pfx, - BUFSIZ); - prefix2str(&m->p, - buf_target_pfx, - BUFSIZ); - vnc_zlog_debug_verbose( - "%s: update rfd %p attached to pfx %s (targ=%s)", - __func__, m->rfd, - buf_attach_pfx, - buf_target_pfx); - } + char buf_attach_pfx[PREFIX_STRLEN]; + char buf_target_pfx[PREFIX_STRLEN]; + + prefix2str(&m->node->p, + buf_attach_pfx, + sizeof(buf_attach_pfx)); + prefix2str(&m->p, + buf_target_pfx, + sizeof(buf_target_pfx)); + vnc_zlog_debug_verbose( + "%s: update rfd %p attached to pfx %s (targ=%s)", + __func__, m->rfd, + buf_attach_pfx, + buf_target_pfx); /* * update its RIB @@ -1271,8 +1269,9 @@ static void rfapiMonitorEthDetachImport( assert(rn); #if DEBUG_L2_EXTRA - char buf_prefix[BUFSIZ]; - prefix2str(&rn->p, buf_prefix, BUFSIZ); + char buf_prefix[PREFIX_STRLEN]; + + prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); #endif /* diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 36ae6e727377..bd79518bfd75 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -207,7 +207,7 @@ void rfapiRibCheckCounts( } } - if (checkstats && bgp && bgp->rfapi) { + if (checkstats && bgp->rfapi) { if (t_pfx_active != bgp->rfapi->rib_prefix_count_total) { vnc_zlog_debug_verbose( "%s: actual total pfx count %u != running %u", @@ -342,7 +342,7 @@ rfapiRibStartTimer(struct rfapi_descriptor *rfd, struct rfapi_info *ri, { struct thread *t = ri->timer; struct rfapi_rib_tcb *tcb = NULL; - char buf_prefix[BUFSIZ]; + char buf_prefix[PREFIX_STRLEN]; if (t) { tcb = t->arg; @@ -363,7 +363,7 @@ rfapiRibStartTimer(struct rfapi_descriptor *rfd, struct rfapi_info *ri, UNSET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED); } - prefix2str(&rn->p, buf_prefix, BUFSIZ); + prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); vnc_zlog_debug_verbose("%s: rfd %p pfx %s life %u", __func__, rfd, buf_prefix, ri->lifetime); ri->timer = NULL; @@ -688,13 +688,10 @@ static void rfapiRibBi2Ri(struct bgp_info *bi, struct rfapi_info *ri, memcpy(&vo->v.l2addr.macaddr, bi->extra->vnc.import.rd.val + 2, ETH_ALEN); - if (bi->attr) { - (void)rfapiEcommunityGetLNI( - bi->attr->ecommunity, - &vo->v.l2addr.logical_net_id); - (void)rfapiEcommunityGetEthernetTag( - bi->attr->ecommunity, &vo->v.l2addr.tag_id); - } + (void)rfapiEcommunityGetLNI(bi->attr->ecommunity, + &vo->v.l2addr.logical_net_id); + (void)rfapiEcommunityGetEthernetTag(bi->attr->ecommunity, + &vo->v.l2addr.tag_id); /* local_nve_id comes from RD */ vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1]; @@ -710,7 +707,7 @@ static void rfapiRibBi2Ri(struct bgp_info *bi, struct rfapi_info *ri, /* * If there is an auxiliary IP address (L2 can have it), copy it */ - if (bi && bi->extra && bi->extra->vnc.import.aux_prefix.family) { + if (bi->extra && bi->extra->vnc.import.aux_prefix.family) { ri->rk.aux_prefix = bi->extra->vnc.import.aux_prefix; } } @@ -855,7 +852,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, struct list *lPendCost = NULL; struct list *delete_list = NULL; int printedprefix = 0; - char buf_prefix[BUFSIZ]; + char buf_prefix[PREFIX_STRLEN]; int rib_node_started_nonempty = 0; int sendingsomeroutes = 0; @@ -866,7 +863,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, #endif assert(pn); - prefix2str(&pn->p, buf_prefix, BUFSIZ); + prefix2str(&pn->p, buf_prefix, sizeof(buf_prefix)); vnc_zlog_debug_verbose("%s: afi=%d, %s pn->info=%p", __func__, afi, buf_prefix, pn->info); @@ -916,8 +913,8 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, while (0 == skiplist_first(slRibPt, NULL, (void **)&ri)) { - char buf[BUFSIZ]; - char buf2[BUFSIZ]; + char buf[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; listnode_add(delete_list, ri); vnc_zlog_debug_verbose( @@ -935,8 +932,8 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, ri->timer = NULL; } - prefix2str(&ri->rk.vn, buf, BUFSIZ); - prefix2str(&ri->un, buf2, BUFSIZ); + prefix2str(&ri->rk.vn, buf, sizeof(buf)); + prefix2str(&ri->un, buf2, sizeof(buf2)); vnc_zlog_debug_verbose( "%s: put dl pfx=%s vn=%s un=%s cost=%d life=%d vn_options=%p", __func__, buf_prefix, buf, buf2, @@ -1126,7 +1123,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, } else { - char buf_rd[BUFSIZ]; + char buf_rd[RD_ADDRSTRLEN]; /* not found: add new route to RIB */ ori = rfapi_info_new(); @@ -1405,13 +1402,14 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, ri->last_sent_time = rfapi_time(NULL); #if DEBUG_RIB_SL_RD { - char buf_rd[BUFSIZ]; - prefix_rd2str(&ri->rk.rd, - buf_rd, - sizeof(buf_rd)); + char buf_rd[RD_ADDRSTRLEN]; + vnc_zlog_debug_verbose( "%s: move route to recently deleted list, rd=%s", - __func__, buf_rd); + __func__, + prefix_rd2str(&ri->rk.rd, + buf_rd, + sizeof(buf_rd))); } #endif @@ -1595,7 +1593,7 @@ void rfapiRibUpdatePendingNode( afi_t afi; uint32_t queued_flag; int count = 0; - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; vnc_zlog_debug_verbose("%s: entry", __func__); @@ -1608,7 +1606,7 @@ void rfapiRibUpdatePendingNode( prefix = &it_node->p; afi = family2afi(prefix->family); - prefix2str(prefix, buf, BUFSIZ); + prefix2str(prefix, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: prefix=%s", __func__, buf); pn = route_node_get(rfd->rib_pending[afi], prefix); @@ -1814,9 +1812,9 @@ int rfapiRibFTDFilterRecentPrefix( #if DEBUG_FTD_FILTER_RECENT { - char buf_pfx[BUFSIZ]; + char buf_pfx[PREFIX_STRLEN]; - prefix2str(&it_rn->p, buf_pfx, BUFSIZ); + prefix2str(&it_rn->p, buf_pfx, sizeof(buf_pfx)); vnc_zlog_debug_verbose("%s: prefix %s", __func__, buf_pfx); } #endif @@ -1978,14 +1976,15 @@ rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd, #if DEBUG_NHL { - char str_vn[BUFSIZ]; - char str_aux_prefix[BUFSIZ]; + char str_vn[PREFIX_STRLEN]; + char str_aux_prefix[PREFIX_STRLEN]; str_vn[0] = 0; str_aux_prefix[0] = 0; - prefix2str(&rk.vn, str_vn, BUFSIZ); - prefix2str(&rk.aux_prefix, str_aux_prefix, BUFSIZ); + prefix2str(&rk.vn, str_vn, sizeof(str_vn)); + prefix2str(&rk.aux_prefix, str_aux_prefix, + sizeof(str_aux_prefix)); if (!rk.aux_prefix.family) { } @@ -2075,11 +2074,11 @@ rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd, route_unlock_node(trn); { - char str_pfx[BUFSIZ]; - char str_pfx_vn[BUFSIZ]; + char str_pfx[PREFIX_STRLEN]; + char str_pfx_vn[PREFIX_STRLEN]; - prefix2str(&pfx, str_pfx, BUFSIZ); - prefix2str(&rk.vn, str_pfx_vn, BUFSIZ); + prefix2str(&pfx, str_pfx, sizeof(str_pfx)); + prefix2str(&rk.vn, str_pfx_vn, sizeof(str_pfx_vn)); vnc_zlog_debug_verbose( "%s: added pfx=%s nh[vn]=%s, cost=%u, lifetime=%u, allowed=%d", __func__, str_pfx, str_pfx_vn, nhp->prefix.cost, @@ -2114,9 +2113,9 @@ void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it, { struct rfapi_descriptor *rfd; struct listnode *node; - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - prefix2str(&it_node->p, buf, BUFSIZ); + prefix2str(&it_node->p, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%s", __func__, it, afi, it_node, buf); @@ -2290,21 +2289,21 @@ static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty, for (rc = skiplist_next(sl, NULL, (void **)&ri, &cursor); !rc; rc = skiplist_next(sl, NULL, (void **)&ri, &cursor)) { - char str_vn[BUFSIZ]; - char str_un[BUFSIZ]; + char str_vn[PREFIX_STRLEN]; + char str_un[PREFIX_STRLEN]; char str_lifetime[BUFSIZ]; char str_age[BUFSIZ]; char *p; - char str_rd[BUFSIZ]; + char str_rd[RD_ADDRSTRLEN]; ++routes_displayed; - prefix2str(&ri->rk.vn, str_vn, BUFSIZ); + prefix2str(&ri->rk.vn, str_vn, sizeof(str_vn)); p = index(str_vn, '/'); if (p) *p = 0; - prefix2str(&ri->un, str_un, BUFSIZ); + prefix2str(&ri->un, str_un, sizeof(str_un)); p = index(str_un, '/'); if (p) *p = 0; @@ -2325,11 +2324,10 @@ static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty, str_rd[0] = 0; /* start empty */ #if DEBUG_RIB_SL_RD - str_rd[0] = ' '; - prefix_rd2str(&ri->rk.rd, str_rd + 1, BUFSIZ - 1); + prefix_rd2str(&ri->rk.rd, str_rd, sizeof(str_rd)); #endif - fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s%s\n", + fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %s\n", deleted ? 'r' : ' ', *printedprefix ? "" : str_pfx, str_vn, str_un, ri->cost, str_lifetime, str_age, str_rd); @@ -2352,13 +2350,13 @@ static void rfapiRibShowRibSl(void *stream, struct prefix *pfx, const char *vty_newline; int nhs_displayed = 0; - char str_pfx[BUFSIZ]; + char str_pfx[PREFIX_STRLEN]; int printedprefix = 0; if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) return; - prefix2str(pfx, str_pfx, BUFSIZ); + prefix2str(pfx, str_pfx, sizeof(str_pfx)); nhs_displayed += print_rib_sl(fp, vty, out, sl, 0, str_pfx, &printedprefix); @@ -2418,7 +2416,7 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, rn = route_next(rn)) { struct skiplist *sl; - char str_pfx[BUFSIZ]; + char str_pfx[PREFIX_STRLEN]; int printedprefix = 0; if (!show_removed) @@ -2472,7 +2470,7 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, str_un, BUFSIZ)); } - prefix2str(&rn->p, str_pfx, BUFSIZ); + prefix2str(&rn->p, str_pfx, sizeof(str_pfx)); // fp(out, " %s\n", buf); /* prefix */ routes_displayed++; diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 6af99278bce3..37ca5edc96df 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1146,7 +1146,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, char buf_age[BUFSIZ]; - if (bi && bi->extra && bi->extra->vnc.import.create_time) { + if (bi->extra && bi->extra->vnc.import.create_time) { rfapiFormatAge(bi->extra->vnc.import.create_time, buf_age, BUFSIZ); } else { @@ -1163,7 +1163,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, * print that on the next line */ - if (bi && bi->extra + if (bi->extra && bi->extra->vnc.import.aux_prefix.family) { const char *sp; @@ -1527,11 +1527,9 @@ void rfapiPrintRfapiIpPrefix(void *stream, struct rfapi_ip_prefix *p) void rfapiPrintRd(struct vty *vty, struct prefix_rd *prd) { - char buf[BUFSIZ]; + char buf[RD_ADDRSTRLEN]; - buf[0] = 0; - prefix_rd2str(prd, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix_rd2str(prd, buf, sizeof(buf)); vty_out(vty, "%s", buf); } @@ -1599,7 +1597,7 @@ void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd) int rc; afi_t afi; struct rfapi_adb *adb; - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; vty_out(vty, "%-10p ", rfd); rfapiPrintRfapiIpAddr(vty, &rfd->un_addr); @@ -1651,8 +1649,7 @@ void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd) if (family != adb->u.s.prefix_ip.family) continue; - prefix2str(&adb->u.s.prefix_ip, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + prefix2str(&adb->u.s.prefix_ip, buf, sizeof(buf)); vty_out(vty, " Adv Pfx: %s%s", buf, HVTYNL); rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN, @@ -1664,8 +1661,7 @@ void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd) rc == 0; rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL, (void **)&adb, &cursor)) { - prefix2str(&adb->u.s.prefix_eth, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + prefix2str(&adb->u.s.prefix_eth, buf, sizeof(buf)); vty_out(vty, " Adv Pfx: %s%s", buf, HVTYNL); diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index b348c399bfc1..d4921ce40a80 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -437,14 +437,11 @@ static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi) continue; { - char prefixstr[BUFSIZ]; - - prefixstr[0] = 0; - inet_ntop(rn->p.family, &rn->p.u.prefix, prefixstr, - BUFSIZ); - vnc_zlog_debug_verbose("%s: checking prefix %s/%d", - __func__, prefixstr, - rn->p.prefixlen); + char prefixstr[PREFIX_STRLEN]; + + prefix2str(&rn->p, prefixstr, sizeof(prefixstr)); + vnc_zlog_debug_verbose("%s: checking prefix %s", + __func__, prefixstr); } for (ri = rn->info; ri; ri = ri->next) { @@ -1856,14 +1853,13 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) continue; { - char prefixstr[BUFSIZ]; + char prefixstr[PREFIX_STRLEN]; - prefixstr[0] = 0; - inet_ntop(rn->p.family, &rn->p.u.prefix, - prefixstr, BUFSIZ); + prefix2str(&rn->p, prefixstr, + sizeof(prefixstr)); vnc_zlog_debug_verbose( - "%s: checking prefix %s/%d", __func__, - prefixstr, rn->p.prefixlen); + "%s: checking prefix %s", __func__, + prefixstr); } /* diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 05fbd180560d..f7e86123b4f3 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -205,15 +205,15 @@ static void print_rhn_list(const char *tag1, const char *tag2) /* XXX uses secret knowledge of skiplist structure */ for (p = sl->header->forward[0]; p; p = p->forward[0]) { - char kbuf[BUFSIZ]; - char hbuf[BUFSIZ]; - char ubuf[BUFSIZ]; + char kbuf[PREFIX_STRLEN]; + char hbuf[PREFIX_STRLEN]; + char ubuf[PREFIX_STRLEN]; pb = p->value; - prefix2str(p->key, kbuf, BUFSIZ); - prefix2str(&pb->hpfx, hbuf, BUFSIZ); - prefix2str(&pb->upfx, ubuf, BUFSIZ); + prefix2str(p->key, kbuf, sizeof(kbuf)); + prefix2str(&pb->hpfx, hbuf, sizeof(hbuf)); + prefix2str(&pb->upfx, ubuf, sizeof(ubuf)); vnc_zlog_debug_verbose( "RHN Entry %d (q=%p): kpfx=%s, upfx=%s, hpfx=%s, ubi=%p", @@ -259,14 +259,11 @@ static void vnc_rhnck(char *tag) * pfx */ assert(!vnc_prefix_cmp(&pb->hpfx, pkey)); if (vnc_prefix_cmp(&pb->hpfx, &pfx_orig_nexthop)) { - char str_onh[BUFSIZ]; - char str_nve_pfx[BUFSIZ]; + char str_onh[PREFIX_STRLEN]; + char str_nve_pfx[PREFIX_STRLEN]; - prefix2str(&pfx_orig_nexthop, str_onh, BUFSIZ); - str_onh[BUFSIZ - 1] = 0; - - prefix2str(&pb->hpfx, str_nve_pfx, BUFSIZ); - str_nve_pfx[BUFSIZ - 1] = 0; + prefix2str(&pfx_orig_nexthop, str_onh, sizeof(str_onh)); + prefix2str(&pb->hpfx, str_nve_pfx, sizeof(str_nve_pfx)); vnc_zlog_debug_verbose( "%s: %s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s", @@ -529,10 +526,9 @@ static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd( return; { - char str_nh[BUFSIZ]; + char str_nh[PREFIX_STRLEN]; - prefix2str(ubi_nexthop, str_nh, BUFSIZ); - str_nh[BUFSIZ - 1] = 0; + prefix2str(ubi_nexthop, str_nh, sizeof(str_nh)); vnc_zlog_debug_verbose("%s: ubi_nexthop=%s", __func__, str_nh); } @@ -574,18 +570,16 @@ static void vnc_import_bgp_add_route_mode_resolve_nve( /*debugging */ if (VNC_DEBUG(VERBOSE)) { - char str_pfx[BUFSIZ]; - char str_nh[BUFSIZ]; + char str_pfx[PREFIX_STRLEN]; + char str_nh[PREFIX_STRLEN]; struct prefix nh; - prefix2str(prefix, str_pfx, BUFSIZ); - str_pfx[BUFSIZ - 1] = 0; + prefix2str(prefix, str_pfx, sizeof(str_pfx)); nh.prefixlen = 0; rfapiUnicastNexthop2Prefix(afi, info->attr, &nh); if (nh.prefixlen) { - prefix2str(&nh, str_nh, BUFSIZ); - str_nh[BUFSIZ - 1] = 0; + prefix2str(&nh, str_nh, sizeof(str_nh)); } else { str_nh[0] = '?'; str_nh[1] = 0; @@ -718,11 +712,9 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, uint32_t *med = NULL; { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - buf[0] = 0; - prefix2str(prefix, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(prefix, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s(prefix=%s) entry", __func__, buf); } @@ -789,11 +781,9 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, } if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - buf[0] = 0; - prefix2str(vn_pfx, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(vn_pfx, buf, sizeof(buf)); vnc_zlog_debug_any("%s vn_pfx=%s", __func__, buf); } @@ -867,11 +857,9 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, } if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - buf[0] = 0; - rfapiRfapiIpAddr2Str(&vnaddr, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + rfapiRfapiIpAddr2Str(&vnaddr, buf, sizeof(buf)); vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf); } @@ -910,11 +898,9 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, uint32_t local_pref; { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - buf[0] = 0; - prefix2str(prefix, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(prefix, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s(prefix=%s) entry", __func__, buf); } @@ -997,11 +983,9 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, } if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; - buf[0] = 0; - prefix2str(vn_pfx, buf, BUFSIZ); - buf[BUFSIZ - 1] = 0; + prefix2str(vn_pfx, buf, sizeof(buf)); vnc_zlog_debug_any("%s vn_pfx=%s", __func__, buf); } @@ -1183,7 +1167,8 @@ static void vnc_import_bgp_del_route_mode_nvegroup(struct bgp *bgp, assert(afi); - assert((rfg = bgp->rfapi_cfg->rfg_redist)); + rfg = bgp->rfapi_cfg->rfg_redist; + assert(rfg); /* * Compute VN address @@ -1297,11 +1282,9 @@ static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( return; { - char str_nh[BUFSIZ]; - - prefix2str(ubi_nexthop, str_nh, BUFSIZ); - str_nh[BUFSIZ - 1] = 0; + char str_nh[PREFIX_STRLEN]; + prefix2str(ubi_nexthop, str_nh, sizeof(str_nh)); vnc_zlog_debug_verbose("%s: ubi_nexthop=%s", __func__, str_nh); } @@ -1450,7 +1433,7 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( return; } - if (bgp && bgp->rfapi) + if (bgp->rfapi) sl = bgp->rfapi->resolve_nve_nexthop; if (!sl) { @@ -1477,11 +1460,11 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( sizeof(struct prefix)); /* keep valgrind happy */ if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { - char hbuf[BUFSIZ]; - char ubuf[BUFSIZ]; + char hbuf[PREFIX_STRLEN]; + char ubuf[PREFIX_STRLEN]; - prefix2str(&pb->hpfx, hbuf, BUFSIZ); - prefix2str(&pb->upfx, ubuf, BUFSIZ); + prefix2str(&pb->hpfx, hbuf, sizeof(hbuf)); + prefix2str(&pb->upfx, ubuf, sizeof(ubuf)); vnc_zlog_debug_any( "%s: examining RHN Entry (q=%p): upfx=%s, hpfx=%s, ubi=%p", @@ -1509,14 +1492,12 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( * Sanity check */ if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) { - char str_unh[BUFSIZ]; - char str_nve_pfx[BUFSIZ]; + char str_unh[PREFIX_STRLEN]; + char str_nve_pfx[PREFIX_STRLEN]; - prefix2str(&pfx_unicast_nexthop, str_unh, BUFSIZ); - str_unh[BUFSIZ - 1] = 0; - - prefix2str(prefix, str_nve_pfx, BUFSIZ); - str_nve_pfx[BUFSIZ - 1] = 0; + prefix2str(&pfx_unicast_nexthop, str_unh, + sizeof(str_unh)); + prefix2str(prefix, str_nve_pfx, sizeof(str_nve_pfx)); vnc_zlog_debug_verbose( "%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s", @@ -1535,9 +1516,9 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( #if DEBUG_RHN_LIST /* debug */ { - char pbuf[BUFSIZ]; + char pbuf[PREFIX_STRLEN]; - prefix2str(prefix, pbuf, BUFSIZ); + prefix2str(prefix, pbuf, sizeof(pbuf)); vnc_zlog_debug_verbose( "%s: advancing past RHN Entry (q=%p): with prefix %s", @@ -1565,10 +1546,9 @@ void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( int rc; { - char str_pfx[BUFSIZ]; + char str_pfx[PREFIX_STRLEN]; - prefix2str(prefix, str_pfx, BUFSIZ); - str_pfx[BUFSIZ - 1] = 0; + prefix2str(prefix, str_pfx, sizeof(str_pfx)); vnc_zlog_debug_verbose("%s(bgp=%p, nve prefix=%s)", __func__, bgp, str_pfx); @@ -1597,7 +1577,7 @@ void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( return; } - if (bgp && bgp->rfapi) + if (bgp->rfapi) sl = bgp->rfapi->resolve_nve_nexthop; if (!sl) { @@ -1636,14 +1616,12 @@ void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( * Sanity check */ if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) { - char str_unh[BUFSIZ]; - char str_nve_pfx[BUFSIZ]; - - prefix2str(&pfx_unicast_nexthop, str_unh, BUFSIZ); - str_unh[BUFSIZ - 1] = 0; + char str_unh[PREFIX_STRLEN]; + char str_nve_pfx[PREFIX_STRLEN]; - prefix2str(prefix, str_nve_pfx, BUFSIZ); - str_nve_pfx[BUFSIZ - 1] = 0; + prefix2str(&pfx_unicast_nexthop, str_unh, + sizeof(str_unh)); + prefix2str(prefix, str_nve_pfx, sizeof(str_nve_pfx)); vnc_zlog_debug_verbose( "%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s", @@ -2092,11 +2070,9 @@ void vnc_import_bgp_exterior_add_route_interior( /*debugging */ { - char str_pfx[BUFSIZ]; - - prefix2str(&rn_interior->p, str_pfx, BUFSIZ); - str_pfx[BUFSIZ - 1] = 0; + char str_pfx[PREFIX_STRLEN]; + prefix2str(&rn_interior->p, str_pfx, sizeof(str_pfx)); vnc_zlog_debug_verbose("%s: interior prefix=%s, bi type=%d", __func__, str_pfx, bi_interior->type); } @@ -2337,11 +2313,10 @@ void vnc_import_bgp_exterior_add_route_interior( (void **)&pfx_exterior, &cursor)) { struct prefix pfx_nexthop; - char buf[BUFSIZ]; + char buf[PREFIX_STRLEN]; afi_t afi_exterior = family2afi(pfx_exterior->family); prefix2str(pfx_exterior, buf, sizeof(buf)); - buf[sizeof(buf) - 1] = 0; vnc_zlog_debug_verbose( "%s: checking exterior orphan at prefix %s", __func__, buf); @@ -2480,10 +2455,9 @@ void vnc_import_bgp_exterior_del_route_interior( /*debugging */ { - char str_pfx[BUFSIZ]; + char str_pfx[PREFIX_STRLEN]; - prefix2str(&rn_interior->p, str_pfx, BUFSIZ); - str_pfx[BUFSIZ - 1] = 0; + prefix2str(&rn_interior->p, str_pfx, sizeof(str_pfx)); vnc_zlog_debug_verbose("%s: interior prefix=%s, bi type=%d", __func__, str_pfx, bi_interior->type); @@ -2639,12 +2613,12 @@ void vnc_import_bgp_add_route(struct bgp *bgp, struct prefix *prefix, if (VNC_DEBUG(VERBOSE)) { struct prefix pfx_nexthop; - char buf[BUFSIZ]; - char buf_nh[BUFSIZ]; + char buf[PREFIX_STRLEN]; + char buf_nh[PREFIX_STRLEN]; - prefix2str(prefix, buf, BUFSIZ); + prefix2str(prefix, buf, sizeof(buf)); rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop); - prefix2str(&pfx_nexthop, buf_nh, BUFSIZ); + prefix2str(&pfx_nexthop, buf_nh, sizeof(buf_nh)); vnc_zlog_debug_verbose("%s: pfx %s, nh %s", __func__, buf, buf_nh); @@ -2709,12 +2683,12 @@ void vnc_import_bgp_del_route(struct bgp *bgp, struct prefix *prefix, { struct prefix pfx_nexthop; - char buf[BUFSIZ]; - char buf_nh[BUFSIZ]; + char buf[PREFIX_STRLEN]; + char buf_nh[PREFIX_STRLEN]; - prefix2str(prefix, buf, BUFSIZ); + prefix2str(prefix, buf, sizeof(buf)); rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop); - prefix2str(&pfx_nexthop, buf_nh, BUFSIZ); + prefix2str(&pfx_nexthop, buf_nh, sizeof(buf_nh)); vnc_zlog_debug_verbose("%s: pfx %s, nh %s", __func__, buf, buf_nh); diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index 5c71df238fe5..92d7e6fc76be 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -30,6 +30,7 @@ #include "lib/command.h" #include "lib/zclient.h" #include "lib/stream.h" +#include "lib/ringbuf.h" #include "lib/memory.h" #include "bgpd/bgpd.h" @@ -183,22 +184,32 @@ static void vnc_redistribute_add(struct prefix *p, u_int32_t metric, vncHD1VR.peer->status = Established; /* keep bgp core happy */ bgp_sync_delete(vncHD1VR.peer); /* don't need these */ - if (vncHD1VR.peer->ibuf) { - stream_free(vncHD1VR.peer - ->ibuf); /* don't need it */ + + /* + * since this peer is not on the I/O thread, this lock + * is not strictly necessary, but serves as a reminder + * to those who may meddle... + */ + pthread_mutex_lock(&vncHD1VR.peer->io_mtx); + { + // we don't need any I/O related facilities + if (vncHD1VR.peer->ibuf) + stream_fifo_free(vncHD1VR.peer->ibuf); + if (vncHD1VR.peer->obuf) + stream_fifo_free(vncHD1VR.peer->obuf); + + if (vncHD1VR.peer->ibuf_work) + ringbuf_del(vncHD1VR.peer->ibuf_work); + if (vncHD1VR.peer->obuf_work) + stream_free(vncHD1VR.peer->obuf_work); + vncHD1VR.peer->ibuf = NULL; - } - if (vncHD1VR.peer->obuf) { - stream_fifo_free( - vncHD1VR.peer - ->obuf); /* don't need it */ vncHD1VR.peer->obuf = NULL; + vncHD1VR.peer->obuf_work = NULL; + vncHD1VR.peer->ibuf_work = NULL; } - if (vncHD1VR.peer->work) { - stream_free(vncHD1VR.peer - ->work); /* don't need it */ - vncHD1VR.peer->work = NULL; - } + pthread_mutex_unlock(&vncHD1VR.peer->io_mtx); + /* base code assumes have valid host pointer */ vncHD1VR.peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, ".zebra."); @@ -374,6 +385,8 @@ static void vnc_zebra_route_msg(struct prefix *p, unsigned int nhp_count, struct zapi_route api; struct zapi_nexthop *api_nh; int i; + struct in_addr **nhp_ary4 = nhp_ary; + struct in6_addr **nhp_ary6 = nhp_ary; if (!nhp_count) { vnc_zlog_debug_verbose("%s: empty nexthop list, skipping", @@ -383,6 +396,7 @@ static void vnc_zebra_route_msg(struct prefix *p, unsigned int nhp_count, memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_VNC; api.safi = SAFI_UNICAST; api.prefix = *p; @@ -391,20 +405,16 @@ static void vnc_zebra_route_msg(struct prefix *p, unsigned int nhp_count, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = MIN(nhp_count, multipath_num); for (i = 0; i < api.nexthop_num; i++) { - struct in_addr *nhp_ary4; - struct in6_addr *nhp_ary6; api_nh = &api.nexthops[i]; switch (p->family) { case AF_INET: - nhp_ary4 = nhp_ary; - memcpy(&api_nh->gate.ipv4, &nhp_ary4[i], + memcpy(&api_nh->gate.ipv4, nhp_ary4[i], sizeof(api_nh->gate.ipv4)); api_nh->type = NEXTHOP_TYPE_IPV4; break; case AF_INET6: - nhp_ary6 = nhp_ary; - memcpy(&api_nh->gate.ipv6, &nhp_ary6[i], + memcpy(&api_nh->gate.ipv6, nhp_ary6[i], sizeof(api_nh->gate.ipv6)); api_nh->type = NEXTHOP_TYPE_IPV6; break; diff --git a/configure.ac b/configure.ac index 16cc8901a331..3603a930fa40 100755 --- a/configure.ac +++ b/configure.ac @@ -40,7 +40,7 @@ AS_IF([test "$host" != "$build"], [ ( CPPFLAGS="$HOST_CPPFLAGS"; \ CFLAGS="$HOST_CFLAGS"; \ LDFLAGS="$HOST_LDFLAGS"; \ - cd hosttools; "${abssrc}/configure" "--host=$build" "--build=$build"; ) + cd hosttools; "${abssrc}/configure" "--host=$build" "--build=$build" "--enable-clippy-only" "--disable-nhrpd" "--disable-vtysh"; ) AC_MSG_NOTICE([...]) AC_MSG_NOTICE([... cross-compilation: finished self-configuring for build platform tools]) @@ -354,6 +354,8 @@ AC_ARG_ENABLE(logfile_mask, AS_HELP_STRING([--enable-logfile-mask=ARG], [set mask for log files])) AC_ARG_ENABLE(shell_access, AS_HELP_STRING([--enable-shell-access], [Allow users to access shell/telnet/ssh])) +AC_ARG_ENABLE(realms, + AS_HELP_STRING([--enable-realms], [enable REALMS support under Linux])) AC_ARG_ENABLE(rtadv, AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature])) AC_ARG_ENABLE(irdp, @@ -392,7 +394,10 @@ AC_ARG_ENABLE([oldvpn_commands], AS_HELP_STRING([--enable-oldvpn-commands], [Keep old vpn commands])) AC_ARG_ENABLE(rpki, AS_HELP_STRING([--enable-rpki], [enable RPKI prefix validation support])) +AC_ARG_ENABLE([clippy-only], + AS_HELP_STRING([--enable-clippy-only], [Only build clippy])) +AS_IF([test "${enable_clippy_only}" != "yes"], [ AC_CHECK_HEADERS(json-c/json.h) AC_CHECK_LIB(json-c, json_object_get, LIBS="$LIBS -ljson-c", [], [-lm]) if test $ac_cv_lib_json_c_json_object_get = no; then @@ -401,6 +406,7 @@ if test $ac_cv_lib_json_c_json_object_get = no; then AC_MSG_ERROR([lib json is needed to compile]) fi fi +]) AC_ARG_ENABLE([dev_build], AS_HELP_STRING([--enable-dev-build], [build for development])) @@ -875,6 +881,7 @@ case "$host_os" in AC_DEFINE(OPEN_BSD,,OpenBSD) AC_DEFINE(KAME,1,KAME IPv6) + AC_DEFINE(BSD_V6_SYSCTL,1,BSD v6 sysctl to turn on and off forwarding) if test "x${enable_pimd}" != "xno"; then case "$host_os" in @@ -891,12 +898,29 @@ case "$host_os" in AC_DEFINE(HAVE_NET_RT_IFLIST,,NET_RT_IFLIST) AC_DEFINE(KAME,1,KAME IPv6) + AC_DEFINE(BSD_V6_SYSCTL,1,BSD v6 sysctl to turn on and off forwarding) ;; esac AM_CONDITIONAL(SOLARIS, test "${SOLARIS}" = "solaris") AC_SYS_LARGEFILE +dnl ------------------------ +dnl Integrated REALMS option +dnl ------------------------ +if test "${enable_realms}" = "yes"; then + case "$host_os" in + linux*) + AC_DEFINE(SUPPORT_REALMS,, Realms support) + ;; + *) + echo "Sorry, only Linux has REALMS support" + exit 1 + ;; + esac +fi +AM_CONDITIONAL([SUPPORT_REALMS], [test "${enable_realms}" = "yes"]) + dnl --------------------- dnl Integrated VTY option dnl --------------------- @@ -1312,6 +1336,7 @@ AM_CONDITIONAL(BABELD, test "${enable_babeld}" != "no") AM_CONDITIONAL(OSPF6D, test "${enable_ospf6d}" != "no") AM_CONDITIONAL(ISISD, test "${enable_isisd}" != "no") AM_CONDITIONAL(PIMD, test "${enable_pimd}" != "no") +AM_CONDITIONAL(PBRD, test "${enable_pbrd}" != "no") if test "${enable_bgp_announce}" = "no";then AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra) diff --git a/debianpkg/Makefile.am b/debianpkg/Makefile.am index 5ab5b4c4fc21..06e5bda3d422 100644 --- a/debianpkg/Makefile.am +++ b/debianpkg/Makefile.am @@ -29,8 +29,13 @@ EXTRA_DIST = README.Debian README.Maintainer \ backports/ubuntu16.04/debian/source/format \ backports/ubuntu16.04/exclude \ backports/ubuntu16.04/versionext \ + backports/ubuntu17.10/debian/control \ + backports/ubuntu17.10/debian/source/format \ + backports/ubuntu17.10/exclude \ + backports/ubuntu17.10/versionext \ frr-doc.docs frr-doc.info frr-doc.install \ frr-doc.lintian-overrides frr.conf \ + frr-dbg.lintian-overrides \ frr.dirs frr.docs frr.install \ frr.lintian-overrides frr.logrotate \ frr.manpages frr.pam frr.postinst frr.postrm \ diff --git a/debianpkg/README.deb_build.md b/debianpkg/README.deb_build.md index 3156c3672dd3..889e83174416 100644 --- a/debianpkg/README.deb_build.md +++ b/debianpkg/README.deb_build.md @@ -1,21 +1,26 @@ Building your own FRRouting Debian Package ========================================== -(Tested on Ubuntu 12.04, 14.04, 16.04 and Debian 8) +(Tested on Ubuntu 12.04, 14.04, 16.04, 17.10, Debian 8 and 9) + +**Note:** If you try to build for a different distro, then it will most likely +fail because of the missing backport. See debianpkg/backports/README about +adding a new backport. 1. Follow the package installation as outlined in doc/Building_on_XXXX.md (XXXX refers your OS Distribution) to install the required build packages 2. Install the following additional packages: - apt-get install realpath equivs groff fakeroot debhelper + apt-get install realpath equivs groff fakeroot debhelper devscripts 3. Checkout FRR under a **unpriviledged** user account git clone https://github.com/frrouting/frr.git frr + cd frr + # git checkout - if different branch than master 4. Run Bootstrap and make distribution tar.gz - cd frr ./bootstrap.sh ./configure --with-pkg-extra-version=-MyDebPkgVersion make dist @@ -44,7 +49,7 @@ Building your own FRRouting Debian Package and multiple `frr_*.debian.tar.xz` and `frr_*.dsc` for the debian package source on each backport supported distribution -6. Create a new directory to build the package and populate with package src +7. Create a new directory to build the package and populate with package src mkdir frrpkg cd frrpkg @@ -53,12 +58,21 @@ Building your own FRRouting Debian Package . /etc/os-release tar xf ~/frr/frr_*${ID}${VERSION_ID}*.debian.tar.xz -7. Build Debian Package Dependencies and install them as needed +8. Build Debian Package Dependencies and install them as needed sudo mk-build-deps --install debian/control -8. Build Debian Package +9. Build Debian Package + + Building with standard options: + + debuild -b -uc -us + + Or change some options: + (see `rules` file for available options) + export WANT_BGP_VNC=1 + export WANT_CUMULUS_MODE=1 debuild -b -uc -us DONE. @@ -83,7 +97,7 @@ allowed. sudo update-rc.d frr defaults - - On `systemd` based systems (Debian 8, Ubuntu 14.04, 16.04) + - On `systemd` based systems (Debian 8, 9, Ubuntu 14.04, 16.04, 17.10) sudo systemctl enable frr @@ -93,7 +107,7 @@ allowed. sudo invoke-rc.d frr start - - on `systemd` based systems (Debian 8, Ubuntu 14.04, 16.04) + - on `systemd` based systems (Debian 8, 9, Ubuntu 14.04, 16.04, 17.10) sudo systemctl start frr diff --git a/debianpkg/backports/ubuntu17.10/debian/control b/debianpkg/backports/ubuntu17.10/debian/control new file mode 100644 index 000000000000..692501db18ed --- /dev/null +++ b/debianpkg/backports/ubuntu17.10/debian/control @@ -0,0 +1,54 @@ +Source: frr +Section: net +Priority: optional +Maintainer: Nobody +Uploaders: Nobody +XSBC-Original-Maintainer: +Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), imagemagick, ghostscript, groff, autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev, git, dh-autoreconf, libjson-c-dev, libjson-c2 | libjson-c3, dh-systemd, libsystemd-dev, bison, flex, libc-ares-dev, pkg-config, python (>= 2.7), python-ipaddr +Standards-Version: 3.9.6 +Homepage: http://www.frrouting.org/ + +Package: frr +Architecture: any +Depends: ${shlibs:Depends}, logrotate (>= 3.2-11), iproute2 | iproute, ${misc:Depends}, libc-ares2 +Pre-Depends: adduser +Conflicts: zebra, zebra-pj, quagga +Replaces: zebra, zebra-pj +Suggests: snmpd +Description: BGP/OSPF/RIP/RIPng/ISIS/PIM/LDP routing daemon forked from Quagga + FRR is free software which manages TCP/IP based routing protocols. + It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2, RIPng, + PIM and LDP as well as the IPv6 versions of these. + . + FRR is a fork of Quagga with an open community model. The main git + lives on https://github.com/frrouting/frr.git + +Package: frr-dbg +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, frr (= ${binary:Version}) +Priority: optional +Section: debug +Description: BGP/OSPF/RIP/RIPng/ISIS/PIM/LDP routing daemon (debug symbols) + This package provides debugging symbols for all binary packages built + from frr source package. It's highly recommended to have this package + installed before reporting any FRR crashes to either FRR developers or + Debian package maintainers. + +Package: frr-doc +Section: net +Architecture: all +Depends: ${misc:Depends} +Suggests: frr +Description: BGP/OSPF/RIP/RIPng/ISIS/PIM/LDP routing daemon (documentation) + This package includes info files for frr, a free software which manages + TCP/IP based routing protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, + IS-IS, RIPv1, RIPv2, RIPng, PIM and LDP as well as the IPv6 versions of these. + +Package: frr-pythontools +Section: net +Architecture: all +Depends: ${misc:Depends}, frr (= ${binary:Version}), python (>= 2.7), python-ipaddr +Description: BGP/OSPF/RIP/RIPng/ISIS/PIM/LDP routing daemon (Python Tools) + This package includes info files for frr, a free software which manages + TCP/IP based routing protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, + IS-IS, RIPv1, RIPv2, RIPng, PIM and LDP as well as the IPv6 versions of these. diff --git a/debianpkg/backports/ubuntu17.10/debian/source/format b/debianpkg/backports/ubuntu17.10/debian/source/format new file mode 100644 index 000000000000..163aaf8d82b6 --- /dev/null +++ b/debianpkg/backports/ubuntu17.10/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debianpkg/backports/ubuntu17.10/exclude b/debianpkg/backports/ubuntu17.10/exclude new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/debianpkg/backports/ubuntu17.10/versionext b/debianpkg/backports/ubuntu17.10/versionext new file mode 100644 index 000000000000..bfbeccd6530f --- /dev/null +++ b/debianpkg/backports/ubuntu17.10/versionext @@ -0,0 +1 @@ +-1~ubuntu17.10+1 diff --git a/debianpkg/frr-dbg.lintian-overrides b/debianpkg/frr-dbg.lintian-overrides new file mode 100644 index 000000000000..7880bba29a7b --- /dev/null +++ b/debianpkg/frr-dbg.lintian-overrides @@ -0,0 +1 @@ +frr-dbg: debug-file-with-no-debug-symbols usr/lib/debug/usr/lib/libfrrfpm_pb.so.0.0.0 diff --git a/debianpkg/frr.preinst b/debianpkg/frr.preinst index 29162e3b56a9..467bea971834 100644 --- a/debianpkg/frr.preinst +++ b/debianpkg/frr.preinst @@ -30,7 +30,7 @@ fi # frr and as such we need to intelligently # check to see if the frr user is in the frrvty # group. -if ! /usr/bin/id frr | grep &>/dev/null 'frrvty'; then +if ! id frr | grep &>/dev/null 'frrvty'; then usermod -a -G frrvty frr >/dev/null fi diff --git a/debianpkg/rules b/debianpkg/rules index 04d28762aa49..a6a9077da181 100755 --- a/debianpkg/rules +++ b/debianpkg/rules @@ -190,7 +190,7 @@ override_dh_systemd_enable: # backports SRCPKG = frr -KNOWN_BACKPORTS = debian8 debian9 ubuntu12.04 ubuntu14.04 ubuntu16.04 +KNOWN_BACKPORTS = debian8 debian9 ubuntu12.04 ubuntu14.04 ubuntu16.04 ubuntu17.10 DEBIAN_VERSION := $(shell dh_testdir && \ dpkg-parsechangelog -c1 < debian/changelog | \ sed -rn 's/^Version: ?//p') diff --git a/doc/Makefile.am b/doc/Makefile.am index 6c80ddeaa4a6..7aaa36556f25 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -59,17 +59,37 @@ $(srcdir)/frr.info: $(frr_TEXINFOS) defines.texi frr.dvi: $(frr_TEXINFOS) defines.texi frr.html: $(frr_TEXINFOS) defines.texi -frr_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \ +frr_TEXINFOS = \ + appendix.texi \ + basic.texi \ + bgpd.texi \ + isisd.texi \ + filter.texi \ vnc.texi \ babeld.texi \ - install.texi ipv6.texi kernel.texi main.texi \ + install.texi \ + ipv6.texi \ + kernel.texi \ + main.texi \ nhrpd.texi \ eigrpd.texi \ - ospf6d.texi ospfd.texi \ - overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \ - snmp.texi vtysh.texi routeserver.texi $(figures_png) \ - snmptrap.texi ospf_fundamentals.texi isisd.texi $(figures_txt) \ - rpki.texi + ospf6d.texi \ + ospfd.texi \ + overview.texi \ + protocol.texi \ + ripd.texi \ + ripngd.texi \ + routemap.texi \ + snmp.texi \ + vtysh.texi \ + routeserver.texi \ + $(figures_png) \ + snmptrap.texi \ + ospf_fundamentals.texi \ + isisd.texi $(figures_txt) \ + rpki.texi \ + pimd.texi \ + #END .png.eps: $(PNGTOEPS) $< "$@" diff --git a/doc/cli.md b/doc/cli.md index 253617e9b527..723237ebb8c3 100644 --- a/doc/cli.md +++ b/doc/cli.md @@ -210,7 +210,7 @@ all DEFPY statements**: ... -#include "filename_clippy.c" +#include "daemon/filename_clippy.c" DEFPY(...) DEFPY(...) diff --git a/doc/install.texi b/doc/install.texi index 19d9614420f8..d0d56804b059 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -85,6 +85,10 @@ Enable Traffic Engineering Extension for ISIS (RFC5305) @item --enable-multipath=@var{ARG} Enable support for Equal Cost Multipath. @var{ARG} is the maximum number of ECMP paths to allow, set to 0 to allow unlimited number of paths. +@item --enable-realms +Enable the support of linux Realms. Convert tag values from 1-255 +into a realm value when inserting into the linux kernel. Then +routing policy can be assigned to the realm. See the tc man page. @item --disable-rtadv Disable support IPV6 router advertisement in zebra. @item --enable-gcc-rdynamic diff --git a/doc/isisd.texi b/doc/isisd.texi index bbc2896755e9..404698d8053d 100644 --- a/doc/isisd.texi +++ b/doc/isisd.texi @@ -373,36 +373,37 @@ log file /var/log/zebra.log ! interface eth0 ip address 10.2.2.2/24 - mpls-te on - mpls-te link metric 10 - mpls-te link max-bw 1.25e+06 - mpls-te link max-rsv-bw 1.25e+06 - mpls-te link unrsv-bw 0 1.25e+06 - mpls-te link unrsv-bw 1 1.25e+06 - mpls-te link unrsv-bw 2 1.25e+06 - mpls-te link unrsv-bw 3 1.25e+06 - mpls-te link unrsv-bw 4 1.25e+06 - mpls-te link unrsv-bw 5 1.25e+06 - mpls-te link unrsv-bw 6 1.25e+06 - mpls-te link unrsv-bw 7 1.25e+06 - mpls-te link rsc-clsclr 0xab + link-params + enable + metric 100 + max-bw 1.25e+07 + max-rsv-bw 1.25e+06 + unrsv-bw 0 1.25e+06 + unrsv-bw 1 1.25e+06 + unrsv-bw 2 1.25e+06 + unrsv-bw 3 1.25e+06 + unrsv-bw 4 1.25e+06 + unrsv-bw 5 1.25e+06 + unrsv-bw 6 1.25e+06 + unrsv-bw 7 1.25e+06 + admin-grp 0xab ! interface eth1 ip address 10.1.1.1/24 - mpls-te on - mpls-te link metric 10 - mpls-te link max-bw 1.25e+06 - mpls-te link max-rsv-bw 1.25e+06 - mpls-te link unrsv-bw 0 1.25e+06 - mpls-te link unrsv-bw 1 1.25e+06 - mpls-te link unrsv-bw 2 1.25e+06 - mpls-te link unrsv-bw 3 1.25e+06 - mpls-te link unrsv-bw 4 1.25e+06 - mpls-te link unrsv-bw 5 1.25e+06 - mpls-te link unrsv-bw 6 1.25e+06 - mpls-te link unrsv-bw 7 1.25e+06 - mpls-te link rsc-clsclr 0xab - mpls-te neighbor 10.1.1.2 as 65000 + link-params + enable + metric 100 + max-bw 1.25e+07 + max-rsv-bw 1.25e+06 + unrsv-bw 0 1.25e+06 + unrsv-bw 1 1.25e+06 + unrsv-bw 2 1.25e+06 + unrsv-bw 3 1.25e+06 + unrsv-bw 4 1.25e+06 + unrsv-bw 5 1.25e+06 + unrsv-bw 6 1.25e+06 + unrsv-bw 7 1.25e+06 + neighbor 10.1.1.2 as 65000 @end group @end example diff --git a/doc/ospfd.texi b/doc/ospfd.texi index cc3321151037..26a7637048c1 100644 --- a/doc/ospfd.texi +++ b/doc/ospfd.texi @@ -843,36 +843,37 @@ log file /var/log/zebra.log ! interface eth0 ip address 198.168.1.1/24 - mpls-te on - mpls-te link metric 10 - mpls-te link max-bw 1.25e+06 - mpls-te link max-rsv-bw 1.25e+06 - mpls-te link unrsv-bw 0 1.25e+06 - mpls-te link unrsv-bw 1 1.25e+06 - mpls-te link unrsv-bw 2 1.25e+06 - mpls-te link unrsv-bw 3 1.25e+06 - mpls-te link unrsv-bw 4 1.25e+06 - mpls-te link unrsv-bw 5 1.25e+06 - mpls-te link unrsv-bw 6 1.25e+06 - mpls-te link unrsv-bw 7 1.25e+06 - mpls-te link rsc-clsclr 0xab + link-params + enable + admin-grp 0xa1 + metric 100 + max-bw 1.25e+07 + max-rsv-bw 1.25e+06 + unrsv-bw 0 1.25e+06 + unrsv-bw 1 1.25e+06 + unrsv-bw 2 1.25e+06 + unrsv-bw 3 1.25e+06 + unrsv-bw 4 1.25e+06 + unrsv-bw 5 1.25e+06 + unrsv-bw 6 1.25e+06 + unrsv-bw 7 1.25e+06 ! interface eth1 ip address 192.168.2.1/24 - mpls-te on - mpls-te link metric 10 - mpls-te link max-bw 1.25e+06 - mpls-te link max-rsv-bw 1.25e+06 - mpls-te link unrsv-bw 0 1.25e+06 - mpls-te link unrsv-bw 1 1.25e+06 - mpls-te link unrsv-bw 2 1.25e+06 - mpls-te link unrsv-bw 3 1.25e+06 - mpls-te link unrsv-bw 4 1.25e+06 - mpls-te link unrsv-bw 5 1.25e+06 - mpls-te link unrsv-bw 6 1.25e+06 - mpls-te link unrsv-bw 7 1.25e+06 - mpls-te link rsc-clsclr 0xab - mpls-te neighbor 192.168.2.2 as 65000 + link-params + enable + metric 10 + max-bw 1.25e+07 + max-rsv-bw 1.25e+06 + unrsv-bw 0 1.25e+06 + unrsv-bw 1 1.25e+06 + unrsv-bw 2 1.25e+06 + unrsv-bw 3 1.25e+06 + unrsv-bw 4 1.25e+06 + unrsv-bw 5 1.25e+06 + unrsv-bw 6 1.25e+06 + unrsv-bw 7 1.25e+06 + neighbor 192.168.2.2 as 65000 @end group @end example diff --git a/doc/pimd.texi b/doc/pimd.texi new file mode 100644 index 000000000000..30e85afbb0d6 --- /dev/null +++ b/doc/pimd.texi @@ -0,0 +1,366 @@ +@c -*-texinfo-*- +@c This is part of the Frr Manual. +@c @value{COPYRIGHT_STR} +@c See file frr.texi for copying conditions. +@node PIM +@chapter PIM + +PIM -- Protocol Independent Multicast + +@command{pimd} supports pim-sm as well as igmp v2 and v3. pim is +vrf aware and can work within the context of vrf's in order to +do S,G mrouting. + +@menu +* Starting and Stopping pimd:: +* PIM Configuration:: +* PIM Interface Configuration:: +* PIM Multicast RIB insertion:: +* Show PIM Information:: +* PIM Debug Commands:: +@end menu + +@node Starting and Stopping pimd +@section Starting and Stopping pimd + +The default configuration file name of @command{pimd}'s is +@file{pimd.conf}. When invocation @command{pimd} searches directory +@value{INSTALL_PREFIX_ETC}. If @file{pimd.conf} is not there +then next search current directory. + +@command{pimd} requires zebra for proper operation. Additionally +@command{pimd} depends on routing properly setup and working +in the network that it is working on. + +@example +@group +# zebra -d +# pimd -d +@end group +@end example + +Please note that @command{zebra} must be invoked before @command{pimd}. + +To stop @command{pimd}. Please use @command{kill `cat +/var/run/pimd.pid`}. Certain signals have special meanings to @command{pimd}. + +@table @samp +@item SIGUSR1 +Rotate @command{pimd} logfile. +@item SIGINT +@itemx SIGTERM +@command{pimd} sweeps all installed PIM mroutes then terminates properly. +@end table + +@command{pimd} invocation options. Common options that can be specified +(@pxref{Common Invocation Options}). + +@node PIM Configuration + +@deffn Command {ip pim rp A.B.C.D A.B.C.D/M} {} +In order to use pim, it is necessary to configure a RP for join +messages to be sent to. Currently the only methodology to +do this is via static rp commands. All routers in the +pim network must agree on these values. The first ip address +is the RP's address and the second value is the matching +prefix of group ranges covered. This command is vrf aware, +to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim spt-switchover infinity-and-beyond} {} +On the last hop router if it is desired to not switch over +to the SPT tree. Configure this command. This command is +vrf aware, to configure for a vrf, enter the vrf submode. +#end deffn + +@deffn Comand {ip pim ecmp} {} +If pim has the a choice of ECMP nexthops for a particular +RPF, pim will cause S,G flows to be spread out amongst +the nexthops. If this command is not specified then +the first nexthop found will be used. This command +is vrf aware, to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim ecmp rebalance} {} +If pim is using ECMP and an interface goes down, cause +pim to rebalance all S,G flows aross the remaining +nexthops. If this command is not configured pim only +modifies those S,G flows that were using the interface +that went down. This command is vrf aware, to configure +for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim join-prune-interval (60-600)} {} +Modify the join/prune interval that pim uses to the +new value. Time is specified in seconds. This command +is vrf aware, to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim keep-alive-timer (31-60000)} {} +Modify the time out value for a S,G flow from 31-60000 +seconds. 31 seconds is choosen for a lower bound +because some hardware platforms cannot see data flowing +in better than 30 second chunks. This comand is vrf +aware, to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim packets (1-100)} {} +When processing packets from a neighbor process the +number of packets incoming at one time before moving +on to the next task. The default value is 3 packets. +This command is only useful at scale when you can +possibly have a large number of pim control packets +flowing. This command is vrf aware, to configure for +a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim register-suppress-time (5-60000)} {} +Modify the time that pim will register suppress a FHR +will send register notifications to the kernel. This command +is vrf aware, to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim send-v6-secondary} {} +When sending pim hello packets tell pim to send +any v6 secondary addresses on the interface. This +information is used to allow pim to use v6 nexthops +in it's decision for RPF lookup. This command +is vrf aware, to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip pim ssm prefix-list WORD} {} +Specify a range of group addresses via a prefix-list +that forces pim to never do SM over. This command +is vrf aware, to configure for a vrf, enter the vrf submode. +@end deffn + +@deffn Command {ip multicast rpf-lookup-mode WORD} {} +Modify how PIM does RPF lookups in the zebra routing table. +You can use these choices: +@table @lookup_modes +@item longer-prefix +Lookup the RPF in both tables using the longer prefix as a match +@item lower-distance +Lookup the RPF in both tables using the lower distance as a match +@item mrib-only +Lookup in the Multicast RIB only +@item mrib-then-urib +Lookup in the Multicast RIB then the Unicast Rib, returning first found. +This is the default value for lookup if this command is not entered +@item urib-only +Lookup in the Unicast Rib only. +@end table +@end deffn + +@node PIM Interface Configuration +@section PIM Interface Configuration + +PIM interface commands allow you to configure an +interface as either a Receiver or a interface +that you would like to form pim neighbors on. If the +interface is in a vrf, enter the interface command with +the vrf keyword at the end. + +@deffn {PIM Interface Command] {ip pim bfd} {} +Turns on BFD support for PIM for this interface. +@end deffn + +@deffn {PIM Interface Command} {ip pim drpriority (1-4294967295)} {} +Set the DR Priority for the interface. This command is useful +to allow the user to influence what node becomes the DR for a +lan segment. +@end deffn + +@deffn {PIM Interface Command} {ip pim hello (1-180) (1-180)} {} +Set the pim hello and hold interval for a interface. +@end deffn + +@deffn {PIM Interface Command} {ip pim sm} {} +Tell pim that we would like to use this interface to form +pim neighbors over. Please note we will *not* accept +igmp reports over this interface with this command. +@end deffn + +@deffn {PIM Interface Command} {ip igmp} {} +Tell pim to receive IGMP reports and Query on this +interface. The default version is v3. This command +is useful on the LHR. +@end deffn + +@deffn {PIM Interface Command} {ip igmp query-interval (1-1800)} {} +Set the IGMP query interval that PIM will use. +@end deffn + +@deffn {PIM Interface Command} {ip igmp query-max-response-time (10-250)} {} +Set the IGMP query response timeout value. If an report is not returned +in the specified time we will assume the S,G or *,G has timed out. +@end deffn + +@deffn {PIM Interface Command} {ip igmp version (2-3)} {} +Set the IGMP version used on this interface. The default value +is 3. +@end deffn + +@deffn {PIM Interface Command} {ip multicat boundary oil WORD} {} +Set a pim multicast boundary, based upon the WORD prefix-list. If +a pim join or IGMP report is received on this interface and the Group +is denyed by the prefix-list, PIM will ignore the join or report. +@end deffn + +@node PIM Multicast RIB insertion:: +@section PIM Multicast RIB insertion:: + +In order to influence Multicast RPF lookup, it is possible to insert +into zebra routes for the Multicast RIB. These routes are only +used for RPF lookup and will not be used by zebra for insertion +into the kernel *or* for normal rib processing. As such it is +possible to create weird states with these commands. Use with +caution. Most of the time this will not be necessary. + +@deffn {PIM Multicast RIB insertion} {ip mroute A.B.C.D/M A.B.C.D (1-255)} {} +Insert into the Multicast Rib Route A.B.C.D/M with specified nexthop. The distance can be specified as well if desired. +@end deffn + +@deffn {PIM Multicast RIB insertion} {ip mroute A.B.C.D/M INTERFACE (1-255)} {} +Insert into the Multicast Rib Route A.B.C.D/M using the specified INTERFACE. +The distance can be specified as well if desired. +@end deffn + +@node Show PIM Information:: +@section Show PIM Information + +All PIM show commands are vrf aware and typically allow you to insert +a specified vrf command if information is desired about a specific vrf. +If no vrf is specified then the default vrf is assumed. Finally +the special keyword 'all' allows you to look at all vrfs for the command. +Naming a vrf 'all' will cause great confusion. + +@deffn {Show PIM Information} {show ip multicast} +Display various information about the interfaces used in this pim +instance. +@end deffn + +@deffn {Show PIM Information} {show ip mroute} +Display information about installed into the kernel S,G mroutes. +@end deffn + +@deffn {Show PIM Information} {show ip mroute count} +Display information about installed into the kernel S,G mroutes +and in addition display data about packet flow for the mroutes. +@end deffn + +@deffn {Show PIM Information} {show ip pim assert} +Display information about asserts in the PIM system for S,G mroutes. +@end deffn + +@deffn {Show PIM Information} {show ip pim assert-internal} +Display internal assert state for S,G mroutes +@end deffn + +@deffn {Show PIM Information} {show ip pim assert-metric} +Display metric information about assert state for S,G mroutes +@end deffn + +@deffn {Show PIM Information} {show ip pim assert-winner-metric} +Display winner metric for assert state for S,G mroutes +@end deffn + +@deffn {Show PIM Information} {show ip pim group-type} +Display SSM group ranges +@end deffn + +@deffn {Show PIM Information} {show ip pim interface} +Display information about interfaces PIM is using. +@end deffn + +@deffn {Show PIM Information} {show ip pim join} +Display information about PIM joins received. +@end deffn + +@deffn {Show PIM Information} {show ip pim local-membership} {} +Display information about PIM interface local-membership +@end deffn + +@deffn {Show PIM Information} {show ip pim neighbor} {} +Display information about PIM neighbors +@end deffn + +@deffn {Show PIM Information} {show ip pim nexthop} {} +Display information about pim nexthops that are being +used +@end deffn + +@deffn {Show PIM Information} {show ip pim nexthop-lookup} {} +Display information about a S,G pair and how the RPF would +be choosen. This is especially useful if there are ECMP's +available from the RPF lookup. +@end deffn + +@deffn {Show PIM Information} {show ip pim rp-info} {} +Display information about RP's that are configured on +this router +@end deffn + +@deffn {Show PIM Information} {show ip pim rpf} {} +Display information about currently being used S,G's +and their RPF lookup information. Additionally display +some statistics about what has been happening on the +router +@end deffn + +@deffn {show PIM Information} {show ip pim secondary} {} +Display information about an interface and all the +secondary addresses associated with it +@end deffn + +@deffn {show PIM Information} {show ip pim state} {} +Display information about known S,G's and incoming +interface as well as the OIL and how they were choosen +@end deffn + +@deffn {show PIM Information} {show ip pim upstream} {} +Display upstream information about a S,G mroute +@end deffn + +@deffn {show PIM Information} {show ip pim upstream-join-desired} {} +Display upstream information for S,G's and if we desire to +join the mcast tree +@end deffn + +@deffn {show PIM Information} {show ip pim upstream-rpf} {} +Display upstream information for S,G's and the RPF data +associated with them +@end deffn + +@deffn {show PIM Information} {show ip rpf} {} +Display the multicast RIB created in zebra +@end deffn + +@node PIM Debug Commands +@section PIM Debug Commands + +The debugging subsystem for PIM behaves in accordance with how FRR handles debugging. You can specify debugging at the enable cli mode as well as the configure cli mode. If you specify debug commands in the configuration cli mode, the debug commands can be persistent across restarts of the FRR pimd if the config was written out. + +@deffn {PIM Debug Commands} {debug pim events} +This turns on debugging for PIM system events. Especially timers. +@end deffn + +@deffn {PIM Debug Commands} {debug pim nht} +This turns on debugging for PIM nexthop tracking. It will display information about RPF lookups and information about when a nexthop changes. +@end deffn + +@deffn {PIM Debug Commands} {debug pim packet-dump} +This turns on an extraordinary amount of data. Each pim packet sent and received is dumped for debugging purposes. This should be considered a developer only command +@end deffn + +@deffn {PIM Debug Commands} {debug pim packets} +This turns on information about packet generation for sending and about packet handling from a received packet +@end deffn + +@deffn {PIM Debug Commands} {debug pim trace} +This traces pim code and how it is running. +@end deffn + +@deffn {PIM Debug Commands} {debug pim zebra} +This gathers data about events from zebra that come up through the zapi +@end deffn diff --git a/doc/routemap.texi b/doc/routemap.texi index b72f539c4666..3e683f44d198 100644 --- a/doc/routemap.texi +++ b/doc/routemap.texi @@ -171,6 +171,11 @@ Matches the specified @var{as_path}. Matches the specified @var{metric}. @end deffn +@deffn {Route-map Command} {match tag @var{tag}} {} +Matches the specified tag value associated with the route. +This tag value can be in the range of (1-4294967295). +@end deffn + @deffn {Route-map Command} {match local-preference @var{metric}} {} Matches the specified @var{local-preference}. @end deffn @@ -198,6 +203,14 @@ in this manner. @node Route Map Set Command @section Route Map Set Command +@deffn {Route-map Command} {set tag @var{tag}} {} +Set a tag on the matched route. This tag value can be from +(1-4294967295). Additionally if you have compiled with +the --enable-realms configure option. Tag values from (1-255) +are sent to the linux kernel as a realm value. Then route +policy can be applied. See the tc man page. +@end deffn + @deffn {Route-map Command} {set ip next-hop @var{ipv4_address}} {} Set the BGP nexthop address. @end deffn diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index ecabee4aa7ac..ea6f1f3f621d 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -425,7 +425,7 @@ int eigrp_write(struct thread *thread) iov[0].iov_base = (char *)&iph; iov[0].iov_len = iph.ip_hl << EIGRP_WRITE_IPHL_SHIFT; - iov[1].iov_base = STREAM_PNT(ep->s); + iov[1].iov_base = stream_pnt(ep->s); iov[1].iov_len = ep->length; /* send final fragment (could be first) */ @@ -555,7 +555,7 @@ int eigrp_read(struct thread *thread) by eigrp_recv_packet() to be correct). */ stream_forward_getp(ibuf, (iph->ip_hl * 4)); - eigrph = (struct eigrp_header *)STREAM_PNT(ibuf); + eigrph = (struct eigrp_header *)stream_pnt(ibuf); if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV) && IS_DEBUG_EIGRP_TRANSMIT(0, PACKET_DETAIL)) diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c index 0ccffde72bf0..84396f0878ac 100644 --- a/eigrpd/eigrp_reply.c +++ b/eigrpd/eigrp_reply.c @@ -170,9 +170,11 @@ void eigrp_reply_receive(struct eigrp *eigrp, struct ip *iph, */ if (!dest) { char buf[PREFIX_STRLEN]; + zlog_err("%s: Received prefix %s which we do not know about", __PRETTY_FUNCTION__, - prefix2str(&dest_addr, buf, strlen(buf))); + prefix2str(&dest_addr, buf, sizeof(buf))); + eigrp_IPv4_InternalTLV_free(tlv); continue; } diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index 00438f2f47c0..f18d39d57567 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -366,6 +366,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_EIGRP; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); @@ -407,6 +408,7 @@ void eigrp_zebra_route_delete(struct prefix *p) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_EIGRP; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h new file mode 100644 index 000000000000..bc6688012c42 --- /dev/null +++ b/include/linux/fib_rules.h @@ -0,0 +1,74 @@ +#ifndef __LINUX_FIB_RULES_H +#define __LINUX_FIB_RULES_H + +#include +#include + +/* rule is permanent, and cannot be deleted */ +#define FIB_RULE_PERMANENT 0x00000001 +#define FIB_RULE_INVERT 0x00000002 +#define FIB_RULE_UNRESOLVED 0x00000004 +#define FIB_RULE_IIF_DETACHED 0x00000008 +#define FIB_RULE_DEV_DETACHED FIB_RULE_IIF_DETACHED +#define FIB_RULE_OIF_DETACHED 0x00000010 + +/* try to find source address in routing lookups */ +#define FIB_RULE_FIND_SADDR 0x00010000 + +struct fib_rule_hdr { + __u8 family; + __u8 dst_len; + __u8 src_len; + __u8 tos; + + __u8 table; + __u8 res1; /* reserved */ + __u8 res2; /* reserved */ + __u8 action; + + __u32 flags; +}; + +enum { + FRA_UNSPEC, + FRA_DST, /* destination address */ + FRA_SRC, /* source address */ + FRA_IIFNAME, /* interface name */ +#define FRA_IFNAME FRA_IIFNAME + FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */ + FRA_UNUSED2, + FRA_PRIORITY, /* priority/preference */ + FRA_UNUSED3, + FRA_UNUSED4, + FRA_UNUSED5, + FRA_FWMARK, /* mark */ + FRA_FLOW, /* flow/class id */ + FRA_UNUSED6, + FRA_SUPPRESS_IFGROUP, + FRA_SUPPRESS_PREFIXLEN, + FRA_TABLE, /* Extended table id */ + FRA_FWMASK, /* mask for netfilter mark */ + FRA_OIFNAME, + FRA_PAD, + FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + __FRA_MAX +}; + +#define FRA_MAX (__FRA_MAX - 1) + +enum { + FR_ACT_UNSPEC, + FR_ACT_TO_TBL, /* Pass to fixed table */ + FR_ACT_GOTO, /* Jump to another rule */ + FR_ACT_NOP, /* No operation */ + FR_ACT_RES3, + FR_ACT_RES4, + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ + __FR_ACT_MAX, +}; + +#define FR_ACT_MAX (__FR_ACT_MAX - 1) + +#endif diff --git a/include/subdir.am b/include/subdir.am index 98bd14800222..de38cd221e06 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -6,4 +6,5 @@ noinst_HEADERS += \ include/linux/neighbour.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ + include/linux/fib_rules.h \ # end diff --git a/isisd/dict.h b/isisd/dict.h index a5ee922318d8..32683c57d58a 100644 --- a/isisd/dict.h +++ b/isisd/dict.h @@ -14,8 +14,6 @@ * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * - * $Id: dict.h,v 1.3 2005/09/25 12:04:25 hasso Exp $ - * $Name: $ */ #ifndef DICT_H diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 591af3b8eddf..a4c6b4c75d05 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -46,15 +46,14 @@ #include "privs.h" struct bpf_insn llcfilter[] = { - /* check first byte */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN), + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, + ETHER_HDR_LEN), /* check first byte */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5), - /* check second byte */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN + 1), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3), - /* check third byte */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN + 2), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, + 3), /* check second byte */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */ BPF_STMT(BPF_RET + BPF_K, (u_int)-1), BPF_STMT(BPF_RET + BPF_K, 0)}; u_int readblen = 0; @@ -242,14 +241,15 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, u_char *ssnpa) assert(bpf_hdr->bh_caplen == bpf_hdr->bh_datalen); - offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETH_ALEN; + offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN; /* then we lose the BPF, LLC and ethernet headers */ stream_write(circuit->rcv_stream, readbuff + offset, - bpf_hdr->bh_caplen - LLC_LEN - ETH_ALEN); + bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN); stream_set_getp(circuit->rcv_stream, 0); - memcpy(ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETH_ALEN, ETH_ALEN); + memcpy(ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETH_ALEN, + ETH_ALEN); if (ioctl(circuit->fd, BIOCFLUSH, &one) < 0) zlog_warn("Flushing failed: %s", safe_strerror(errno)); @@ -263,7 +263,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) ssize_t written; size_t buflen; - buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETH_ALEN; + buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN; if (buflen > sizeof(sock_buff)) { zlog_warn( "isis_send_pdu_bcast: sock_buff size %zu is less than " @@ -289,12 +289,12 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) /* * Then the LLC */ - sock_buff[ETH_ALEN] = ISO_SAP; - sock_buff[ETH_ALEN + 1] = ISO_SAP; - sock_buff[ETH_ALEN + 2] = 0x03; + sock_buff[ETHER_HDR_LEN] = ISO_SAP; + sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP; + sock_buff[ETHER_HDR_LEN + 2] = 0x03; /* then we copy the data */ - memcpy(sock_buff + (LLC_LEN + ETH_ALEN), circuit->snd_stream->data, + memcpy(sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data, stream_get_endp(circuit->snd_stream)); /* now we can send this */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 30679367c04d..0b7dc86ad6c8 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -676,7 +676,8 @@ int isis_circuit_up(struct isis_circuit *circuit) circuit->lsp_queue = list_new(); circuit->lsp_hash = isis_lsp_hash_new(); - circuit->lsp_queue_last_push = monotime(NULL); + circuit->lsp_queue_last_push[0] = circuit->lsp_queue_last_push[1] = + monotime(NULL); return ISIS_OK; } diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index ac1e15f6bf6f..ab181189a983 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -84,7 +84,7 @@ struct isis_circuit { struct thread *t_send_lsp; struct list *lsp_queue; /* LSPs to be txed (both levels) */ struct isis_lsp_hash *lsp_hash; /* Hashtable synchronized with lsp_queue */ - time_t lsp_queue_last_push; /* timestamp used to enforce transmit + time_t lsp_queue_last_push[2]; /* timestamp used to enforce transmit * interval; * for scalability, use one timestamp per * circuit, instead of one per lsp per diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index ff9114c506a6..614f46c78bb6 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1873,12 +1873,12 @@ int lsp_tick(struct thread *thread) if (!circuit->lsp_queue) continue; - if (now - circuit->lsp_queue_last_push + if (now - circuit->lsp_queue_last_push[level] < MIN_LSP_RETRANS_INTERVAL) { continue; } - circuit->lsp_queue_last_push = now; + circuit->lsp_queue_last_push[level] = now; for (ALL_LIST_ELEMENTS_RO( lsp_list, lspnode, lsp)) { diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 3008fb6a1eb1..a076bb555c2c 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1249,7 +1249,7 @@ static void init_spt(struct isis_spftree *spftree, int mtid, int level, } static int isis_run_spf(struct isis_area *area, int level, int family, - u_char *sysid) + u_char *sysid, struct timeval *nowtv) { int retval = ISIS_OK; struct isis_vertex *vertex; @@ -1263,9 +1263,8 @@ static int isis_run_spf(struct isis_area *area, int level, int family, uint16_t mtid; /* Get time that can't roll backwards. */ - monotime(&time_now); - start_time = time_now.tv_sec; - start_time = (start_time * 1000000) + time_now.tv_usec; + start_time = nowtv->tv_sec; + start_time = (start_time * 1000000) + nowtv->tv_usec; if (family == AF_INET) spftree = area->spftree[level - 1]; @@ -1372,9 +1371,11 @@ static int isis_run_spf_cb(struct thread *thread) area->area_tag, level); if (area->ip_circuits) - retval = isis_run_spf(area, level, AF_INET, isis->sysid); + retval = isis_run_spf(area, level, AF_INET, isis->sysid, + &thread->real); if (area->ipv6_circuits) - retval = isis_run_spf(area, level, AF_INET6, isis->sysid); + retval = isis_run_spf(area, level, AF_INET6, isis->sysid, + &thread->real); return retval; } @@ -1435,9 +1436,8 @@ int isis_spf_schedule(struct isis_area *area, int level) timer, &area->spf_timer[level - 1]); if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %d sec from now", - area->area_tag, level, - area->min_spf_interval[level - 1] - diff); + zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %ld sec from now", + area->area_tag, level, timer); return ISIS_OK; } diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 573b81591cf7..0512a18a2a48 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -261,6 +261,7 @@ static void isis_zebra_route_add_route(struct prefix *prefix, memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_ISIS; api.safi = SAFI_UNICAST; api.prefix = *prefix; @@ -329,6 +330,7 @@ static void isis_zebra_route_del_route(struct prefix *prefix, memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_ISIS; api.safi = SAFI_UNICAST; api.prefix = *prefix; diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 3335be08a890..f638d6a65bbb 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -295,17 +295,26 @@ int l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) { /* check for a remote label */ - if (fnh->remote_label == NO_LABEL) + if (fnh->remote_label == NO_LABEL) { + log_warnx("%s: pseudowire %s: no remote label", __func__, + pw->ifname); return (0); + } /* MTUs must match */ - if (pw->l2vpn->mtu != pw->remote_mtu) + if (pw->l2vpn->mtu != pw->remote_mtu) { + log_warnx("%s: pseudowire %s: MTU mismatch detected", __func__, + pw->ifname); return (0); + } /* check pw status if applicable */ if ((pw->flags & F_PW_STATUSTLV) && - pw->remote_status != PW_FORWARDING) + pw->remote_status != PW_FORWARDING) { + log_warnx("%s: pseudowire %s: remote end is down", __func__, + pw->ifname); return (0); + } return (1); } @@ -550,7 +559,8 @@ l2vpn_pw_ctl(pid_t pid) sizeof(pwctl.ifname)); pwctl.pwid = pw->pwid; pwctl.lsr_id = pw->lsr_id; - if (pw->local_status == PW_FORWARDING && + if (pw->enabled && + pw->local_status == PW_FORWARDING && pw->remote_status == PW_FORWARDING) pwctl.status = 1; diff --git a/ldpd/lde.c b/ldpd/lde.c index b597d967d734..63e1e3994636 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -767,11 +767,12 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) sizeof(kr)); break; case FEC_TYPE_PWID: - if (fn->local_label == NO_LABEL || + pw = (struct l2vpn_pw *) fn->data; + if (!pw || fn->local_label == NO_LABEL || fnh->remote_label == NO_LABEL) return; - pw = (struct l2vpn_pw *) fn->data; + pw->enabled = true; pw2zpw(pw, &zpw); zpw.local_label = fn->local_label; zpw.remote_label = fnh->remote_label; @@ -818,6 +819,10 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; + if (!pw) + return; + + pw->enabled = false; pw2zpw(pw, &zpw); zpw.local_label = fn->local_label; zpw.remote_label = fnh->remote_label; diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c index ee4f0843b7d7..39e20ef7c84d 100644 --- a/ldpd/ldp_debug.c +++ b/ldpd/ldp_debug.c @@ -47,58 +47,58 @@ ldp_vty_debug(struct vty *vty, const char *negate, const char *type_str, if (dir_str[0] == 'r') { if (negate) - DEBUG_OFF(hello, HELLO_RECV); + DEBUG_OFF(hello, LDP_DEBUG_HELLO_RECV); else - DEBUG_ON(hello, HELLO_RECV); + DEBUG_ON(hello, LDP_DEBUG_HELLO_RECV); } else { if (negate) - DEBUG_OFF(hello, HELLO_SEND); + DEBUG_OFF(hello, LDP_DEBUG_HELLO_SEND); else - DEBUG_ON(hello, HELLO_SEND); + DEBUG_ON(hello, LDP_DEBUG_HELLO_SEND); } } else if (strcmp(type_str, "errors") == 0) { if (negate) - DEBUG_OFF(errors, ERRORS); + DEBUG_OFF(errors, LDP_DEBUG_ERRORS); else - DEBUG_ON(errors, ERRORS); + DEBUG_ON(errors, LDP_DEBUG_ERRORS); } else if (strcmp(type_str, "event") == 0) { if (negate) - DEBUG_OFF(event, EVENT); + DEBUG_OFF(event, LDP_DEBUG_EVENT); else - DEBUG_ON(event, EVENT); + DEBUG_ON(event, LDP_DEBUG_EVENT); } else if (strcmp(type_str, "labels") == 0) { if (negate) - DEBUG_OFF(labels, LABELS); + DEBUG_OFF(labels, LDP_DEBUG_LABELS); else - DEBUG_ON(labels, LABELS); + DEBUG_ON(labels, LDP_DEBUG_LABELS); } else if (strcmp(type_str, "messages") == 0) { if (dir_str == NULL) return (CMD_WARNING_CONFIG_FAILED); if (dir_str[0] == 'r') { if (negate) { - DEBUG_OFF(msg, MSG_RECV); - DEBUG_OFF(msg, MSG_RECV_ALL); + DEBUG_OFF(msg, LDP_DEBUG_MSG_RECV); + DEBUG_OFF(msg, LDP_DEBUG_MSG_RECV_ALL); } else { - DEBUG_ON(msg, MSG_RECV); + DEBUG_ON(msg, LDP_DEBUG_MSG_RECV); if (all) - DEBUG_ON(msg, MSG_RECV_ALL); + DEBUG_ON(msg, LDP_DEBUG_MSG_RECV_ALL); } } else { if (negate) { - DEBUG_OFF(msg, MSG_SEND); - DEBUG_OFF(msg, MSG_SEND_ALL); + DEBUG_OFF(msg, LDP_DEBUG_MSG_SEND); + DEBUG_OFF(msg, LDP_DEBUG_MSG_SEND_ALL); } else { - DEBUG_ON(msg, MSG_SEND); + DEBUG_ON(msg, LDP_DEBUG_MSG_SEND); if (all) - DEBUG_ON(msg, MSG_SEND_ALL); + DEBUG_ON(msg, LDP_DEBUG_MSG_SEND_ALL); } } } else if (strcmp(type_str, "zebra") == 0) { if (negate) - DEBUG_OFF(zebra, ZEBRA); + DEBUG_OFF(zebra, LDP_DEBUG_ZEBRA); else - DEBUG_ON(zebra, ZEBRA); + DEBUG_ON(zebra, LDP_DEBUG_ZEBRA); } main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, @@ -112,27 +112,27 @@ ldp_vty_show_debugging(struct vty *vty) { vty_out (vty, "LDP debugging status:\n"); - if (LDP_DEBUG(hello, HELLO_RECV)) + if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) vty_out (vty," LDP discovery debugging is on (inbound)\n"); - if (LDP_DEBUG(hello, HELLO_SEND)) + if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) vty_out (vty," LDP discovery debugging is on (outbound)\n"); - if (LDP_DEBUG(errors, ERRORS)) + if (LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) vty_out (vty, " LDP errors debugging is on\n"); - if (LDP_DEBUG(event, EVENT)) + if (LDP_DEBUG(event, LDP_DEBUG_EVENT)) vty_out (vty, " LDP events debugging is on\n"); - if (LDP_DEBUG(labels, LABELS)) + if (LDP_DEBUG(labels, LDP_DEBUG_LABELS)) vty_out (vty, " LDP labels debugging is on\n"); - if (LDP_DEBUG(msg, MSG_RECV_ALL)) + if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) vty_out (vty, " LDP detailed messages debugging is on (inbound)\n"); - else if (LDP_DEBUG(msg, MSG_RECV)) + else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) vty_out (vty," LDP messages debugging is on (inbound)\n"); - if (LDP_DEBUG(msg, MSG_SEND_ALL)) + if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) vty_out (vty, " LDP detailed messages debugging is on (outbound)\n"); - else if (LDP_DEBUG(msg, MSG_SEND)) + else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) vty_out (vty," LDP messages debugging is on (outbound)\n"); - if (LDP_DEBUG(zebra, ZEBRA)) + if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) vty_out (vty, " LDP zebra debugging is on\n"); vty_out (vty, "\n"); @@ -144,48 +144,48 @@ ldp_debug_config_write(struct vty *vty) { int write = 0; - if (CONF_LDP_DEBUG(hello, HELLO_RECV)) { + if (CONF_LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) { vty_out (vty,"debug mpls ldp discovery hello recv\n"); write = 1; } - if (CONF_LDP_DEBUG(hello, HELLO_SEND)) { + if (CONF_LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) { vty_out (vty,"debug mpls ldp discovery hello sent\n"); write = 1; } - if (CONF_LDP_DEBUG(errors, ERRORS)) { + if (CONF_LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) { vty_out (vty, "debug mpls ldp errors\n"); write = 1; } - if (CONF_LDP_DEBUG(event, EVENT)) { + if (CONF_LDP_DEBUG(event, LDP_DEBUG_EVENT)) { vty_out (vty, "debug mpls ldp event\n"); write = 1; } - if (CONF_LDP_DEBUG(labels, LABELS)) { + if (CONF_LDP_DEBUG(labels, LDP_DEBUG_LABELS)) { vty_out (vty, "debug mpls ldp labels\n"); write = 1; } - if (CONF_LDP_DEBUG(msg, MSG_RECV_ALL)) { + if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) { vty_out (vty, "debug mpls ldp messages recv all\n"); write = 1; - } else if (CONF_LDP_DEBUG(msg, MSG_RECV)) { + } else if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) { vty_out (vty, "debug mpls ldp messages recv\n"); write = 1; } - if (CONF_LDP_DEBUG(msg, MSG_SEND_ALL)) { + if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) { vty_out (vty, "debug mpls ldp messages sent all\n"); write = 1; - } else if (CONF_LDP_DEBUG(msg, MSG_SEND)) { + } else if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) { vty_out (vty, "debug mpls ldp messages sent\n"); write = 1; } - if (CONF_LDP_DEBUG(zebra, ZEBRA)) { + if (CONF_LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) { vty_out (vty, "debug mpls ldp zebra\n"); write = 1; } diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h index a0972cea8e66..8ae144d93a3d 100644 --- a/ldpd/ldp_debug.h +++ b/ldpd/ldp_debug.h @@ -46,11 +46,11 @@ struct ldp_debug { extern struct ldp_debug conf_ldp_debug; extern struct ldp_debug ldp_debug; -#define CONF_DEBUG_ON(a, b) (conf_ldp_debug.a |= (LDP_DEBUG_ ## b)) -#define CONF_DEBUG_OFF(a, b) (conf_ldp_debug.a &= ~(LDP_DEBUG_ ## b)) +#define CONF_DEBUG_ON(a, b) (conf_ldp_debug.a |= (b)) +#define CONF_DEBUG_OFF(a, b) (conf_ldp_debug.a &= ~(b)) -#define TERM_DEBUG_ON(a, b) (ldp_debug.a |= (LDP_DEBUG_ ## b)) -#define TERM_DEBUG_OFF(a, b) (ldp_debug.a &= ~(LDP_DEBUG_ ## b)) +#define TERM_DEBUG_ON(a, b) (ldp_debug.a |= (b)) +#define TERM_DEBUG_OFF(a, b) (ldp_debug.a &= ~(b)) #define DEBUG_ON(a, b) \ do { \ @@ -66,48 +66,48 @@ extern struct ldp_debug ldp_debug; TERM_DEBUG_OFF(a, b); \ } while (0) -#define LDP_DEBUG(a, b) (ldp_debug.a & LDP_DEBUG_ ## b) -#define CONF_LDP_DEBUG(a, b) (conf_ldp_debug.a & LDP_DEBUG_ ## b) +#define LDP_DEBUG(a, b) (ldp_debug.a & b) +#define CONF_LDP_DEBUG(a, b) (conf_ldp_debug.a & b) #define debug_hello_recv(emsg, ...) \ do { \ - if (LDP_DEBUG(hello, HELLO_RECV)) \ + if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) \ log_debug("discovery[recv]: " emsg, __VA_ARGS__); \ } while (0) #define debug_hello_send(emsg, ...) \ do { \ - if (LDP_DEBUG(hello, HELLO_SEND)) \ + if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) \ log_debug("discovery[send]: " emsg, __VA_ARGS__); \ } while (0) #define debug_err(emsg, ...) \ do { \ - if (LDP_DEBUG(errors, ERRORS)) \ + if (LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) \ log_debug("error: " emsg, __VA_ARGS__); \ } while (0) #define debug_evt(emsg, ...) \ do { \ - if (LDP_DEBUG(event, EVENT)) \ + if (LDP_DEBUG(event, LDP_DEBUG_EVENT)) \ log_debug("event: " emsg, __VA_ARGS__); \ } while (0) #define debug_labels(emsg, ...) \ do { \ - if (LDP_DEBUG(labels, LABELS)) \ + if (LDP_DEBUG(labels, LDP_DEBUG_LABELS)) \ log_debug("labels: " emsg, __VA_ARGS__); \ } while (0) #define debug_msg_recv(emsg, ...) \ do { \ - if (LDP_DEBUG(msg, MSG_RECV)) \ + if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) \ log_debug("msg[in]: " emsg, __VA_ARGS__); \ } while (0) #define debug_msg_send(emsg, ...) \ do { \ - if (LDP_DEBUG(msg, MSG_SEND)) \ + if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) \ log_debug("msg[out]: " emsg, __VA_ARGS__); \ } while (0) @@ -121,25 +121,25 @@ do { \ #define debug_kalive_recv(emsg, ...) \ do { \ - if (LDP_DEBUG(msg, MSG_RECV_ALL)) \ + if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) \ log_debug("kalive[in]: " emsg, __VA_ARGS__); \ } while (0) #define debug_kalive_send(emsg, ...) \ do { \ - if (LDP_DEBUG(msg, MSG_SEND_ALL)) \ + if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) \ log_debug("kalive[out]: " emsg, __VA_ARGS__); \ } while (0) #define debug_zebra_in(emsg, ...) \ do { \ - if (LDP_DEBUG(zebra, ZEBRA)) \ + if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) \ log_debug("zebra[in]: " emsg, __VA_ARGS__); \ } while (0) #define debug_zebra_out(emsg, ...) \ do { \ - if (LDP_DEBUG(zebra, ZEBRA)) \ + if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) \ log_debug("zebra[out]: " emsg, __VA_ARGS__); \ } while (0) diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index e703a9ff61ad..a12a4b913ee4 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -450,18 +450,38 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, /* loop through all the nexthops */ for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; - - switch (kr.af) { - case AF_INET: + switch (api_nh->type) { + case NEXTHOP_TYPE_IPV4: + if (kr.af != AF_INET) + continue; + kr.nexthop.v4 = api_nh->gate.ipv4; + kr.ifindex = 0; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (kr.af != AF_INET) + continue; kr.nexthop.v4 = api_nh->gate.ipv4; + kr.ifindex = api_nh->ifindex; break; - case AF_INET6: + case NEXTHOP_TYPE_IPV6: + if (kr.af != AF_INET6) + continue; kr.nexthop.v6 = api_nh->gate.ipv6; + kr.ifindex = 0; break; - default: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (kr.af != AF_INET6) + continue; + kr.nexthop.v6 = api_nh->gate.ipv6; + kr.ifindex = api_nh->ifindex; + break; + case NEXTHOP_TYPE_IFINDEX: + if (!(kr.flags & F_CONNECTED)) + continue; break; + default: + continue; } - kr.ifindex = api_nh->ifindex;; debug_zebra_in("route %s %s/%d nexthop %s ifindex %u (%s)", (add) ? "add" : "delete", log_addr(kr.af, &kr.prefix), diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 5580ea5d676d..4824f1720b3b 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -409,6 +409,7 @@ struct l2vpn_pw { uint32_t pwid; char ifname[IF_NAMESIZE]; unsigned int ifindex; + bool enabled; uint32_t remote_group; uint16_t remote_mtu; uint32_t local_status; diff --git a/ldpd/subdir.am b/ldpd/subdir.am index db71cee618bd..2d87be0cda80 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -38,6 +38,7 @@ ldpd_libldp_a_SOURCES = \ ldpd/util.c \ # end +ldpd/ldp_vty_cmds_clippy.c: $(CLIPPY_DEPS) ldpd/ldp_vty_cmds.$(OBJEXT): ldpd/ldp_vty_cmds_clippy.c noinst_HEADERS += \ diff --git a/lib/command.c b/lib/command.c index 83c91c4c6078..1d89a5c1d580 100644 --- a/lib/command.c +++ b/lib/command.c @@ -65,6 +65,7 @@ const char *node_names[] = { "logical-router", // NS_NODE, "vrf", // VRF_NODE, "interface", // INTERFACE_NODE, + "nexthop-group", // NH_GROUP_NODE, "zebra", // ZEBRA_NODE, "table", // TABLE_NODE, "rip", // RIP_NODE, @@ -1294,6 +1295,7 @@ void cmd_exit(struct vty *vty) case PW_NODE: case NS_NODE: case VRF_NODE: + case NH_GROUP_NODE: case ZEBRA_NODE: case BGP_NODE: case RIP_NODE: @@ -1380,6 +1382,7 @@ DEFUN (config_end, case PW_NODE: case NS_NODE: case VRF_NODE: + case NH_GROUP_NODE: case ZEBRA_NODE: case RIP_NODE: case RIPNG_NODE: diff --git a/lib/command.h b/lib/command.h index 42dd1c5325d9..a11a4465e8a9 100644 --- a/lib/command.h +++ b/lib/command.h @@ -88,6 +88,7 @@ enum node_type { NS_NODE, /* Logical-Router node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ + NH_GROUP_NODE, /* Nexthop-Group mode node. */ ZEBRA_NODE, /* zebra connection node. */ TABLE_NODE, /* rtm_table selection node. */ RIP_NODE, /* RIP protocol mode node. */ @@ -190,7 +191,7 @@ struct cmd_node { #define CMD_NOT_MY_INSTANCE 14 /* Argc max counts. */ -#define CMD_ARGC_MAX 25 +#define CMD_ARGC_MAX 256 /* Turn off these macros when uisng cpp with extract.pl */ #ifndef VTYSH_EXTRACT_PL diff --git a/lib/command_match.c b/lib/command_match.c index c60373f910ab..f6b07a0b2089 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -28,8 +28,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack") -#define MAXDEPTH 256 - #ifdef TRACE_MATCHER #define TM 1 #else @@ -84,7 +82,7 @@ static enum match_type match_mac(const char *, bool); enum matcher_rv command_match(struct graph *cmdgraph, vector vline, struct list **argv, const struct cmd_element **el) { - struct graph_node *stack[MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; enum matcher_rv status; *argv = NULL; @@ -200,7 +198,7 @@ static enum matcher_rv command_match_r(struct graph_node *start, vector vline, /* check history/stack of tokens * this disallows matching the same one more than once if there is a * circle in the graph (used for keyword arguments) */ - if (n == MAXDEPTH) + if (n == CMD_ARGC_MAX) return MATCHER_NO_MATCH; if (!token->allowrepeat) for (size_t s = 0; s < n; s++) diff --git a/lib/csv.c b/lib/csv.c index 27d0fe402997..e6a5eae2e2bb 100644 --- a/lib/csv.c +++ b/lib/csv.c @@ -424,7 +424,7 @@ void csv_clone_record(csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec) void csv_remove_record(csv_t *csv, csv_record_t *rec) { - csv_field_t *fld, *p_fld; + csv_field_t *fld = NULL, *p_fld; /* first check if rec belongs to this csv */ if (!csv_is_record_valid(csv, rec)) { diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 8b23640fa40e..de522e5ef951 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -19,12 +19,14 @@ #include #include +#include #include "frr_pthread.h" #include "memory.h" #include "hash.h" DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread"); +DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives"); static unsigned int next_id = 0; @@ -182,3 +184,8 @@ unsigned int frr_pthread_get_id() { return next_id++; } + +void frr_pthread_yield(void) +{ + (void)sched_yield(); +} diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 9dee5fcca4a3..7915b43a46d8 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -21,8 +21,11 @@ #define _FRR_PTHREAD_H #include +#include "memory.h" #include "thread.h" +DECLARE_MTYPE(PTHREAD_PRIM); + struct frr_pthread { /* pthread id */ @@ -130,6 +133,9 @@ int frr_pthread_stop(unsigned int id, void **result); /* Stops all frr_pthread's. */ void frr_pthread_stop_all(void); +/* Yields the current thread of execution */ +void frr_pthread_yield(void); + /* Returns a unique identifier for use with frr_pthread_new(). * * Internally, this is an integer that increments after each call to this diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index 861f7a5f0c2e..d4df5130e7e3 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -47,46 +47,43 @@ void frrzmq_finish(void) } } -/* read callback integration */ -struct frrzmq_cb { - struct thread *thread; - void *zmqsock; - void *arg; - int fd; - - bool cancelled; - - void (*cb_msg)(void *arg, void *zmqsock); - void (*cb_part)(void *arg, void *zmqsock, - zmq_msg_t *msg, unsigned partnum); -}; - - static int frrzmq_read_msg(struct thread *t) { - struct frrzmq_cb *cb = THREAD_ARG(t); + struct frrzmq_cb **cbp = THREAD_ARG(t); + struct frrzmq_cb *cb; zmq_msg_t msg; unsigned partno; + unsigned char read = 0; int ret, more; size_t moresz; + if (!cbp) + return 1; + cb = (*cbp); + if (!cb || !cb->zmqsock) + return 1; + while (1) { - zmq_pollitem_t polli = { - .socket = cb->zmqsock, - .events = ZMQ_POLLIN - }; + zmq_pollitem_t polli = {.socket = cb->zmqsock, + .events = ZMQ_POLLIN}; ret = zmq_poll(&polli, 1, 0); if (ret < 0) goto out_err; + if (!(polli.revents & ZMQ_POLLIN)) break; - if (cb->cb_msg) { - cb->cb_msg(cb->arg, cb->zmqsock); + if (cb->read.cb_msg) { + cb->read.cb_msg(cb->read.arg, cb->zmqsock); + read = 1; - if (cb->cancelled) { - XFREE(MTYPE_ZEROMQ_CB, cb); + if (cb->read.cancelled) { + frrzmq_check_events(cbp, &cb->write, + ZMQ_POLLOUT); + cb->read.thread = NULL; + if (cb->write.cancelled && !cb->write.thread) + XFREE(MTYPE_ZEROMQ_CB, cb); return 0; } continue; @@ -104,11 +101,17 @@ static int frrzmq_read_msg(struct thread *t) zmq_msg_close(&msg); goto out_err; } + read = 1; - cb->cb_part(cb->arg, cb->zmqsock, &msg, partno); - if (cb->cancelled) { + cb->read.cb_part(cb->read.arg, cb->zmqsock, &msg, + partno); + if (cb->read.cancelled) { zmq_msg_close(&msg); - XFREE(MTYPE_ZEROMQ_CB, cb); + frrzmq_check_events(cbp, &cb->write, + ZMQ_POLLOUT); + cb->read.thread = NULL; + if (cb->write.cancelled && !cb->write.thread) + XFREE(MTYPE_ZEROMQ_CB, cb); return 0; } @@ -116,8 +119,8 @@ static int frrzmq_read_msg(struct thread *t) * message; don't use zmq_msg_more here */ moresz = sizeof(more); more = 0; - ret = zmq_getsockopt(cb->zmqsock, ZMQ_RCVMORE, - &more, &moresz); + ret = zmq_getsockopt(cb->zmqsock, ZMQ_RCVMORE, &more, + &moresz); if (ret < 0) { zmq_msg_close(&msg); goto out_err; @@ -128,64 +131,221 @@ static int frrzmq_read_msg(struct thread *t) zmq_msg_close(&msg); } - funcname_thread_add_read_write(THREAD_READ, t->master, frrzmq_read_msg, - cb, cb->fd, &cb->thread, t->funcname, t->schedfrom, - t->schedfrom_line); + if (read) + frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT); + + funcname_thread_add_read_write( + THREAD_READ, t->master, frrzmq_read_msg, cbp, cb->fd, + &cb->read.thread, t->funcname, t->schedfrom, t->schedfrom_line); return 0; out_err: - zlog_err("ZeroMQ error: %s(%d)", strerror (errno), errno); - return 0; + zlog_err("ZeroMQ read error: %s(%d)", strerror(errno), errno); + if (cb->read.cb_error) + cb->read.cb_error(cb->read.arg, cb->zmqsock); + return 1; } -struct frrzmq_cb *funcname_frrzmq_thread_add_read( - struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*partfunc)(void *arg, void *zmqsock, - zmq_msg_t *msg, unsigned partnum), - void *arg, void *zmqsock, debugargdef) +int funcname_frrzmq_thread_add_read(struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, + zmq_msg_t *msg, + unsigned partnum), + void (*errfunc)(void *arg, void *zmqsock), + void *arg, void *zmqsock, + struct frrzmq_cb **cbp, debugargdef) { int fd, events; size_t len; struct frrzmq_cb *cb; + if (!cbp) + return -1; if (!(msgfunc || partfunc) || (msgfunc && partfunc)) - return NULL; + return -1; + len = sizeof(fd); + if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len)) + return -1; + len = sizeof(events); + if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len)) + return -1; + + if (*cbp) + cb = *cbp; + else { + cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb)); + cb->write.cancelled = 1; + if (!cb) + return -1; + *cbp = cb; + } + + cb->zmqsock = zmqsock; + cb->fd = fd; + cb->read.arg = arg; + cb->read.cb_msg = msgfunc; + cb->read.cb_part = partfunc; + cb->read.cb_error = errfunc; + cb->read.cancelled = 0; + + if (events & ZMQ_POLLIN) { + if (cb->read.thread) { + thread_cancel(cb->read.thread); + cb->read.thread = NULL; + } + funcname_thread_add_event(master, frrzmq_read_msg, cbp, fd, + &cb->read.thread, funcname, schedfrom, + fromln); + } else + funcname_thread_add_read_write( + THREAD_READ, master, frrzmq_read_msg, cbp, fd, + &cb->read.thread, funcname, schedfrom, fromln); + return 0; +} + +static int frrzmq_write_msg(struct thread *t) +{ + struct frrzmq_cb **cbp = THREAD_ARG(t); + struct frrzmq_cb *cb; + unsigned char written = 0; + int ret; + + if (!cbp) + return 1; + cb = (*cbp); + if (!cb || !cb->zmqsock) + return 1; + + while (1) { + zmq_pollitem_t polli = {.socket = cb->zmqsock, + .events = ZMQ_POLLOUT}; + ret = zmq_poll(&polli, 1, 0); + + if (ret < 0) + goto out_err; + + if (!(polli.revents & ZMQ_POLLOUT)) + break; + + if (cb->write.cb_msg) { + cb->write.cb_msg(cb->write.arg, cb->zmqsock); + written = 1; + + if (cb->write.cancelled) { + frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN); + cb->write.thread = NULL; + if (cb->read.cancelled && !cb->read.thread) + XFREE(MTYPE_ZEROMQ_CB, cb); + return 0; + } + continue; + } + } + + if (written) + frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN); + + funcname_thread_add_read_write(THREAD_WRITE, t->master, + frrzmq_write_msg, cbp, cb->fd, + &cb->write.thread, t->funcname, + t->schedfrom, t->schedfrom_line); + return 0; + +out_err: + zlog_err("ZeroMQ write error: %s(%d)", strerror(errno), errno); + if (cb->write.cb_error) + cb->write.cb_error(cb->write.arg, cb->zmqsock); + return 1; +} +int funcname_frrzmq_thread_add_write(struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*errfunc)(void *arg, void *zmqsock), + void *arg, void *zmqsock, + struct frrzmq_cb **cbp, debugargdef) +{ + int fd, events; + size_t len; + struct frrzmq_cb *cb; + + if (!cbp) + return -1; + if (!msgfunc) + return -1; len = sizeof(fd); if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len)) - return NULL; + return -1; len = sizeof(events); if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len)) - return NULL; + return -1; - cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb)); - if (!cb) - return NULL; + if (*cbp) + cb = *cbp; + else { + cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb)); + cb->read.cancelled = 1; + if (!cb) + return -1; + *cbp = cb; + } - cb->arg = arg; cb->zmqsock = zmqsock; - cb->cb_msg = msgfunc; - cb->cb_part = partfunc; cb->fd = fd; + cb->write.arg = arg; + cb->write.cb_msg = msgfunc; + cb->write.cb_part = NULL; + cb->write.cb_error = errfunc; + cb->write.cancelled = 0; + + if (events & ZMQ_POLLOUT) { + if (cb->write.thread) { + thread_cancel(cb->write.thread); + cb->write.thread = NULL; + } + funcname_thread_add_event(master, frrzmq_write_msg, cbp, fd, + &cb->write.thread, funcname, + schedfrom, fromln); + } else + funcname_thread_add_read_write( + THREAD_WRITE, master, frrzmq_write_msg, cbp, fd, + &cb->write.thread, funcname, schedfrom, fromln); + return 0; +} - if (events & ZMQ_POLLIN) - funcname_thread_add_event(master, - frrzmq_read_msg, cb, fd, &cb->thread, - funcname, schedfrom, fromln); - else - funcname_thread_add_read_write(THREAD_READ, master, - frrzmq_read_msg, cb, fd, &cb->thread, - funcname, schedfrom, fromln); - return cb; +void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core) +{ + if (!cb || !*cb) + return; + core->cancelled = 1; + if (core->thread) { + thread_cancel(core->thread); + core->thread = NULL; + } + if ((*cb)->read.cancelled && !(*cb)->read.thread + && (*cb)->write.cancelled && (*cb)->write.thread) + XFREE(MTYPE_ZEROMQ_CB, *cb); } -void frrzmq_thread_cancel(struct frrzmq_cb *cb) +void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core, + int event) { - if (!cb->thread) { - /* canceling from within callback */ - cb->cancelled = 1; + struct frrzmq_cb *cb; + int events; + size_t len; + + if (!cbp) + return; + cb = (*cbp); + if (!cb || !cb->zmqsock) + return; + + if (zmq_getsockopt(cb->zmqsock, ZMQ_EVENTS, &events, &len)) return; + if (events & event && core->thread && !core->cancelled) { + struct thread_master *tm = core->thread->master; + thread_cancel(core->thread); + core->thread = NULL; + thread_add_event(tm, (event == ZMQ_POLLIN ? frrzmq_read_msg + : frrzmq_write_msg), + cbp, cb->fd, &core->thread); } - thread_cancel(cb->thread); - XFREE(MTYPE_ZEROMQ_CB, cb); } diff --git a/lib/frr_zmq.h b/lib/frr_zmq.h index 69c6f8580dbb..1146b879640c 100644 --- a/lib/frr_zmq.h +++ b/lib/frr_zmq.h @@ -33,6 +33,26 @@ * foo_LDFLAGS = libfrrzmq.la libfrr.la $(ZEROMQ_LIBS) */ +/* callback integration */ +struct cb_core { + struct thread *thread; + void *arg; + + bool cancelled; + + void (*cb_msg)(void *arg, void *zmqsock); + void (*cb_part)(void *arg, void *zmqsock, zmq_msg_t *msg, + unsigned partnum); + void (*cb_error)(void *arg, void *zmqsock); +}; +struct frrzmq_cb { + void *zmqsock; + int fd; + + struct cb_core read; + struct cb_core write; +}; + /* libzmq's context * * this is mostly here as a convenience, it has IPv6 enabled but nothing @@ -40,21 +60,27 @@ */ extern void *frrzmq_context; -extern void frrzmq_init (void); -extern void frrzmq_finish (void); +extern void frrzmq_init(void); +extern void frrzmq_finish(void); #define debugargdef const char *funcname, const char *schedfrom, int fromln /* core event registration, one of these 2 macros should be used */ -#define frrzmq_thread_add_read_msg(m,f,a,z) funcname_frrzmq_thread_add_read( \ - m,f,NULL,a,z,#f,__FILE__,__LINE__) -#define frrzmq_thread_add_read_part(m,f,a,z) funcname_frrzmq_thread_add_read( \ - m,NULL,f,a,z,#f,__FILE__,__LINE__) +#define frrzmq_thread_add_read_msg(m, f, e, a, z, d) \ + funcname_frrzmq_thread_add_read(m, f, NULL, e, a, z, d, #f, __FILE__, \ + __LINE__) +#define frrzmq_thread_add_read_part(m, f, e, a, z, d) \ + funcname_frrzmq_thread_add_read(m, NULL, f, e, a, z, d, #f, __FILE__, \ + __LINE__) +#define frrzmq_thread_add_write_msg(m, f, e, a, z, d) \ + funcname_frrzmq_thread_add_write(m, f, e, a, z, d, #f, __FILE__, \ + __LINE__) +struct cb_core; struct frrzmq_cb; -/* Set up a POLLIN notification to be called from the libfrr main loop. - * This has the following properties: +/* Set up a POLLIN or POLLOUT notification to be called from the libfrr main + * loop. This has the following properties: * * - since ZeroMQ works with edge triggered notifications, it will loop and * dispatch as many events as ZeroMQ has pending at the time libfrr calls @@ -67,22 +93,35 @@ struct frrzmq_cb; * - if partfunc is specified, the message is read and partfunc is called * for each ZeroMQ multi-part subpart. Note that you can't send replies * before all parts have been read because that violates the ZeroMQ FSM. + * - write version doesn't allow for partial callback, you must handle the + * whole message (all parts) in msgfunc callback * - you can safely cancel the callback from within itself * - installing a callback will check for pending events (ZMQ_EVENTS) and * may schedule the event to run as soon as libfrr is back in its main * loop. + */ +extern int funcname_frrzmq_thread_add_read( + struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, zmq_msg_t *msg, + unsigned partnum), + void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock, + struct frrzmq_cb **cb, debugargdef); +extern int funcname_frrzmq_thread_add_write( + struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock), + void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock, + struct frrzmq_cb **cb, debugargdef); + +extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core); + +/* + * http://api.zeromq.org/4-2:zmq-getsockopt#toc10 * - * TODO #1: add ZMQ_POLLERR / error callback - * TODO #2: add frrzmq_check_events() function to check for edge triggered - * things that may have happened after a zmq_send() call or so + * As the descriptor is edge triggered, applications must update the state of + * ZMQ_EVENTS after each invocation of zmq_send or zmq_recv.To be more explicit: + * after calling zmq_send the socket may become readable (and vice versa) + * without triggering a read event on the file descriptor. */ -extern struct frrzmq_cb *funcname_frrzmq_thread_add_read( - struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*partfunc)(void *arg, void *zmqsock, - zmq_msg_t *msg, unsigned partnum), - void *arg, void *zmqsock, debugargdef); - -extern void frrzmq_thread_cancel(struct frrzmq_cb *cb); +extern void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core, + int event); #endif /* _FRRZMQ_H */ diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 66b042ad974c..79c951dd6916 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -33,8 +33,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") -#define MAXDEPTH 64 - /** headers **/ void grammar_sandbox_init(void); void pretty_print_graph(struct vty *vty, struct graph_node *, int, int, @@ -262,7 +260,7 @@ DEFUN (grammar_test_show, { check_nodegraph(); - struct graph_node *stack[MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3, stack, 0); return CMD_SUCCESS; @@ -277,8 +275,8 @@ DEFUN (grammar_test_dot, { check_nodegraph(); - struct graph_node *stack[MAXDEPTH]; - struct graph_node *visited[MAXDEPTH * MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; + struct graph_node *visited[CMD_ARGC_MAX * CMD_ARGC_MAX]; size_t vpos = 0; FILE *ofd = fopen(argv[2]->arg, "w"); @@ -334,7 +332,7 @@ static void cmd_graph_permute(struct list *out, struct graph_node **stack, return; } - if (++stackpos == MAXDEPTH) + if (++stackpos == CMD_ARGC_MAX) return; for (i = 0; i < vector_active(gn->to); i++) { @@ -354,7 +352,7 @@ static void cmd_graph_permute(struct list *out, struct graph_node **stack, static struct list *cmd_graph_permutations(struct graph *graph) { char accumulate[2048] = ""; - struct graph_node *stack[MAXDEPTH]; + struct graph_node *stack[CMD_ARGC_MAX]; struct list *rv = list_new(); rv->cmp = cmd_permute_cmp; @@ -532,7 +530,7 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level, vty_out(vty, " ?'%s'", tok->desc); vty_out(vty, " "); - if (stackpos == MAXDEPTH) { + if (stackpos == CMD_ARGC_MAX) { vty_out(vty, " -aborting! (depth limit)\n"); return; } @@ -586,7 +584,7 @@ static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start, if (visited[i] == start) return; visited[(*visitpos)++] = start; - if ((*visitpos) == MAXDEPTH * MAXDEPTH) + if ((*visitpos) == CMD_ARGC_MAX * CMD_ARGC_MAX) return; snprintf(tokennum, sizeof(tokennum), "%d?", tok->type); @@ -626,7 +624,7 @@ static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start, } fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color); - if (stackpos == MAXDEPTH) + if (stackpos == CMD_ARGC_MAX) return; stack[stackpos++] = start; diff --git a/lib/if.c b/lib/if.c index 0fe7da1c0d53..fdcd563a5ded 100644 --- a/lib/if.c +++ b/lib/if.c @@ -219,6 +219,18 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) struct vrf *vrf; struct interface if_tmp; + if (vrf_id == VRF_UNKNOWN) { + struct interface *ifp; + + RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) { + ifp = if_lookup_by_index(ifindex, vrf->vrf_id); + if (ifp) + return ifp; + } + + return NULL; + } + vrf = vrf_lookup_by_id(vrf_id); if (!vrf) return NULL; @@ -663,8 +675,9 @@ DEFUN_NOSH (no_interface, "Interface's name\n" VRF_CMD_HELP_STR) { + int idx_vrf = 4; const char *ifname = argv[2]->arg; - const char *vrfname = (argc > 3) ? argv[3]->arg : NULL; + const char *vrfname = (argc > 3) ? argv[idx_vrf]->arg : NULL; // deleting interface struct interface *ifp; diff --git a/lib/if.h b/lib/if.h index eb8af2041b21..79f96a7c452e 100644 --- a/lib/if.h +++ b/lib/if.h @@ -452,6 +452,13 @@ struct nbr_connected { /* Prototypes. */ extern int if_cmp_name_func(char *, char *); +/* + * Passing in VRF_UNKNOWN is a valid thing to do, unless we + * are creating a new interface. + * + * This is useful for vrf route-leaking. So more than anything + * else think before you use VRF_UNKNOWN + */ extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id); extern struct interface *if_create(const char *name, vrf_id_t vrf_id); extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); diff --git a/lib/log.c b/lib/log.c index 7589934b6942..bf65ac7c7da4 100644 --- a/lib/log.c +++ b/lib/log.c @@ -945,6 +945,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW), DESC_ENTRY(ZEBRA_VNI_ADD), DESC_ENTRY(ZEBRA_VNI_DEL), + DESC_ENTRY(ZEBRA_L3VNI_ADD), + DESC_ENTRY(ZEBRA_L3VNI_DEL), DESC_ENTRY(ZEBRA_REMOTE_VTEP_ADD), DESC_ENTRY(ZEBRA_REMOTE_VTEP_DEL), DESC_ENTRY(ZEBRA_MACIP_ADD), diff --git a/lib/md5.c b/lib/md5.c index cce67d7ec1d2..8989a93c597b 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -1,7 +1,3 @@ -/* $USAGI: md5.c,v 1.2 2000/11/02 11:59:24 yoshfuji Exp $ */ -/* $KAME: md5.c,v 1.2 2000/05/27 07:07:48 jinmei Exp $ */ -/* $Id: md5.c,v 1.6 2006/01/17 23:39:04 vincent Exp $ */ - /* * Copyright (C) 2004 6WIND * diff --git a/lib/md5.h b/lib/md5.h index adc92d670da8..59291c7df3bd 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -1,7 +1,3 @@ -/* $USAGI: md5.h,v 1.2 2000/11/02 11:59:25 yoshfuji Exp $ */ -/* $KAME: md5.h,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */ -/* $Id: md5.h,v 1.3 2006/01/17 17:40:45 paul Exp $ */ - /* * Copyright (C) 2004 6WIND * diff --git a/lib/mpls.h b/lib/mpls.h index bf98eecd8161..6ef00375e8ac 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -81,6 +81,12 @@ typedef unsigned int mpls_lse_t; /* MPLS label value as a 32-bit (mostly we only care about the label value). */ typedef unsigned int mpls_label_t; +struct mpls_label_stack { + uint8_t num_labels; + uint8_t reserved[3]; + mpls_label_t label[0]; /* 1 or more labels */ +}; + /* The MPLS explicit-null label is 0 which means when you memset a mpls_label_t * to zero you have set that variable to explicit-null which was probably not * your intent. The work-around is to use one bit to indicate if the diff --git a/lib/nexthop.c b/lib/nexthop.c index f6b2c9788d50..02da70d61bea 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -124,7 +124,7 @@ const char *nexthop_type_to_str(enum nexthop_types_t nh_type) */ int nexthop_labels_match(struct nexthop *nh1, struct nexthop *nh2) { - struct nexthop_label *nhl1, *nhl2; + struct mpls_label_stack *nhl1, *nhl2; nhl1 = nh1->nh_label; nhl2 = nh2->nh_label; @@ -145,47 +145,6 @@ struct nexthop *nexthop_new(void) return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); } -/* Add nexthop to the end of a nexthop list. */ -void nexthop_add(struct nexthop **target, struct nexthop *nexthop) -{ - struct nexthop *last; - - for (last = *target; last && last->next; last = last->next) - ; - if (last) - last->next = nexthop; - else - *target = nexthop; - nexthop->prev = last; -} - -void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, - struct nexthop *rparent) -{ - struct nexthop *nexthop; - struct nexthop *nh1; - - for (nh1 = nh; nh1; nh1 = nh1->next) { - nexthop = nexthop_new(); - nexthop->ifindex = nh1->ifindex; - nexthop->type = nh1->type; - nexthop->flags = nh1->flags; - memcpy(&nexthop->gate, &nh1->gate, sizeof(nh1->gate)); - memcpy(&nexthop->src, &nh1->src, sizeof(nh1->src)); - memcpy(&nexthop->rmap_src, &nh1->rmap_src, sizeof(nh1->rmap_src)); - nexthop->rparent = rparent; - if (nh1->nh_label) - nexthop_add_labels(nexthop, nh1->nh_label_type, - nh1->nh_label->num_labels, - &nh1->nh_label->label[0]); - nexthop_add(tnh, nexthop); - - if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) - copy_nexthops(&nexthop->resolved, nh1->resolved, - nexthop); - } -} - /* Free nexthop. */ void nexthop_free(struct nexthop *nexthop) { @@ -210,12 +169,12 @@ void nexthops_free(struct nexthop *nexthop) void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, u_int8_t num_labels, mpls_label_t *label) { - struct nexthop_label *nh_label; + struct mpls_label_stack *nh_label; int i; nexthop->nh_label_type = type; nh_label = XCALLOC(MTYPE_NH_LABEL, - sizeof(struct nexthop_label) + sizeof(struct mpls_label_stack) + num_labels * sizeof(mpls_label_t)); nh_label->num_labels = num_labels; for (i = 0; i < num_labels; i++) diff --git a/lib/nexthop.h b/lib/nexthop.h index 20b0cd522773..5c4ec0cb8bfa 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -55,13 +55,6 @@ enum blackhole_type { ((type) == NEXTHOP_TYPE_IFINDEX || (type) == NEXTHOP_TYPE_BLACKHOLE) \ ? (type) : ((type) | 1) -/* Nexthop label structure. */ -struct nexthop_label { - u_int8_t num_labels; - u_int8_t reserved[3]; - mpls_label_t label[0]; /* 1 or more labels. */ -}; - /* Nexthop structure. */ struct nexthop { struct nexthop *next; @@ -80,6 +73,7 @@ struct nexthop { #define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */ #define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */ #define NEXTHOP_FLAG_DUPLICATE (1 << 6) /* nexthop duplicates another active one */ +#define NEXTHOP_FLAG_EVPN_RVTEP (1 << 7) /* EVPN remote vtep nexthop */ #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) @@ -106,39 +100,11 @@ struct nexthop { enum lsp_types_t nh_label_type; /* Label(s) associated with this nexthop. */ - struct nexthop_label *nh_label; + struct mpls_label_stack *nh_label; }; -/* The following for loop allows to iterate over the nexthop - * structure of routes. - * - * head: The pointer to the first nexthop in the chain. - * - * nexthop: The pointer to the current nexthop, either in the - * top-level chain or in a resolved chain. - */ -#define ALL_NEXTHOPS(head, nexthop) \ - (nexthop) = (head); \ - (nexthop); \ - (nexthop) = nexthop_next(nexthop) - -extern int zebra_rnh_ip_default_route; -extern int zebra_rnh_ipv6_default_route; - -static inline int nh_resolve_via_default(int family) -{ - if (((family == AF_INET) && zebra_rnh_ip_default_route) - || ((family == AF_INET6) && zebra_rnh_ipv6_default_route)) - return 1; - else - return 0; -} - struct nexthop *nexthop_new(void); -void nexthop_add(struct nexthop **target, struct nexthop *nexthop); -void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, - struct nexthop *rparent); void nexthop_free(struct nexthop *nexthop); void nexthops_free(struct nexthop *nexthop); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c new file mode 100644 index 000000000000..3050763a7c9e --- /dev/null +++ b/lib/nexthop_group.c @@ -0,0 +1,176 @@ +/* + * Nexthop Group structure definition. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include +#include + +static __inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2); +RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) + +struct nhgc_entry_head nhgc_entries; + +static __inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2) +{ + return strcmp(nhgc1->name, nhgc2->name); +} + +/* Add nexthop to the end of a nexthop list. */ +void nexthop_add(struct nexthop **target, struct nexthop *nexthop) +{ + struct nexthop *last; + + for (last = *target; last && last->next; last = last->next) + ; + if (last) + last->next = nexthop; + else + *target = nexthop; + nexthop->prev = last; +} + +void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, + struct nexthop *rparent) +{ + struct nexthop *nexthop; + struct nexthop *nh1; + + for (nh1 = nh; nh1; nh1 = nh1->next) { + nexthop = nexthop_new(); + nexthop->ifindex = nh1->ifindex; + nexthop->type = nh1->type; + nexthop->flags = nh1->flags; + memcpy(&nexthop->gate, &nh1->gate, sizeof(nh1->gate)); + memcpy(&nexthop->src, &nh1->src, sizeof(nh1->src)); + memcpy(&nexthop->rmap_src, &nh1->rmap_src, sizeof(nh1->rmap_src)); + nexthop->rparent = rparent; + if (nh1->nh_label) + nexthop_add_labels(nexthop, nh1->nh_label_type, + nh1->nh_label->num_labels, + &nh1->nh_label->label[0]); + nexthop_add(tnh, nexthop); + + if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) + copy_nexthops(&nexthop->resolved, nh1->resolved, + nexthop); + } +} + +static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) +{ + /* + * Future Work Here + */ + return; +} + +static struct nexthop_group_cmd *nhgc_find(const char *name) +{ + struct nexthop_group_cmd find; + + strlcpy(find.name, name, sizeof(find.name)); + + return RB_FIND(nhgc_entry_head, &nhgc_entries, &find); +} + +static struct nexthop_group_cmd *nhgc_get(const char *name) +{ + struct nexthop_group_cmd *nhgc; + + nhgc = nhgc_find(name); + if (!nhgc) { + nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc)); + strcpy(nhgc->name, name); + + RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc); + } + + return nhgc; +} + +static void nhgc_delete(struct nexthop_group_cmd *nhgc) +{ + nhgc_delete_nexthops(nhgc); + RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc); +} + +DEFINE_QOBJ_TYPE(nexthop_group_cmd) + +DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", + "Enter into the nexthop-group submode\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[1]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_get(nhg_name); + VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", + NO_STR + "Enter into the nexthop-group submode\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[2]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_find(nhg_name); + if (nhgc) + nhgc_delete(nhgc); + + return CMD_SUCCESS; +} + +struct cmd_node nexthop_group_node = { + NH_GROUP_NODE, + "%s(config-nh-group)# ", + 1 +}; + +static int nexthop_group_write(struct vty *vty) +{ + struct nexthop_group_cmd *nhgc; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + vty_out(vty, "nexthop-group %s\n", nhgc->name); + vty_out(vty, "!\n"); + } + + return 1; +} + +void nexthop_group_init(void) +{ + RB_INIT(nhgc_entry_head, &nhgc_entries); + install_node(&nexthop_group_node, nexthop_group_write); + install_element(CONFIG_NODE, &nexthop_group_cmd); + install_element(CONFIG_NODE, &no_nexthop_group_cmd); +} diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h new file mode 100644 index 000000000000..3cdcc0a051b9 --- /dev/null +++ b/lib/nexthop_group.h @@ -0,0 +1,71 @@ +/* + * Nexthop Group structure definition. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __NEXTHOP_GROUP__ +#define __NEXTHOP_GROUP__ + +/* + * What is a nexthop group? + * + * A nexthop group is a collection of nexthops that make up + * the ECMP path for the route. + * + * This module provides a proper abstraction to this idea. + */ +struct nexthop_group { + struct nexthop *nexthop; +}; + +void nexthop_group_init(void); + +void nexthop_add(struct nexthop **target, struct nexthop *nexthop); +void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, + struct nexthop *rparent); + +/* The following for loop allows to iterate over the nexthop + * structure of routes. + * + * head: The pointer to the first nexthop in the chain. + * + * nexthop: The pointer to the current nexthop, either in the + * top-level chain or in a resolved chain. + */ +#define ALL_NEXTHOPS(head, nhop) \ + (nhop) = (head.nexthop); \ + (nhop); \ + (nhop) = nexthop_next(nhop) + +struct nexthop_group_cmd { + + RB_ENTRY(nexthop_group_cmd) nhgc_entry; + + char name[80]; + + struct nexthop_group nhg; + + QOBJ_FIELDS; +}; +RB_HEAD(nhgc_entry_head, nexthp_group_cmd); +RB_PROTOTYPE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) +DECLARE_QOBJ_TYPE(nexthop_group_cmd); + +#endif diff --git a/lib/ns.h b/lib/ns.h index c492d6600bed..79b4cab04d03 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -25,10 +25,11 @@ #include "openbsd-tree.h" #include "linklist.h" -typedef u_int16_t ns_id_t; +typedef u_int32_t ns_id_t; -/* The default NS ID */ +/* the default NS ID */ #define NS_DEFAULT 0 +#define NS_UNKNOWN UINT32_MAX /* Default netns directory (Linux) */ #define NS_RUN_DIR "/var/run/netns" diff --git a/lib/plist.c b/lib/plist.c index 3c491d6a3b43..a95749cf0ed7 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1321,7 +1321,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, } #ifndef VTYSH_EXTRACT_PL -#include "plist_clippy.c" +#include "lib/plist_clippy.c" #endif DEFPY (ip_prefix_list, diff --git a/lib/prefix.c b/lib/prefix.c index 10f77bda875a..9f13cb8bb13b 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -301,7 +301,7 @@ static const struct in6_addr maskbytes6[] = { #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) -static int is_zero_mac(const struct ethaddr *mac) +int is_zero_mac(struct ethaddr *mac) { int i = 0; @@ -1043,10 +1043,11 @@ static const char *prefixevpn2str(const struct prefix *p, char *str, int size) family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p) ? AF_INET : AF_INET6; - snprintf(str, size, "[%d]:[%u][%s]/%d", + snprintf(str, size, "[%d]:[%u][%s/%d]/%d", p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag, inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf, PREFIX2STR_BUFFER), + p->u.prefix_evpn.ip_prefix_length, p->prefixlen); } else { sprintf(str, "Unsupported EVPN route type %d", diff --git a/lib/prefix.h b/lib/prefix.h index 0732cf12905a..7e947ea48abc 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -348,6 +348,7 @@ extern void masklen2ip6(const int, struct in6_addr *); extern const char *inet6_ntoa(struct in6_addr); +extern int is_zero_mac(struct ethaddr *mac); extern int prefix_str2mac(const char *str, struct ethaddr *mac); extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); @@ -398,4 +399,13 @@ static inline int is_default_prefix(const struct prefix *p) return 0; } +static inline int is_host_route(struct prefix *p) +{ + if (p->family == AF_INET) + return (p->prefixlen == IPV4_MAX_BITLEN); + else if (p->family == AF_INET6) + return (p->prefixlen == IPV6_MAX_BITLEN); + return 0; +} + #endif /* _ZEBRA_PREFIX_H */ diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index e881d49225e4..28d26149e5d3 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -223,6 +223,25 @@ int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt, return 0; } +int ptm_lib_cleanup_msg(ptm_lib_handle_t *hdl, void *ctxt) +{ + ptm_lib_msg_ctxt_t *p_ctxt = ctxt; + csv_t *csv; + + if (!p_ctxt) { + ERRLOG("%s: no context \n", __FUNCTION__); + return -1; + } + + csv = p_ctxt->csv; + + csv_clean(csv); + csv_free(csv); + free(p_ctxt); + + return 0; +} + int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len) { ptm_lib_msg_ctxt_t *p_ctxt = ctxt; diff --git a/lib/ptm_lib.h b/lib/ptm_lib.h index bc8fe4ac54d3..fc4d520dcb41 100644 --- a/lib/ptm_lib.h +++ b/lib/ptm_lib.h @@ -63,5 +63,6 @@ int ptm_lib_find_key_in_msg(void *, const char *, char *); int ptm_lib_init_msg(ptm_lib_handle_t *, int, int, void *, void **); int ptm_lib_append_msg(ptm_lib_handle_t *, void *, const char *, const char *); int ptm_lib_complete_msg(ptm_lib_handle_t *, void *, char *, int *); +int ptm_lib_cleanup_msg(ptm_lib_handle_t *, void *); #endif diff --git a/lib/ringbuf.c b/lib/ringbuf.c new file mode 100644 index 000000000000..11db502a9487 --- /dev/null +++ b/lib/ringbuf.c @@ -0,0 +1,133 @@ +/* + * Circular buffer implementation. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "ringbuf.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, RINGBUFFER, "Ring buffer") + +struct ringbuf *ringbuf_new(size_t size) +{ + struct ringbuf *buf = XCALLOC(MTYPE_RINGBUFFER, sizeof(struct ringbuf)); + buf->data = XCALLOC(MTYPE_RINGBUFFER, size); + buf->size = size; + buf->empty = true; + return buf; +} + +void ringbuf_del(struct ringbuf *buf) +{ + XFREE(MTYPE_RINGBUFFER, buf->data); + XFREE(MTYPE_RINGBUFFER, buf); +} + +size_t ringbuf_remain(struct ringbuf *buf) +{ + ssize_t diff = buf->end - buf->start; + diff += ((diff == 0) && !buf->empty) ? buf->size : 0; + diff += (diff < 0) ? buf->size : 0; + return (size_t)diff; +} + +size_t ringbuf_space(struct ringbuf *buf) +{ + return buf->size - ringbuf_remain(buf); +} + +size_t ringbuf_put(struct ringbuf *buf, const void *data, size_t size) +{ + const uint8_t *dp = data; + size_t space = ringbuf_space(buf); + size_t copysize = MIN(size, space); + size_t tocopy = copysize; + if (tocopy >= buf->size - buf->end) { + size_t ts = buf->size - buf->end; + memcpy(buf->data + buf->end, dp, ts); + buf->end = 0; + tocopy -= ts; + dp += ts; + } + memcpy(buf->data + buf->end, dp, tocopy); + buf->end += tocopy; + buf->empty = (buf->start == buf->end) && (buf->empty && !copysize); + return copysize; +} + +size_t ringbuf_get(struct ringbuf *buf, void *data, size_t size) +{ + uint8_t *dp = data; + size_t remain = ringbuf_remain(buf); + size_t copysize = MIN(remain, size); + size_t tocopy = copysize; + if (tocopy >= buf->size - buf->start) { + size_t ts = buf->size - buf->start; + memcpy(dp, buf->data + buf->start, ts); + buf->start = 0; + tocopy -= ts; + dp += ts; + } + memcpy(dp, buf->data + buf->start, tocopy); + buf->start = buf->start + tocopy; + buf->empty = (buf->start == buf->end) && (buf->empty || copysize); + return copysize; +} + +size_t ringbuf_peek(struct ringbuf *buf, size_t offset, void *data, size_t size) +{ + uint8_t *dp = data; + size_t remain = ringbuf_remain(buf); + if (offset >= remain) + return 0; + size_t copysize = MAX(MIN(remain - offset, size), (size_t) 0); + size_t tocopy = copysize; + size_t cstart = (buf->start + offset) % buf->size; + if (tocopy >= buf->size - cstart) { + size_t ts = buf->size - cstart; + memcpy(dp, buf->data + cstart, ts); + cstart = 0; + tocopy -= ts; + dp += ts; + } + memcpy(dp, buf->data + cstart, tocopy); + return copysize; +} + +size_t ringbuf_copy(struct ringbuf *to, struct ringbuf *from, size_t size) +{ + size_t tocopy = MIN(ringbuf_space(to), size); + uint8_t *cbuf = XCALLOC(MTYPE_TMP, tocopy); + tocopy = ringbuf_peek(from, 0, cbuf, tocopy); + size_t put = ringbuf_put(to, cbuf, tocopy); + XFREE(MTYPE_TMP, cbuf); + return put; +} + +void ringbuf_reset(struct ringbuf *buf) +{ + buf->start = buf->end = 0; + buf->empty = true; +} + +void ringbuf_wipe(struct ringbuf *buf) +{ + memset(buf->data, 0x00, buf->size); + ringbuf_reset(buf); +} diff --git a/lib/ringbuf.h b/lib/ringbuf.h new file mode 100644 index 000000000000..15049e3eeab7 --- /dev/null +++ b/lib/ringbuf.h @@ -0,0 +1,125 @@ +/* + * Circular buffer implementation. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _FRR_RINGBUF_H_ +#define _FRR_RINGBUF_H_ + +#include +#include + +#include "memory.h" + +struct ringbuf { + size_t size; + ssize_t start; + ssize_t end; + bool empty; + uint8_t *data; +}; + +/* + * Creates a new ring buffer. + * + * @param size buffer size, in bytes + * @return the newly created buffer + */ +struct ringbuf *ringbuf_new(size_t size); + +/* + * Deletes a ring buffer and frees all associated resources. + * + * @param buf the ring buffer to destroy + */ +void ringbuf_del(struct ringbuf *buf); + +/* + * Get amount of data left to read from the buffer. + * + * @return number of readable bytes + */ +size_t ringbuf_remain(struct ringbuf *buf); + +/* + * Get amount of space left to write to the buffer + * + * @return number of writeable bytes + */ +size_t ringbuf_space(struct ringbuf *buf); + + +/* + * Put data into the ring buffer. + * + * @param data the data to put in the buffer + * @param size how much of data to put in + * @return number of bytes written; will be less than size if there was not + * enough space + */ +size_t ringbuf_put(struct ringbuf *buf, const void *data, size_t size); + +/* + * Get data from the ring buffer. + * + * @param data where to put the data + * @param size how much of data to get + * @return number of bytes read into data; will be less than size if there was + * not enough data to read + */ +size_t ringbuf_get(struct ringbuf *buf, void *data, size_t size); + +/* + * Peek data from the ring buffer. + * + * @param offset where to get the data from, in bytes offset from the + * start of the data + * @param data where to put the data + * @param size how much data to get + * @return number of bytes read into data; will be less than size + * if there was not enough data to read; will be -1 if the + * offset exceeds the amount of data left in the ring + * buffer + */ +size_t ringbuf_peek(struct ringbuf *buf, size_t offset, void *data, + size_t size); + +/* + * Copy data from one ringbuf to another. + * + * @param to destination ringbuf + * @param from source ringbuf + * @param size how much data to copy + * @return amount of data copied + */ +size_t ringbuf_copy(struct ringbuf *to, struct ringbuf *from, size_t size); + +/* + * Reset buffer. Does not wipe. + * + * @param buf + */ +void ringbuf_reset(struct ringbuf *buf); + +/* + * Reset buffer. Wipes. + * + * @param buf + */ +void ringbuf_wipe(struct ringbuf *buf); + +#endif /* _FRR_RINGBUF_H_ */ diff --git a/lib/route_types.txt b/lib/route_types.txt index 4e764a14c139..886c672800a9 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -78,6 +78,7 @@ ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct" ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP" +ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, "PBR" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-" @@ -103,3 +104,4 @@ ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)" ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" +ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)" diff --git a/lib/stream.h b/lib/stream.h index 1048180fac03..4d387f9564a4 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -123,10 +123,15 @@ struct stream_fifo { #define STREAM_CONCAT_REMAIN(S1, S2, size) ((size) - (S1)->endp - (S2)->endp) /* deprecated macros - do not use in new code */ +#if CONFDATE > 20181128 +CPP_NOTICE("lib: time to remove deprecated stream.h macros") +#endif #define STREAM_PNT(S) stream_pnt((S)) -#define STREAM_DATA(S) ((S)->data) #define STREAM_REMAIN(S) STREAM_WRITEABLE((S)) +/* this macro is deprecated, but not slated for removal anytime soon */ +#define STREAM_DATA(S) ((S)->data) + /* Stream prototypes. * For stream_{put,get}S, the S suffix mean: * diff --git a/lib/subdir.am b/lib/subdir.am index c8eddc8e25b5..e999ea4fe917 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,6 +42,7 @@ lib_libfrr_la_SOURCES = \ lib/module.c \ lib/network.c \ lib/nexthop.c \ + lib/nexthop_group.c \ lib/ns.c \ lib/openbsd-tree.c \ lib/pid_output.c \ @@ -51,6 +52,7 @@ lib_libfrr_la_SOURCES = \ lib/privs.c \ lib/ptm_lib.c \ lib/qobj.c \ + lib/ringbuf.c \ lib/routemap.c \ lib/sbuf.c \ lib/sha256.c \ @@ -77,6 +79,8 @@ lib_libfrr_la_SOURCES = \ lib/plist_clippy.c: $(CLIPPY_DEPS) lib/plist.lo: lib/plist_clippy.c +lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) +lib/nexthop_group.lo: lib/nexthop_group_clippy.c pkginclude_HEADERS += \ lib/bfd.h \ @@ -119,6 +123,7 @@ pkginclude_HEADERS += \ lib/mpls.h \ lib/network.h \ lib/nexthop.h \ + lib/nexthop_group.h \ lib/ns.h \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ @@ -130,6 +135,7 @@ pkginclude_HEADERS += \ lib/pw.h \ lib/qobj.h \ lib/queue.h \ + lib/ringbuf.h \ lib/routemap.h \ lib/sbuf.h \ lib/sha256.h \ diff --git a/lib/thread.c b/lib/thread.c index d26db8855029..9d64663d9c0d 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -919,6 +919,8 @@ struct thread *funcname_thread_add_event(struct thread_master *m, */ static void thread_cancel_rw(struct thread_master *master, int fd, short state) { + bool found = false; + /* Cancel POLLHUP too just in case some bozo set it */ state |= POLLHUP; @@ -926,8 +928,18 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) nfds_t i; for (i = 0; i < master->handler.pfdcount; i++) - if (master->handler.pfds[i].fd == fd) + if (master->handler.pfds[i].fd == fd) { + found = true; break; + } + + if (!found) { + zlog_debug( + "[!] Received cancellation request for nonexistent rw job"); + zlog_debug("[!] threadmaster: %s | fd: %d", + master->name ? master->name : "", fd); + return; + } /* NOT out event. */ master->handler.pfds[i].events &= ~(state); diff --git a/lib/vrf.c b/lib/vrf.c index 056f778a3e41..2fa3a9c0ef51 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -94,7 +94,7 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) int new = 0; if (debug_vrf) - zlog_debug("VRF_GET: %s(%d)", name, vrf_id); + zlog_debug("VRF_GET: %s(%u)", name, vrf_id); /* Nothing to see, move along here */ if (!name && vrf_id == VRF_UNKNOWN) @@ -225,6 +225,17 @@ static void vrf_disable(struct vrf *vrf) (*vrf_master.vrf_disable_hook)(vrf); } +const char *vrf_id_to_name(vrf_id_t vrf_id) +{ + struct vrf *vrf; + + vrf = vrf_lookup_by_id(vrf_id); + if (vrf) + return vrf->name; + + return "n/a"; +} + vrf_id_t vrf_name_to_id(const char *name) { struct vrf *vrf; @@ -256,8 +267,8 @@ void *vrf_info_lookup(vrf_id_t vrf_id) * VRF bit-map */ -#define VRF_BITMAP_NUM_OF_GROUPS 8 -#define VRF_BITMAP_NUM_OF_BITS_IN_GROUP (UINT16_MAX / VRF_BITMAP_NUM_OF_GROUPS) +#define VRF_BITMAP_NUM_OF_GROUPS 1024 +#define VRF_BITMAP_NUM_OF_BITS_IN_GROUP (UINT32_MAX / VRF_BITMAP_NUM_OF_GROUPS) #define VRF_BITMAP_NUM_OF_BYTES_IN_GROUP \ (VRF_BITMAP_NUM_OF_BITS_IN_GROUP / CHAR_BIT + 1) /* +1 for ensure */ @@ -344,7 +355,7 @@ static void vrf_autocomplete(vector comps, struct cmd_token *token) struct vrf *vrf = NULL; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (vrf->vrf_id != 0) + if (vrf->vrf_id != VRF_DEFAULT) vector_set(comps, XSTRDUP(MTYPE_COMPLETION, vrf->name)); } } diff --git a/lib/vrf.h b/lib/vrf.h index e93e99377694..7e625769e7f3 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -32,8 +32,7 @@ /* The default VRF ID */ #define VRF_DEFAULT 0 -#define VRF_UNKNOWN UINT16_MAX -#define VRF_ALL UINT16_MAX - 1 +#define VRF_UNKNOWN UINT32_MAX /* Pending: May need to refine this. */ #ifndef IFLA_VRF_MAX @@ -103,6 +102,7 @@ extern struct vrf_name_head vrfs_by_name; extern struct vrf *vrf_lookup_by_id(vrf_id_t); extern struct vrf *vrf_lookup_by_name(const char *); extern struct vrf *vrf_get(vrf_id_t, const char *); +extern const char *vrf_id_to_name(vrf_id_t vrf_id); extern vrf_id_t vrf_name_to_id(const char *); #define VRF_GET_ID(V, NAME) \ diff --git a/lib/zassert.h b/lib/zassert.h index 5c06d933e6fb..d45e1be5f839 100644 --- a/lib/zassert.h +++ b/lib/zassert.h @@ -1,6 +1,4 @@ /* - * $Id: zassert.h,v 1.2 2004/12/03 18:01:04 ajs Exp $ - * * This file is part of Quagga. * * Quagga is free software; you can redistribute it and/or modify it diff --git a/lib/zclient.c b/lib/zclient.c index 655e4e1a800e..a4bd2bda32b1 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -291,7 +291,7 @@ void zclient_create_header(struct stream *s, uint16_t command, vrf_id_t vrf_id) stream_putw(s, ZEBRA_HEADER_SIZE); stream_putc(s, ZEBRA_HEADER_MARKER); stream_putc(s, ZSERV_VERSION); - stream_putw(s, vrf_id); + stream_putl(s, vrf_id); stream_putw(s, command); } @@ -306,7 +306,7 @@ int zclient_read_header(struct stream *s, int sock, u_int16_t *size, *size -= ZEBRA_HEADER_SIZE; STREAM_GETC(s, *marker); STREAM_GETC(s, *version); - STREAM_GETW(s, *vrf_id); + STREAM_GETL(s, *vrf_id); STREAM_GETW(s, *cmd); if (*version != ZSERV_VERSION || *marker != ZEBRA_HEADER_MARKER) { @@ -912,6 +912,8 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api) stream_putl(s, api->flags); stream_putc(s, api->message); stream_putc(s, api->safi); + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_put(s, &(api->rmac), sizeof(struct ethaddr)); /* Put prefix information. */ stream_putc(s, api->prefix.family); @@ -940,6 +942,8 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api) } stream_putw(s, api->nexthop_num); + if (api->nexthop_num) + stream_putw(s, api->nh_vrf_id); for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; @@ -1032,6 +1036,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETL(s, api->flags); STREAM_GETC(s, api->message); STREAM_GETC(s, api->safi); + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_get(&(api->rmac), s, sizeof(struct ethaddr)); /* Prefix. */ STREAM_GETC(s, api->prefix.family); @@ -1087,6 +1093,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) return -1; } + if (api->nexthop_num) + STREAM_GETW(s, api->nh_vrf_id); + for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; @@ -1673,7 +1682,7 @@ struct interface *zebra_interface_vrf_update_read(struct stream *s, { unsigned int ifindex; struct interface *ifp; - vrf_id_t new_id = VRF_DEFAULT; + vrf_id_t new_id; /* Get interface index. */ ifindex = stream_getl(s); @@ -2039,7 +2048,7 @@ static int zclient_read(struct thread *thread) length = stream_getw(zclient->ibuf); marker = stream_getc(zclient->ibuf); version = stream_getc(zclient->ibuf); - vrf_id = stream_getw(zclient->ibuf); + vrf_id = stream_getl(zclient->ibuf); command = stream_getw(zclient->ibuf); if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) { @@ -2205,6 +2214,16 @@ static int zclient_read(struct thread *thread) (*zclient->local_vni_del)(command, zclient, length, vrf_id); break; + case ZEBRA_L3VNI_ADD: + if (zclient->local_l3vni_add) + (*zclient->local_l3vni_add)(command, zclient, length, + vrf_id); + break; + case ZEBRA_L3VNI_DEL: + if (zclient->local_l3vni_del) + (*zclient->local_l3vni_del)(command, zclient, length, + vrf_id); + break; case ZEBRA_MACIP_ADD: if (zclient->local_macip_add) (*zclient->local_macip_add)(command, zclient, length, @@ -2332,9 +2351,9 @@ void zclient_interface_set_master(struct zclient *client, zclient_create_header(s, ZEBRA_INTERFACE_SET_MASTER, master->vrf_id); - stream_putw(s, master->vrf_id); + stream_putl(s, master->vrf_id); stream_putl(s, master->ifindex); - stream_putw(s, slave->vrf_id); + stream_putl(s, slave->vrf_id); stream_putl(s, slave->ifindex); stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/lib/zclient.h b/lib/zclient.h index de580446712b..c24f2b2a4e4d 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -40,7 +40,7 @@ #define ZEBRA_MAX_PACKET_SIZ 4096 /* Zebra header size. */ -#define ZEBRA_HEADER_SIZE 8 +#define ZEBRA_HEADER_SIZE 10 /* special socket path name to use TCP * @ is used as first character because that's abstract socket names on Linux @@ -112,6 +112,8 @@ typedef enum { ZEBRA_ADVERTISE_ALL_VNI, ZEBRA_VNI_ADD, ZEBRA_VNI_DEL, + ZEBRA_L3VNI_ADD, + ZEBRA_L3VNI_DEL, ZEBRA_REMOTE_VTEP_ADD, ZEBRA_REMOTE_VTEP_DEL, ZEBRA_MACIP_ADD, @@ -200,6 +202,8 @@ struct zclient { int (*fec_update)(int, struct zclient *, uint16_t); int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t); + int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t); + int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t); int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); @@ -223,7 +227,7 @@ struct zserv_header { * always set to 255 in new zserv. */ uint8_t version; -#define ZSERV_VERSION 4 +#define ZSERV_VERSION 5 vrf_id_t vrf_id; uint16_t command; }; @@ -277,6 +281,9 @@ struct zapi_route { u_int32_t mtu; vrf_id_t vrf_id; + vrf_id_t nh_vrf_id; + + struct ethaddr rmac; }; /* Zebra IPv4 route message API. */ @@ -414,6 +421,11 @@ extern struct interface *zebra_interface_vrf_update_read(struct stream *s, vrf_id_t *new_vrf_id); extern void zebra_interface_if_set_value(struct stream *, struct interface *); extern void zebra_router_id_update_read(struct stream *s, struct prefix *rid); + +#if CONFDATE > 20180823 +CPP_NOTICE("zapi_ipv4_route, zapi_ipv6_route, zapi_ipv4_route_ipv6_nexthop as well as the zapi_ipv4 and zapi_ipv6 data structures should be removed now"); +#endif + extern int zapi_ipv4_route(u_char, struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *) __attribute__((deprecated)); diff --git a/lib/zebra.h b/lib/zebra.h index fa5fa89f7761..b9a795d16099 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -409,6 +409,7 @@ extern const char *zserv_command_string(unsigned int command); #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_SCOPE_LINK 0x100 #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 +#define ZEBRA_FLAG_EVPN_ROUTE 0x400 /* ZEBRA_FLAG_BLACKHOLE was 0x04 */ /* ZEBRA_FLAG_REJECT was 0x80 */ @@ -485,7 +486,7 @@ typedef u_int16_t zebra_size_t; typedef u_int16_t zebra_command_t; /* VRF ID type. */ -typedef u_int16_t vrf_id_t; +typedef uint32_t vrf_id_t; typedef uint32_t route_tag_t; #define ROUTE_TAG_MAX UINT32_MAX diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 2612d8e045be..2f084f8422f3 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -95,6 +95,8 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix memset(&api, 0, sizeof(api)); api.type = ZEBRA_ROUTE_NHRP; api.safi = SAFI_UNICAST; + api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.prefix = *p; switch (type) { diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index d270b9547edc..8847611492b0 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -901,6 +901,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) ospf6_route_copy_nexthops(route, abr_entry); + /* (7) If the routes are identical, copy the next hops over to existing route. ospf6's route table implementation will otherwise string both routes, but keep the older one as the best route since the routes @@ -910,6 +911,12 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) if (old && (ospf6_route_cmp(route, old) == 0)) { ospf6_route_merge_nexthops(old, route); + + if (is_debug) + zlog_debug("%s: Update route: %s nh count %u", + __PRETTY_FUNCTION__, + buf, listcount(route->nh_list)); + /* Update RIB/FIB */ if (table->hook_add) (*table->hook_add)(old); @@ -918,7 +925,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) ospf6_route_delete(route); } else { if (is_debug) - zlog_debug("Install route: %s", buf); + zlog_debug("Install route: %s nh count %u", + buf, listcount(route->nh_list)); /* ospf6_ia_add_nw_route (table, &prefix, route); */ ospf6_route_add(route, table); } diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index bd5e2bd1d357..252e4a454549 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -117,7 +117,9 @@ static void ospf6_area_lsdb_hook_remove(struct ospf6_lsa *lsa) static void ospf6_area_route_hook_add(struct ospf6_route *route) { - struct ospf6_route *copy = ospf6_route_copy(route); + struct ospf6_route *copy; + + copy = ospf6_route_copy(route); ospf6_route_add(copy, ospf6->route_table); } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index c65578c11e34..745b87b890ed 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -173,11 +173,136 @@ static route_tag_t ospf6_as_external_lsa_get_tag(struct ospf6_lsa *lsa) return ntohl(network_order); } +void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, + struct ospf6_route *route) +{ + struct ospf6_route *old_route; + struct ospf6_path *ecmp_path, *o_path = NULL; + struct listnode *anode; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + char buf[PREFIX2STR_BUFFER]; + bool route_found = false; + + for (old_route = old; old_route; old_route = old_route->next) { + if (ospf6_route_is_same(old_route, route) && + (old_route->path.type == route->path.type) && + (old_route->path.cost == route->path.cost) && + (old_route->path.u.cost_e2 == route->path.u.cost_e2)) { + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&old_route->prefix, buf, + sizeof(buf)); + zlog_debug("%s: old route %s path cost %u [%u]", + __PRETTY_FUNCTION__, buf, + old_route->path.cost, + ospf6_route_is_same(old_route, + route)); + } + route_found = true; + /* check if this path exists already in + * route->paths list, if so, replace nh_list + * from asbr_entry. + */ + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + if ((o_path->origin.id == route->path.origin.id) + && (o_path->origin.adv_router == + route->path.origin.adv_router)) + break; + } + /* If path is not found in old_route paths's list, + * add a new path to route paths list and merge + * nexthops in route->path->nh_list. + * Otherwise replace existing path's nh_list. + */ + if (o_path == NULL) { + ecmp_path = ospf6_path_dup(&route->path); + + /* Add a nh_list to new ecmp path */ + ospf6_copy_nexthops(ecmp_path->nh_list, + route->nh_list); + /* Merge nexthop to existing route's nh_list */ + ospf6_route_merge_nexthops(old_route, route); + + /* Update RIB/FIB */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add) + (old_route); + + /* Add the new path to route's path list */ + listnode_add_sort(old_route->paths, ecmp_path); + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&route->prefix, buf, + sizeof(buf)); + zlog_debug("%s: route %s another path added with nh %u, Paths %u", + __PRETTY_FUNCTION__, buf, + listcount(ecmp_path->nh_list), + old_route->paths ? + listcount(old_route->paths) + : 0); + } + } else { + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, + nnode, nh)) { + for (ALL_LIST_ELEMENTS( + old_route->nh_list, + rnode, rnext, rnh)) { + if (!ospf6_nexthop_is_same(rnh, + nh)) + continue; + + listnode_delete( + old_route->nh_list, + rnh); + ospf6_nexthop_delete(rnh); + } + } + list_delete_all_node(o_path->nh_list); + ospf6_copy_nexthops(o_path->nh_list, + route->nh_list); + + /* Merge nexthop to existing route's nh_list */ + ospf6_route_merge_nexthops(old_route, + route); + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&route->prefix, + buf, sizeof(buf)); + zlog_debug("%s: existing route %s with effective nh count %u", + __PRETTY_FUNCTION__, buf, + old_route->nh_list ? + listcount(old_route->nh_list) + : 0); + } + + /* Update RIB/FIB */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add) + (old_route); + + } + /* Delete the new route its info added to existing + * route. + */ + ospf6_route_delete(route); + break; + } + } + + if (!route_found) { + /* Add new route to existing node in ospf6 route table. */ + ospf6_route_add(route, ospf6->route_table); + } +} + void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) { struct ospf6_as_external_lsa *external; struct prefix asbr_id; - struct ospf6_route *asbr_entry, *route; + struct ospf6_route *asbr_entry, *route, *old; + struct ospf6_path *path; char buf[PREFIX2STR_BUFFER]; external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( @@ -245,12 +370,34 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) ospf6_route_copy_nexthops(route, asbr_entry); + path = ospf6_path_dup(&route->path); + ospf6_copy_nexthops(path->nh_list, asbr_entry->nh_list); + listnode_add_sort(route->paths, path); + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("AS-External route add: %s", buf); + zlog_debug("%s: AS-External %u route add %s cost %u(%u) nh %u", + __PRETTY_FUNCTION__, + (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) + ? 1 : 2, buf, route->path.cost, + route->path.u.cost_e2, + listcount(route->nh_list)); + } + + old = ospf6_route_lookup(&route->prefix, ospf6->route_table); + if (!old) { + /* Add the new route to ospf6 instance route table. */ + ospf6_route_add(route, ospf6->route_table); + } else { + /* RFC 2328 16.4 (6) + * ECMP: Keep new equal preference path in current + * route's path list, update zebra with new effective + * list along with addition of ECMP path. + */ + ospf6_asbr_update_route_ecmp_path(old, route); } - ospf6_route_add(route, ospf6->route_table); } void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa) @@ -291,16 +438,126 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa) nroute = ospf6_route_next(route); if (route->type != OSPF6_DEST_TYPE_NETWORK) continue; - if (route->path.origin.type != lsa->header->type) - continue; - if (route->path.origin.id != lsa->header->id) - continue; - if (route->path.origin.adv_router != lsa->header->adv_router) + + /* Route has multiple ECMP paths remove, + * matching path and update effective route's nh list. + */ + if (listcount(route->paths) > 1) { + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + struct ospf6_path *o_path; + bool nh_updated = false; + + /* Iterate all paths of route to find maching with LSA + * remove from route path list. If route->path is same, + * replace from paths list. + */ + for (ALL_LIST_ELEMENTS(route->paths, anode, anext, + o_path)) { + if (o_path->origin.type != lsa->header->type) + continue; + if (o_path->origin.id != lsa->header->id) + continue; + if (o_path->origin.adv_router != + lsa->header->adv_router) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&prefix, buf, sizeof(buf)); + zlog_debug( + "%s: route %s path found with nh %u", + __PRETTY_FUNCTION__, buf, + listcount(o_path->nh_list)); + } + + /* Remove found path's nh_list from + * the route's nh_list. + */ + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, + nnode, nh)) { + for (ALL_LIST_ELEMENTS(route->nh_list, + rnode, rnext, rnh)) { + if (!ospf6_nexthop_is_same(rnh, + nh)) + continue; + listnode_delete(route->nh_list, + rnh); + ospf6_nexthop_delete(rnh); + } + } + /* Delete the path from route's path list */ + listnode_delete(route->paths, o_path); + ospf6_path_free(o_path); + nh_updated = true; + } + + if (nh_updated) { + /* Iterate all paths and merge nexthop, + * unlesss any of the nexthop similar to + * ones deleted as part of path deletion. + */ + + for (ALL_LIST_ELEMENTS(route->paths, anode, + anext, o_path)) { + ospf6_merge_nexthops(route->nh_list, + o_path->nh_list); + } + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&route->prefix, buf, + sizeof(buf)); + zlog_debug("%s: AS-External %u route %s update paths %u nh %u" + , __PRETTY_FUNCTION__, + (route->path.type == + OSPF6_PATH_TYPE_EXTERNAL1) + ? 1 : 2, buf, + listcount(route->paths), + listcount(route->nh_list)); + } + + /* Update RIB/FIB w/ effective nh_list */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add)(route); + + /* route's path is similar to lsa header, + * replace route's path with route's + * paths list head. + */ + if (route->path.origin.id == lsa->header->id && + route->path.origin.adv_router == + lsa->header->adv_router) { + struct ospf6_path *h_path; + + h_path = (struct ospf6_path *) + listgetdata(listhead(route->paths)); + route->path.origin.type = + h_path->origin.type; + route->path.origin.id = + h_path->origin.id; + route->path.origin.adv_router = + h_path->origin.adv_router; + } + } continue; + } else { + if (route->path.origin.type != lsa->header->type) + continue; + if (route->path.origin.id != lsa->header->id) + continue; + if (route->path.origin.adv_router != + lsa->header->adv_router) + continue; + } if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("AS-External route remove: %s", buf); + zlog_debug("%s: AS-External %u route remove %s cost %u(%u) nh %u", + __PRETTY_FUNCTION__, + route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 + ? 1 : 2, buf, route->path.cost, + route->path.u.cost_e2, + listcount(route->nh_list)); } ospf6_route_remove(route, ospf6->route_table); } @@ -369,10 +626,21 @@ static void ospf6_asbr_routemap_update(const char *mapname) return; for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { - if (ospf6->rmap[type].name) + if (ospf6->rmap[type].name) { ospf6->rmap[type].map = route_map_lookup_by_name( ospf6->rmap[type].name); - else + + if (mapname && ospf6->rmap[type].map && + (strcmp(ospf6->rmap[type].name, mapname) == 0)) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("%s: route-map %s update, reset redist %s", + __PRETTY_FUNCTION__, mapname, + ZROUTE_NAME(type)); + + ospf6_zebra_no_redistribute(type); + ospf6_zebra_redistribute(type); + } + } else ospf6->rmap[type].map = NULL; } } diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 73053452e6a2..7f4665ac2b50 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -93,5 +93,7 @@ extern void ospf6_asbr_send_externals_to_area(struct ospf6_area *); extern int config_write_ospf6_debug_asbr(struct vty *vty); extern void install_element_ospf6_debug_asbr(void); +extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, + struct ospf6_route *route); #endif /* OSPF6_ASBR_H */ diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 98f93b06e674..fc6c46c7e75d 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1008,6 +1008,103 @@ DEFUN (show_ipv6_ospf6_interface, return CMD_SUCCESS; } +static int ospf6_interface_show_traffic(struct vty *vty, + uint32_t vrf_id, + struct interface *intf_ifp, + int display_once) +{ + struct interface *ifp; + struct vrf *vrf = NULL; + struct ospf6_interface *oi = NULL; + + vrf = vrf_lookup_by_id(vrf_id); + + if (!display_once) { + vty_out(vty, "\n"); + vty_out(vty, "%-12s%-17s%-17s%-17s%-17s%-17s\n", + "Interface", " HELLO", " DB-Desc", " LS-Req", + " LS-Update", " LS-Ack"); + vty_out(vty, "%-10s%-18s%-18s%-17s%-17s%-17s\n", "", + " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx"); + vty_out(vty, + "--------------------------------------------------------------------------------------------\n"); + } + + if (intf_ifp == NULL) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) + oi = (struct ospf6_interface *)ifp->info; + else + continue; + + vty_out(vty, + "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u\n", + oi->interface->name, oi->hello_in, + oi->hello_out, + oi->db_desc_in, oi->db_desc_out, + oi->ls_req_in, oi->ls_req_out, + oi->ls_upd_in, oi->ls_upd_out, + oi->ls_ack_in, oi->ls_ack_out); + } + } else { + oi = intf_ifp->info; + if (oi == NULL) + return CMD_WARNING; + + vty_out(vty, + "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u\n", + oi->interface->name, oi->hello_in, + oi->hello_out, + oi->db_desc_in, oi->db_desc_out, + oi->ls_req_in, oi->ls_req_out, + oi->ls_upd_in, oi->ls_upd_out, + oi->ls_ack_in, oi->ls_ack_out); + } + + return CMD_SUCCESS; +} + +/* show interface */ +DEFUN (show_ipv6_ospf6_interface_traffic, + show_ipv6_ospf6_interface_traffic_cmd, + "show ipv6 ospf6 interface traffic [IFNAME]", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + "Protocol Packet counters\n" + IFNAME_STR) +{ + int idx_ifname = 0; + int display_once = 0; + char *intf_name = NULL; + struct interface *ifp = NULL; + + if (argv_find(argv, argc, "IFNAME", &idx_ifname)) { + intf_name = argv[idx_ifname]->arg; + ifp = if_lookup_by_name(intf_name, VRF_DEFAULT); + if (ifp == NULL) { + vty_out(vty, + "No such Interface: %s\n", + intf_name); + return CMD_WARNING; + } + if (ifp->info == NULL) { + vty_out(vty, + " OSPF not enabled on this interface %s\n", + intf_name); + return 0; + } + } + + ospf6_interface_show_traffic(vty, VRF_DEFAULT, ifp, + display_once); + + + return CMD_SUCCESS; +} + + DEFUN (show_ipv6_ospf6_interface_ifname_prefix, show_ipv6_ospf6_interface_ifname_prefix_cmd, "show ipv6 ospf6 interface IFNAME prefix [] []", @@ -1841,6 +1938,8 @@ void ospf6_interface_init(void) install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_interface_traffic_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_cost_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 3844132366e6..b67d9a9f2e6f 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -117,6 +117,19 @@ struct ospf6_interface { /* BFD information */ void *bfd_info; + /* Statistics Fields */ + u_int32_t hello_in; + u_int32_t hello_out; + u_int32_t db_desc_in; + u_int32_t db_desc_out; + u_int32_t ls_req_in; + u_int32_t ls_req_out; + u_int32_t ls_upd_in; + u_int32_t ls_upd_out; + u_int32_t ls_ack_in; + u_int32_t ls_ack_out; + u_int32_t discarded; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(ospf6_interface) diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index b1d940952ca0..77653ea33fa8 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1404,7 +1404,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug(" route %s add", buf); + zlog_debug(" route %s add with nh count %u", buf, + listcount(route->nh_list)); } ospf6_route_add(route, oa->route_table); diff --git a/ospf6d/ospf6_memory.c b/ospf6d/ospf6_memory.c index 56c232d6da98..1c3523b43d32 100644 --- a/ospf6d/ospf6_memory.c +++ b/ospf6d/ospf6_memory.c @@ -41,4 +41,5 @@ DEFINE_MTYPE(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex") DEFINE_MTYPE(OSPF6D, OSPF6_SPFTREE, "OSPF6 SPF tree") DEFINE_MTYPE(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop") DEFINE_MTYPE(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info") +DEFINE_MTYPE(OSPF6D, OSPF6_PATH, "OSPF6 Path") DEFINE_MTYPE(OSPF6D, OSPF6_OTHER, "OSPF6 other") diff --git a/ospf6d/ospf6_memory.h b/ospf6d/ospf6_memory.h index fe72ee366976..548af5e321e7 100644 --- a/ospf6d/ospf6_memory.h +++ b/ospf6d/ospf6_memory.h @@ -40,6 +40,7 @@ DECLARE_MTYPE(OSPF6_VERTEX) DECLARE_MTYPE(OSPF6_SPFTREE) DECLARE_MTYPE(OSPF6_NEXTHOP) DECLARE_MTYPE(OSPF6_EXTERNAL_INFO) +DECLARE_MTYPE(OSPF6_PATH) DECLARE_MTYPE(OSPF6_OTHER) #endif /* _QUAGGA_OSPF6_MEMORY_H */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 1307b374a511..d76438ea5033 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -321,6 +321,8 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, backupseen++; } + oi->hello_in++; + /* Execute neighbor events */ thread_execute(master, hello_received, on, 0); if (twoway) @@ -776,6 +778,8 @@ static void ospf6_dbdesc_recv(struct in6_addr *src, struct in6_addr *dst, dbdesc->reserved2 = 0; } + oi->db_desc_in++; + if (ntohl(oh->router_id) < ntohl(ospf6->router_id)) ospf6_dbdesc_recv_master(oh, on); else if (ntohl(ospf6->router_id) < ntohl(oh->router_id)) @@ -811,6 +815,8 @@ static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst, return; } + oi->ls_req_in++; + /* Process each request */ for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header)); p + sizeof(struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END(oh); @@ -1370,6 +1376,8 @@ static void ospf6_lsupdate_recv(struct in6_addr *src, struct in6_addr *dst, lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh + sizeof(struct ospf6_header)); + oi->ls_upd_in++; + /* Process LSAs */ for (p = (char *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); p < OSPF6_MESSAGE_END(oh) @@ -1407,6 +1415,8 @@ static void ospf6_lsack_recv(struct in6_addr *src, struct in6_addr *dst, return; } + oi->ls_ack_in++; + for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header)); p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh); p += sizeof(struct ospf6_lsa_header)) { @@ -1777,6 +1787,8 @@ int ospf6_hello_send(struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_HELLO; oh->length = htons(p - sendbuf); + oi->hello_out++; + ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh); return 0; } @@ -1852,6 +1864,8 @@ int ospf6_dbdesc_send(struct thread *thread) else dst = &on->linklocal_addr; + on->ospf6_if->db_desc_out++; + ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh); return 0; @@ -1955,6 +1969,8 @@ int ospf6_lsreq_send(struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_LSREQ; oh->length = htons(p - sendbuf); + on->ospf6_if->ls_req_out++; + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6, on->ospf6_if, oh); @@ -1979,6 +1995,8 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on, { if (on) { + on->ospf6_if->ls_upd_out++; + if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) || (on->ospf6_if->state == OSPF6_INTERFACE_DR) || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) { @@ -1989,6 +2007,9 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on, &on->linklocal_addr, on->ospf6_if, oh); } } else if (oi) { + + oi->ls_upd_out++; + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || (oi->state == OSPF6_INTERFACE_DR) || (oi->state == OSPF6_INTERFACE_BDR)) { @@ -2185,8 +2206,11 @@ int ospf6_lsupdate_send_interface(struct thread *thread) lsupdate->lsa_number = htonl(lsa_cnt); ospf6_send_lsupdate(NULL, oi, oh); - zlog_debug("%s: LSUpdate length %d", - __PRETTY_FUNCTION__, ntohs(oh->length)); + if (IS_OSPF6_DEBUG_MESSAGE( + OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) + zlog_debug("%s: LSUpdate length %d", + __PRETTY_FUNCTION__, + ntohs(oh->length)); memset(sendbuf, 0, iobuflen); oh = (struct ospf6_header *)sendbuf; @@ -2263,6 +2287,8 @@ int ospf6_lsack_send_neighbor(struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_LSACK; oh->length = htons(p - sendbuf); + on->ospf6_if->ls_ack_out++; + ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr, on->ospf6_if, oh); @@ -2288,6 +2314,8 @@ int ospf6_lsack_send_neighbor(struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_LSACK; oh->length = htons(p - sendbuf); + on->ospf6_if->ls_ack_out++; + ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr, on->ospf6_if, oh); } diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 3c77c483ea07..735b28a69330 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -215,7 +215,7 @@ void ospf6_copy_nexthops(struct list *dst, struct list *src) if (ospf6_nexthop_is_set(nh)) { nh_new = ospf6_nexthop_create(); ospf6_nexthop_copy(nh_new, nh); - listnode_add(dst, nh_new); + listnode_add_sort(dst, nh_new); } } } @@ -231,7 +231,7 @@ void ospf6_merge_nexthops(struct list *dst, struct list *src) if (!ospf6_route_find_nexthop(dst, nh)) { nh_new = ospf6_nexthop_create(); ospf6_nexthop_copy(nh_new, nh); - listnode_add(dst, nh_new); + listnode_add_sort(dst, nh_new); } } } @@ -241,18 +241,25 @@ int ospf6_route_cmp_nexthops(struct ospf6_route *a, struct ospf6_route *b) { struct listnode *anode, *bnode; struct ospf6_nexthop *anh, *bnh; + bool identical = false; if (a && b) { if (listcount(a->nh_list) == listcount(b->nh_list)) { for (ALL_LIST_ELEMENTS_RO(a->nh_list, anode, anh)) { + identical = false; for (ALL_LIST_ELEMENTS_RO(b->nh_list, bnode, - bnh)) - if (!ospf6_nexthop_is_same(anh, bnh)) - return (1); + bnh)) { + if (ospf6_nexthop_is_same(anh, bnh)) + identical = true; + } + /* Currnet List A element not found List B + * Non-Identical lists return */ + if (identical == false) + return 1; } - return (0); + return 0; } else - return (1); + return 1; } /* One of the routes doesn't exist ? */ return (1); @@ -331,14 +338,49 @@ int ospf6_route_get_first_nh_index(struct ospf6_route *route) return (-1); } -static int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b) +int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b) { - if ((a)->ifindex == (b)->ifindex && - IN6_ARE_ADDR_EQUAL(&(a)->address, &(b)->address)) + if (a->ifindex < b->ifindex) + return -1; + else if (a->ifindex > b->ifindex) return 1; + else + return memcmp(&a->address, &b->address, + sizeof(struct in6_addr)); + return 0; } +static int ospf6_path_cmp(struct ospf6_path *a, struct ospf6_path *b) +{ + if (a->origin.adv_router < b->origin.adv_router) + return -1; + else if (a->origin.adv_router > b->origin.adv_router) + return 1; + else + return 0; +} + +void ospf6_path_free(struct ospf6_path *op) +{ + if (op->nh_list) + list_delete_and_null(&op->nh_list); + XFREE(MTYPE_OSPF6_PATH, op); +} + +struct ospf6_path *ospf6_path_dup(struct ospf6_path *path) +{ + struct ospf6_path *new; + + new = XCALLOC(MTYPE_OSPF6_PATH, sizeof(struct ospf6_path)); + memcpy(new, path, sizeof(struct ospf6_path)); + new->nh_list = list_new(); + new->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp; + new->nh_list->del = (void (*) (void *))ospf6_nexthop_delete; + + return new; +} + struct ospf6_route *ospf6_route_create(void) { struct ospf6_route *route; @@ -346,6 +388,9 @@ struct ospf6_route *ospf6_route_create(void) route->nh_list = list_new(); route->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp; route->nh_list->del = (void (*) (void *))ospf6_nexthop_delete; + route->paths = list_new(); + route->paths->cmp = (int (*)(void *, void *))ospf6_path_cmp; + route->paths->del = (void (*)(void *))ospf6_path_free; return route; } @@ -354,6 +399,8 @@ void ospf6_route_delete(struct ospf6_route *route) if (route) { if (route->nh_list) list_delete_and_null(&route->nh_list); + if (route->paths) + list_delete_and_null(&route->paths); XFREE(MTYPE_OSPF6_ROUTE, route); } } @@ -452,7 +499,13 @@ ospf6_route_lookup_identical(struct ospf6_route *route, for (target = ospf6_route_lookup(&route->prefix, table); target; target = target->next) { - if (ospf6_route_is_identical(target, route)) + if (target->type == route->type && + (memcmp(&target->prefix, &route->prefix, + sizeof(struct prefix)) == 0) && + target->path.type == route->path.type && + target->path.cost == route->path.cost && + target->path.u.cost_e2 == route->path.u.cost_e2 && + ospf6_route_cmp_nexthops(target, route) == 0) return target; } return NULL; @@ -1071,6 +1124,7 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route) vty_out(vty, "Metric: %d (%d)\n", route->path.cost, route->path.u.cost_e2); + vty_out(vty, "Paths count: %u\n", route->paths->count); vty_out(vty, "Nexthop count: %u\n", route->nh_list->count); /* Nexthops */ vty_out(vty, "Nexthop:\n"); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 9eacadbdb7de..b759828c39a8 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -96,6 +96,9 @@ struct ospf6_path { u_int32_t cost_config; } u; u_int32_t tag; + + /* nh list for this path */ + struct list *nh_list; }; #define OSPF6_PATH_TYPE_NONE 0 @@ -149,6 +152,9 @@ struct ospf6_route { /* path */ struct ospf6_path path; + /* List of Paths. */ + struct list *paths; + /* nexthop */ struct list *nh_list; }; @@ -256,6 +262,7 @@ extern void ospf6_linkstate_prefix2str(struct prefix *prefix, char *buf, int size); extern struct ospf6_nexthop *ospf6_nexthop_create(void); +extern int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b); extern void ospf6_nexthop_delete(struct ospf6_nexthop *nh); extern void ospf6_clear_nexthops(struct list *nh_list); extern int ospf6_num_nexthops(struct list *nh_list); @@ -331,5 +338,7 @@ extern int config_write_ospf6_debug_route(struct vty *vty); extern void install_element_ospf6_debug_route(void); extern void ospf6_route_init(void); extern void ospf6_clean(void); +extern void ospf6_path_free(struct ospf6_path *op); +extern struct ospf6_path *ospf6_path_dup(struct ospf6_path *path); #endif /* OSPF6_ROUTE_H */ diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index c148107449a6..a44864596005 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -914,7 +914,7 @@ static u_char *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, if (len) id = htonl(*offset); offset += len; - offsetlen -= len; + //offsetlen -= len; // Add back in if we need it again if (exact) { if (v->magic & OSPFv3WWASTABLE) { @@ -1080,8 +1080,8 @@ static u_char *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, len = (offsetlen < 1 ? 0 : 1); if (len) instid = *offset; - offset += len; - offsetlen -= len; + //offset += len; // Add back in if we ever start using again + //offsetlen -= len; if (exact) { oi = ospf6_interface_lookup_by_ifindex(ifindex); @@ -1241,8 +1241,8 @@ static u_char *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, len = (offsetlen < 1 ? 0 : 1); if (len) rtrid = htonl(*offset); - offset += len; - offsetlen -= len; + //offset += len; // Add back in if we ever start looking at data + //offsetlen -= len; if (exact) { oi = ospf6_interface_lookup_by_ifindex(ifindex); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 340d90159ff8..6e6d8a7f0097 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -145,6 +145,7 @@ static struct ospf6_vertex *ospf6_vertex_create(struct ospf6_lsa *lsa) v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END(lsa->header) + 3); v->nh_list = list_new(); + v->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp; v->nh_list->del = (void (*) (void *))ospf6_nexthop_delete; v->parent = NULL; diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index e0844765d3e4..5d1144335be4 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -95,6 +95,13 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route) static void ospf6_top_brouter_hook_add(struct ospf6_route *route) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: brouter %s add with nh count %u", + __PRETTY_FUNCTION__, buf, listcount(route->nh_list)); + } ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix)); ospf6_asbr_lsentry_add(route); ospf6_abr_originate_summary(route); @@ -102,6 +109,13 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route) static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: brouter %s del with nh count %u", + __PRETTY_FUNCTION__, buf, listcount(route->nh_list)); + } route->flag |= OSPF6_ROUTE_REMOVE; ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix)); ospf6_asbr_lsentry_remove(route); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index cc87c499ee50..2a419ddfc616 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -337,6 +337,7 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -387,6 +388,7 @@ void ospf6_zebra_add_discard(struct ospf6_route *request) if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -420,6 +422,7 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request) if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; diff --git a/ospfd/.gitignore b/ospfd/.gitignore index 018a363a93b3..f0d800efb4b9 100644 --- a/ospfd/.gitignore +++ b/ospfd/.gitignore @@ -15,4 +15,4 @@ TAGS *~ *.loT *.a -*.clippy.c +*clippy.c diff --git a/ospfd/OSPF-ALIGNMENT.txt b/ospfd/OSPF-ALIGNMENT.txt index dac6182fdeab..905bd228b169 100644 --- a/ospfd/OSPF-ALIGNMENT.txt +++ b/ospfd/OSPF-ALIGNMENT.txt @@ -1,5 +1,3 @@ -$Id: OSPF-ALIGNMENT.txt,v 1.1 2004/11/17 17:59:52 gdt Exp $ - Greg Troxel 2004-11-17 diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index f7aa94ad188c..ea94bab6b3b2 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -327,8 +327,7 @@ static int ospf_abr_nssa_am_elected(struct ospf_area *area) struct router_lsa *rlsa; struct in_addr *best = NULL; - LSDB_LOOP(ROUTER_LSDB(area), rn, lsa) - { + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) { /* sanity checks */ if (!lsa || (lsa->data->type != OSPF_ROUTER_LSA) || IS_LSA_SELF(lsa)) @@ -978,7 +977,7 @@ static void ospf_abr_process_nssa_translates(struct ospf *ospf) inet_ntoa(area->area_id)); LSDB_LOOP(NSSA_LSDB(area), rn, lsa) - ospf_abr_translate_nssa(area, lsa); + ospf_abr_translate_nssa(area, lsa); } if (IS_DEBUG_OSPF_NSSA) @@ -1325,14 +1324,14 @@ ospf_abr_unapprove_translates(struct ospf *ospf) /* For NSSA Translations */ and we would want to flush any residuals anyway */ LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) { - UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); - if (IS_DEBUG_OSPF_NSSA) - zlog_debug( - "ospf_abr_unapprove_translates(): " - "approved unset on link id %s", - inet_ntoa(lsa->data->id)); - } + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) { + UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "ospf_abr_unapprove_translates(): " + "approved unset on link id %s", + inet_ntoa(lsa->data->id)); + } if (IS_DEBUG_OSPF_NSSA) zlog_debug("ospf_abr_unapprove_translates(): Stop"); @@ -1355,24 +1354,24 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf) "considering area %s", inet_ntoa(area->area_id)); LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - if (ospf_lsa_is_self_originated(ospf, lsa)) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "ospf_abr_unapprove_summaries(): " - "approved unset on summary link id %s", - inet_ntoa(lsa->data->id)); - UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); - } + if (ospf_lsa_is_self_originated(ospf, lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_abr_unapprove_summaries(): " + "approved unset on summary link id %s", + inet_ntoa(lsa->data->id)); + UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + } LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - if (ospf_lsa_is_self_originated(ospf, lsa)) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "ospf_abr_unapprove_summaries(): " - "approved unset on asbr-summary link id %s", - inet_ntoa(lsa->data->id)); - UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); - } + if (ospf_lsa_is_self_originated(ospf, lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_abr_unapprove_summaries(): " + "approved unset on asbr-summary link id %s", + inet_ntoa(lsa->data->id)); + UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + } } if (IS_DEBUG_OSPF_EVENT) @@ -1633,7 +1632,7 @@ static void ospf_abr_remove_unapproved_translates(struct ospf *ospf) zlog_debug("ospf_abr_remove_unapproved_translates(): Start"); LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - ospf_abr_remove_unapproved_translates_apply(ospf, lsa); + ospf_abr_remove_unapproved_translates_apply(ospf, lsa); if (IS_DEBUG_OSPF_NSSA) zlog_debug("ospf_abr_remove_unapproved_translates(): Stop"); @@ -1657,14 +1656,14 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) inet_ntoa(area->area_id)); LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - if (ospf_lsa_is_self_originated(ospf, lsa)) - if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) - ospf_lsa_flush_area(lsa, area); + if (ospf_lsa_is_self_originated(ospf, lsa)) + if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) + ospf_lsa_flush_area(lsa, area); LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - if (ospf_lsa_is_self_originated(ospf, lsa)) - if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) - ospf_lsa_flush_area(lsa, area); + if (ospf_lsa_is_self_originated(ospf, lsa)) + if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) + ospf_lsa_flush_area(lsa, area); } if (IS_DEBUG_OSPF_EVENT) diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 8c1ad5ff0caa..9ebaeffa69d6 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -1313,22 +1313,28 @@ int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv, /* Check msg type. */ if (mask & Power2[OSPF_ROUTER_LSA]) LSDB_LOOP(ROUTER_LSDB(area), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_NETWORK_LSA]) LSDB_LOOP(NETWORK_LSDB(area), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_SUMMARY_LSA]) LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); } } @@ -1336,14 +1342,16 @@ int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv, if (ospf->lsdb) { if (mask & Power2[OSPF_AS_EXTERNAL_LSA]) LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback(lsa, (void *)¶m, + seqnum); } /* For AS-external opaque LSAs */ if (ospf->lsdb) { if (mask & Power2[OSPF_OPAQUE_AS_LSA]) LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa) - apiserver_sync_callback(lsa, (void *)¶m, seqnum); + apiserver_sync_callback(lsa, (void *)¶m, + seqnum); } /* Send a reply back to client with return code */ @@ -1945,16 +1953,19 @@ void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv, case OSPF_OPAQUE_LINK_LSA: for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa) - apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0); + apiserver_flush_opaque_type_callback( + lsa, (void *)¶m, 0); break; case OSPF_OPAQUE_AREA_LSA: for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa) - apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0); + apiserver_flush_opaque_type_callback( + lsa, (void *)¶m, 0); break; case OSPF_OPAQUE_AS_LSA: LSDB_LOOP(OPAQUE_LINK_LSDB(ospf), rn, lsa) - apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0); + apiserver_flush_opaque_type_callback(lsa, + (void *)¶m, 0); break; default: break; diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 2f1b27f0f196..d2af974833f4 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -649,7 +649,7 @@ static int ospf_ase_calculate_timer(struct thread *t) /* Calculate external route for each AS-external-LSA */ LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - ospf_ase_calculate_route(ospf, lsa); + ospf_ase_calculate_route(ospf, lsa); /* This version simple adds to the table all NSSA areas */ if (ospf->anyNSSA) @@ -661,11 +661,12 @@ static int ospf_ase_calculate_timer(struct thread *t) if (area->external_routing == OSPF_AREA_NSSA) LSDB_LOOP(NSSA_LSDB(area), rn, lsa) - ospf_ase_calculate_route(ospf, lsa); + ospf_ase_calculate_route(ospf, + lsa); } /* kevinm: And add the NSSA routes in ospf_top */ LSDB_LOOP(NSSA_LSDB(ospf), rn, lsa) - ospf_ase_calculate_route(ospf, lsa); + ospf_ase_calculate_route(ospf, lsa); /* Compare old and new external routing table and install the difference info zebra/kernel */ @@ -679,8 +680,9 @@ static int ospf_ase_calculate_timer(struct thread *t) monotime(&stop_time); - zlog_info("SPF Processing Time(usecs): External Routes: %lld\n", - (stop_time.tv_sec - start_time.tv_sec) * 1000000LL + if (IS_DEBUG_OSPF_EVENT) + zlog_info("SPF Processing Time(usecs): External Routes: %lld\n", + (stop_time.tv_sec - start_time.tv_sec) * 1000000LL + (stop_time.tv_usec - start_time.tv_usec)); } return 0; diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 6876054a633e..6a410f4ed3fd 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -230,7 +230,7 @@ static void ospf_packet_hello_dump(struct stream *s, u_int16_t length) struct ospf_hello *hello; int i; - hello = (struct ospf_hello *)STREAM_PNT(s); + hello = (struct ospf_hello *)stream_pnt(s); zlog_debug("Hello"); zlog_debug(" NetworkMask %s", inet_ntoa(hello->network_mask)); @@ -278,7 +278,7 @@ static void ospf_router_lsa_dump(struct stream *s, u_int16_t length) struct router_lsa *rl; int i, len; - rl = (struct router_lsa *)STREAM_PNT(s); + rl = (struct router_lsa *)stream_pnt(s); zlog_debug(" Router-LSA"); zlog_debug(" flags %s", @@ -303,7 +303,7 @@ static void ospf_network_lsa_dump(struct stream *s, u_int16_t length) struct network_lsa *nl; int i, cnt; - nl = (struct network_lsa *)STREAM_PNT(s); + nl = (struct network_lsa *)stream_pnt(s); cnt = (ntohs(nl->header.length) - (OSPF_LSA_HEADER_SIZE + 4)) / 4; zlog_debug(" Network-LSA"); @@ -325,7 +325,7 @@ static void ospf_summary_lsa_dump(struct stream *s, u_int16_t length) int size; int i; - sl = (struct summary_lsa *)STREAM_PNT(s); + sl = (struct summary_lsa *)stream_pnt(s); zlog_debug(" Summary-LSA"); zlog_debug(" Network Mask %s", inet_ntoa(sl->mask)); @@ -342,7 +342,7 @@ static void ospf_as_external_lsa_dump(struct stream *s, u_int16_t length) int size; int i; - al = (struct as_external_lsa *)STREAM_PNT(s); + al = (struct as_external_lsa *)stream_pnt(s); zlog_debug(" %s", ospf_lsa_type_msg[al->header.type].str); zlog_debug(" Network Mask %s", inet_ntoa(al->mask)); @@ -366,7 +366,7 @@ static void ospf_lsa_header_list_dump(struct stream *s, u_int16_t length) /* LSA Headers. */ while (length > 0) { - lsa = (struct lsa_header *)STREAM_PNT(s); + lsa = (struct lsa_header *)stream_pnt(s); ospf_lsa_header_dump(lsa); stream_forward_getp(s, OSPF_LSA_HEADER_SIZE); @@ -382,7 +382,7 @@ static void ospf_packet_db_desc_dump(struct stream *s, u_int16_t length) u_int32_t gp; gp = stream_get_getp(s); - dd = (struct ospf_db_desc *)STREAM_PNT(s); + dd = (struct ospf_db_desc *)stream_pnt(s); zlog_debug("Database Description"); zlog_debug(" Interface MTU %d", ntohs(dd->mtu)); @@ -452,7 +452,7 @@ static void ospf_packet_ls_upd_dump(struct stream *s, u_int16_t length) break; } - lsa = (struct lsa_header *)STREAM_PNT(s); + lsa = (struct lsa_header *)stream_pnt(s); lsa_len = ntohs(lsa->length); ospf_lsa_header_dump(lsa); @@ -566,7 +566,7 @@ void ospf_packet_dump(struct stream *s) gp = stream_get_getp(s); /* OSPF Header dump. */ - ospfh = (struct ospf_header *)STREAM_PNT(s); + ospfh = (struct ospf_header *)stream_pnt(s); /* Until detail flag is set, return. */ if (!(term_debug_ospf_packet[ospfh->type - 1] & OSPF_DEBUG_DETAIL)) diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c index c65d8b874361..e570f3337a95 100644 --- a/ospfd/ospf_ia.c +++ b/ospfd/ospf_ia.c @@ -283,7 +283,7 @@ static void ospf_examine_summaries(struct ospf_area *area, struct route_node *rn; LSDB_LOOP(lsdb_rt, rn, lsa) - process_summary_lsa(area, rt, rtrs, lsa); + process_summary_lsa(area, rt, rtrs, lsa); } int ospf_area_is_transit(struct ospf_area *area) @@ -583,7 +583,7 @@ static void ospf_examine_transit_summaries(struct ospf_area *area, struct route_node *rn; LSDB_LOOP(lsdb_rt, rn, lsa) - process_transit_summary_lsa(area, rt, rtrs, lsa); + process_transit_summary_lsa(area, rt, rtrs, lsa); } void ospf_ia_routing(struct ospf *ospf, struct route_table *rt, diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index c28e500d5b73..0f1dd63dfb54 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -49,6 +49,7 @@ #include "ospfd/ospf_route.h" #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_abr.h" u_int32_t get_metric(u_char *metric) @@ -437,7 +438,7 @@ static char link_info_set(struct stream *s, struct in_addr id, if (ret == OSPF_MAX_LSA_SIZE) { zlog_warn( "%s: Out of space in LSA stream, left %zd, size %zd", - __func__, STREAM_REMAIN(s), STREAM_SIZE(s)); + __func__, STREAM_WRITEABLE(s), STREAM_SIZE(s)); return 0; } } @@ -1871,8 +1872,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, if (area->external_routing != OSPF_AREA_NSSA && !type7) continue; - LSDB_LOOP(NSSA_LSDB(area), rn, lsa) - { + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) { if (lsa->data->id.s_addr == type5->data->id.s_addr) { type7 = lsa; @@ -2503,6 +2503,7 @@ static struct ospf_lsa *ospf_external_lsa_install(struct ospf *ospf, * abr_task. */ ospf_translated_nssa_refresh(ospf, new, NULL); + ospf_schedule_abr_task(ospf); } } @@ -3005,27 +3006,27 @@ int ospf_lsa_maxage_walker(struct thread *thread) for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { LSDB_LOOP(ROUTER_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(NETWORK_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(NSSA_LSDB(area), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); } /* for AS-external-LSAs. */ if (ospf->lsdb) { LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa) - ospf_lsa_maxage_walker_remover(ospf, lsa); + ospf_lsa_maxage_walker_remover(ospf, lsa); } OSPF_TIMER_ON(ospf->t_maxage_walker, ospf_lsa_maxage_walker, @@ -3348,20 +3349,20 @@ void ospf_flush_self_originated_lsas_now(struct ospf *ospf) } LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - ospf_lsa_flush_schedule(ospf, lsa); + ospf_lsa_flush_schedule(ospf, lsa); LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - ospf_lsa_flush_schedule(ospf, lsa); + ospf_lsa_flush_schedule(ospf, lsa); LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa) - ospf_lsa_flush_schedule(ospf, lsa); + ospf_lsa_flush_schedule(ospf, lsa); LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa) - ospf_lsa_flush_schedule(ospf, lsa); + ospf_lsa_flush_schedule(ospf, lsa); } if (need_to_flush_ase) { LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - ospf_lsa_flush_schedule(ospf, lsa); + ospf_lsa_flush_schedule(ospf, lsa); LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa) - ospf_lsa_flush_schedule(ospf, lsa); + ospf_lsa_flush_schedule(ospf, lsa); } /* diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index ed5e8e027d82..022a5a138af1 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -38,7 +38,7 @@ #include "ospfd/ospf_lsdb.h" #include "ospfd/ospf_neighbor.h" #include "ospfd/ospf_packet.h" - +#include "ospfd/ospf_dump.h" /* Join to the OSPF ALL SPF ROUTERS multicast group. */ int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p, @@ -56,10 +56,11 @@ int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p, "on # of multicast group memberships has been exceeded?", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); - else - zlog_debug( - "interface %s [%u] join AllSPFRouters Multicast group.", - inet_ntoa(p->u.prefix4), ifindex); + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("interface %s [%u] join AllSPFRouters Multicast group.", + inet_ntoa(p->u.prefix4), ifindex); + } return ret; } @@ -78,10 +79,11 @@ int ospf_if_drop_allspfrouters(struct ospf *top, struct prefix *p, "ifindex %u, AllSPFRouters): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); - else - zlog_debug( - "interface %s [%u] leave AllSPFRouters Multicast group.", - inet_ntoa(p->u.prefix4), ifindex); + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("interface %s [%u] leave AllSPFRouters Multicast group.", + inet_ntoa(p->u.prefix4), ifindex); + } return ret; } diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index a0ff2bfccf86..54d5dd5d1680 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -280,37 +280,37 @@ static int nsm_negotiation_done(struct ospf_neighbor *nbr) ospf_proactively_arp(nbr); LSDB_LOOP(ROUTER_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); LSDB_LOOP(NETWORK_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); /* Process only if the neighbor is opaque capable. */ if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); } if (CHECK_FLAG(nbr->options, OSPF_OPTION_NP)) { LSDB_LOOP(NSSA_LSDB(area), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); } if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK && area->external_routing == OSPF_AREA_DEFAULT) LSDB_LOOP(EXTERNAL_LSDB(nbr->oi->ospf), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); if (CHECK_FLAG(nbr->options, OSPF_OPTION_O) && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK && area->external_routing == OSPF_AREA_DEFAULT)) LSDB_LOOP(OPAQUE_AS_LSDB(nbr->oi->ospf), rn, lsa) - ospf_db_summary_add(nbr, lsa); + ospf_db_summary_add(nbr, lsa); return 0; } @@ -702,12 +702,12 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) oi->ospf); } - zlog_info( - "nsm_change_state(%s, %s -> %s): " - "scheduling new router-LSA origination", - inet_ntoa(nbr->router_id), - lookup_msg(ospf_nsm_state_msg, old_state, NULL), - lookup_msg(ospf_nsm_state_msg, state, NULL)); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info("%s:(%s, %s -> %s): " + "scheduling new router-LSA origination", + __PRETTY_FUNCTION__, inet_ntoa(nbr->router_id), + lookup_msg(ospf_nsm_state_msg, old_state, NULL), + lookup_msg(ospf_nsm_state_msg, state, NULL)); ospf_router_lsa_update_area(oi->area); diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 5a1f28b036be..6f9da9254289 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -1181,7 +1181,7 @@ void ospf_opaque_lsa_dump(struct stream *s, u_int16_t length) { struct ospf_lsa lsa; - lsa.data = (struct lsa_header *)STREAM_PNT(s); + lsa.data = (struct lsa_header *)stream_pnt(s); show_opaque_info_detail(NULL, &lsa); return; } diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 33792bbff3f4..867035961021 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -617,7 +617,7 @@ static void ospf_write_frags(int fd, struct ospf_packet *op, struct ip *iph, iph->ip_off += offset; stream_forward_getp(op->s, iovp->iov_len); - iovp->iov_base = STREAM_PNT(op->s); + iovp->iov_base = stream_pnt(op->s); } /* setup for final fragment */ @@ -763,7 +763,7 @@ static int ospf_write(struct thread *thread) iov[0].iov_base = (char *)&iph; iov[0].iov_len = iph.ip_hl << OSPF_WRITE_IPHL_SHIFT; - iov[1].iov_base = STREAM_PNT(op->s); + iov[1].iov_base = stream_pnt(op->s); iov[1].iov_len = op->length; #ifdef GNU_LINUX @@ -891,7 +891,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, /* increment statistics. */ oi->hello_in++; - hello = (struct ospf_hello *)STREAM_PNT(s); + hello = (struct ospf_hello *)stream_pnt(s); /* If Hello is myself, silently discard. */ if (IPV4_ADDR_SAME(&ospfh->router_id, &oi->ospf->router_id)) { @@ -1119,7 +1119,7 @@ static void ospf_db_desc_proc(struct stream *s, struct ospf_interface *oi, stream_forward_getp(s, OSPF_DB_DESC_MIN_SIZE); for (size -= OSPF_DB_DESC_MIN_SIZE; size >= OSPF_LSA_HEADER_SIZE; size -= OSPF_LSA_HEADER_SIZE) { - lsah = (struct lsa_header *)STREAM_PNT(s); + lsah = (struct lsa_header *)stream_pnt(s); stream_forward_getp(s, OSPF_LSA_HEADER_SIZE); /* Unknown LS type. */ @@ -1268,7 +1268,7 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, /* Increment statistics. */ oi->db_desc_in++; - dd = (struct ospf_db_desc *)STREAM_PNT(s); + dd = (struct ospf_db_desc *)stream_pnt(s); nbr = ospf_nbr_lookup(oi, iph, ospfh); if (nbr == NULL) { @@ -1363,9 +1363,11 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, if (IPV4_ADDR_CMP(&nbr->router_id, &oi->ospf->router_id) > 0) { /* We're Slave---obey */ - zlog_info( - "Packet[DD]: Neighbor %s Negotiation done (Slave).", - inet_ntoa(nbr->router_id)); + if (CHECK_FLAG(oi->ospf->config, + OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info("Packet[DD]: Neighbor %s Negotiation done (Slave).", + inet_ntoa(nbr->router_id)); + nbr->dd_seqnum = ntohl(dd->dd_seqnum); /* Reset I/MS */ @@ -1374,10 +1376,12 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, } else { /* We're Master, ignore the initial DBD from * Slave */ - zlog_info( - "Packet[DD]: Neighbor %s: Initial DBD from Slave, " - "ignoring.", - inet_ntoa(nbr->router_id)); + if (CHECK_FLAG(oi->ospf->config, + OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "Packet[DD]: Neighbor %s: Initial DBD from Slave, " + "ignoring.", + inet_ntoa(nbr->router_id)); break; } } @@ -1661,7 +1665,7 @@ static struct list *ospf_ls_upd_list_lsa(struct ospf_neighbor *nbr, for (; size >= OSPF_LSA_HEADER_SIZE && count > 0; size -= length, stream_forward_getp(s, length), count--) { - lsah = (struct lsa_header *)STREAM_PNT(s); + lsah = (struct lsa_header *)stream_pnt(s); length = ntohs(lsah->length); if (length > size) { @@ -2219,10 +2223,10 @@ static void ospf_ls_ack(struct ip *iph, struct ospf_header *ospfh, struct ospf_lsa *lsa, *lsr; lsa = ospf_lsa_new(); - lsa->data = (struct lsa_header *)STREAM_PNT(s); + lsa->data = (struct lsa_header *)stream_pnt(s); lsa->vrf_id = oi->ospf->vrf_id; - /* lsah = (struct lsa_header *) STREAM_PNT (s); */ + /* lsah = (struct lsa_header *) stream_pnt (s); */ size -= OSPF_LSA_HEADER_SIZE; stream_forward_getp(s, OSPF_LSA_HEADER_SIZE); @@ -2936,7 +2940,7 @@ int ospf_read(struct thread *thread) by ospf_recv_packet() to be correct). */ stream_forward_getp(ibuf, iph->ip_hl * 4); - ospfh = (struct ospf_header *)STREAM_PNT(ibuf); + ospfh = (struct ospf_header *)stream_pnt(ibuf); if (MSG_OK != ospf_packet_examin( ospfh, stream_get_endp(ibuf) - stream_get_getp(ibuf))) diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 36ae091f99ec..b28aebb81f5c 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -2589,8 +2589,9 @@ static void ospfTrapNbrStateChange(struct ospf_neighbor *on) char msgbuf[16]; ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf)); - zlog_info("ospfTrapNbrStateChange trap sent: %s now %s", - inet_ntoa(on->address.u.prefix4), msgbuf); + if (IS_DEBUG_OSPF_EVENT) + zlog_info("%s: trap sent: %s now %s", __PRETTY_FUNCTION__, + inet_ntoa(on->address.u.prefix4), msgbuf); oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE); index[IN_ADDR_SIZE] = 0; @@ -2646,9 +2647,10 @@ static void ospfTrapIfStateChange(struct ospf_interface *oi) { oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)]; - zlog_info("ospfTrapIfStateChange trap sent: %s now %s", - inet_ntoa(oi->address->u.prefix4), - lookup_msg(ospf_ism_state_msg, oi->state, NULL)); + if (IS_DEBUG_OSPF_EVENT) + zlog_info("%s: trap sent: %s now %s", __PRETTY_FUNCTION__, + inet_ntoa(oi->address->u.prefix4), + lookup_msg(ospf_ism_state_msg, oi->state, NULL)); oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE); index[IN_ADDR_SIZE] = 0; diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 65437dba9e38..22fff1b53d50 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -1463,9 +1463,7 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) } if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SPF: calculation timer delay = %ld", delay); - - zlog_info("SPF: Scheduled in %ld msec", delay); + zlog_debug("SPF: calculation timer delay = %ld msec", delay); ospf->t_spf_calc = NULL; thread_add_timer_msec(master, ospf_spf_calculate_timer, ospf, delay, diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index f1848f1eaff9..6d28c2cdc5d3 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -187,7 +187,7 @@ static void ospf_show_vrf_name(struct ospf *ospf, struct vty *vty, } #ifndef VTYSH_EXTRACT_PL -#include "ospf_vty_clippy.c" +#include "ospfd/ospf_vty_clippy.c" #endif DEFUN_NOSH (router_ospf, @@ -218,7 +218,7 @@ DEFUN_NOSH (router_ospf, if (ospf->vrf_id != VRF_UNKNOWN) ospf->oi_running = 1; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("Config command 'router ospf %d' received, vrf %s id %d oi_running %u", + zlog_debug("Config command 'router ospf %d' received, vrf %s id %u oi_running %u", instance, ospf->name ? ospf->name : "NIL", ospf->vrf_id, ospf->oi_running); VTY_PUSH_CONTEXT(OSPF_NODE, ospf); @@ -449,7 +449,7 @@ DEFUN (ospf_passive_interface, { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); int idx_ipv4 = 2; - struct interface *ifp; + struct interface *ifp = NULL; struct in_addr addr = {.s_addr = INADDR_ANY}; int ret; struct ospf_if_params *params; @@ -459,12 +459,13 @@ DEFUN (ospf_passive_interface, ospf_passive_interface_default(ospf, OSPF_IF_PASSIVE); return CMD_SUCCESS; } + if (ospf->vrf_id != VRF_UNKNOWN) + ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id, 0); - ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id, 0); if (ifp == NULL) { vty_out(vty, "interface %s not found.\n", (char *)argv[1]->arg); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } params = IF_DEF_PARAMS(ifp); @@ -521,7 +522,7 @@ DEFUN (no_ospf_passive_interface, { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); int idx_ipv4 = 3; - struct interface *ifp; + struct interface *ifp = NULL; struct in_addr addr = {.s_addr = INADDR_ANY}; struct ospf_if_params *params; int ret; @@ -532,11 +533,13 @@ DEFUN (no_ospf_passive_interface, return CMD_SUCCESS; } - ifp = if_get_by_name(argv[2]->arg, ospf->vrf_id, 0); + if (ospf->vrf_id != VRF_UNKNOWN) + ifp = if_get_by_name(argv[2]->arg, ospf->vrf_id, 0); + if (ifp == NULL) { vty_out(vty, "interface %s not found.\n", - (char *)argv[1]->arg); - return CMD_WARNING; + (char *)argv[2]->arg); + return CMD_WARNING_CONFIG_FAILED; } params = IF_DEF_PARAMS(ifp); @@ -616,7 +619,7 @@ DEFUN (ospf_network_area, ret = ospf_network_set(ospf, &p, area_id, format); if (ret == 0) { vty_out(vty, "There is already same network statement.\n"); - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; } return CMD_SUCCESS; @@ -1971,12 +1974,13 @@ DEFUN (ospf_area_authentication_message_digest, "Use message-digest authentication\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - int idx_ipv4_number = 1; + int idx = 0; struct ospf_area *area; struct in_addr area_id; int format; - VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + argv_find(argv, argc, "area", &idx); + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx + 1]->arg); area = ospf_area_get(ospf, area_id); ospf_area_display_format_set(ospf, area, format); @@ -3689,14 +3693,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, { struct interface *ifp; struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); - json_object *json_vrf = NULL; - json_object *json_interface_sub = NULL; + json_object *json_vrf = NULL, *json_intf_array = NULL; + json_object *json_interface_sub = NULL, *json_interface = NULL; if (use_json) { if (use_vrf) json_vrf = json_object_new_object(); else json_vrf = json; + json_intf_array = json_object_new_array(); } if (ospf->instance) { @@ -3710,21 +3715,29 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); if (intf_name == NULL) { + if (use_json) + json_object_object_add(json_vrf, "interfaces", + json_intf_array); /* Show All Interfaces.*/ FOR_ALL_INTERFACES (vrf, ifp) { if (ospf_oi_count(ifp)) { - if (use_json) + if (use_json) { + json_interface = + json_object_new_object(); json_interface_sub = json_object_new_object(); - + } show_ip_ospf_interface_sub(vty, ospf, ifp, json_interface_sub, use_json); - if (use_json) + if (use_json) { + json_object_array_add(json_intf_array, + json_interface); json_object_object_add( - json_vrf, ifp->name, + json_interface, ifp->name, json_interface_sub); + } } } } else { @@ -3737,15 +3750,23 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, else vty_out(vty, "No such interface name\n"); } else { - if (use_json) + if (use_json) { json_interface_sub = json_object_new_object(); + json_interface = json_object_new_object(); + json_object_object_add(json_vrf, "interfaces", + json_intf_array); + } show_ip_ospf_interface_sub( vty, ospf, ifp, json_interface_sub, use_json); - if (use_json) - json_object_object_add(json_vrf, ifp->name, + if (use_json) { + json_object_array_add(json_intf_array, + json_interface); + json_object_object_add(json_interface, + ifp->name, json_interface_sub); + } } } @@ -6163,7 +6184,7 @@ static void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, show_database_header[type]); LSDB_LOOP(AREA_LSDB(area, type), rn, lsa) - show_lsa_summary(vty, lsa, self); + show_lsa_summary(vty, lsa, self); vty_out(vty, "\n"); } @@ -6185,7 +6206,7 @@ static void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, vty_out(vty, "%s\n", show_database_header[type]); LSDB_LOOP(AS_LSDB(ospf, type), rn, lsa) - show_lsa_summary(vty, lsa, self); + show_lsa_summary(vty, lsa, self); vty_out(vty, "\n"); } @@ -9492,9 +9513,9 @@ DEFUN (show_ip_ospf_route, } if (uj) { + /* Keep Non-pretty format */ vty_out(vty, "%s\n", - json_object_to_json_string_ext(json, - JSON_C_TO_STRING_PRETTY)); + json_object_to_json_string(json)); json_object_free(json); } @@ -9518,9 +9539,9 @@ DEFUN (show_ip_ospf_route, if (ospf) { ret = show_ip_ospf_route_common(vty, ospf, json, use_vrf); + /* Keep Non-pretty format */ if (uj) - 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(json)); } if (uj) @@ -9579,7 +9600,7 @@ DEFUN (show_ip_ospf_vrfs, for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { json_object *json_vrf = NULL; const char *name = NULL; - int vrf_id_ui = 0; + int64_t vrf_id_ui = 0; count++; @@ -9593,7 +9614,8 @@ DEFUN (show_ip_ospf_vrfs, else name = ospf->name; - vrf_id_ui = (ospf->vrf_id == VRF_UNKNOWN) ? -1 : ospf->vrf_id; + vrf_id_ui = (ospf->vrf_id == VRF_UNKNOWN) ? -1 : + (int64_t) ospf->vrf_id; if (uj) { json_object_int_add(json_vrf, "vrfId", vrf_id_ui); @@ -9635,30 +9657,27 @@ const char *ospf_int_type_str[] = {"unknown", /* should never be used. */ "virtual-link", /* should never be used. */ "loopback"}; -static int config_write_interface_one(struct vty *vty, struct ospf *ospf) +static int config_write_interface_one(struct vty *vty, struct vrf *vrf) { - struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); struct listnode *node; struct interface *ifp; struct crypt_key *ck; struct route_node *rn = NULL; struct ospf_if_params *params; int write = 0; + struct ospf *ospf = vrf->info; FOR_ALL_INTERFACES (vrf, ifp) { - struct vrf *vrf = NULL; if (memcmp(ifp->name, "VLINK", 5) == 0) continue; - vrf = vrf_lookup_by_id(ifp->vrf_id); - vty_frame(vty, "!\n"); - if (ifp->vrf_id == VRF_DEFAULT || vrf == NULL) + if (ifp->vrf_id == VRF_DEFAULT) vty_frame(vty, "interface %s\n", ifp->name); else vty_frame(vty, "interface %s vrf %s\n", - ifp->name, vrf->name); + ifp->name, vrf->name); if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); @@ -9670,26 +9689,27 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Interface Network print. */ if (OSPF_IF_PARAM_CONFIGURED(params, type) && params->type != OSPF_IFTYPE_LOOPBACK) { - if (params->type != ospf_default_iftype(ifp)) { + if (params->type != + ospf_default_iftype(ifp)) { vty_out(vty, " ip ospf network %s", ospf_int_type_str [params->type]); if (params != IF_DEF_PARAMS(ifp)) vty_out(vty, " %s", inet_ntoa( - rn->p.u.prefix4)); + rn->p.u.prefix4)); vty_out(vty, "\n"); } } /* OSPF interface authentication print */ if (OSPF_IF_PARAM_CONFIGURED(params, auth_type) - && params->auth_type != OSPF_AUTH_NOTSET) { + && params->auth_type != OSPF_AUTH_NOTSET) { const char *auth_str; /* Translation tables are not that much help - * here due to syntax - * of the simple option */ + * here due to syntax + * of the simple option */ switch (params->auth_type) { case OSPF_AUTH_NULL: @@ -9719,7 +9739,7 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Simple Authentication Password print. */ if (OSPF_IF_PARAM_CONFIGURED(params, auth_simple) - && params->auth_simple[0] != '\0') { + && params->auth_simple[0] != '\0') { vty_out(vty, " ip ospf authentication-key %s", params->auth_simple); if (params != IF_DEF_PARAMS(ifp)) @@ -9730,21 +9750,23 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Cryptographic Authentication Key print. */ if (params && params->auth_crypt) { - for (ALL_LIST_ELEMENTS_RO(params->auth_crypt, - node, ck)) { + for (ALL_LIST_ELEMENTS_RO( + params->auth_crypt, + node, ck)) { vty_out(vty, " ip ospf message-digest-key %d md5 %s", - ck->key_id, ck->auth_key); + ck->key_id, + ck->auth_key); if (params != IF_DEF_PARAMS(ifp)) vty_out(vty, " %s", - inet_ntoa(rn->p.u.prefix4)); + inet_ntoa(rn->p.u.prefix4)); vty_out(vty, "\n"); } } /* Interface Output Cost print. */ if (OSPF_IF_PARAM_CONFIGURED(params, - output_cost_cmd)) { + output_cost_cmd)) { vty_out(vty, " ip ospf cost %u", params->output_cost_cmd); if (params != IF_DEF_PARAMS(ifp)) @@ -9755,7 +9777,8 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Hello Interval print. */ if (OSPF_IF_PARAM_CONFIGURED(params, v_hello) - && params->v_hello != OSPF_HELLO_INTERVAL_DEFAULT) { + && params->v_hello != + OSPF_HELLO_INTERVAL_DEFAULT) { vty_out(vty, " ip ospf hello-interval %u", params->v_hello); if (params != IF_DEF_PARAMS(ifp)) @@ -9767,18 +9790,19 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Router Dead Interval print. */ if (OSPF_IF_PARAM_CONFIGURED(params, v_wait) - && params->v_wait - != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT) { + && params->v_wait + != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT) { vty_out(vty, " ip ospf dead-interval "); /* fast hello ? */ if (OSPF_IF_PARAM_CONFIGURED(params, - fast_hello)) + fast_hello)) vty_out(vty, "minimal hello-multiplier %d", params->fast_hello); else - vty_out(vty, "%u", params->v_wait); + vty_out(vty, "%u", + params->v_wait); if (params != IF_DEF_PARAMS(ifp)) vty_out(vty, " %s", @@ -9788,8 +9812,8 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Router Priority print. */ if (OSPF_IF_PARAM_CONFIGURED(params, priority) - && params->priority - != OSPF_ROUTER_PRIORITY_DEFAULT) { + && params->priority + != OSPF_ROUTER_PRIORITY_DEFAULT) { vty_out(vty, " ip ospf priority %u", params->priority); if (params != IF_DEF_PARAMS(ifp)) @@ -9800,9 +9824,9 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) /* Retransmit Interval print. */ if (OSPF_IF_PARAM_CONFIGURED(params, - retransmit_interval) - && params->retransmit_interval - != OSPF_RETRANSMIT_INTERVAL_DEFAULT) { + retransmit_interval) + && params->retransmit_interval + != OSPF_RETRANSMIT_INTERVAL_DEFAULT) { vty_out(vty, " ip ospf retransmit-interval %u", params->retransmit_interval); if (params != IF_DEF_PARAMS(ifp)) @@ -9812,20 +9836,21 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) } /* Transmit Delay print. */ - if (OSPF_IF_PARAM_CONFIGURED(params, transmit_delay) - && params->transmit_delay - != OSPF_TRANSMIT_DELAY_DEFAULT) { + if (OSPF_IF_PARAM_CONFIGURED(params, + transmit_delay) + && params->transmit_delay + != OSPF_TRANSMIT_DELAY_DEFAULT) { vty_out(vty, " ip ospf transmit-delay %u", params->transmit_delay); if (params != IF_DEF_PARAMS(ifp)) vty_out(vty, " %s", - inet_ntoa(rn->p.u.prefix4)); + inet_ntoa(rn->p.u.prefix4)); vty_out(vty, "\n"); } /* Area print. */ if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { - if (ospf->instance) + if (ospf && ospf->instance) vty_out(vty, " ip ospf %d", ospf->instance); else @@ -9833,39 +9858,42 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) size_t buflen = MAX(strlen("4294967295"), - strlen("255.255.255.255")); + strlen("255.255.255.255")); char buf[buflen]; area_id2str(buf, sizeof(buf), ¶ms->if_area, - params->if_area_id_fmt); + params->if_area_id_fmt); vty_out(vty, " area %s", buf); if (params != IF_DEF_PARAMS(ifp)) vty_out(vty, " %s", - inet_ntoa(rn->p.u.prefix4)); + inet_ntoa(rn->p.u.prefix4)); vty_out(vty, "\n"); } /* bfd print. */ - ospf_bfd_write_config(vty, params); + if (params->bfd_info) + ospf_bfd_write_config(vty, params); /* MTU ignore print. */ if (OSPF_IF_PARAM_CONFIGURED(params, mtu_ignore) - && params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) { + && params->mtu_ignore != + OSPF_MTU_IGNORE_DEFAULT) { if (params->mtu_ignore == 0) vty_out(vty, " no ip ospf mtu-ignore"); else vty_out(vty, " ip ospf mtu-ignore"); if (params != IF_DEF_PARAMS(ifp)) vty_out(vty, " %s", - inet_ntoa(rn->p.u.prefix4)); + inet_ntoa(rn->p.u.prefix4)); vty_out(vty, "\n"); } while (1) { if (rn == NULL) - rn = route_top(IF_OIFS_PARAMS(ifp)); + rn = route_top( + IF_OIFS_PARAMS(ifp)); else rn = route_next(rn); @@ -9881,6 +9909,7 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) vty_endframe(vty, NULL); } + return write; } @@ -9888,12 +9917,12 @@ static int config_write_interface_one(struct vty *vty, struct ospf *ospf) static int config_write_interface(struct vty *vty) { int write = 0; - struct ospf *ospf = NULL; - struct listnode *node = NULL; + struct vrf *vrf = NULL; - /* Traverse all ospf [vrf] instances */ - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) - write += config_write_interface_one(vty, ospf); + /* Display all VRF aware OSPF interface configuration */ + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + write += config_write_interface_one(vty, vrf); + } return write; } diff --git a/ospfd/ospf_vty.h b/ospfd/ospf_vty.h index 9fd0c3ed4cc3..5eb6842be35c 100644 --- a/ospfd/ospf_vty.h +++ b/ospfd/ospf_vty.h @@ -44,6 +44,7 @@ vty_out(vty, \ "%% You can't configure %s to backbone\n", \ NAME); \ + return CMD_WARNING; \ } \ } diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 66be29dbb417..58e8a921d51f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -389,6 +389,7 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, memset(&api, 0, sizeof(api)); api.vrf_id = ospf->vrf_id; + api.nh_vrf_id = ospf->vrf_id; api.type = ZEBRA_ROUTE_OSPF; api.instance = ospf->instance; api.safi = SAFI_UNICAST; @@ -466,6 +467,7 @@ void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *p, memset(&api, 0, sizeof(api)); api.vrf_id = ospf->vrf_id; + api.nh_vrf_id = ospf->vrf_id; api.type = ZEBRA_ROUTE_OSPF; api.instance = ospf->instance; api.safi = SAFI_UNICAST; @@ -487,6 +489,7 @@ void ospf_zebra_add_discard(struct ospf *ospf, struct prefix_ipv4 *p) memset(&api, 0, sizeof(api)); api.vrf_id = ospf->vrf_id; + api.nh_vrf_id = ospf->vrf_id; api.type = ZEBRA_ROUTE_OSPF; api.instance = ospf->instance; api.safi = SAFI_UNICAST; @@ -506,6 +509,7 @@ void ospf_zebra_delete_discard(struct ospf *ospf, struct prefix_ipv4 *p) memset(&api, 0, sizeof(api)); api.vrf_id = ospf->vrf_id; + api.nh_vrf_id = ospf->vrf_id; api.type = ZEBRA_ROUTE_OSPF; api.instance = ospf->instance; api.safi = SAFI_UNICAST; diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index ceb8440eebd9..6d583e9b4ac4 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -159,8 +159,8 @@ void ospf_router_id_update(struct ospf *ospf) struct ospf_lsa *lsa; LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - if (IS_LSA_SELF(lsa)) - ospf_lsa_flush_schedule(ospf, lsa); + if (IS_LSA_SELF(lsa)) + ospf_lsa_flush_schedule(ospf, lsa); } ospf->router_id = router_id; @@ -183,8 +183,7 @@ void ospf_router_id_update(struct ospf *ospf) struct route_node *rn; struct ospf_lsa *lsa; - LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - { + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) { /* AdvRouter and Router ID is the same. */ if (IPV4_ADDR_SAME(&lsa->data->adv_router, &ospf->router_id)) { @@ -241,7 +240,7 @@ static struct ospf *ospf_new(u_short instance, const char *name) new->name = XSTRDUP(MTYPE_OSPF_TOP, name); vrf = vrf_lookup_by_name(new->name); if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: Create new ospf instance with vrf_name %s vrf_id %d", + zlog_debug("%s: Create new ospf instance with vrf_name %s vrf_id %u", __PRETTY_FUNCTION__, name, new->vrf_id); if (vrf) ospf_vrf_link(new, vrf); @@ -693,9 +692,9 @@ static void ospf_finish_final(struct ospf *ospf) stream_free(ospf->ibuf); LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa) - ospf_discard_from_db(ospf, ospf->lsdb, lsa); + ospf_discard_from_db(ospf, ospf->lsdb, lsa); LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa) - ospf_discard_from_db(ospf, ospf->lsdb, lsa); + ospf_discard_from_db(ospf, ospf->lsdb, lsa); ospf_lsdb_delete_all(ospf->lsdb); ospf_lsdb_free(ospf->lsdb); @@ -830,20 +829,20 @@ static void ospf_area_free(struct ospf_area *area) /* Free LSDBs. */ LSDB_LOOP(ROUTER_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); LSDB_LOOP(NETWORK_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); LSDB_LOOP(NSSA_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa) - ospf_discard_from_db(area->ospf, area->lsdb, lsa); + ospf_discard_from_db(area->ospf, area->lsdb, lsa); ospf_opaque_type10_lsa_term(area); ospf_lsdb_delete_all(area->lsdb); @@ -1031,9 +1030,15 @@ int ospf_network_set(struct ospf *ospf, struct prefix_ipv4 *p, rn = route_node_get(ospf->networks, (struct prefix *)p); if (rn->info) { - /* There is already same network statement. */ + network = rn->info; route_unlock_node(rn); - return 0; + + if (IPV4_ADDR_SAME(&area_id, &network->area_id)) { + return 1; + } else { + /* There is already same network statement. */ + return 0; + } } rn->info = network = ospf_network_new(area_id); @@ -2008,7 +2013,7 @@ void ospf_vrf_unlink(struct ospf *ospf, struct vrf *vrf) static int ospf_vrf_new(struct vrf *vrf) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF Created: %s(%d)", __PRETTY_FUNCTION__, + zlog_debug("%s: VRF Created: %s(%u)", __PRETTY_FUNCTION__, vrf->name, vrf->vrf_id); return 0; @@ -2018,7 +2023,7 @@ static int ospf_vrf_new(struct vrf *vrf) static int ospf_vrf_delete(struct vrf *vrf) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF Deletion: %s(%d)", __PRETTY_FUNCTION__, + zlog_debug("%s: VRF Deletion: %s(%u)", __PRETTY_FUNCTION__, vrf->name, vrf->vrf_id); return 0; @@ -2031,7 +2036,7 @@ static int ospf_vrf_enable(struct vrf *vrf) vrf_id_t old_vrf_id = VRF_DEFAULT; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF %s id %d enabled", + zlog_debug("%s: VRF %s id %u enabled", __PRETTY_FUNCTION__, vrf->name, vrf->vrf_id); ospf = ospf_lookup_by_name(vrf->name); @@ -2040,7 +2045,7 @@ static int ospf_vrf_enable(struct vrf *vrf) /* We have instance configured, link to VRF and make it "up". */ ospf_vrf_link(ospf, vrf); if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: ospf linked to vrf %s vrf_id %d (old id %d)", + zlog_debug("%s: ospf linked to vrf %s vrf_id %u (old id %u)", __PRETTY_FUNCTION__, vrf->name, ospf->vrf_id, old_vrf_id); diff --git a/pbrd/.gitignore b/pbrd/.gitignore new file mode 100644 index 000000000000..f8142d776921 --- /dev/null +++ b/pbrd/.gitignore @@ -0,0 +1,16 @@ +!Makefile +Makefile.in +libpbr.a +pbrd +tags +TAGS +.deps +*.o +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*clippy.c +*~ +*.loT diff --git a/pbrd/Makefile b/pbrd/Makefile new file mode 100644 index 000000000000..e8999c3409ca --- /dev/null +++ b/pbrd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. pbrd/pbrd +%: ALWAYS + @$(MAKE) -s -C .. pbrd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c new file mode 100644 index 000000000000..4b58fbab093c --- /dev/null +++ b/pbrd/pbr_main.c @@ -0,0 +1,159 @@ +/* + * PBR - main code + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include "getopt.h" +#include "thread.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "plist.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "keychain.h" +#include "distribute.h" +#include "libfrr.h" +#include "routemap.h" +#include "nexthop.h" +#include "nexthop_group.h" + +#include "pbr_zebra.h" +#include "pbr_map.h" +#include "pbr_vty.h" + +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, +}; + +struct zebra_privs_t pbr_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +struct option longopts[] = {{0}}; + +/* Master of threads. */ +struct thread_master *master; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); +} + +/* SIGINT / SIGTERM handler. */ +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct quagga_signal_t pbr_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +#define PBR_VTY_PORT 2615 + +FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT, + + .proghelp = "Implementation of PBR.", + + .signals = pbr_signals, + .n_signals = array_size(pbr_signals), + + .privs = &pbr_privs, ) + +int main(int argc, char **argv, char **envp) +{ + frr_preinit(&pbrd_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + break; + } + } + + master = frr_init(); + + vrf_init(NULL, NULL, NULL, NULL); + nexthop_group_init(); + + pbr_map_init(); + pbr_zebra_init(); + pbr_vty_init(); + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c new file mode 100644 index 000000000000..49cc5121bf57 --- /dev/null +++ b/pbrd/pbr_map.c @@ -0,0 +1,45 @@ +/* + * PBR-map Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "thread.h" +#include "prefix.h" +#include "table.h" + +#include "pbr_map.h" + +static __inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2); + +RB_GENERATE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare) + +struct pbr_map_entry_head pbr_maps = RB_INITIALIZER(&pbr_maps); + +static __inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2) +{ + return (pbrmap2->seqno - pbrmap1->seqno); +} + +extern void pbr_map_init(void) +{ +} diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h new file mode 100644 index 000000000000..b94b4de43de5 --- /dev/null +++ b/pbrd/pbr_map.h @@ -0,0 +1,59 @@ +/* + * PBR-map Header + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __PBR_MAP_H__ +#define __PBR_MAP_H__ + +struct pbr_map { + /* + * RB Tree of the pbr_maps + */ + RB_ENTRY(pbr_map) pbr_map_entry; + + /* + * The sequence of where we are for display + */ + uint32_t seqno; + + /* + * Our policy Catchers + */ + struct prefix *src; + struct prefix *dst; + + /* + * The interface to apply this to + */ + struct interface *ifp; + + /* + * The name of the nexthop group + */ + char *nhop_group; +}; +RB_HEAD(pbr_map_entry_head, pbr_map); +RB_PROTOTYPE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare) + +extern struct pbr_map_entry_head pbr_maps; + +extern void pbr_map_init(void); + +#endif diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c new file mode 100644 index 000000000000..e2a9e349a1df --- /dev/null +++ b/pbrd/pbr_vty.c @@ -0,0 +1,72 @@ +/* + * PBR - vty code + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "nexthop.h" +#include "log.h" + +#include "pbrd/pbr_zebra.h" +#include "pbrd/pbr_vty.h" +#ifndef VTYSH_EXTRACT_PL +#include "pbrd/pbr_vty_clippy.c" +#endif + +DEFPY (pbr_map, + pbr_map_cmd, + "pbr-policy (1-100000)$seqno {src $src|dest $dst} nexthop-group NAME$nhgroup", + "Policy to use\n" + "Sequence Number\n" + "The Source\n" + "IP Address\n" + "IPv6 Address\n" + "dest\n" + "IP Address\n" + "IPv6 Address\n" + "Nexthop group\n" + "Name of the Nexthop Group\n") +{ + return CMD_SUCCESS; +} + +static struct cmd_node interface_node = { + INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */ +}; + +static int pbr_interface_config_write(struct vty *vty) +{ + vty_out(vty, "!\n"); + + return 1; +} + +void pbr_vty_init(void) +{ + install_node(&interface_node, + pbr_interface_config_write); + if_cmd_init(); + + install_element(INTERFACE_NODE, &pbr_map_cmd); + return; +} diff --git a/pbrd/pbr_vty.h b/pbrd/pbr_vty.h new file mode 100644 index 000000000000..cdd63abc9469 --- /dev/null +++ b/pbrd/pbr_vty.h @@ -0,0 +1,26 @@ +/* + * VTY library for PBR + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __PBR_VTY_H__ +#define __PBR_VTY_H__ + +extern void pbr_vty_init(void); +#endif diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c new file mode 100644 index 000000000000..256355a45809 --- /dev/null +++ b/pbrd/pbr_zebra.c @@ -0,0 +1,203 @@ +/* + * Zebra connect code. + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "thread.h" +#include "command.h" +#include "network.h" +#include "prefix.h" +#include "routemap.h" +#include "table.h" +#include "stream.h" +#include "memory.h" +#include "zclient.h" +#include "filter.h" +#include "plist.h" +#include "log.h" +#include "nexthop.h" + +#include "pbr_zebra.h" + +/* Zebra structure to hold current status. */ +struct zclient *zclient = NULL; + +/* For registering threads. */ +extern struct thread_master *master; + +static struct interface *zebra_interface_if_lookup(struct stream *s) +{ + char ifname_tmp[INTERFACE_NAMSIZ]; + + /* Read interface name. */ + stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); + + /* And look it up. */ + return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); +} + +/* Inteface addition message from zebra. */ +static int interface_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + + if (!ifp->info) + return 0; + + return 0; +} + +static int interface_delete(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct stream *s; + + s = zclient->ibuf; + /* zebra_interface_state_read () updates interface structure in iflist + */ + ifp = zebra_interface_state_read(s, vrf_id); + + if (ifp == NULL) + return 0; + + if_set_index(ifp, IFINDEX_INTERNAL); + + return 0; +} + +static int interface_address_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + + zebra_interface_address_read(command, zclient->ibuf, vrf_id); + + return 0; +} + +static int interface_address_delete(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + + if (!c) + return 0; + + connected_free(c); + return 0; +} + +static int interface_state_up(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + + zebra_interface_if_lookup(zclient->ibuf); + + return 0; +} + +static int interface_state_down(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + + zebra_interface_state_read(zclient->ibuf, vrf_id); + + return 0; +} + +static int notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct prefix p; + enum zapi_route_notify_owner note; + + if (!zapi_route_notify_decode(zclient->ibuf, &p, ¬e)) + return -1; + + return 0; +} + +static void zebra_connected(struct zclient *zclient) +{ + zclient_send_reg_requests(zclient, VRF_DEFAULT); +} + +void route_add(struct prefix *p, struct nexthop *nh) +{ + struct zapi_route api; + struct zapi_nexthop *api_nh; + + memset(&api, 0, sizeof(api)); + api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_PBR; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, p, sizeof(*p)); + + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + + api_nh = &api.nexthops[0]; + api_nh->gate.ipv4 = nh->gate.ipv4; + api_nh->type = nh->type; + api_nh->ifindex = nh->ifindex; + api.nexthop_num = 1; + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); +} + +void route_delete(struct prefix *p) +{ + struct zapi_route api; + + memset(&api, 0, sizeof(api)); + api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_PBR; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, p, sizeof(*p)); + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + + return; +} + +extern struct zebra_privs_t pbr_privs; + +void pbr_zebra_init(void) +{ + struct zclient_options opt = { .receive_notify = true }; + + zclient = zclient_new_notify(master, &opt); + + zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs); + zclient->zebra_connected = zebra_connected; + zclient->interface_add = interface_add; + zclient->interface_delete = interface_delete; + zclient->interface_up = interface_state_up; + zclient->interface_down = interface_state_down; + zclient->interface_address_add = interface_address_add; + zclient->interface_address_delete = interface_address_delete; + zclient->notify_owner = notify_owner; +} diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h new file mode 100644 index 000000000000..7bb2111b9764 --- /dev/null +++ b/pbrd/pbr_zebra.h @@ -0,0 +1,29 @@ +/* + * Zebra connect library for PBR + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __PBR_ZEBRA_H__ +#define __PBR_ZEBRA_H__ + +extern void pbr_zebra_init(void); + +extern void route_add(struct prefix *p, struct nexthop *nh); +extern void route_delete(struct prefix *p); +#endif diff --git a/pbrd/pbrd.conf.sample b/pbrd/pbrd.conf.sample new file mode 100644 index 000000000000..bb1c2edca825 --- /dev/null +++ b/pbrd/pbrd.conf.sample @@ -0,0 +1,3 @@ +! +! +log stdout diff --git a/pbrd/subdir.am b/pbrd/subdir.am new file mode 100644 index 000000000000..64463c4c8f00 --- /dev/null +++ b/pbrd/subdir.am @@ -0,0 +1,22 @@ +# +# pbrd +# + +if PBRD +noinst_LIBRARIES += pbrd/libpbr.a +sbin_PROGRAMS += pbrd/pbrd +dist_examples_DATA += pbrd/pbrd.conf.sample +endif + +pbrd_libpbr_a_SOURCES = \ + pbrd/pbr_zebra.c \ + pbrd/pbr_vty.c \ + pbrd/pbr_map.c \ + # end + +pbrd/pbr_vty_clippy.c: $(CLIPPY_DEPS) +pbrd/pbr_vty.$(OBJEXT): pbrd/pbr_vty_clippy.c + +pbrd_pbrd_SOURCES = pbrd/pbr_main.c +pbrd_pbrd_LDADD = pbrd/libpbr.a lib/libfrr.la @LIBCAP@ + diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 10b68ab73594..76ba505ad4c1 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -7333,6 +7333,7 @@ static int interface_pim_use_src_cmd_worker(struct vty *vty, const char *source) { int result; struct in_addr source_addr; + int ret = CMD_SUCCESS; VTY_DECLVAR_CONTEXT(interface, ifp); result = inet_pton(AF_INET, source, &source_addr); @@ -7347,16 +7348,19 @@ static int interface_pim_use_src_cmd_worker(struct vty *vty, const char *source) case PIM_SUCCESS: break; case PIM_IFACE_NOT_FOUND: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "Pim not enabled on this interface\n"); break; case PIM_UPDATE_SOURCE_DUP: + ret = CMD_WARNING; vty_out(vty, "%% Source already set to %s\n", source); break; default: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% Source set failed\n"); } - return result ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS; + return ret; } DEFUN (interface_pim_use_source, @@ -7485,6 +7489,7 @@ static int ip_msdp_peer_cmd_worker(struct pim_instance *pim, struct vty *vty, enum pim_msdp_err result; struct in_addr peer_addr; struct in_addr local_addr; + int ret = CMD_SUCCESS; result = inet_pton(AF_INET, peer, &peer_addr); if (result <= 0) { @@ -7506,19 +7511,23 @@ static int ip_msdp_peer_cmd_worker(struct pim_instance *pim, struct vty *vty, case PIM_MSDP_ERR_NONE: break; case PIM_MSDP_ERR_OOM: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% Out of memory\n"); break; case PIM_MSDP_ERR_PEER_EXISTS: + ret = CMD_WARNING; vty_out(vty, "%% Peer exists\n"); break; case PIM_MSDP_ERR_MAX_MESH_GROUPS: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% Only one mesh-group allowed currently\n"); break; default: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% peer add failed\n"); } - return result ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS; + return ret; } DEFUN_HIDDEN (ip_msdp_peer, @@ -7581,6 +7590,7 @@ static int ip_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim, { enum pim_msdp_err result; struct in_addr mbr_ip; + int ret = CMD_SUCCESS; result = inet_pton(AF_INET, mbr, &mbr_ip); if (result <= 0) { @@ -7594,19 +7604,23 @@ static int ip_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim, case PIM_MSDP_ERR_NONE: break; case PIM_MSDP_ERR_OOM: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% Out of memory\n"); break; case PIM_MSDP_ERR_MG_MBR_EXISTS: + ret = CMD_WARNING; vty_out(vty, "%% mesh-group member exists\n"); break; case PIM_MSDP_ERR_MAX_MESH_GROUPS: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% Only one mesh-group allowed currently\n"); break; default: + ret = CMD_WARNING_CONFIG_FAILED; vty_out(vty, "%% member add failed\n"); } - return result ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS; + return ret; } DEFUN (ip_msdp_mesh_group_member, diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index b2a8a6ff8f14..f02cf7ed31c4 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -205,6 +205,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) if (!pim_ifp->sec_addr_list) { zlog_err("%s: failure: secondary addresslist", __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); } pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free; pim_ifp->sec_addr_list->cmp = diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 5f597b17b1b0..7d3b783adf63 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -640,6 +640,13 @@ static int on_ifjoin_prune_pending_timer(struct thread *t) ch = THREAD_ARG(t); + if (PIM_DEBUG_TRACE) + zlog_debug("%s: IFCHANNEL%s %s Prune Pending Timer Popped", + __PRETTY_FUNCTION__, + pim_str_sg_dump(&ch->sg), + pim_ifchannel_ifjoin_name(ch->ifjoin_state, + ch->flags)); + if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) { ifp = ch->interface; pim_ifp = ifp->info; @@ -665,16 +672,19 @@ static int on_ifjoin_prune_pending_timer(struct thread *t) * message on RP path upon prune timer expiry. */ ch->ifjoin_state = PIM_IFJOIN_PRUNE; - if (ch->upstream) + if (ch->upstream) { + struct pim_upstream *parent = + ch->upstream->parent; + pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); + + pim_jp_agg_single_upstream_send(&parent->rpf, + parent, + true); + } } /* from here ch may have been deleted */ - } else { - zlog_warn( - "%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state", - __PRETTY_FUNCTION__, pim_str_sg_dump(&ch->sg), - pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags)); } return 0; diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index 1fccbaeafa3b..8da610a3a6d5 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -136,7 +136,7 @@ static int pim_vrf_new(struct vrf *vrf) { struct pim_instance *pim = pim_instance_init(vrf); - zlog_debug("VRF Created: %s(%d)", vrf->name, vrf->vrf_id); + zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); if (pim == NULL) { zlog_err("%s %s: pim class init failure ", __FILE__, __PRETTY_FUNCTION__); @@ -159,7 +159,7 @@ static int pim_vrf_delete(struct vrf *vrf) { struct pim_instance *pim = vrf->info; - zlog_debug("VRF Deletion: %s(%d)", vrf->name, vrf->vrf_id); + zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); pim_ssmpingd_destroy(pim); pim_instance_terminate(pim); diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c index 11efc158e967..978d979245bc 100644 --- a/pimd/pim_msdp_packet.c +++ b/pimd/pim_msdp_packet.c @@ -221,7 +221,7 @@ int pim_msdp_write(struct thread *thread) writenum = stream_get_endp(s) - stream_get_getp(s); /* Call write() system call */ - num = write(mp->fd, STREAM_PNT(s), writenum); + num = write(mp->fd, stream_pnt(s), writenum); if (num < 0) { /* write failed either retry needed or error */ if (ERRNO_IO_RETRY(errno)) { diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index e2984e1d1312..089639c77e72 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -811,7 +811,7 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, char buf[PREFIX2STR_BUFFER]; prefix2str(&p, buf, sizeof(buf)); zlog_debug( - "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%d up %ld rp %d", + "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d", __PRETTY_FUNCTION__, buf, pim->vrf->name, nexthop_num, pnc->nexthop_num, vrf_id, pnc->upstream_hash->count, listcount(pnc->rp_list)); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 689e9a744987..6e8255876645 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -80,7 +80,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, if (PIM_DEBUG_ZEBRA) { zlog_debug( - "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d", + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags, ifp->metric, ifp->mtu, if_is_operative(ifp)); @@ -130,7 +130,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, if (PIM_DEBUG_ZEBRA) { zlog_debug( - "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d", + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags, ifp->metric, ifp->mtu, if_is_operative(ifp)); @@ -158,7 +158,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, if (PIM_DEBUG_ZEBRA) { zlog_debug( - "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d", + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags, ifp->metric, ifp->mtu, if_is_operative(ifp)); @@ -213,7 +213,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, if (PIM_DEBUG_ZEBRA) { zlog_debug( - "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d", + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags, ifp->metric, ifp->mtu, if_is_operative(ifp)); @@ -293,7 +293,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, if (PIM_DEBUG_ZEBRA) { char buf[BUFSIZ]; prefix2str(p, buf, BUFSIZ); - zlog_debug("%s: %s(%d) connected IP address %s flags %u %s", + zlog_debug("%s: %s(%u) connected IP address %s flags %u %s", __PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf, c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" @@ -372,7 +372,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, char buf[BUFSIZ]; prefix2str(p, buf, BUFSIZ); zlog_debug( - "%s: %s(%d) disconnected IP address %s flags %u %s", + "%s: %s(%u) disconnected IP address %s flags %u %s", __PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf, c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) diff --git a/pkgsrc/README.txt b/pkgsrc/README.txt index 13ec449a1f54..b70bb3fc1d6a 100644 --- a/pkgsrc/README.txt +++ b/pkgsrc/README.txt @@ -1,5 +1,3 @@ -$Id: README.txt,v 1.1 2004/08/27 15:57:35 gdt Exp $ - This directory contains files for use with the pkgsrc framework (http://www.pkgsrc.org) used with NetBSD and other operating systems. Eventually it will be hooked into automake such that they can be diff --git a/redhat/daemons b/redhat/daemons index 889e288e5766..1dd7c95d23b1 100644 --- a/redhat/daemons +++ b/redhat/daemons @@ -50,6 +50,7 @@ nhrpd=no eigrpd=no babeld=no sharpd=no +pbrd=no # # Command line options for the daemons # @@ -66,6 +67,7 @@ nhrpd_options=("-A 127.0.0.1") eigrpd_options=("-A 127.0.0.1") babeld_options=("-A 127.0.0.1") sharpd_options=("-A 127.0.0.1") +pbrd_options=("-A 127.0.0.1") # # If the vtysh_enable is yes, then the unified config is read diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 4e05f7828c84..3b13abd305b0 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -22,7 +22,7 @@ %{!?with_multipath: %global with_multipath 256 } %{!?frr_user: %global frr_user frr } %{!?vty_group: %global vty_group frrvty } -%{!?with_fpm: %global with_fpm 0 } +%{!?with_fpm: %global with_fpm 1 } %{!?with_watchfrr: %global with_watchfrr 1 } %{!?with_bgp_vnc: %global with_bgp_vnc 0 } %{!?with_pimd: %global with_pimd 1 } @@ -78,7 +78,7 @@ %{!?frr_gid: %global frr_gid 92 } %{!?vty_gid: %global vty_gid 85 } -%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d +%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd %if %{with_ldpd} %define daemon_ldpd ldpd @@ -554,6 +554,9 @@ rm -rf %{buildroot} %{_libdir}/lib*.so.0 %attr(755,root,root) %{_libdir}/lib*.so.0.* %endif +%if %{with_fpm} +%attr(755,root,root) %{_libdir}/frr/modules/zebra_fpm.so +%endif %attr(755,root,root) %{_libdir}/frr/modules/zebra_irdp.so %{_bindir}/* %config(noreplace) /etc/frr/[!v]*.conf* diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 041635e1533d..52a5d93c4ff4 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -48,6 +48,7 @@ static void rip_zebra_ipv4_send(struct route_node *rp, u_char cmd) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_RIP; api.safi = SAFI_UNICAST; diff --git a/ripd/ripd.conf.sample b/ripd/ripd.conf.sample index 2902ff9cca15..e11bf0bb2362 100644 --- a/ripd/ripd.conf.sample +++ b/ripd/ripd.conf.sample @@ -2,8 +2,6 @@ ! ! RIPd sample configuration file ! -! $Id: ripd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ -! hostname ripd password zebra ! diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 18a8d14f093e..ea069d877fe2 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -48,6 +48,7 @@ static void ripng_zebra_ipv6_send(struct route_node *rp, u_char cmd) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_RIPNG; api.safi = SAFI_UNICAST; api.prefix = rp->p; diff --git a/ripngd/ripngd.conf.sample b/ripngd/ripngd.conf.sample index ad673e57e0ba..28f08c399a67 100644 --- a/ripngd/ripngd.conf.sample +++ b/ripngd/ripngd.conf.sample @@ -2,8 +2,6 @@ ! ! RIPngd sample configuration file ! -! $Id: ripngd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ -! hostname ripngd password zebra ! diff --git a/sharpd/.gitignore b/sharpd/.gitignore new file mode 100644 index 000000000000..c396f3ef949b --- /dev/null +++ b/sharpd/.gitignore @@ -0,0 +1,18 @@ +Makefile +Makefile.in +*.o +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.a +*.libs +.arch-inventory +.arch-ids +*~ +*.loT +*clippy.c +sharpd +sharpd.conf diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 47bb37ce929f..a35157faa19e 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -29,7 +29,9 @@ #include "sharpd/sharp_zebra.h" #include "sharpd/sharp_vty.h" +#ifndef VTYSH_EXTRACT_PL #include "sharpd/sharp_vty_clippy.c" +#endif extern uint32_t total_routes; extern uint32_t installed_routes; diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 4a5ae13c4341..25bb512a8b6c 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -159,6 +159,7 @@ void route_add(struct prefix *p, struct nexthop *nh) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_SHARP; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); @@ -180,6 +181,7 @@ void route_delete(struct prefix *p) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; + api.nh_vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_SHARP; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); diff --git a/tests/Makefile.am b/tests/Makefile.am index 2ee05fa93549..f4ab2a126a29 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -64,6 +64,7 @@ check_PROGRAMS = \ lib/test_memory \ lib/test_nexthop_iter \ lib/test_privs \ + lib/test_ringbuf \ lib/test_srcdest_table \ lib/test_segv \ lib/test_sig \ @@ -116,6 +117,7 @@ lib_test_nexthop_iter_SOURCES = lib/test_nexthop_iter.c helpers/c/prng.c lib_test_privs_SOURCES = lib/test_privs.c lib_test_srcdest_table_SOURCES = lib/test_srcdest_table.c \ helpers/c/prng.c +lib_test_ringbuf_SOURCES = lib/test_ringbuf.c lib_test_segv_SOURCES = lib/test_segv.c lib_test_sig_SOURCES = lib/test_sig.c lib_test_stream_SOURCES = lib/test_stream.c @@ -156,6 +158,7 @@ lib_test_heavy_LDADD = $(ALL_TESTS_LDADD) -lm lib_test_memory_LDADD = $(ALL_TESTS_LDADD) lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) lib_test_privs_LDADD = $(ALL_TESTS_LDADD) +lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD) lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD) lib_test_segv_LDADD = $(ALL_TESTS_LDADD) lib_test_sig_LDADD = $(ALL_TESTS_LDADD) @@ -196,6 +199,7 @@ EXTRA_DIST = \ lib/cli/test_cli.py \ lib/cli/test_cli.refout \ lib/test_nexthop_iter.py \ + lib/test_ringbuf.py \ lib/test_srcdest_table.py \ lib/test_stream.py \ lib/test_stream.refout \ diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 46462d79c40c..56808bc8ad05 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -29,6 +29,7 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_attr.h" +#include "bgpd/bgp_packet.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -1273,20 +1274,20 @@ static int handle_attr_test(struct aspath_tests *t) asp = make_aspath(t->segment->asdata, t->segment->len, 0); - peer.ibuf = stream_new(BGP_MAX_PACKET_SIZE); + peer.curr = stream_new(BGP_MAX_PACKET_SIZE); peer.obuf = stream_fifo_new(); peer.bgp = &bgp; peer.host = (char *)"none"; peer.fd = -1; peer.cap = t->cap; - stream_write(peer.ibuf, t->attrheader, t->len); - datalen = aspath_put(peer.ibuf, asp, t->as4 == AS4_DATA); + stream_write(peer.curr, t->attrheader, t->len); + datalen = aspath_put(peer.curr, asp, t->as4 == AS4_DATA); if (t->old_segment) { char dummyaspath[] = {BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, t->old_segment->len}; - stream_write(peer.ibuf, dummyaspath, sizeof(dummyaspath)); - stream_write(peer.ibuf, t->old_segment->asdata, + stream_write(peer.curr, dummyaspath, sizeof(dummyaspath)); + stream_write(peer.curr, t->old_segment->asdata, t->old_segment->len); datalen += sizeof(dummyaspath) + t->old_segment->len; } diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index e8700a8b4ac8..a5092708e2e0 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -796,14 +796,15 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) int oldfailed = failed; int len = t->len; #define RANDOM_FUZZ 35 - stream_reset(peer->ibuf); - stream_put(peer->ibuf, NULL, RANDOM_FUZZ); - stream_set_getp(peer->ibuf, RANDOM_FUZZ); + + stream_reset(peer->curr); + stream_put(peer->curr, NULL, RANDOM_FUZZ); + stream_set_getp(peer->curr, RANDOM_FUZZ); switch (type) { case CAPABILITY: - stream_putc(peer->ibuf, BGP_OPEN_OPT_CAP); - stream_putc(peer->ibuf, t->len); + stream_putc(peer->curr, BGP_OPEN_OPT_CAP); + stream_putc(peer->curr, t->len); break; case DYNCAP: /* for (i = 0; i < BGP_MARKER_SIZE; i++) @@ -812,7 +813,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) stream_putc (s, BGP_MSG_CAPABILITY);*/ break; } - stream_write(peer->ibuf, t->data, t->len); + stream_write(peer->curr, t->data, t->len); printf("%s: %s\n", t->name, t->desc); @@ -825,7 +826,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) as4 = peek_for_as4_capability(peer, len); printf("peek_for_as4: as4 is %u\n", as4); /* and it should leave getp as it found it */ - assert(stream_get_getp(peer->ibuf) == RANDOM_FUZZ); + assert(stream_get_getp(peer->curr) == RANDOM_FUZZ); ret = bgp_open_option_parse(peer, len, &capability); break; @@ -837,7 +838,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) exit(1); } - if (!ret && t->validate_afi) { + if (ret != BGP_Stop && t->validate_afi) { afi_t afi; safi_t safi; @@ -865,10 +866,20 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) failed++; } + /* + * Some of the functions used return BGP_Stop on error and some return + * -1. If we have -1, keep it; if we have BGP_Stop, transform it to the + * correct pass/fail code + */ + if (ret != -1) + ret = (ret == BGP_Stop) ? -1 : 0; + printf("parsed?: %s\n", ret ? "no" : "yes"); - if (ret != t->parses) + if (ret != t->parses) { + printf("t->parses: %d\nret: %d\n", t->parses, ret); failed++; + } if (tty) printf("%s", @@ -919,6 +930,8 @@ int main(void) peer->afc_adv[i][j] = 1; } + peer->curr = stream_new(BGP_MAX_PACKET_SIZE); + i = 0; while (mp_segments[i].name) parse_test(peer, &mp_segments[i++], CAPABILITY); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 30d5fdd6cdbe..6df784b98487 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -36,6 +36,7 @@ #include "bgpd/bgp_packet.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_vty.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -1045,11 +1046,11 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) .startp = BGP_INPUT_PNT(peer), }; #define RANDOM_FUZZ 35 - stream_reset(peer->ibuf); - stream_put(peer->ibuf, NULL, RANDOM_FUZZ); - stream_set_getp(peer->ibuf, RANDOM_FUZZ); + stream_reset(peer->curr); + stream_put(peer->curr, NULL, RANDOM_FUZZ); + stream_set_getp(peer->curr, RANDOM_FUZZ); - stream_write(peer->ibuf, t->data, t->len); + stream_write(peer->curr, t->data, t->len); printf("%s: %s\n", t->name, t->desc); @@ -1097,7 +1098,9 @@ int main(void) term_bgp_debug_as4 = -1UL; qobj_init(); - master = thread_master_create(NULL); + cmd_init(0); + bgp_vty_init(); + master = thread_master_create("test mp attr"); bgp_master_init(master); vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); @@ -1112,6 +1115,7 @@ int main(void) peer = peer_create_accept(bgp); peer->host = (char *)"foo"; peer->status = Established; + peer->curr = stream_new(BGP_MAX_PACKET_SIZE); for (i = AFI_IP; i < AFI_MAX; i++) for (j = SAFI_UNICAST; j < SAFI_MAX; j++) { diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index 298dd1e185fb..c58a85eed37a 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -80,6 +80,6 @@ int main(int argc, char *argv[]) peer->fd = open(argv[1], O_RDONLY|O_NONBLOCK); t.arg = peer; peer->t_read = &t; - - printf("bgp_read_packet returns: %d\n", bgp_read(&t)); + + // printf("bgp_read_packet returns: %d\n", bgp_read(&t)); } diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 9bf56dde14d5..fed1d5a53735 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -1,6 +1,4 @@ /* - * $Id: main.c,v 1.1 2005/04/25 16:42:24 paul Exp $ - * * This file is part of Quagga. * * Quagga is free software; you can redistribute it and/or modify it diff --git a/tests/lib/test_heavy.c b/tests/lib/test_heavy.c index 604a5733cab4..e2a0a2d49a1e 100644 --- a/tests/lib/test_heavy.c +++ b/tests/lib/test_heavy.c @@ -1,6 +1,4 @@ /* - * $Id: heavy.c,v 1.3 2005/04/25 16:42:24 paul Exp $ - * * This file is part of Quagga. * * Quagga is free software; you can redistribute it and/or modify it diff --git a/tests/lib/test_heavy_thread.c b/tests/lib/test_heavy_thread.c index 9a75780060cd..075bcb6daf7b 100644 --- a/tests/lib/test_heavy_thread.c +++ b/tests/lib/test_heavy_thread.c @@ -1,6 +1,4 @@ /* - * $Id: heavy-thread.c,v 1.2 2005/04/25 16:42:24 paul Exp $ - * * This file is part of Quagga. * * Quagga is free software; you can redistribute it and/or modify it diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index 1984f28e635d..421c34543657 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -1,6 +1,4 @@ /* - * $Id: test-privs.c,v 1.1 2005/10/11 03:48:28 paul Exp $ - * * This file is part of Quagga. * * Quagga is free software; you can redistribute it and/or modify it diff --git a/tests/lib/test_ringbuf.c b/tests/lib/test_ringbuf.c new file mode 100644 index 000000000000..7ba5a29b6225 --- /dev/null +++ b/tests/lib/test_ringbuf.c @@ -0,0 +1,190 @@ +/* + * Circular buffer tests. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include "ringbuf.h" + +static void validate_state(struct ringbuf *buf, size_t size, size_t contains) +{ + assert(buf->size == size); + assert(ringbuf_remain(buf) == contains); + assert(ringbuf_space(buf) == buf->size - contains); + assert(buf->empty != (bool)contains); +} + +int main(int argc, char **argv) +{ + struct ringbuf *soil = ringbuf_new(BUFSIZ); + + validate_state(soil, BUFSIZ, 0); + + /* verify reset functionality on clean buffer */ + printf("Validating reset on empty buffer...\n"); + ringbuf_reset(soil); + + validate_state(soil, BUFSIZ, 0); + + /* put one byte */ + printf("Validating write...\n"); + uint8_t walnut = 47; + assert(ringbuf_put(soil, &walnut, sizeof(walnut)) == 1); + + validate_state(soil, BUFSIZ, 1); + + /* validate read limitations */ + printf("Validating read limits...\n"); + uint8_t nuts[2]; + assert(ringbuf_get(soil, &nuts, sizeof(nuts)) == 1); + + /* reset */ + printf("Validating reset on full buffer...\n"); + ringbuf_reset(soil); + validate_state(soil, BUFSIZ, 0); + + /* copy stack garbage to buffer */ + printf("Validating big write...\n"); + uint8_t compost[BUFSIZ]; + assert(ringbuf_put(soil, &compost, sizeof(compost)) == BUFSIZ); + + validate_state(soil, BUFSIZ, BUFSIZ); + assert(soil->start == 0); + assert(soil->end == 0); + + /* read 15 bytes of garbage */ + printf("Validating read...\n"); + assert(ringbuf_get(soil, &compost, 15) == 15); + + validate_state(soil, BUFSIZ, BUFSIZ - 15); + assert(soil->start == 15); + assert(soil->end == 0); + + /* put another 10 bytes and validate wraparound */ + printf("Validating wraparound...\n"); + assert(ringbuf_put(soil, &compost[BUFSIZ/2], 10) == 10); + + validate_state(soil, BUFSIZ, BUFSIZ - 15 + 10); + assert(soil->start == 15); + assert(soil->end == 10); + + /* put another 15 bytes and validate state */ + printf("Validating size limits...\n"); + assert(ringbuf_put(soil, &compost, 15) == 5); + validate_state(soil, BUFSIZ, BUFSIZ); + + /* read entire buffer */ + printf("Validating big read...\n"); + assert(ringbuf_get(soil, &compost, BUFSIZ) == BUFSIZ); + + validate_state(soil, BUFSIZ, 0); + assert(soil->empty = true); + assert(soil->start == soil->end); + assert(soil->start == 15); + + /* read empty buffer */ + printf("Validating empty read...\n"); + assert(ringbuf_get(soil, &compost, 1) == 0); + validate_state(soil, BUFSIZ, 0); + + /* reset, validate state */ + printf("Validating reset...\n"); + ringbuf_reset(soil); + validate_state(soil, BUFSIZ, 0); + assert(soil->start == 0); + assert(soil->end == 0); + + /* wipe, validate state */ + printf("Validating wipe...\n"); + memset(&compost, 0x00, sizeof(compost)); + ringbuf_wipe(soil); + assert(memcmp(&compost, soil->data, sizeof(compost)) == 0); + + /* validate maximum write */ + printf("Validating very big write...\n"); + const char flower[BUFSIZ * 2]; + assert(ringbuf_put(soil, &flower, sizeof(flower)) == BUFSIZ); + + validate_state(soil, BUFSIZ, BUFSIZ); + + /* wipe, validate state */ + printf("Validating wipe...\n"); + memset(&compost, 0x00, sizeof(compost)); + ringbuf_wipe(soil); + assert(memcmp(&compost, soil->data, sizeof(compost)) == 0); + + /* validate simple data encode / decode */ + const char *organ = "seed"; + printf("Encoding: '%s'\n", organ); + assert(ringbuf_put(soil, organ, strlen(organ)) == 4); + char water[strlen(organ) + 1]; + assert(ringbuf_get(soil, &water, strlen(organ)) == 4); + water[strlen(organ)] = '\0'; + printf("Retrieved: '%s'\n", water); + + validate_state(soil, BUFSIZ, 0); + + /* validate simple data encode / decode across ring boundary */ + soil->start = soil->size - 2; + soil->end = soil->start; + const char *phloem = "root"; + printf("Encoding: '%s'\n", phloem); + assert(ringbuf_put(soil, phloem, strlen(phloem)) == 4); + char xylem[strlen(phloem) + 1]; + assert(ringbuf_get(soil, &xylem, 100) == 4); + xylem[strlen(phloem)] = '\0'; + printf("Retrieved: '%s'\n", xylem); + + ringbuf_wipe(soil); + + /* validate simple data peek across ring boundary */ + soil->start = soil->size - 2; + soil->end = soil->start; + const char *cytoplasm = "tree"; + printf("Encoding: '%s'\n", cytoplasm); + assert(ringbuf_put(soil, cytoplasm, strlen(cytoplasm)) == 4); + char chloroplast[strlen(cytoplasm) + 1]; + assert(ringbuf_peek(soil, 2, &chloroplast[0], 100) == 2); + assert(ringbuf_peek(soil, 0, &chloroplast[2], 2) == 2); + chloroplast[strlen(cytoplasm)] = '\0'; + assert(!strcmp(chloroplast, "eetr")); + printf("Retrieved: '%s'\n", chloroplast); + + printf("Deleting...\n"); + ringbuf_del(soil); + + printf("Creating new buffer...\n"); + soil = ringbuf_new(15); + soil->start = soil->end = 7; + + /* validate data encode of excessive data */ + const char *twenty = "vascular plants----"; + char sixteen[16]; + printf("Encoding: %s\n", twenty); + assert(ringbuf_put(soil, twenty, strlen(twenty)) == 15); + assert(ringbuf_get(soil, sixteen, 20)); + sixteen[15] = '\0'; + printf("Retrieved: %s\n", sixteen); + assert(!strcmp(sixteen, "vascular plants")); + + printf("Deleting...\n"); + ringbuf_del(soil); + + printf("Done.\n"); + return 0; +} diff --git a/tests/lib/test_ringbuf.py b/tests/lib/test_ringbuf.py new file mode 100644 index 000000000000..860d872f860c --- /dev/null +++ b/tests/lib/test_ringbuf.py @@ -0,0 +1,4 @@ +import frrtest + +class TestRingbuf(frrtest.TestExitNonzero): + program = './test_ringbuf' diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c index c270ec3d18f8..b6624915e874 100644 --- a/tests/lib/test_zmq.c +++ b/tests/lib/test_zmq.c @@ -23,6 +23,7 @@ #include "frr_zmq.h" DEFINE_MTYPE_STATIC(LIB, TESTBUF, "zmq test buffer") +DEFINE_MTYPE_STATIC(LIB, ZMQMSG, "zmq message") static struct thread_master *master; @@ -31,6 +32,25 @@ static void msg_buf_free(void *data, void *hint) XFREE(MTYPE_TESTBUF, data); } +static int recv_delim(void *zmqsock) +{ + /* receive delim */ + zmq_msg_t zdelim; + int more; + zmq_msg_init(&zdelim); + zmq_msg_recv(&zdelim, zmqsock, 0); + more = zmq_msg_more(&zdelim); + zmq_msg_close(&zdelim); + return more; +} +static void send_delim(void *zmqsock) +{ + /* Send delim */ + zmq_msg_t zdelim; + zmq_msg_init(&zdelim); + zmq_msg_send(&zdelim, zmqsock, ZMQ_SNDMORE); + zmq_msg_close(&zdelim); +} static void run_client(int syncfd) { int i, j; @@ -38,13 +58,14 @@ static void run_client(int syncfd) char dummy; void *zmqctx = NULL; void *zmqsock; + int more; read(syncfd, &dummy, 1); zmqctx = zmq_ctx_new(); zmq_ctx_set(zmqctx, ZMQ_IPV6, 1); - zmqsock = zmq_socket(zmqctx, ZMQ_REQ); + zmqsock = zmq_socket(zmqctx, ZMQ_DEALER); if (zmq_connect(zmqsock, "tcp://127.0.0.1:17171")) { perror("zmq_connect"); exit(1); @@ -52,22 +73,28 @@ static void run_client(int syncfd) /* single-part */ for (i = 0; i < 8; i++) { - snprintf(buf, sizeof(buf), "msg #%d %c%c%c", - i, 'a' + i, 'b' + i, 'c' + i); + snprintf(buf, sizeof(buf), "msg #%d %c%c%c", i, 'a' + i, + 'b' + i, 'c' + i); printf("client send: %s\n", buf); fflush(stdout); - zmq_send(zmqsock, buf, strlen(buf) + 1, 0); - zmq_recv(zmqsock, buf, sizeof(buf), 0); - printf("client recv: %s\n", buf); + send_delim(zmqsock); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + more = recv_delim(zmqsock); + while (more) { + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("client recv: %s\n", buf); + size_t len = sizeof(more); + if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len)) + break; + } } /* multipart */ for (i = 2; i < 5; i++) { - int more; - printf("---\n"); + send_delim(zmqsock); + zmq_msg_t part; for (j = 1; j <= i; j++) { - zmq_msg_t part; char *dyn = XMALLOC(MTYPE_TESTBUF, 32); snprintf(dyn, 32, "part %d/%d", j, i); @@ -79,7 +106,7 @@ static void run_client(int syncfd) zmq_msg_send(&part, zmqsock, j < i ? ZMQ_SNDMORE : 0); } - zmq_msg_t part; + recv_delim(zmqsock); do { char *data; @@ -90,26 +117,85 @@ static void run_client(int syncfd) } while (more); zmq_msg_close(&part); } + + /* write callback */ + printf("---\n"); + snprintf(buf, 32, "Done receiving"); + printf("client send: %s\n", buf); + fflush(stdout); + send_delim(zmqsock); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + /* wait for message from server */ + more = recv_delim(zmqsock); + while (more) { + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("client recv: %s\n", buf); + size_t len = sizeof(more); + if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len)) + break; + } + zmq_close(zmqsock); zmq_ctx_term(zmqctx); } static struct frrzmq_cb *cb; +static void recv_id_and_delim(void *zmqsock, zmq_msg_t *msg_id) +{ + /* receive id */ + zmq_msg_init(msg_id); + zmq_msg_recv(msg_id, zmqsock, 0); + /* receive delim */ + recv_delim(zmqsock); +} +static void send_id_and_delim(void *zmqsock, zmq_msg_t *msg_id) +{ + /* Send Id */ + zmq_msg_send(msg_id, zmqsock, ZMQ_SNDMORE); + send_delim(zmqsock); +} +static void serverwritefn(void *arg, void *zmqsock) +{ + zmq_msg_t *msg_id = (zmq_msg_t *)arg; + char buf[32] = "Test write callback"; + size_t i; + + for (i = 0; i < strlen(buf); i++) + buf[i] = toupper(buf[i]); + printf("server send: %s\n", buf); + fflush(stdout); + send_id_and_delim(zmqsock, msg_id); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + + /* send just once */ + frrzmq_thread_cancel(&cb, &cb->write); + + zmq_msg_close(msg_id); + XFREE(MTYPE_ZMQMSG, msg_id); +} static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg, - unsigned partnum) + unsigned partnum) { + static int num = 0; int more = zmq_msg_more(msg); char *in = zmq_msg_data(msg); size_t i; zmq_msg_t reply; char *out; + /* Id */ + if (partnum == 0) { + send_id_and_delim(zmqsock, msg); + return; + } + /* Delim */ + if (partnum == 1) + return; + + printf("server recv part %u (more: %d): %s\n", partnum, more, in); fflush(stdout); - /* REQ-REP doesn't allow sending a reply here */ - if (more) - return; out = XMALLOC(MTYPE_TESTBUF, strlen(in) + 1); for (i = 0; i < strlen(in); i++) @@ -118,39 +204,66 @@ static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg, zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL); zmq_msg_send(&reply, zmqsock, ZMQ_SNDMORE); + if (more) + return; + out = XMALLOC(MTYPE_TESTBUF, 32); snprintf(out, 32, "msg# was %u", partnum); zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL); zmq_msg_send(&reply, zmqsock, 0); + + zmq_msg_close(&reply); + + if (++num < 7) + return; + + /* write callback test */ + char buf[32]; + zmq_msg_t *msg_id = XMALLOC(MTYPE_ZMQMSG, sizeof(zmq_msg_t)); + recv_id_and_delim(zmqsock, msg_id); + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("server recv: %s\n", buf); + fflush(stdout); + + frrzmq_thread_add_write_msg(master, serverwritefn, NULL, msg_id, + zmqsock, &cb); } static void serverfn(void *arg, void *zmqsock) { static int num = 0; + zmq_msg_t msg_id; char buf[32]; size_t i; + + recv_id_and_delim(zmqsock, &msg_id); zmq_recv(zmqsock, buf, sizeof(buf), 0); printf("server recv: %s\n", buf); fflush(stdout); for (i = 0; i < strlen(buf); i++) buf[i] = toupper(buf[i]); + send_id_and_delim(zmqsock, &msg_id); + zmq_msg_close(&msg_id); zmq_send(zmqsock, buf, strlen(buf) + 1, 0); if (++num < 4) return; /* change to multipart callback */ - frrzmq_thread_cancel(cb); + frrzmq_thread_cancel(&cb, &cb->read); + frrzmq_thread_cancel(&cb, &cb->write); - cb = frrzmq_thread_add_read_part(master, serverpartfn, NULL, zmqsock); + frrzmq_thread_add_read_part(master, serverpartfn, NULL, NULL, zmqsock, + &cb); } static void sigchld(void) { printf("child exited.\n"); - frrzmq_thread_cancel(cb); + frrzmq_thread_cancel(&cb, &cb->read); + frrzmq_thread_cancel(&cb, &cb->write); } static struct quagga_signal_t sigs[] = { @@ -170,13 +283,13 @@ static void run_server(int syncfd) signal_init(master, array_size(sigs), sigs); frrzmq_init(); - zmqsock = zmq_socket(frrzmq_context, ZMQ_REP); + zmqsock = zmq_socket(frrzmq_context, ZMQ_ROUTER); if (zmq_bind(zmqsock, "tcp://*:17171")) { perror("zmq_bind"); exit(1); } - cb = frrzmq_thread_add_read_msg(master, serverfn, NULL, zmqsock); + frrzmq_thread_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb); write(syncfd, &dummy, sizeof(dummy)); while (thread_fetch(master, &t)) diff --git a/tests/lib/test_zmq.refout b/tests/lib/test_zmq.refout index 61f45f02b137..acac50553d15 100644 --- a/tests/lib/test_zmq.refout +++ b/tests/lib/test_zmq.refout @@ -11,40 +11,57 @@ client send: msg #3 def server recv: msg #3 def client recv: MSG #3 DEF client send: msg #4 efg -server recv part 0 (more: 0): msg #4 efg +server recv part 2 (more: 0): msg #4 efg client recv: MSG #4 EFG +client recv: msg# was 2 client send: msg #5 fgh -client recv: msg# was 0 +server recv part 2 (more: 0): msg #5 fgh +client recv: MSG #5 FGH +client recv: msg# was 2 client send: msg #6 ghi -server recv part 0 (more: 0): msg #6 ghi +server recv part 2 (more: 0): msg #6 ghi client recv: MSG #6 GHI +client recv: msg# was 2 client send: msg #7 hij -client recv: msg# was 0 +server recv part 2 (more: 0): msg #7 hij +client recv: MSG #7 HIJ +client recv: msg# was 2 --- client send: part 1/2 client send: part 2/2 -server recv part 0 (more: 1): part 1/2 -server recv part 1 (more: 0): part 2/2 +server recv part 2 (more: 1): part 1/2 +server recv part 3 (more: 0): part 2/2 +client recv (more: 1): PART 1/2 client recv (more: 1): PART 2/2 -client recv (more: 0): msg# was 1 +client recv (more: 0): msg# was 3 --- client send: part 1/3 client send: part 2/3 client send: part 3/3 -server recv part 0 (more: 1): part 1/3 -server recv part 1 (more: 1): part 2/3 -server recv part 2 (more: 0): part 3/3 +server recv part 2 (more: 1): part 1/3 +server recv part 3 (more: 1): part 2/3 +server recv part 4 (more: 0): part 3/3 +client recv (more: 1): PART 1/3 +client recv (more: 1): PART 2/3 client recv (more: 1): PART 3/3 -client recv (more: 0): msg# was 2 +client recv (more: 0): msg# was 4 --- client send: part 1/4 client send: part 2/4 client send: part 3/4 client send: part 4/4 -server recv part 0 (more: 1): part 1/4 -server recv part 1 (more: 1): part 2/4 -server recv part 2 (more: 1): part 3/4 -server recv part 3 (more: 0): part 4/4 +server recv part 2 (more: 1): part 1/4 +server recv part 3 (more: 1): part 2/4 +server recv part 4 (more: 1): part 3/4 +server recv part 5 (more: 0): part 4/4 +client recv (more: 1): PART 1/4 +client recv (more: 1): PART 2/4 +client recv (more: 1): PART 3/4 client recv (more: 1): PART 4/4 -client recv (more: 0): msg# was 3 +client recv (more: 0): msg# was 5 +--- +client send: Done receiving +server recv: Done receiving +server send: TEST WRITE CALLBACK +client recv: TEST WRITE CALLBACK child exited. diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index ac17fed03a06..9a96c0490ae0 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -34,3 +34,4 @@ nhrpd=no eigrpd=no babeld=no sharpd=no +pbrd=no diff --git a/tools/etc/frr/daemons.conf b/tools/etc/frr/daemons.conf index 3f40836c90bd..e6c0cde9686b 100644 --- a/tools/etc/frr/daemons.conf +++ b/tools/etc/frr/daemons.conf @@ -17,10 +17,11 @@ nhrpd_options=" --daemon -A 127.0.0.1" eigrpd_options=" --daemon -A 127.0.0.1" babeld_options=" --daemon -A 127.0.0.1" sharpd_options=" --daemon -A 127.0.0.1" +pbrd_options=" --daemon -A 127.0.0.1" # The list of daemons to watch is automatically generated by the init script. watchfrr_enable=yes -watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB -t 30) +watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB) # If valgrind_enable is 'yes' the frr daemons will be started via valgrind. # The use case for doing so is tracking down memory leaks, etc in frr. diff --git a/tools/frr b/tools/frr index 0f6140d3ecf3..065be665ae4f 100755 --- a/tools/frr +++ b/tools/frr @@ -21,7 +21,7 @@ V_PATH=/var/run/frr # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd" MAX_INSTANCES=5 RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 2c651ffbd52c..0b7e80962cbd 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -434,7 +434,14 @@ def load_contexts(self): new_ctx = False log.debug('LINE %-50s: entering new context, %-50s', line, ctx_keys) - elif "vni " in line: + # The 'vni' keyword under 'router bgp X/address-family l2vpn evpn' creates + # a sub-context but the 'vni' keyword in other places (such as 'vrf BLUE') + # does not. + elif ("vni " in line and + len(ctx_keys) == 2 and + ctx_keys[0].startswith('router bgp') and + ctx_keys[1] == 'address-family l2vpn evpn'): + main_ctx_key = [] # Save old context first diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 3ddb6aba5409..d9354ff1b7a5 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -140,6 +140,10 @@ if SNMP vtysh_scan += $(top_srcdir)/lib/agentx.c endif +if PBRD +vtysh_scan += $(top_srcdir)/pbrd/pbr_vty.c +endif + vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \ $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \ @@ -147,6 +151,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ $(top_srcdir)/lib/ns.c \ + $(top_srcdir)/lib/nexthop_group.c \ $(top_srcdir)/zebra/interface.c \ $(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 76343ded602c..b74857aa1cfb 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -46,6 +46,9 @@ DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy") +/* Destination for vtysh output */ +FILE *outputfile; + /* Struct VTY. */ struct vty *vty; @@ -74,8 +77,9 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .next = NULL}, {.fd = -1, .name = "eigrpd", .flag = VTYSH_EIGRPD, .next = NULL}, {.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL}, - {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL}, + {.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL}, {.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL}, + {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL}, }; enum vtysh_write_integrated vtysh_write_integrated = @@ -138,16 +142,25 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line, bufvalid += nread; - end = memmem(buf, bufvalid - buf, terminator, - sizeof(terminator)); - if (end + sizeof(terminator) + 1 > bufvalid) + if (bufvalid - buf >= 4) + end = memmem(bufvalid - 4, 4, terminator, + sizeof(terminator)); + + if (end && end + sizeof(terminator) + 1 > bufvalid) /* found \0\0\0 but return code hasn't been read yet */ end = NULL; if (end) ret = end[sizeof(terminator)]; - while (bufvalid > buf && (end > buf || !end)) { - size_t textlen = (end ? end : bufvalid) - buf; + /* + * calculate # bytes we have, up to & not including the + * terminator if present + */ + size_t textlen = (end ? end : bufvalid) - buf; + + /* feed line processing callback if present */ + while (callback && bufvalid > buf && (end > buf || !end)) { + textlen = (end ? end : bufvalid) - buf; char *eol = memchr(buf, '\n', textlen); if (eol) /* line break */ @@ -165,8 +178,7 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line, /* continue reading */ break; - /* eol is at a line end now, either \n => \0 or \0\0\0 - */ + /* eol is at line end now, either \n => \0 or \0\0\0 */ assert(eol && eol <= bufvalid); if (fp) { @@ -186,6 +198,14 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line, end -= eol - buf; } + /* else if no callback, dump raw */ + if (!callback) { + if (fp) + fwrite(buf, 1, textlen, fp); + memmove(buf, buf + textlen, bufvalid - buf - textlen); + bufvalid -= textlen; + } + if (bufvalid == buf + bufsz) { char *new; bufsz *= 2; @@ -375,21 +395,21 @@ static int vtysh_execute_func(const char *line, int pager) fprintf(stdout, "%% Command incomplete.\n"); break; case CMD_SUCCESS_DAEMON: { - /* FIXME: Don't open pager for exit commands. popen() causes - * problems - * if exited from vtysh at all. This hack shouldn't cause any - * problem - * but is really ugly. */ - if (pager && vtysh_pager_name + /* + * FIXME: Don't open pager for exit commands. popen() causes + * problems if exited from vtysh at all. This hack shouldn't + * cause any problem but is really ugly. + */ + fp = outputfile; + if (pager && vtysh_pager_name && outputfile == stdout && (strncmp(line, "exit", 4) != 0)) { fp = popen(vtysh_pager_name, "w"); if (fp == NULL) { perror("popen failed for pager"); - fp = stdout; + fp = outputfile; } else closepager = 1; - } else - fp = stdout; + } if (!strcmp(cmd->string, "configure terminal")) { for (i = 0; i < array_size(vtysh_client); i++) { @@ -405,7 +425,7 @@ static int vtysh_execute_func(const char *line, int pager) if (vline == NULL) { if (pager && vtysh_pager_name && fp - && closepager) { + && fp != outputfile && closepager) { if (pclose(fp) == -1) { perror("pclose failed for pager"); } @@ -455,7 +475,7 @@ static int vtysh_execute_func(const char *line, int pager) (*cmd->func)(cmd, vty, 0, NULL); } } - if (pager && vtysh_pager_name && fp && closepager) { + if (pager && vtysh_pager_name && fp && closepager && fp != outputfile) { if (pclose(fp) == -1) { perror("pclose failed for pager"); } @@ -537,19 +557,19 @@ int vtysh_mark_file(const char *filename) switch (vty->node) { case LDP_IPV4_IFACE_NODE: if (strncmp(vty_buf_copy, " ", 3)) { - fprintf(stdout, " end\n"); + fprintf(outputfile, " end\n"); vty->node = LDP_IPV4_NODE; } break; case LDP_IPV6_IFACE_NODE: if (strncmp(vty_buf_copy, " ", 3)) { - fprintf(stdout, " end\n"); + fprintf(outputfile, " end\n"); vty->node = LDP_IPV6_NODE; } break; case LDP_PSEUDOWIRE_NODE: if (strncmp(vty_buf_copy, " ", 2)) { - fprintf(stdout, " end\n"); + fprintf(outputfile, " end\n"); vty->node = LDP_L2VPN_NODE; } break; @@ -558,7 +578,7 @@ int vtysh_mark_file(const char *filename) } if (vty_buf_trimmed[0] == '!' || vty_buf_trimmed[0] == '#') { - fprintf(stdout, "%s", vty->buf); + fprintf(outputfile, "%s", vty->buf); continue; } @@ -566,7 +586,7 @@ int vtysh_mark_file(const char *filename) vline = cmd_make_strvec(vty->buf); if (vline == NULL) { - fprintf(stdout, "%s", vty->buf); + fprintf(outputfile, "%s", vty->buf); continue; } @@ -609,15 +629,15 @@ int vtysh_mark_file(const char *filename) || prev_node == BGP_IPV6M_NODE || prev_node == BGP_EVPN_NODE) && (tried == 1)) { - fprintf(stdout, "exit-address-family\n"); + fprintf(outputfile, "exit-address-family\n"); } else if ((prev_node == BGP_EVPN_VNI_NODE) && (tried == 1)) { - fprintf(stdout, "exit-vni\n"); + fprintf(outputfile, "exit-vni\n"); } else if ((prev_node == KEYCHAIN_KEY_NODE) && (tried == 1)) { - fprintf(stdout, "exit\n"); + fprintf(outputfile, "exit\n"); } else if (tried) { - fprintf(stdout, "end\n"); + fprintf(outputfile, "end\n"); } } /* If command didn't succeed in any node, continue with return @@ -667,12 +687,12 @@ int vtysh_mark_file(const char *filename) u_int i; int cmd_stat = CMD_SUCCESS; - fprintf(stdout, "%s", vty->buf); + fprintf(outputfile, "%s", vty->buf); for (i = 0; i < array_size(vtysh_client); i++) { if (cmd->daemon & vtysh_client[i].flag) { cmd_stat = vtysh_client_execute( &vtysh_client[i], vty->buf, - stdout); + outputfile); if (cmd_stat != CMD_SUCCESS) break; } @@ -686,7 +706,7 @@ int vtysh_mark_file(const char *filename) } } /* This is the end */ - fprintf(stdout, "\nend\n"); + fprintf(outputfile, "\nend\n"); vty_close(vty); XFREE(MTYPE_VTYSH_CMD, vty_buf_copy); @@ -749,7 +769,7 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) if (cmd->daemon & vtysh_client[i].flag) { cmd_stat = vtysh_client_execute( &vtysh_client[i], vty->buf, - stdout); + outputfile); /* * CMD_WARNING - Can mean that the * command was @@ -964,6 +984,11 @@ static struct cmd_node vrf_node = { VRF_NODE, "%s(config-vrf)# ", }; +static struct cmd_node nh_group_node = { + NH_GROUP_NODE, + "%s(config-nh-group)# ", +}; + static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# "}; static struct cmd_node zebra_node = {ZEBRA_NODE, "%s(config-router)# "}; @@ -1491,6 +1516,7 @@ static int vtysh_exit(struct vty *vty) case PW_NODE: case NS_NODE: case VRF_NODE: + case NH_GROUP_NODE: case ZEBRA_NODE: case BGP_NODE: case RIP_NODE: @@ -1773,6 +1799,20 @@ DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME", return CMD_SUCCESS; } +DEFUNSH(VTYSH_PBRD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, + "nexthop-group NAME", + "Nexthop Group configuration\n" + "Name of the Nexthop Group\n") +{ + vty->node = NH_GROUP_NODE; + return CMD_SUCCESS; +} + +DEFSH(VTYSH_PBRD, vtysh_no_nexthop_group_cmd, "no nexthop-group NAME", + NO_STR + "Nexthop Group Configuration\n" + "Name of the Nexthop Group\n") + DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME", "Select a VRF to configure\n" "VRF's name\n") @@ -1809,6 +1849,18 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } +DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, + "exit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_VRF, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, + "qui", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_nexthop_group(self, vty, argc, argv); +} + /* TODO Implement interface description commands in ripngd, ospf6d * and isisd. */ DEFSH(VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_OSPFD | VTYSH_EIGRPD, @@ -1854,7 +1906,7 @@ DEFUN (vtysh_show_thread, fprintf(stdout, "Thread statistics for %s:\n", vtysh_client[i].name); ret = vtysh_client_execute(&vtysh_client[i], line, - stdout); + outputfile); fprintf(stdout, "\n"); } return ret; @@ -1875,7 +1927,7 @@ DEFUN (vtysh_show_work_queues, fprintf(stdout, "Work queue statistics for %s:\n", vtysh_client[i].name); ret = vtysh_client_execute(&vtysh_client[i], line, - stdout); + outputfile); fprintf(stdout, "\n"); } @@ -1905,7 +1957,7 @@ DEFUN (vtysh_show_work_queues_daemon, } ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n", - stdout); + outputfile); return ret; } @@ -1932,9 +1984,9 @@ static int show_per_daemon(const char *line, const char *headline) for (i = 0; i < array_size(vtysh_client); i++) if (vtysh_client[i].fd >= 0) { - fprintf(stdout, headline, vtysh_client[i].name); + fprintf(outputfile, headline, vtysh_client[i].name); ret = vtysh_client_execute(&vtysh_client[i], line, - stdout); + outputfile); fprintf(stdout, "\n"); } @@ -2226,20 +2278,19 @@ DEFUN (vtysh_write_terminal, { u_int i; char line[] = "do write terminal\n"; - FILE *fp = NULL; + FILE *fp = outputfile; - if (vtysh_pager_name) { + if (fp == stdout && vtysh_pager_name) { fp = popen(vtysh_pager_name, "w"); if (fp == NULL) { perror("popen"); exit(1); } - } else - fp = stdout; + } - vty_out(vty, "Building configuration...\n"); - vty_out(vty, "\nCurrent configuration:\n"); - vty_out(vty, "!\n"); + fprintf(outputfile, "Building configuration...\n"); + fprintf(outputfile, "\nCurrent configuration:\n"); + fprintf(outputfile, "!\n"); for (i = 0; i < array_size(vtysh_client); i++) if ((argc < 3) @@ -2251,7 +2302,7 @@ DEFUN (vtysh_write_terminal, vtysh_config_dump(fp); - if (vtysh_pager_name && fp) { + if (vtysh_pager_name && fp && fp != outputfile) { fflush(fp); if (pclose(fp) == -1) { perror("pclose"); @@ -2260,7 +2311,7 @@ DEFUN (vtysh_write_terminal, fp = NULL; } - vty_out(vty, "end\n"); + fprintf(outputfile, "end\n"); return CMD_SUCCESS; } @@ -2429,7 +2480,7 @@ DEFUN (vtysh_write_memory, char line[] = "do write memory\n"; u_int i; - fprintf(stdout, + fprintf(outputfile, "Note: this version of vtysh never writes vtysh.conf\n"); /* If integrated frr.conf explicitely set. */ @@ -2441,7 +2492,7 @@ DEFUN (vtysh_write_memory, if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1) ret = vtysh_client_execute(&vtysh_client[i], "do write integrated", - stdout); + outputfile); if (ret != CMD_SUCCESS) { printf("\nWarning: attempting direct configuration write without " @@ -2452,10 +2503,10 @@ DEFUN (vtysh_write_memory, return ret; } - fprintf(stdout, "Building Configuration...\n"); + fprintf(outputfile, "Building Configuration...\n"); for (i = 0; i < array_size(vtysh_client); i++) - ret = vtysh_client_execute(&vtysh_client[i], line, stdout); + ret = vtysh_client_execute(&vtysh_client[i], line, outputfile); return ret; } @@ -2484,7 +2535,7 @@ DEFUN (vtysh_terminal_length, lines = strtol(argv[idx_number]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { - vty_out(vty, "length is malformed\n"); + fprintf(outputfile, "length is malformed\n"); return CMD_WARNING; } @@ -2527,8 +2578,8 @@ DEFUN (vtysh_show_daemons, for (i = 0; i < array_size(vtysh_client); i++) if (vtysh_client[i].fd >= 0) - vty_out(vty, " %s", vtysh_client[i].name); - vty_out(vty, "\n"); + fprintf(outputfile, " %s", vtysh_client[i].name); + fprintf(outputfile, "\n"); return CMD_SUCCESS; } @@ -2703,6 +2754,38 @@ DEFUN (config_list, return cmd_list_cmds(vty, argc == 2); } +DEFUN (vtysh_output_file, + vtysh_output_file_cmd, + "output file FILE", + "Direct vtysh output to file\n" + "Direct vtysh output to file\n" + "Path to dump output to\n") +{ + const char *path = argv[argc - 1]->arg; + outputfile = fopen(path, "a"); + if (!outputfile) { + fprintf(stdout, "Failed to open file '%s': %s\n", path, + safe_strerror(errno)); + outputfile = stdout; + } + return CMD_SUCCESS; +} + +DEFUN (no_vtysh_output_file, + no_vtysh_output_file_cmd, + "no output file [FILE]", + NO_STR + "Direct vtysh output to file\n" + "Direct vtysh output to file\n" + "Path to dump output to\n") +{ + if (outputfile != stdout) { + fclose(outputfile); + outputfile = stdout; + } + return CMD_SUCCESS; +} + DEFUN(find, find_cmd, "find COMMAND...", @@ -2736,6 +2819,8 @@ static void vtysh_install_default(enum node_type node) { install_element(node, &config_list_cmd); install_element(node, &find_cmd); + install_element(node, &vtysh_output_file_cmd); + install_element(node, &no_vtysh_output_file_cmd); } /* Making connection to protocol daemon. */ @@ -2964,6 +3049,12 @@ static const struct cmd_variable_handler vtysh_var_handler[] = { .completions = vtysh_autocomplete}, {.completions = NULL}}; +void vtysh_uninit() +{ + if (outputfile != stdout) + fclose(outputfile); +} + void vtysh_init_vty(void) { /* Make vty structure. */ @@ -2971,6 +3062,9 @@ void vtysh_init_vty(void) vty->type = VTY_SHELL; vty->node = VIEW_NODE; + /* set default output */ + outputfile = stdout; + /* Initialize commands. */ cmd_init(0); cmd_variable_handler_register(vtysh_var_handler); @@ -2983,6 +3077,7 @@ void vtysh_init_vty(void) install_node(&link_params_node, NULL); install_node(&ns_node, NULL); install_node(&vrf_node, NULL); + install_node(&nh_group_node, NULL); install_node(&rmap_node, NULL); install_node(&zebra_node, NULL); install_node(&bgp_vpnv4_node, NULL); @@ -3165,6 +3260,11 @@ void vtysh_init_vty(void) install_element(NS_NODE, &vtysh_exit_ns_cmd); install_element(NS_NODE, &vtysh_quit_ns_cmd); + install_element(CONFIG_NODE, &vtysh_nexthop_group_cmd); + install_element(NH_GROUP_NODE, &vtysh_end_all_cmd); + install_element(NH_GROUP_NODE, &vtysh_exit_nexthop_group_cmd); + install_element(NH_GROUP_NODE, &vtysh_quit_nexthop_group_cmd); + install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); install_element(VRF_NODE, &vtysh_quit_vrf_cmd); @@ -3248,6 +3348,7 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &vtysh_vrf_cmd); install_element(CONFIG_NODE, &vtysh_no_vrf_cmd); + install_element(CONFIG_NODE, &vtysh_no_nexthop_group_cmd); /* "write terminal" command. */ install_element(ENABLE_NODE, &vtysh_write_terminal_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index c584d7a9058f..c90be5fa3cef 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -24,20 +24,21 @@ #include "memory.h" DECLARE_MGROUP(MVTYSH) -#define VTYSH_ZEBRA 0x01 -#define VTYSH_RIPD 0x02 -#define VTYSH_RIPNGD 0x04 -#define VTYSH_OSPFD 0x08 -#define VTYSH_OSPF6D 0x10 -#define VTYSH_BGPD 0x20 -#define VTYSH_ISISD 0x40 -#define VTYSH_PIMD 0x100 -#define VTYSH_LDPD 0x200 -#define VTYSH_WATCHFRR 0x400 -#define VTYSH_NHRPD 0x800 -#define VTYSH_EIGRPD 0x1000 -#define VTYSH_BABELD 0x2000 -#define VTYSH_SHARPD 0x4000 +#define VTYSH_ZEBRA 0x0001 +#define VTYSH_RIPD 0x0002 +#define VTYSH_RIPNGD 0x0004 +#define VTYSH_OSPFD 0x0008 +#define VTYSH_OSPF6D 0x0010 +#define VTYSH_BGPD 0x0020 +#define VTYSH_ISISD 0x0040 +#define VTYSH_PIMD 0x0080 +#define VTYSH_LDPD 0x0100 +#define VTYSH_WATCHFRR 0x0200 +#define VTYSH_NHRPD 0x0400 +#define VTYSH_EIGRPD 0x0800 +#define VTYSH_BABELD 0x1000 +#define VTYSH_SHARPD 0x2000 +#define VTYSH_PBRD 0x4000 /* commands in REALLYALL are crucial to correct vtysh operation */ @@ -45,9 +46,9 @@ DECLARE_MGROUP(MVTYSH) /* watchfrr is not in ALL since library CLI functions should not be * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_PIMD|VTYSH_EIGRPD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD @@ -63,6 +64,7 @@ extern char frr_config[]; extern char vtydir[]; void vtysh_init_vty(void); +void vtysh_uninit(void); void vtysh_init_cmd(void); extern int vtysh_connect_all(const char *optional_daemon_name); void vtysh_readline_init(void); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 138a4463212d..533f22459285 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -205,6 +205,9 @@ void vtysh_config_parse_line(void *arg, const char *line) config = config_get(NS_NODE, line); else if (strncmp(line, "vrf", strlen("vrf")) == 0) config = config_get(VRF_NODE, line); + else if (strncmp(line, "nexthop-group", strlen("nexthop-group")) + == 0) + config = config_get(NH_GROUP_NODE, line); else if (strncmp(line, "router-id", strlen("router-id")) == 0) config = config_get(ZEBRA_NODE, line); else if (strncmp(line, "router rip", strlen("router rip")) == 0) diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 57042f8e6209..a4985c423ca3 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -644,6 +644,8 @@ int main(int argc, char **argv, char **env) while (vtysh_rl_gets()) vtysh_execute(line_read); + vtysh_uninit(); + history_truncate_file(history_file, 1000); printf("\n"); diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index c9f721eacb65..dc3dcbf1e940 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -45,7 +45,7 @@ #define FUZZY(X) ((X)+JITTER((X)/20)) #define DEFAULT_PERIOD 5 -#define DEFAULT_TIMEOUT 10 +#define DEFAULT_TIMEOUT 90 #define DEFAULT_RESTART_TIMEOUT 20 #define DEFAULT_LOGLEVEL LOG_INFO #define DEFAULT_MIN_RESTART 60 @@ -596,7 +596,8 @@ static void daemon_send_ready(void) FILE *fp; fp = fopen(DAEMON_VTY_DIR "/watchfrr.started", "w"); - fclose(fp); + if (fp) + fclose(fp); #if defined HAVE_SYSTEMD zlog_notice( "Watchfrr: Notifying Systemd we are up and running"); diff --git a/zebra/connected.c b/zebra/connected.c index 18dc6a970b54..d34fd9021a3e 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -238,11 +238,13 @@ void connected_up(struct interface *ifp, struct connected *ifc) break; } - rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0); + rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ifp->vrf_id, + ZEBRA_ROUTE_CONNECT, 0, 0, + &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0, 0); - rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0); + rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ifp->vrf_id, + ZEBRA_ROUTE_CONNECT, 0, 0, + &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; @@ -396,10 +398,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) * head. */ rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, false); + &p, NULL, &nh, 0, 0, false, NULL); rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, &nh, 0, 0, false); + 0, &p, NULL, &nh, 0, 0, false, NULL); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index af17e49231fa..14905b738bf5 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -387,6 +387,11 @@ static int get_iflink_speed(const char *ifname) return (ecmd.speed_hi << 16) | ecmd.speed; } +uint32_t kernel_get_speed(struct interface *ifp) +{ + return get_iflink_speed(ifp->name); +} + static int netlink_extract_bridge_info(struct rtattr *link_data, struct zebra_l2info_bridge *bridge_info) { @@ -838,6 +843,16 @@ int kernel_address_delete_ipv4(struct interface *ifp, struct connected *ifc) return netlink_address(RTM_DELADDR, AF_INET, ifp, ifc); } +int kernel_address_add_ipv6 (struct interface *ifp, struct connected *ifc) +{ + return netlink_address (RTM_NEWADDR, AF_INET6, ifp, ifc); +} + +int kernel_address_delete_ipv6 (struct interface *ifp, struct connected *ifc) +{ + return netlink_address (RTM_DELADDR, AF_INET6, ifp, ifc); +} + int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id, int startup) { diff --git a/zebra/interface.c b/zebra/interface.c index dd1050ee7f06..07570e64bfc2 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -57,8 +57,29 @@ DEFINE_HOOK(zebra_if_extra_info, (struct vty *vty, struct interface *ifp), DEFINE_HOOK(zebra_if_config_wr, (struct vty *vty, struct interface *ifp), (vty, ifp)) + static void if_down_del_nbr_connected(struct interface *ifp); +static int if_zebra_speed_update(struct thread *thread) +{ + struct interface *ifp = THREAD_ARG(thread); + struct zebra_if *zif = ifp->info; + uint32_t new_speed; + + zif->speed_update = NULL; + + new_speed = kernel_get_speed(ifp); + if (new_speed != ifp->speed) { + zlog_info("%s: %s old speed: %u new speed: %u", + __PRETTY_FUNCTION__, ifp->name, + ifp->speed, new_speed); + ifp->speed = new_speed; + if_add_update(ifp); + } + + return 1; +} + static void zebra_if_node_destroy(route_table_delegate_t *delegate, struct route_table *table, struct route_node *node) @@ -119,6 +140,16 @@ static int if_zebra_new_hook(struct interface *ifp) route_table_init_with_delegate(&zebra_if_table_delegate); ifp->info = zebra_if; + + /* + * Some platforms are telling us that the interface is + * up and ready to go. When we check the speed we + * sometimes get the wrong value. Wait a couple + * of seconds and ask again. Hopefully it's all settled + * down upon startup. + */ + thread_add_timer(zebrad.master, if_zebra_speed_update, + ifp, 15, &zebra_if->speed_update); return 0; } @@ -141,6 +172,8 @@ static int if_zebra_delete_hook(struct interface *ifp) list_delete_and_null(&rtadv->AdvPrefixList); #endif /* HAVE_RTADV */ + THREAD_OFF(zebra_if->speed_update); + XFREE(MTYPE_TMP, zebra_if); } @@ -561,33 +594,35 @@ static void if_delete_connected(struct interface *ifp) struct prefix cp; struct route_node *rn; struct zebra_if *zebra_if; + struct listnode *node; + struct listnode *last = NULL; zebra_if = ifp->info; - if (ifp->connected) { - struct listnode *node; - struct listnode *last = NULL; + if (!ifp->connected) + return; - while ((node = (last ? last->next - : listhead(ifp->connected)))) { - ifc = listgetdata(node); + while ((node = (last ? last->next + : listhead(ifp->connected)))) { + ifc = listgetdata(node); - cp = *CONNECTED_PREFIX(ifc); - apply_mask(&cp); + cp = *CONNECTED_PREFIX(ifc); + apply_mask(&cp); - if (cp.family == AF_INET - && (rn = route_node_lookup(zebra_if->ipv4_subnets, - &cp))) { - struct listnode *anode; - struct listnode *next; - struct listnode *first; - struct list *addr_list; + if (cp.family == AF_INET + && (rn = route_node_lookup(zebra_if->ipv4_subnets, + &cp))) { + struct listnode *anode; + struct listnode *next; + struct listnode *first; + struct list *addr_list; - route_unlock_node(rn); - addr_list = (struct list *)rn->info; + route_unlock_node(rn); + addr_list = (struct list *)rn->info; - /* Remove addresses, secondaries first. */ - first = listhead(addr_list); + /* Remove addresses, secondaries first. */ + first = listhead(addr_list); + if (first) for (anode = first->next; anode || first; anode = next) { if (!anode) { @@ -626,27 +661,26 @@ static void if_delete_connected(struct interface *ifp) last = node; } - /* Free chain list and respective route node. */ - list_delete_and_null(&addr_list); - rn->info = NULL; - route_unlock_node(rn); - } else if (cp.family == AF_INET6) { - connected_down(ifp, ifc); + /* Free chain list and respective route node. */ + list_delete_and_null(&addr_list); + rn->info = NULL; + route_unlock_node(rn); + } else if (cp.family == AF_INET6) { + connected_down(ifp, ifc); - zebra_interface_address_delete_update(ifp, ifc); + zebra_interface_address_delete_update(ifp, ifc); - UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL); - UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL); + UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) - last = node; - else { - listnode_delete(ifp->connected, ifc); - connected_free(ifc); - } - } else { + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) last = node; + else { + listnode_delete(ifp->connected, ifc); + connected_free(ifc); } + } else { + last = node; } } } @@ -734,7 +768,8 @@ void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id) zebra_interface_vrf_update_add(ifp, old_vrf_id); /* Install connected routes (in new VRF). */ - if_install_connected(ifp); + if (if_is_operative(ifp)) + if_install_connected(ifp); static_ifindex_update(ifp, true); diff --git a/zebra/interface.h b/zebra/interface.h index 6bc05e21c5d0..e13721448c68 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -170,7 +170,12 @@ struct rtadvconf { #define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */ u_char inFastRexmit; /* True if we're rexmits faster than usual */ - u_char configured; /* Has operator configured RA? */ + + /* Track if RA was configured by BGP or by the Operator or both */ + u_char ra_configured; /* Was RA configured? */ +#define BGP_RA_CONFIGURED (1<<0) /* BGP configured RA? */ +#define VTY_RA_CONFIGURED (1<<1) /* Operator configured RA? */ +#define VTY_RA_INTERVAL_CONFIGURED (1<<2) /* Operator configured RA interval */ int NumFastReXmitsRemain; /* Loaded first with number of fast rexmits to do */ @@ -268,6 +273,8 @@ struct zebra_if { /* Link fields - for sub-interfaces. */ ifindex_t link_ifindex; struct interface *link; + + struct thread *speed_update; }; DECLARE_HOOK(zebra_if_extra_info, (struct vty *vty, struct interface *ifp), @@ -308,6 +315,8 @@ static inline void zebra_if_set_ziftype(struct interface *ifp, #define IS_ZEBRA_IF_VRF_SLAVE(ifp) \ (((struct zebra_if *)(ifp->info))->zif_slave_type == ZEBRA_IF_SLAVE_VRF) +extern void zebra_if_init(void); + extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, u_int32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 58118ce006f0..8e3a1d1a0363 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -78,6 +78,7 @@ int if_ioctl(u_long request, caddr_t buffer) return 0; } +#ifndef HAVE_NETLINK static int if_ioctl_ipv6(u_long request, caddr_t buffer) { int sock; @@ -108,6 +109,7 @@ static int if_ioctl_ipv6(u_long request, caddr_t buffer) } return 0; } +#endif /* ! HAVE_NETLINK */ /* * get interface metric @@ -460,44 +462,19 @@ struct in6_ifreq { int ifr6_ifindex; }; #endif /* _LINUX_IN6_H */ - /* Interface's address add/delete functions. */ int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc) { - int ret; - struct prefix_ipv6 *p; - struct in6_ifreq ifreq; - - p = (struct prefix_ipv6 *)ifc->address; - - memset(&ifreq, 0, sizeof(struct in6_ifreq)); - - memcpy(&ifreq.ifr6_addr, &p->prefix, sizeof(struct in6_addr)); - ifreq.ifr6_ifindex = ifp->ifindex; - ifreq.ifr6_prefixlen = p->prefixlen; - - ret = if_ioctl_ipv6(SIOCSIFADDR, (caddr_t)&ifreq); - - return ret; +#ifdef HAVE_NETLINK + return kernel_address_add_ipv6 (ifp, ifc); +#endif /* HAVE_NETLINK */ } int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc) { - int ret; - struct prefix_ipv6 *p; - struct in6_ifreq ifreq; - - p = (struct prefix_ipv6 *)ifc->address; - - memset(&ifreq, 0, sizeof(struct in6_ifreq)); - - memcpy(&ifreq.ifr6_addr, &p->prefix, sizeof(struct in6_addr)); - ifreq.ifr6_ifindex = ifp->ifindex; - ifreq.ifr6_prefixlen = p->prefixlen; - - ret = if_ioctl_ipv6(SIOCDIFADDR, (caddr_t)&ifreq); - - return ret; +#ifdef HAVE_NETLINK + return kernel_address_delete_ipv6 (ifp, ifc); +#endif /* HAVE_NETLINK */ } #else /* LINUX_IPV6 */ #ifdef HAVE_STRUCT_IN6_ALIASREQ diff --git a/zebra/ipforward_proc.c b/zebra/ipforward_proc.c index 2834eeeb9cc5..f823ec4384c9 100644 --- a/zebra/ipforward_proc.c +++ b/zebra/ipforward_proc.c @@ -42,6 +42,7 @@ static void dropline(FILE *fp) int ipforward(void) { + int ret = 0; FILE *fp; int ipforwarding = 0; char buf[10]; @@ -58,11 +59,11 @@ int ipforward(void) 1 => ip forwarding enabled 2 => ip forwarding off. */ if (fgets(buf, 6, fp)) - sscanf(buf, "Ip: %d", &ipforwarding); + ret = sscanf(buf, "Ip: %d", &ipforwarding); fclose(fp); - if (ipforwarding == 1) + if (ret == 1 && ipforwarding == 1) return 1; return 0; @@ -127,6 +128,7 @@ char proc_ipv6_forwarding[] = "/proc/sys/net/ipv6/conf/all/forwarding"; int ipforward_ipv6(void) { + int ret = 0; FILE *fp; char buf[5]; int ipforwarding = 0; @@ -137,9 +139,13 @@ int ipforward_ipv6(void) return -1; if (fgets(buf, 2, fp)) - sscanf(buf, "%d", &ipforwarding); + ret = sscanf(buf, "%d", &ipforwarding); fclose(fp); + + if (ret != 1) + return 0; + return ipforwarding; } diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c index 36212a013282..cdf426b9b8ff 100644 --- a/zebra/ipforward_sysctl.c +++ b/zebra/ipforward_sysctl.c @@ -87,11 +87,11 @@ int ipforward_off(void) /* IPv6 forwarding control MIB. */ int mib_ipv6[MIB_SIZ] = {CTL_NET, PF_INET6, -#if defined(KAME) +#if defined(BSD_V6_SYSCTL) IPPROTO_IPV6, IPV6CTL_FORWARDING -#else /* NOT KAME */ +#else /* NOT BSD_V6_SYSCTL */ IPPROTO_IP, IP6CTL_FORWARDING -#endif /* KAME */ +#endif /* BSD_V6_SYSCTL */ }; int ipforward_ipv6(void) diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index a5c36b0daedc..12df3845b202 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -40,10 +40,12 @@ #include "zebra/zserv.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" +#include "zebra/rt.h" #include "zebra/debug.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" #include "zebra/if_netlink.h" +#include "zebra/rule_netlink.h" #ifndef SO_RCVBUFFORCE #define SO_RCVBUFFORCE (33) @@ -84,6 +86,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_NEWNEIGH, "RTM_NEWNEIGH"}, {RTM_DELNEIGH, "RTM_DELNEIGH"}, {RTM_GETNEIGH, "RTM_GETNEIGH"}, + {RTM_NEWRULE, "RTM_NEWRULE"}, + {RTM_DELRULE, "RTM_DELRULE"}, + {RTM_GETRULE, "RTM_GETRULE"}, {0}}; static const struct message rtproto_str[] = { @@ -261,6 +266,12 @@ static int netlink_information_fetch(struct sockaddr_nl *snl, case RTM_DELNEIGH: return netlink_neigh_change(snl, h, ns_id); break; + case RTM_NEWRULE: + return netlink_rule_change(snl, h, ns_id, startup); + break; + case RTM_DELRULE: + return netlink_rule_change(snl, h, ns_id, startup); + break; default: if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Unknown netlink nlmsg_type %d vrf %u\n", @@ -787,7 +798,8 @@ void kernel_init(struct zebra_ns *zns) /* Initialize netlink sockets */ groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_MROUTE - | RTMGRP_NEIGH; + | RTMGRP_NEIGH + | RTNLGRP_IPV4_RULE | RTNLGRP_IPV6_RULE; snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 89c933f90ff5..ba028ed09cf6 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1039,7 +1039,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true); + NULL, 0, 0, true, NULL); if (!nh.type) { nh.type = NEXTHOP_TYPE_IPV4; @@ -1048,13 +1048,13 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) - rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, + rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, 0, 0); + &nh, 0, 0, 0, 0, 0); else rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true); + &nh, 0, 0, true, NULL); } if (dest.sa.sa_family == AF_INET6) { /* One day we might have a debug section here like one in the @@ -1085,7 +1085,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true); + NULL, 0, 0, true, NULL); if (!nh.type) { nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX @@ -1096,13 +1096,13 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) - rib_add(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, + rib_add(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, 0, 0); + &nh, 0, 0, 0, 0, 0); else rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true); + &nh, 0, 0, true, NULL); } } diff --git a/zebra/label_manager.c b/zebra/label_manager.c index bf4522b70f52..ace13eda7140 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -41,6 +41,8 @@ struct label_manager lbl_mgr; +extern struct zebra_privs_t zserv_privs; + DEFINE_MGROUP(LBL_MGR, "Label Manager"); DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk"); @@ -119,7 +121,7 @@ static int reply_error(int cmd, struct zserv *zserv, vrf_id_t vrf_id) s = zserv->obuf; stream_reset(s); - zserv_create_header(s, cmd, vrf_id); + zclient_create_header(s, cmd, vrf_id); /* result */ stream_putc(s, 1); @@ -222,6 +224,7 @@ static void lm_zclient_init(char *lm_zserv_path) /* Set default values. */ zclient = zclient_new_notify(zebrad.master, &zclient_options_default); + zclient->privs = &zserv_privs; zclient->sock = -1; zclient->t_connect = NULL; lm_zclient_connect(NULL); diff --git a/zebra/main.c b/zebra/main.c index 36c931c4ee9b..19b16936d9e0 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -295,11 +295,17 @@ int main(int argc, char **argv) zebrad.master = frr_init(); /* Zebra related initialize. */ - zebra_init(); + zserv_init(); rib_init(); zebra_if_init(); zebra_debug_init(); router_id_cmd_init(); + + /* + * Initialize NS( and implicitly the VRF module), and make kernel + * routing socket. */ + zebra_ns_init(); + zebra_vty_init(); access_list_init(); prefix_list_init(); @@ -318,10 +324,6 @@ int main(int argc, char **argv) /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ - /* Initialize NS( and implicitly the VRF module), and make kernel - * routing socket. */ - zebra_ns_init(); - #if defined(HANDLE_ZAPI_FUZZING) if (fuzzing) { zserv_read_file(fuzzing); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index cbf77765a3f6..3e4416ac3b3e 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -519,7 +519,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, afi = family2afi(rn->p.family); if (rmap_name) ret = zebra_import_table_route_map_check( - afi, re->type, &rn->p, re->nexthop, re->vrf_id, + afi, re->type, &rn->p, re->ng.nexthop, re->vrf_id, re->tag, rmap_name); if (ret != RMAP_MATCH) { @@ -553,7 +553,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, newre->nexthop_num = 0; newre->uptime = time(NULL); newre->instance = re->table; - route_entry_copy_nexthops(newre, re->nexthop); + route_entry_copy_nexthops(newre, re->ng.nexthop); rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre); @@ -569,8 +569,8 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) prefix_copy(&p, &rn->p); rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, - re->table, re->flags, &p, NULL, re->nexthop, - zebrad.rtm_table_default, re->metric, false); + re->table, re->flags, &p, NULL, re->ng.nexthop, + zebrad.rtm_table_default, re->metric, false, NULL); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 61beebb40938..21fd492767a7 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -29,6 +29,7 @@ #include "table.h" #include "queue.h" #include "nexthop.h" +#include "nexthop_group.h" #include "vrf.h" #include "if.h" #include "mpls.h" @@ -43,7 +44,7 @@ struct route_entry { struct route_entry *prev; /* Nexthop structure */ - struct nexthop *nexthop; + struct nexthop_group ng; /* Tag */ route_tag_t tag; @@ -59,6 +60,7 @@ struct route_entry { /* VRF identifier. */ vrf_id_t vrf_id; + vrf_id_t nh_vrf_id; /* Which routing table */ uint32_t table; @@ -85,8 +87,7 @@ struct route_entry { /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */ #define ROUTE_ENTRY_NEXTHOPS_CHANGED 0x2 #define ROUTE_ENTRY_CHANGED 0x4 -#define ROUTE_ENTRY_SELECTED_FIB 0x8 -#define ROUTE_ENTRY_LABELS_CHANGED 0x10 +#define ROUTE_ENTRY_LABELS_CHANGED 0x8 /* Nexthop information. */ u_char nexthop_num; @@ -122,6 +123,8 @@ typedef struct rib_dest_t_ { */ struct route_entry *routes; + struct route_entry *selected_fib; + /* * Flags, see below. */ @@ -285,18 +288,18 @@ extern int zebra_check_addr(struct prefix *p); extern void rib_addnode(struct route_node *rn, struct route_entry *re, int process); extern void rib_delnode(struct route_node *rn, struct route_entry *re); -extern int rib_install_kernel(struct route_node *rn, struct route_entry *re, - struct route_entry *old); -extern int rib_uninstall_kernel(struct route_node *rn, struct route_entry *re); +extern void rib_install_kernel(struct route_node *rn, struct route_entry *re, + struct route_entry *old); +extern void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re); /* NOTE: * All rib_add function will not just add prefix into RIB, but * also implicitly withdraw equal prefix of same type. */ -extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, - u_short instance, int flags, struct prefix *p, +extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, vrf_id_t nh_vrf_id, + int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, u_int32_t table_id, u_int32_t metric, u_int32_t mtu, - uint8_t distance); + uint8_t distance, route_tag_t tag); extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *, struct prefix_ipv6 *src_p, struct route_entry *); @@ -304,7 +307,8 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *, extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - u_int32_t table_id, u_int32_t metric, bool fromkernel); + u_int32_t table_id, u_int32_t metric, bool fromkernel, + struct ethaddr *rmac); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t, union g_addr *, @@ -435,4 +439,10 @@ static inline void rib_tables_iter_cleanup(rib_tables_iter_t *iter) DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) + +extern void zebra_vty_init(void); +extern int static_config(struct vty *vty, struct zebra_vrf *zvrf, + afi_t afi, safi_t safi, const char *cmd); +extern pid_t pid; + #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/rt.h b/zebra/rt.h index 3ce15e964022..bb4ff5bee809 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -41,19 +41,64 @@ * success failure should be handled by the caller. */ -extern int kernel_route_rib(struct prefix *, struct prefix *, - struct route_entry *, struct route_entry *); + +enum southbound_results { + SOUTHBOUND_INSTALL_SUCCESS, + SOUTHBOUND_INSTALL_FAILURE, + SOUTHBOUND_DELETE_SUCCESS, + SOUTHBOUND_DELETE_FAILURE, +}; + +/* + * Install/delete the specified prefix p from the kernel + * + * old = NULL, new = pointer - Install new + * old = pointer, new = pointer - Route replace Old w/ New + * old = pointer, new = NULL, - Route Delete + * + * Please note not all kernels support route replace + * semantics so we will end up with a delete than + * a re-add. + */ +extern void kernel_route_rib(struct prefix *p, struct prefix *src_p, + struct route_entry *old, struct route_entry *new); + +/* + * So route install/failure may not be immediately known + * so let's separate it out and allow the result to + * be passed back up. + */ +extern void kernel_route_rib_pass_fail(struct prefix *p, + struct route_entry *re, + enum southbound_results res); extern int kernel_address_add_ipv4(struct interface *, struct connected *); extern int kernel_address_delete_ipv4(struct interface *, struct connected *); +extern int kernel_address_add_ipv6 (struct interface *, struct connected *); +extern int kernel_address_delete_ipv6 (struct interface *, struct connected *); extern int kernel_neigh_update(int, int, uint32_t, char *, int); extern int kernel_interface_set_master(struct interface *master, struct interface *slave); -extern int kernel_add_lsp(zebra_lsp_t *); -extern int kernel_upd_lsp(zebra_lsp_t *); -extern int kernel_del_lsp(zebra_lsp_t *); + +extern void kernel_add_lsp(zebra_lsp_t *lsp); +extern void kernel_upd_lsp(zebra_lsp_t *lsp); +extern void kernel_del_lsp(zebra_lsp_t *lsp); + +/* + * Add the ability to pass back up the lsp install/delete + * success/failure. + * + * This functions goal is similiar to kernel_route_rib_pass_fail + * in that we are separating out the mechanics for + * the install/failure to set/unset flags and to notify + * as needed. + */ +extern void kernel_lsp_pass_fail(zebra_lsp_t *lsp, + enum southbound_results res); + extern int mpls_kernel_init(void); +extern uint32_t kernel_get_speed(struct interface *ifp); extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); extern int kernel_add_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip); @@ -70,4 +115,18 @@ extern int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, struct ethaddr *mac); extern int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip); +/* + * Southbound Initialization routines to get initial starting + * state. + */ +extern void interface_list(struct zebra_ns *zns); +extern void kernel_init(struct zebra_ns *zns); +extern void kernel_terminate(struct zebra_ns *zns); +extern void macfdb_read(struct zebra_ns *zns); +extern void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, + struct interface *br_if); +extern void neigh_read(struct zebra_ns *zns); +extern void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *ifp); +extern void route_read(struct zebra_ns *zns); + #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 3830e1fbdefb..ff7a1a7a8759 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -230,6 +230,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, int metric = 0; u_int32_t mtu = 0; uint8_t distance = 0; + route_tag_t tag = 0; void *dest = NULL; void *gate = NULL; @@ -321,6 +322,11 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, if (tb[RTA_PRIORITY]) metric = *(int *)RTA_DATA(tb[RTA_PRIORITY]); +#if defined(SUPPORT_REALMS) + if (tb[RTA_FLOW]) + tag = *(uint32_t *)RTA_DATA(tb[RTA_FLOW]); +#endif + if (tb[RTA_METRICS]) { struct rtattr *mxrta[RTAX_MAX + 1]; @@ -397,6 +403,9 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, afi = AFI_IP6; if (h->nlmsg_type == RTM_NEWROUTE) { + struct interface *ifp; + vrf_id_t nh_vrf_id = vrf_id; + if (!tb[RTA_MULTIPATH]) { struct nexthop nh; size_t sz = (afi == AFI_IP) ? 4 : 16; @@ -428,8 +437,16 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, if (gate) memcpy(&nh.gate, gate, sz); - rib_add(afi, SAFI_UNICAST, vrf_id, proto, - 0, flags, &p, NULL, &nh, table, metric, mtu, distance); + if (index) { + ifp = if_lookup_by_index(index, + VRF_UNKNOWN); + if (ifp) + nh_vrf_id = ifp->vrf_id; + } + + rib_add(afi, SAFI_UNICAST, vrf_id, nh_vrf_id, proto, + 0, flags, &p, NULL, &nh, table, metric, + mtu, distance, tag); } else { /* This is a multipath route */ @@ -446,9 +463,11 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, re->metric = metric; re->mtu = mtu; re->vrf_id = vrf_id; + re->nh_vrf_id = vrf_id; re->table = table; re->nexthop_num = 0; re->uptime = time(NULL); + re->tag = tag; for (;;) { if (len < (int)sizeof(*rtnh) @@ -456,6 +475,18 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, break; index = rtnh->rtnh_ifindex; + if (index) { + /* + * Yes we are looking this up + * for every nexthop and just + * using the last one looked + * up right now + */ + ifp = if_lookup_by_index(index, + VRF_UNKNOWN); + if (ifp) + re->nh_vrf_id = ifp->vrf_id; + } gate = 0; if (rtnh->rtnh_len > sizeof(*rtnh)) { memset(tb, 0, sizeof(tb)); @@ -535,13 +566,13 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, memcpy(&nh.gate, gate, sz); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, NULL, &nh, - table, metric, true); + table, metric, true, NULL); } else { /* XXX: need to compare the entire list of nexthops * here for NLM_F_APPEND stupidity */ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, NULL, NULL, - table, metric, true); + table, metric, true, NULL); } } @@ -795,7 +826,7 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, struct rtmsg *rtmsg, size_t req_size, int cmd) { - struct nexthop_label *nh_label; + struct mpls_label_stack *nh_label; mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[256]; @@ -932,10 +963,17 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, routedesc, inet6_ntoa(nexthop->gate.ipv6), label_buf, nexthop->ifindex); } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { + + /* + * We have the ifindex so we should always send it + * This is especially useful if we are doing route + * leaking. + */ + if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex); + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { if (cmd == RTM_NEWROUTE) { if (nexthop->rmap_src.ipv4.s_addr) addattr_l(nlmsg, req_size, RTA_PREFSRC, @@ -953,8 +991,6 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, } if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex); - if (cmd == RTM_NEWROUTE) { if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) addattr_l(nlmsg, req_size, RTA_PREFSRC, @@ -995,7 +1031,7 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, struct rtmsg *rtmsg, union g_addr **src) { - struct nexthop_label *nh_label; + struct mpls_label_stack *nh_label; mpls_lse_t out_lse[MPLS_MAX_LABELS]; char label_buf[256]; @@ -1133,11 +1169,18 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, routedesc, inet6_ntoa(nexthop->gate.ipv6), label_buf, nexthop->ifindex); } + + /* + * We have figured out the ifindex so we should always send it + * This is especially useful if we are doing route + * leaking. + */ + if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) + rtnh->rtnh_ifindex = nexthop->ifindex; + /* ifindex */ if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop->type == NEXTHOP_TYPE_IFINDEX) { - rtnh->rtnh_ifindex = nexthop->ifindex; - if (nexthop->rmap_src.ipv4.s_addr) *src = &nexthop->rmap_src; else if (nexthop->src.ipv4.s_addr) @@ -1149,8 +1192,6 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, "nexthop via if %u", routedesc, nexthop->ifindex); } else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - rtnh->rtnh_ifindex = nexthop->ifindex; - if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "netlink_route_multipath() (%s): " @@ -1310,7 +1351,10 @@ static int netlink_route_multipath(int cmd, struct prefix *p, * by the routing protocol and for communicating with protocol peers. */ addattr32(&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); - +#if defined(SUPPORT_REALMS) + if (re->tag > 0 && re->tag <= 255) + addattr32(&req.n, sizeof req, RTA_FLOW, re->tag); +#endif /* Table corresponding to this route. */ if (re->table < 256) req.r.rtm_table = re->table; @@ -1338,7 +1382,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, /* Count overall nexthops so we can decide whether to use singlepath * or multipath case. */ nexthop_num = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; if (cmd == RTM_NEWROUTE @@ -1354,7 +1398,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, /* Singlepath case. */ if (nexthop_num == 1 || multipath_num == 1) { nexthop_num = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { /* * So we want to cover 2 types of blackhole * routes here: @@ -1454,7 +1498,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, rtnh = RTA_DATA(rta); nexthop_num = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (nexthop_num >= multipath_num) break; @@ -1598,33 +1642,51 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) return suc; } -int kernel_route_rib(struct prefix *p, struct prefix *src_p, - struct route_entry *old, struct route_entry *new) +void kernel_route_rib(struct prefix *p, struct prefix *src_p, + struct route_entry *old, struct route_entry *new) { + int ret = 0; + assert(old || new); - if (!old && new) - return netlink_route_multipath(RTM_NEWROUTE, p, src_p, new, 0); - if (old && !new) - return netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0); + if (new) { + if (p->family == AF_INET) + ret = netlink_route_multipath(RTM_NEWROUTE, p, src_p, + new, (old) ? 1 : 0); + else { + /* + * So v6 route replace semantics are not in + * the kernel at this point as I understand it. + * So let's do a delete than an add. + * In the future once v6 route replace semantics + * are in we can figure out what to do here to + * allow working with old and new kernels. + * + * I'm also intentionally ignoring the failure case + * of the route delete. If that happens yeah we're + * screwed. + */ + if (old) + netlink_route_multipath(RTM_DELROUTE, p, + src_p, old, 0); + ret = netlink_route_multipath(RTM_NEWROUTE, p, + src_p, new, 0); + } + kernel_route_rib_pass_fail(p, new, + (!ret) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); + return; + } - if (p->family == AF_INET) - return netlink_route_multipath(RTM_NEWROUTE, p, src_p, new, 1); + if (old) { + ret = netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0); - /* - * So v6 route replace semantics are not in the kernel at this - * point as I understand it. - * So let's do a delete than an add. - * In the future once v6 route replace semantics are in - * we can figure out what to do here to allow working - * with old and new kernels. - * - * I'm also intentionally ignoring the failure case - * of the route delete. If that happens yeah we're - * screwed. - */ - netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0); - return netlink_route_multipath(RTM_NEWROUTE, p, src_p, new, 0); + kernel_route_rib_pass_fail(p, old, + (!ret) ? + SOUTHBOUND_DELETE_SUCCESS : + SOUTHBOUND_DELETE_FAILURE); + } } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, @@ -2414,17 +2476,6 @@ int netlink_mpls_multipath(int cmd, zebra_lsp_t *lsp) _netlink_mpls_build_singlepath(routedesc, nhlfe, &req.n, &req.r, sizeof req, cmd); - if (cmd == RTM_NEWROUTE) { - SET_FLAG(nhlfe->flags, - NHLFE_FLAG_INSTALLED); - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); - } else { - UNSET_FLAG(nhlfe->flags, - NHLFE_FLAG_INSTALLED); - UNSET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); - } nexthop_num++; break; } @@ -2468,18 +2519,6 @@ int netlink_mpls_multipath(int cmd, zebra_lsp_t *lsp) rta, rtnh, &req.r, &src1); rtnh = RTNH_NEXT(rtnh); - - if (cmd == RTM_NEWROUTE) { - SET_FLAG(nhlfe->flags, - NHLFE_FLAG_INSTALLED); - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); - } else { - UNSET_FLAG(nhlfe->flags, - NHLFE_FLAG_INSTALLED); - UNSET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); - } } } diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index e5887949470d..1ec237878728 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -67,7 +67,7 @@ static int sin_masklen(struct in_addr mask) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ #ifdef __OpenBSD__ -static int kernel_rtm_add_labels(struct nexthop_label *nh_label, +static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label, struct sockaddr_mpls *smpls) { if (nh_label->num_labels > 1) { @@ -123,7 +123,7 @@ static int kernel_rtm_ipv4(int cmd, struct prefix *p, struct route_entry *re) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -304,7 +304,7 @@ static int kernel_rtm_ipv6(int cmd, struct prefix *p, struct route_entry *re) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -387,14 +387,14 @@ static int kernel_rtm(int cmd, struct prefix *p, struct route_entry *re) return 0; } -int kernel_route_rib(struct prefix *p, struct prefix *src_p, - struct route_entry *old, struct route_entry *new) +void kernel_route_rib(struct prefix *p, struct prefix *src_p, + struct route_entry *old, struct route_entry *new) { int route = 0; if (src_p && src_p->prefixlen) { zlog_err("route add: IPv6 sourcedest routes unsupported!"); - return 1; + return; } if (zserv_privs.change(ZPRIVS_RAISE)) @@ -409,7 +409,17 @@ int kernel_route_rib(struct prefix *p, struct prefix *src_p, if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); - return route; + if (new) { + kernel_route_rib_pass_fail(p, new, + (!route) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); + } else { + kernel_route_rib_pass_fail(p, old, + (!route) ? + SOUTHBOUND_DELETE_SUCCESS : + SOUTHBOUND_DELETE_FAILURE); + } } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, @@ -463,4 +473,9 @@ extern int kernel_interface_set_master(struct interface *master, return 0; } +uint32_t kernel_get_speed(struct interface *ifp) +{ + return ifp->speed; +} + #endif /* !HAVE_NETLINK */ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 40219599ddac..32418eb82fb4 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -838,16 +838,21 @@ void zebra_interface_radv_set(struct zserv *client, u_short length, zif = ifp->info; if (enable) { + SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED); ipv6_nd_suppress_ra_set(ifp, RA_ENABLE); if (ra_interval - && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval) + && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval + && !CHECK_FLAG(zif->rtadv.ra_configured, + VTY_RA_INTERVAL_CONFIGURED)) zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000; } else { - if (!zif->rtadv.configured) { + UNSET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED); + if (!CHECK_FLAG(zif->rtadv.ra_configured, + VTY_RA_INTERVAL_CONFIGURED)) zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; + if (!CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED)) ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS); - } } stream_failure: return; @@ -870,8 +875,10 @@ DEFUN (ipv6_nd_suppress_ra, return CMD_WARNING_CONFIG_FAILED; } - ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS); - zif->rtadv.configured = 0; + if (!CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED)) + ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS); + + UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED); return CMD_SUCCESS; } @@ -894,7 +901,7 @@ DEFUN (no_ipv6_nd_suppress_ra, } ipv6_nd_suppress_ra_set(ifp, RA_ENABLE); - zif->rtadv.configured = 1; + SET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED); return CMD_SUCCESS; } @@ -929,6 +936,7 @@ DEFUN (ipv6_nd_ra_interval_msec, if (interval % 1000) zns->rtadv.adv_msec_if_count++; + SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); zif->rtadv.MaxRtrAdvInterval = interval; zif->rtadv.MinRtrAdvInterval = 0.33 * interval; zif->rtadv.AdvIntervalTimer = 0; @@ -966,6 +974,7 @@ DEFUN (ipv6_nd_ra_interval, /* convert to milliseconds */ interval = interval * 1000; + SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); zif->rtadv.MaxRtrAdvInterval = interval; zif->rtadv.MinRtrAdvInterval = 0.33 * interval; zif->rtadv.AdvIntervalTimer = 0; @@ -995,9 +1004,15 @@ DEFUN (no_ipv6_nd_ra_interval, if (zif->rtadv.MaxRtrAdvInterval % 1000) zns->rtadv.adv_msec_if_count--; - zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; - zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; + UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); + + if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED)) + zif->rtadv.MaxRtrAdvInterval = 10000; + else + zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; + zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; + zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; return CMD_SUCCESS; } @@ -1552,15 +1567,20 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) if (!(if_is_loopback(ifp) || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))) { - if (zif->rtadv.AdvSendAdvertisements) + if (zif->rtadv.AdvSendAdvertisements + && CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED)) vty_out(vty, " no ipv6 nd suppress-ra\n"); } interval = zif->rtadv.MaxRtrAdvInterval; - if (interval % 1000) - vty_out(vty, " ipv6 nd ra-interval msec %d\n", interval); - else if (interval != RTADV_MAX_RTR_ADV_INTERVAL) - vty_out(vty, " ipv6 nd ra-interval %d\n", interval / 1000); + if (CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED)) { + if (interval % 1000) + vty_out(vty, " ipv6 nd ra-interval msec %d\n", + interval); + else if (interval != RTADV_MAX_RTR_ADV_INTERVAL) + vty_out(vty, " ipv6 nd ra-interval %d\n", + interval / 1000); + } if (zif->rtadv.AdvIntervalOption) vty_out(vty, " ipv6 nd adv-interval-option\n"); diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 62f3224b6e87..cd06355702dc 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -30,7 +30,7 @@ #include "vty.h" #include "zebra/rib.h" -#include "zebra/zserv.h" +#include "zebra/rt.h" /* Thank you, Solaris, for polluting application symbol namespace. */ #undef hook_register @@ -97,8 +97,9 @@ static void handle_route_entry(mib2_ipRouteEntry_t *routeEntry) nh.type = NEXTHOP_TYPE_IPV4; nh.gate.ipv4.s_addr = routeEntry->ipRouteNextHop; - rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, - zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0); + rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, VRF_DEFAULT, + ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &prefix, NULL, + &nh, 0, 0, 0, 0, 0); } void route_read(struct zebra_ns *zns) @@ -262,4 +263,8 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) { } +void kernel_read_pbr_rules(struct zebra_ns *zns) +{ +} + #endif /* SUNOS_5 */ diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index f38cba65e737..e9920460785c 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -24,8 +24,10 @@ #ifdef GNU_LINUX #include "vty.h" -#include "zebra/zserv.h" +#include "zebra/rt.h" +#include "zebra/zebra_pbr.h" #include "zebra/rt_netlink.h" +#include "zebra/rule_netlink.h" void route_read(struct zebra_ns *zns) { @@ -53,4 +55,9 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) netlink_neigh_read_for_vlan(zns, vlan_if); } +void kernel_read_pbr_rules(struct zebra_ns *zns) +{ + netlink_rules_read(zns); +} + #endif /* GNU_LINUX */ diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index 53ed0e7906be..f8622dd7f372 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -28,7 +28,6 @@ #include "log.h" #include "vrf.h" -#include "zebra/zserv.h" #include "zebra/rt.h" #include "zebra/kernel_socket.h" @@ -93,4 +92,8 @@ void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) { } +void kernel_read_pbr_rules(struct zebra_ns *zns) +{ +} + #endif /* !defined(GNU_LINUX) && !defined(SUNOS_5) */ diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c new file mode 100644 index 000000000000..4b8791ee2fc9 --- /dev/null +++ b/zebra/rule_netlink.c @@ -0,0 +1,273 @@ +/* + * Zebra Policy Based Routing (PBR) interaction with the kernel using + * netlink. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#ifdef HAVE_NETLINK + +#include "if.h" +#include "prefix.h" +#include "vrf.h" + +#include +#include "zebra/zserv.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/rt.h" +#include "zebra/interface.h" +#include "zebra/debug.h" +#include "zebra/rtadv.h" +#include "zebra/kernel_netlink.h" +#include "zebra/rule_netlink.h" +#include "zebra/zebra_pbr.h" + +/* definitions */ + +/* static function declarations */ + +/* Private functions */ + +/* Install or uninstall specified rule for a specific interface. + * Form netlink message and ship it. Currently, notify status after + * waiting for netlink status. + */ +static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule, + struct interface *ifp, u_int32_t rule_pri) +{ + int family; + int bytelen; + struct { + struct nlmsghdr n; + struct fib_rule_hdr frh; + char buf[NL_PKT_BUF_SIZE]; + } req; + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct sockaddr_nl snl; + char buf1[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + + memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); + family = PREFIX_FAMILY(&rule->filter.src_ip); + bytelen = (family == AF_INET ? 4 : 16); + + req.n.nlmsg_type = cmd; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + + req.frh.family = family; + req.frh.action = FR_ACT_TO_TBL; + + if (cmd == RTM_NEWRULE) + req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + /* rule's pref # */ + addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule_pri); + + /* interface on which applied */ + addattr_l(&req.n, sizeof(req), FRA_IFNAME, ifp->name, + strlen(ifp->name)+1); + + /* source IP, if specified */ + if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { + req.frh.src_len = rule->filter.src_ip.prefixlen; + addattr_l(&req.n, sizeof(req), FRA_SRC, + &rule->filter.src_ip.u.prefix, bytelen); + } + /* destination IP, if specified */ + if (IS_RULE_FILTERING_ON_DST_IP(rule)) { + req.frh.dst_len = rule->filter.dst_ip.prefixlen; + addattr_l(&req.n, sizeof(req), FRA_DST, + &rule->filter.dst_ip.u.prefix, bytelen); + } + + /* Route table to use to forward, if filter criteria matches. */ + if (rule->action.table < 256) + req.frh.table = rule->action.table; + else { + req.frh.table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof(req), FRA_TABLE, + rule->action.table); + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Tx %s family %s IF %s(%u) Pref %u Src %s " + "Dst %s Table %u", + nl_msg_type_to_str(cmd), + nl_family_to_str(family), + ifp->name, ifp->ifindex, rule_pri, + prefix2str(&rule->filter.src_ip, buf1, sizeof(buf1)), + prefix2str(&rule->filter.dst_ip, buf2, sizeof(buf2)), + rule->action.table); + + /* Ship off the message. + * Note: Currently, netlink_talk() is a blocking call which returns + * back the status. + */ + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + return netlink_talk(netlink_talk_filter, &req.n, + &zns->netlink_cmd, zns, 0); +} + + +/* Public functions */ +/* + * Install specified rule for a specific interface. The preference is what + * goes in the rule to denote relative ordering; it may or may not be the + * same as the rule's user-defined sequence number. + */ +void kernel_add_pbr_rule(struct zebra_pbr_rule *rule, + struct interface *ifp, u_int32_t rule_pri) +{ + int ret = 0; + + ret = netlink_rule_update(RTM_NEWRULE, rule, ifp, rule_pri); + kernel_pbr_rule_add_del_status(rule, ifp, rule_pri, + (!ret) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); +} + +/* + * Uninstall specified rule for a specific interface. + */ +void kernel_del_pbr_rule(struct zebra_pbr_rule *rule, + struct interface *ifp, u_int32_t rule_pri) +{ + int ret = 0; + + ret = netlink_rule_update(RTM_DELRULE, rule, ifp, rule_pri); + kernel_pbr_rule_add_del_status(rule, ifp, rule_pri, + (!ret) ? + SOUTHBOUND_DELETE_SUCCESS : + SOUTHBOUND_DELETE_FAILURE); +} + +/* + * Handle netlink notification informing a rule add or delete. + * Handling of an ADD is TBD. + * DELs are notified up, if other attributes indicate it may be a + * notification of interest. The expectation is that if this corresponds + * to a PBR rule added by FRR, it will be readded. + */ +int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id, int startup) +{ + struct zebra_ns *zns; + struct fib_rule_hdr *frh; + struct rtattr *tb[FRA_MAX + 1]; + int len; + char *ifname; + struct interface *ifp; + u_int32_t rule_pri = 0; + struct zebra_pbr_rule rule; + char buf1[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + + /* Basic validation followed by extracting attributes. */ + if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) + return 0; + + /* TBD */ + if (h->nlmsg_type == RTM_NEWRULE) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); + if (len < 0) + return -1; + + frh = NLMSG_DATA(h); + if (frh->family != AF_INET && frh->family != AF_INET6) + return 0; + if (frh->action != FR_ACT_TO_TBL) + return 0; + + memset(tb, 0, sizeof(tb)); + netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); + + /* TBD: We don't care about rules not specifying an IIF. */ + if (tb[FRA_IFNAME] == NULL) + return 0; + + /* If we don't know the interface, we don't care. */ + ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); + zns = zebra_ns_lookup(ns_id); + ifp = if_lookup_by_name_per_ns(zns, ifname); + if (!ifp) + return 0; + + memset(&rule, 0, sizeof(rule)); + if (tb[FRA_PRIORITY]) + rule_pri = *(u_int32_t *)RTA_DATA(tb[FRA_PRIORITY]); + + if (tb[FRA_SRC]) { + if (frh->family == AF_INET) + memcpy(&rule.filter.src_ip.u.prefix4, + RTA_DATA(tb[FRA_SRC]), 4); + else + memcpy(&rule.filter.src_ip.u.prefix6, + RTA_DATA(tb[FRA_SRC]), 16); + rule.filter.src_ip.prefixlen = frh->src_len; + rule.filter.filter_bm |= PBR_FILTER_SRC_IP; + } + + if (tb[FRA_DST]) { + if (frh->family == AF_INET) + memcpy(&rule.filter.dst_ip.u.prefix4, + RTA_DATA(tb[FRA_DST]), 4); + else + memcpy(&rule.filter.dst_ip.u.prefix6, + RTA_DATA(tb[FRA_DST]), 16); + rule.filter.dst_ip.prefixlen = frh->dst_len; + rule.filter.filter_bm |= PBR_FILTER_DST_IP; + } + + if (tb[FRA_TABLE]) + rule.action.table = *(u_int32_t *)RTA_DATA(tb[FRA_TABLE]); + else + rule.action.table = frh->table; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Rx %s family %s IF %s(%u) Pref %u Src %s " + "Dst %s Table %u", + nl_msg_type_to_str(h->nlmsg_type), + nl_family_to_str(frh->family), + ifp->name, ifp->ifindex, rule_pri, + prefix2str(&rule.filter.src_ip, buf1, sizeof(buf1)), + prefix2str(&rule.filter.dst_ip, buf2, sizeof(buf2)), + rule.action.table); + + return kernel_pbr_rule_del(&rule, ifp, rule_pri); +} + +/* + * Get to know existing PBR rules in the kernel - typically called at startup. + * TBD. + */ +int netlink_rules_read(struct zebra_ns *zns) +{ + return 0; +} + +#endif /* HAVE_NETLINK */ diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h new file mode 100644 index 000000000000..034068b8995b --- /dev/null +++ b/zebra/rule_netlink.h @@ -0,0 +1,42 @@ +/* + * Zebra Policy Based Routing (PBR) interaction with the kernel using + * netlink - public definitions and function declarations. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_RULE_NETLINK_H +#define _ZEBRA_RULE_NETLINK_H + +#ifdef HAVE_NETLINK + +/* + * Handle netlink notification informing a rule add or delete. + */ +extern int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, + ns_id_t ns_id, int startup); + +/* + * Get to know existing PBR rules in the kernel - typically called at startup. + */ +extern int netlink_rules_read(struct zebra_ns *zns); + +#endif /* HAVE_NETLINK */ + +#endif /* _ZEBRA_RULE_NETLINK_H */ diff --git a/zebra/subdir.am b/zebra/subdir.am index 347482362341..0b3185327ae5 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -45,6 +45,7 @@ zebra_zebra_SOURCES = \ zebra/rtread_getmsg.c \ zebra/rtread_netlink.c \ zebra/rtread_sysctl.c \ + zebra/rule_netlink.c \ zebra/zebra_l2.c \ zebra/zebra_memory.c \ zebra/zebra_mpls.c \ @@ -54,6 +55,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_mpls_vty.c \ zebra/zebra_mroute.c \ zebra/zebra_ns.c \ + zebra/zebra_pbr.c \ zebra/zebra_ptm.c \ zebra/zebra_ptm_redistribute.c \ zebra/zebra_pw.c \ @@ -88,12 +90,14 @@ noinst_HEADERS += \ zebra/rt.h \ zebra/rt_netlink.h \ zebra/rtadv.h \ + zebra/rule_netlink.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_memory.h \ zebra/zebra_mpls.h \ zebra/zebra_mroute.h \ zebra/zebra_ns.h \ + zebra/zebra_pbr.h \ zebra/zebra_ptm.h \ zebra/zebra_ptm_redistribute.h \ zebra/zebra_pw.h \ diff --git a/zebra/zebra.conf.sample b/zebra/zebra.conf.sample index a5d0732f6b3d..03042eb083ba 100644 --- a/zebra/zebra.conf.sample +++ b/zebra/zebra.conf.sample @@ -2,8 +2,6 @@ ! ! zebra sample configuration file ! -! $Id: zebra.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ -! hostname Router password zebra enable password zebra diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 0ffa55f1e44a..a6e0882ff89b 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -713,7 +713,15 @@ static int zfpm_read_cb(struct thread *thread) nbyte = stream_read_try(ibuf, zfpm_g->sock, FPM_MSG_HDR_LEN - already); if (nbyte == 0 || nbyte == -1) { - zfpm_connection_down("closed socket in read"); + if (nbyte == -1) { + char buffer[1024]; + + sprintf(buffer, "closed socket in read(%d): %s", + errno, safe_strerror(errno)); + zfpm_connection_down(buffer); + } + else + zfpm_connection_down("closed socket in read"); return 0; } @@ -743,7 +751,15 @@ static int zfpm_read_cb(struct thread *thread) nbyte = stream_read_try(ibuf, zfpm_g->sock, msg_len - already); if (nbyte == 0 || nbyte == -1) { - zfpm_connection_down("failed to read message"); + if (nbyte == -1) { + char buffer[1024]; + + sprintf(buffer, "failed to read message(%d) %s", + errno, safe_strerror(errno)); + zfpm_connection_down(buffer); + } + else + zfpm_connection_down("failed to read message"); return 0; } @@ -842,19 +858,7 @@ static inline int zfpm_encode_route(rib_dest_t *dest, struct route_entry *re, */ struct route_entry *zfpm_route_for_update(rib_dest_t *dest) { - struct route_entry *re; - - RE_DEST_FOREACH_ROUTE (dest, re) { - if (!CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) - continue; - - return re; - } - - /* - * We have no route for this destination. - */ - return NULL; + return dest->selected_fib; } /* @@ -996,7 +1000,7 @@ static int zfpm_write_cb(struct thread *thread) break; bytes_written = - write(zfpm_g->sock, STREAM_PNT(s), bytes_to_write); + write(zfpm_g->sock, stream_pnt(s), bytes_to_write); zfpm_g->stats.write_calls++; num_writes++; diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 27c789137263..97a0e142fb33 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -249,7 +249,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->rtm_type = RTN_UNICAST; ri->metric = &re->metric; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (ri->num_nhs >= multipath_num) break; diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index b850f1fb1e12..e661b6efc777 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -170,7 +170,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, * Figure out the set of nexthops to be added to the message. */ num_nhs = 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (num_nhs >= multipath_num) break; diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 320176ba3a97..09d602727fac 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -104,7 +104,7 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, ifindex_t ifindex, mpls_label_t out_label); static int nhlfe_del(zebra_nhlfe_t *snhlfe); static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, - struct nexthop_label *nh_label); + struct mpls_label_stack *nh_label); static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, @@ -182,7 +182,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, * the label advertised by the recursive nexthop (plus we don't have the * logic yet to push multiple labels). */ - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { /* Skip inactive and recursive entries. */ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; @@ -457,7 +457,7 @@ static int fec_send(zebra_fec_t *fec, struct zserv *client) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_FEC_UPDATE, VRF_DEFAULT); + zclient_create_header(s, ZEBRA_FEC_UPDATE, VRF_DEFAULT); stream_putw(s, rn->p.family); stream_put_prefix(s, &rn->p); @@ -636,7 +636,7 @@ static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, || !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) continue; - for (match_nh = match->nexthop; match_nh; + for (match_nh = match->ng.nexthop; match_nh; match_nh = match_nh->next) { if (match->type == ZEBRA_ROUTE_CONNECT || nexthop->ifindex == match_nh->ifindex) { @@ -687,10 +687,10 @@ static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe, break; } - if (!match || !match->nexthop) + if (!match || !match->ng.nexthop) return 0; - nexthop->ifindex = match->nexthop->ifindex; + nexthop->ifindex = match->ng.nexthop->ifindex; return 1; } @@ -839,18 +839,11 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) */ static void lsp_uninstall_from_kernel(struct hash_backet *backet, void *ctxt) { - int ret; zebra_lsp_t *lsp; lsp = (zebra_lsp_t *)backet->data; - if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { - ret = kernel_del_lsp(lsp); - - if (!ret) { - UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - clear_nhlfe_installed(lsp); - } - } + if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) + kernel_del_lsp(lsp); } /* @@ -871,7 +864,6 @@ static void lsp_schedule(struct hash_backet *backet, void *ctxt) */ static wq_item_status lsp_process(struct work_queue *wq, void *data) { - int ret = 1; zebra_lsp_t *lsp; zebra_nhlfe_t *oldbest, *newbest; char buf[BUFSIZ], buf2[BUFSIZ]; @@ -905,12 +897,7 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) if (newbest) { UNSET_FLAG(lsp->flags, LSP_FLAG_CHANGED); - ret = kernel_add_lsp(lsp); - - if (!ret) - SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - else - clear_nhlfe_installed(lsp); + kernel_add_lsp(lsp); zvrf->lsp_installs++; } @@ -918,25 +905,38 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) /* Installed, may need an update and/or delete. */ if (!newbest) { - ret = kernel_del_lsp(lsp); - - if (!ret) { - UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - clear_nhlfe_installed(lsp); - } + kernel_del_lsp(lsp); zvrf->lsp_removals++; } else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) { + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop; UNSET_FLAG(lsp->flags, LSP_FLAG_CHANGED); UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - ret = kernel_upd_lsp(lsp); + /* + * Any NHLFE that was installed but is not + * selected now needs to have its flags updated. + */ + for (nhlfe = lsp->nhlfe_list; + nhlfe; nhlfe = nhlfe->next) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_INSTALLED) && + !CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_SELECTED)) { + UNSET_FLAG(nhlfe->flags, + NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB); + } + } - if (!ret) - SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - else - clear_nhlfe_installed(lsp); + kernel_upd_lsp(lsp); zvrf->lsp_installs++; } @@ -1217,7 +1217,7 @@ static int nhlfe_del(zebra_nhlfe_t *nhlfe) * Update label for NHLFE entry. */ static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, - struct nexthop_label *nh_label) + struct mpls_label_stack *nh_label) { nhlfe->nexthop->nh_label->label[0] = nh_label->label[0]; } @@ -1659,6 +1659,42 @@ static int mpls_processq_init(struct zebra_t *zebra) /* Public functions */ +void kernel_lsp_pass_fail(zebra_lsp_t *lsp, + enum southbound_results res) +{ + struct nexthop *nexthop; + zebra_nhlfe_t *nhlfe; + + if (!lsp) + return; + + switch (res) { + case SOUTHBOUND_INSTALL_FAILURE: + UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + clear_nhlfe_installed(lsp); + zlog_warn("LSP Install Failure: %u", lsp->ile.in_label); + break; + case SOUTHBOUND_INSTALL_SUCCESS: + SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + break; + case SOUTHBOUND_DELETE_SUCCESS: + UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + clear_nhlfe_installed(lsp); + break; + case SOUTHBOUND_DELETE_FAILURE: + zlog_warn("LSP Deletion Failure: %u", lsp->ile.in_label); + break; + } +} + /* * String to label conversion, labels separated by '/'. * @@ -2222,7 +2258,7 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, if (re == NULL) return -1; - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -2440,7 +2476,7 @@ void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi) for (rn = route_top(table); rn; rn = route_next(rn)) { update = 0; RNODE_FOREACH_RE (rn, re) { - for (nexthop = re->nexthop; nexthop; + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { if (nexthop->nh_label_type != ZEBRA_LSP_LDP) continue; diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index 0abc4959fa47..3c8d25189e98 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -29,16 +29,21 @@ /* * Install Label Forwarding entry into the kernel. */ -int kernel_add_lsp(zebra_lsp_t *lsp) +void kernel_add_lsp(zebra_lsp_t *lsp) { int ret; - if (!lsp || !lsp->best_nhlfe) // unexpected - return -1; + if (!lsp || !lsp->best_nhlfe) { // unexpected + kernel_lsp_pass_fail(lsp, SOUTHBOUND_INSTALL_FAILURE); + return; + } ret = netlink_mpls_multipath(RTM_NEWROUTE, lsp); - return ret; + kernel_lsp_pass_fail(lsp, + (!ret) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); } /* @@ -52,51 +57,48 @@ int kernel_add_lsp(zebra_lsp_t *lsp) * through the metric field (before kernel-MPLS). This shouldn't be an issue * any longer, so REPLACE can be reintroduced. */ -int kernel_upd_lsp(zebra_lsp_t *lsp) +void kernel_upd_lsp(zebra_lsp_t *lsp) { int ret; - zebra_nhlfe_t *nhlfe; - struct nexthop *nexthop; - if (!lsp || !lsp->best_nhlfe) // unexpected - return -1; - - /* Any NHLFE that was installed but is not selected now needs to - * have its flags updated. - */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { - nexthop = nhlfe->nexthop; - if (!nexthop) - continue; - - if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) && - !CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)) { - UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } + if (!lsp || !lsp->best_nhlfe) { // unexpected + kernel_lsp_pass_fail(lsp, SOUTHBOUND_INSTALL_FAILURE); + return; } ret = netlink_mpls_multipath(RTM_NEWROUTE, lsp); - return ret; + kernel_lsp_pass_fail(lsp, + (!ret) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); } /* * Delete Label Forwarding entry from the kernel. */ -int kernel_del_lsp(zebra_lsp_t *lsp) +void kernel_del_lsp(zebra_lsp_t *lsp) { int ret; - if (!lsp) // unexpected - return -1; + if (!lsp) { // unexpected + kernel_lsp_pass_fail(lsp, + SOUTHBOUND_DELETE_FAILURE); + return; + } - if (!CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) - return -1; + if (!CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { + kernel_lsp_pass_fail(lsp, + SOUTHBOUND_DELETE_FAILURE); + return; + } ret = netlink_mpls_multipath(RTM_DELROUTE, lsp); - return ret; + kernel_lsp_pass_fail(lsp, + (!ret) ? + SOUTHBOUND_DELETE_SUCCESS : + SOUTHBOUND_DELETE_FAILURE); } int mpls_kernel_init(void) diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index e4dc570fd92a..6b5318325d2c 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -24,17 +24,17 @@ #if !defined(HAVE_NETLINK) && !defined(OPEN_BSD) -int kernel_add_lsp(zebra_lsp_t *lsp) +void kernel_add_lsp(zebra_lsp_t *lsp) { - return 0; + return; } -int kernel_upd_lsp(zebra_lsp_t *lsp) +void kernel_upd_lsp(zebra_lsp_t *lsp) { - return 0; + return; } -int kernel_del_lsp(zebra_lsp_t *lsp) +void kernel_del_lsp(zebra_lsp_t *lsp) { - return 0; + return; } int mpls_kernel_init(void) { diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 44f89f0b3bd5..2fc93893fa0d 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -279,56 +279,69 @@ static int kernel_lsp_cmd(int action, zebra_lsp_t *lsp) default: break; } - if (action == RTM_ADD || action == RTM_CHANGE) { - SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } else { - UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } } } return (0); } -int kernel_add_lsp(zebra_lsp_t *lsp) +void kernel_add_lsp(zebra_lsp_t *lsp) { int ret; - if (!lsp || !lsp->best_nhlfe) // unexpected - return -1; + if (!lsp || !lsp->best_nhlfe) { // unexpected + kernel_lsp_pass_fail(lsp, SOUTHBOUND_INSTALL_FAILURE); + return; + } ret = kernel_lsp_cmd(RTM_ADD, lsp); - return ret; + kernel_lsp_pass_fail(lsp, + (!ret) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); } -int kernel_upd_lsp(zebra_lsp_t *lsp) +void kernel_upd_lsp(zebra_lsp_t *lsp) { int ret; - if (!lsp || !lsp->best_nhlfe) // unexpected - return -1; + if (!lsp || !lsp->best_nhlfe) { // unexpected + kernel_lsp_pass_fail(lsp, SOUTHBOUND_INSTALL_FAILURE); + return; + } ret = kernel_lsp_cmd(RTM_CHANGE, lsp); - return ret; + kernel_lsp_pass_fail(lsp, + (!ret) ? + SOUTHBOUND_INSTALL_SUCCESS : + SOUTHBOUND_INSTALL_FAILURE); + return; } -int kernel_del_lsp(zebra_lsp_t *lsp) +void kernel_del_lsp(zebra_lsp_t *lsp) { int ret; - if (!lsp) // unexpected - return -1; + if (!lsp) { // unexpected + kernel_lsp_pass_fail(lsp, + SOUTHBOUND_DELETE_FAILURE); + return; + } - if (!CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) - return -1; + if (!CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { + kernel_lsp_pass_fail(lsp, + SOUTHBOUND_DELETE_FAILURE); + return; + } ret = kernel_lsp_cmd(RTM_DELETE, lsp); - return ret; + kernel_lsp_pass_fail(lsp, + (!ret) ? + SOUTHBOUND_DELETE_SUCCESS : + SOUTHBOUND_DELETE_FAILURE); } static int kmpw_install(struct zebra_pw *pw) diff --git a/zebra/zebra_mroute.c b/zebra/zebra_mroute.c index 519c120a67e0..e9cd19ebe0cf 100644 --- a/zebra/zebra_mroute.c +++ b/zebra/zebra_mroute.c @@ -61,7 +61,7 @@ int zebra_ipmr_route_stats(struct zserv *client, u_short length, stream_reset(s); - zserv_create_header(s, ZEBRA_IPMR_ROUTE_STATS, zvrf_id(zvrf)); + zclient_create_header(s, ZEBRA_IPMR_ROUTE_STATS, zvrf_id(zvrf)); stream_put_in_addr(s, &mroute.sg.src); stream_put_in_addr(s, &mroute.sg.grp); stream_put(s, &mroute.lastused, sizeof(mroute.lastused)); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 78072f43bb9e..b3b9c6d18a4c 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -29,6 +29,8 @@ #include "zebra_ns.h" #include "zebra_vrf.h" #include "zebra_memory.h" +#include "rt.h" +#include "zebra_vxlan.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -48,6 +50,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) #endif zns->if_table = route_table_init(); + zebra_vxlan_ns_init(zns); kernel_init(zns); interface_list(zns); route_read(zns); @@ -60,6 +63,7 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) struct zebra_ns *zns = (struct zebra_ns *)(*info); route_table_finish(zns->if_table); + zebra_vxlan_ns_disable(zns); #if defined(HAVE_RTADV) rtadv_terminate(zns); #endif @@ -77,7 +81,7 @@ int zebra_ns_init(void) zebra_vrf_init(); - zebra_ns_enable(0, (void **)&dzns); + zebra_ns_enable(NS_DEFAULT, (void **)&dzns); return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 6cfba93e50ae..78990f76ade3 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -23,6 +23,7 @@ #define __ZEBRA_NS_H__ #include +#include #ifdef HAVE_NETLINK /* Socket interface to kernel */ @@ -49,14 +50,14 @@ struct zebra_ns { struct route_table *if_table; + /* L3-VNI hash table (for EVPN). Only in default instance */ + struct hash *l3vni_table; + #if defined(HAVE_RTADV) struct rtadv rtadv; #endif /* HAVE_RTADV */ }; -#define NS_DEFAULT 0 -#define NS_UNKNOWN UINT16_MAX - struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(void); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c new file mode 100644 index 000000000000..6e521be39eae --- /dev/null +++ b/zebra/zebra_pbr.c @@ -0,0 +1,54 @@ +/* Zebra Policy Based Routing (PBR) main handling. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "zebra/zebra_pbr.h" +#include "zebra/rt.h" + +/* definitions */ + +/* static function declarations */ + +/* Private functions */ + +/* Public functions */ +/* + * Handle success or failure of rule (un)install in the kernel. + */ +void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, + struct interface *ifp, + u_int32_t rule_pri, + enum southbound_results res) +{ +} + +/* + * Handle rule delete notification from kernel. + */ +int kernel_pbr_rule_del(struct zebra_pbr_rule *rule, + struct interface *ifp, + u_int32_t rule_pri) +{ + return 0; +} + + diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h new file mode 100644 index 000000000000..2e80aeb8a359 --- /dev/null +++ b/zebra/zebra_pbr.h @@ -0,0 +1,128 @@ +/* + * Zebra Policy Based Routing (PBR) Data structures and definitions + * These are public definitions referenced by multiple files. + * Copyright (C) 2018 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_PBR_H +#define _ZEBRA_PBR_H + +#include + +#include "prefix.h" +#include "if.h" +#include "rt.h" + +/* + * A PBR filter + * + * The filter or match criteria in a PBR rule. + * For simplicity, all supported filters are grouped into a structure rather + * than delineating further. A bitmask denotes which filters are actually + * specified. + */ +struct zebra_pbr_filter { + u_int32_t filter_bm; +#define PBR_FILTER_SRC_IP (1 << 0) +#define PBR_FILTER_DST_IP (1 << 1) +#define PBR_FILTER_SRC_PORT (1 << 2) +#define PBR_FILTER_DST_PORT (1 << 3) + + /* Source and Destination IP address with masks. */ + struct prefix src_ip; + struct prefix dst_ip; + + /* Source and Destination higher-layer (TCP/UDP) port numbers. */ + u_int16_t src_port; + u_int16_t dst_port; +}; + +#define IS_RULE_FILTERING_ON_SRC_IP(r) \ + (r->filter.filter_bm & PBR_FILTER_SRC_IP) +#define IS_RULE_FILTERING_ON_DST_IP(r) \ + (r->filter.filter_bm & PBR_FILTER_DST_IP) +#define IS_RULE_FILTERING_ON_SRC_PORT(r) \ + (r->filter.filter_bm & PBR_FILTER_SRC_PORT) +#define IS_RULE_FILTERING_ON_DST_PORT(r) \ + (r->filter.filter_bm & PBR_FILTER_DST_PORT) + +/* + * A PBR action + * + * The action corresponding to a PBR rule. + * While the user specifies the action in a particular way, the forwarding + * plane implementation (Linux only) requires that to be encoded into a + * route table and the rule then point to that route table; in some cases, + * the user criteria may directly point to a table too. + */ +struct zebra_pbr_action { + u_int32_t table; +}; + +/* + * A PBR rule + * + * This is a combination of the filter criteria and corresponding action. + * Rules also have a user-defined sequence number which defines the relative + * order amongst rules. + */ +struct zebra_pbr_rule { + u_int32_t seq; + struct zebra_pbr_filter filter; + struct zebra_pbr_action action; +}; + + +/* + * Install specified rule for a specific interface. + * It is possible that the user-defined sequence number and the one in the + * forwarding plane may not coincide, hence the API requires a separate + * rule priority - maps to preference/FRA_PRIORITY on Linux. + */ +extern void kernel_add_pbr_rule(struct zebra_pbr_rule *rule, + struct interface *ifp, u_int32_t rule_pri); + +/* + * Uninstall specified rule for a specific interface. + */ +extern void kernel_del_pbr_rule(struct zebra_pbr_rule *rule, + struct interface *ifp, u_int32_t rule_pri); + +/* + * Get to know existing PBR rules in the kernel - typically called at startup. + */ +extern void kernel_read_pbr_rules(struct zebra_ns *zns); + +/* + * Handle success or failure of rule (un)install in the kernel. + */ +extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, + struct interface *ifp, + u_int32_t rule_pri, + enum southbound_results res); + +/* + * Handle rule delete notification from kernel. + */ +extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule, + struct interface *ifp, + u_int32_t rule_pri); + +#endif /* _ZEBRA_PBR_H */ diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index dcd4fb02398a..953f74ecec17 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -432,7 +432,7 @@ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp, } else { zlog_debug( "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d " - "with src %s/%d and vrf %d %s event", + "with src %s/%d and vrf %u %s event", inet_ntop(dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN), dp->prefixlen, @@ -817,6 +817,7 @@ int zebra_ptm_bfd_dst_register(struct zserv *client, u_short length, zebra_ptm_send_message(ptm_cb.out_data, data_len); stream_failure: + ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); return 0; } @@ -946,6 +947,7 @@ int zebra_ptm_bfd_dst_deregister(struct zserv *client, u_short length, zebra_ptm_send_message(ptm_cb.out_data, data_len); stream_failure: + ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); return 0; } diff --git a/zebra/zebra_ptm_redistribute.c b/zebra/zebra_ptm_redistribute.c index 1378ea186d51..8fddd400cc04 100644 --- a/zebra/zebra_ptm_redistribute.c +++ b/zebra/zebra_ptm_redistribute.c @@ -41,7 +41,7 @@ static int zsend_interface_bfd_update(int cmd, struct zserv *client, s = client->obuf; stream_reset(s); - zserv_create_header(s, cmd, vrf_id); + zclient_create_header(s, cmd, vrf_id); if (ifp) stream_putl(s, ifp->ifindex); else @@ -96,8 +96,7 @@ static int zsend_bfd_peer_replay(int cmd, struct zserv *client) s = client->obuf; stream_reset(s); - zserv_create_header( - s, cmd, VRF_DEFAULT); // Pending: adjust when multi-vrf bfd work + zclient_create_header(s, cmd, VRF_DEFAULT); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index d3492fb41c1a..8da8d7069522 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -73,7 +73,7 @@ struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, pw->protocol = protocol; pw->vrf_id = zvrf_id(zvrf); pw->client = client; - pw->status = PW_STATUS_UP; + pw->status = PW_STATUS_DOWN; pw->local_label = MPLS_NO_LABEL; pw->remote_label = MPLS_NO_LABEL; pw->flags = F_PSEUDOWIRE_CWORD; @@ -256,7 +256,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) * Need to ensure that there's a label binding for all nexthops. * Otherwise, ECMP for this route could render the pseudowire unusable. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { if (!nexthop->nh_label) { if (IS_ZEBRA_DEBUG_PW) zlog_warn("%s: unlabeled route for %s", diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 791f319120be..bb35ec0a151f 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -50,6 +50,7 @@ #include "zebra/zebra_rnh.h" #include "zebra/interface.h" #include "zebra/connected.h" +#include "zebra/zebra_vxlan.h" DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) @@ -181,7 +182,7 @@ int zebra_check_addr(struct prefix *p) /* Add nexthop to the end of a rib node's nexthop list */ void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop) { - nexthop_add(&re->nexthop, nexthop); + nexthop_add(&re->ng.nexthop, nexthop); re->nexthop_num++; } @@ -191,8 +192,8 @@ void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop) */ void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh) { - assert(!re->nexthop); - copy_nexthops(&re->nexthop, nh, NULL); + assert(!re->ng.nexthop); + copy_nexthops(&re->ng.nexthop, nh, NULL); for (struct nexthop *nexthop = nh; nexthop; nexthop = nexthop->next) re->nexthop_num++; } @@ -205,7 +206,7 @@ void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop) if (nexthop->prev) nexthop->prev->next = nexthop->next; else - re->nexthop = nexthop->next; + re->ng.nexthop = nexthop->next; re->nexthop_num--; } @@ -255,11 +256,12 @@ struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, if (src) nexthop->src.ipv4 = *src; nexthop->ifindex = ifindex; - ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id); + ifp = if_lookup_by_index(nexthop->ifindex, re->nh_vrf_id); /*Pending: need to think if null ifp here is ok during bootup? There was a crash because ifp here was coming to be NULL */ if (ifp) - if (connected_is_unnumbered(ifp)) { + if (connected_is_unnumbered(ifp) || + CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); } @@ -370,6 +372,12 @@ static void nexthop_set_resolved(afi_t afi, struct nexthop *newhop, break; } + /* Copy labels of the resolved route */ + if (newhop->nh_label) + nexthop_add_labels(resolved_hop, newhop->nh_label_type, + newhop->nh_label->num_labels, + &newhop->nh_label->label[0]); + resolved_hop->rparent = nexthop; nexthop_add(&nexthop->resolved, resolved_hop); } @@ -383,10 +391,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, struct prefix p; struct route_table *table; struct route_node *rn; - struct route_entry *match; + struct route_entry *match = NULL; int resolved; struct nexthop *newhop; struct interface *ifp; + rib_dest_t *dest; if ((nexthop->type == NEXTHOP_TYPE_IPV4) || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -394,7 +403,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (set) { UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - zebra_deregister_rnh_static_nexthops(re->vrf_id, + zebra_deregister_rnh_static_nexthops(re->nh_vrf_id, nexthop->resolved, top); nexthops_free(nexthop->resolved); nexthop->resolved = NULL; @@ -413,7 +422,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, * address in the routing table. */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { - ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id); + ifp = if_lookup_by_index(nexthop->ifindex, re->nh_vrf_id); if (ifp && connected_is_unnumbered(ifp)) { if (if_is_operative(ifp)) return 1; @@ -441,7 +450,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, break; } /* Lookup table. */ - table = zebra_vrf_table(afi, SAFI_UNICAST, re->vrf_id); + table = zebra_vrf_table(afi, SAFI_UNICAST, re->nh_vrf_id); if (!table) return 0; @@ -463,20 +472,15 @@ static int nexthop_active(afi_t afi, struct route_entry *re, /* However, do not resolve over default route unless explicitly * allowed. */ if (is_default_prefix(&rn->p) - && !nh_resolve_via_default(p.family)) + && !rnh_resolve_via_default(p.family)) return 0; - RNODE_FOREACH_RE (rn, match) { - if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED)) - continue; - - /* if the next hop is imported from another table, skip - * it */ - if (match->type == ZEBRA_ROUTE_TABLE) - continue; - if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB)) - break; - } + dest = rib_dest_from_rnode(rn); + if (dest && dest->selected_fib && + !CHECK_FLAG(dest->selected_fib->status, + ROUTE_ENTRY_REMOVED) && + dest->selected_fib->type != ZEBRA_ROUTE_TABLE) + match = dest->selected_fib; /* If there is no selected route or matched route is EGP, go up tree. */ @@ -492,7 +496,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ - newhop = match->nexthop; + newhop = match->ng.nexthop; if (newhop) { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -501,7 +505,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_INTERNAL)) { resolved = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) { + for (ALL_NEXTHOPS(match->ng, newhop)) { if (!CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB)) continue; @@ -524,7 +528,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return resolved; } else if (re->type == ZEBRA_ROUTE_STATIC) { resolved = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) { + for (ALL_NEXTHOPS(match->ng, newhop)) { if (!CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB)) continue; @@ -553,7 +557,7 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, struct prefix p; struct route_table *table; struct route_node *rn; - struct route_entry *match; + struct route_entry *match = NULL; struct nexthop *newhop; /* Lookup table. */ @@ -574,15 +578,14 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, rn = route_node_match(table, (struct prefix *)&p); while (rn) { + rib_dest_t *dest; + route_unlock_node(rn); - /* Pick up selected route. */ - RNODE_FOREACH_RE (rn, match) { - if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED)) - continue; - if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB)) - break; - } + dest = rib_dest_from_rnode(rn); + if (dest && dest->selected_fib && + !CHECK_FLAG(dest->selected_fib->status, ROUTE_ENTRY_REMOVED)) + match = dest->selected_fib; /* If there is no selected route or matched route is EGP, go up tree. */ @@ -595,7 +598,7 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, } else { if (match->type != ZEBRA_ROUTE_CONNECT) { int found = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) + for (ALL_NEXTHOPS(match->ng, newhop)) if (CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB)) { found = 1; @@ -689,8 +692,9 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; struct route_node *rn; - struct route_entry *match; + struct route_entry *match = NULL; struct nexthop *nexthop; + rib_dest_t *dest; /* Lookup table. */ table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id); @@ -705,13 +709,11 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* Unlock node. */ route_unlock_node(rn); + dest = rib_dest_from_rnode(rn); - RNODE_FOREACH_RE (rn, match) { - if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED)) - continue; - if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB)) - break; - } + if (dest && dest->selected_fib && + !CHECK_FLAG(dest->selected_fib->status, ROUTE_ENTRY_REMOVED)) + match = dest->selected_fib; if (!match) return NULL; @@ -719,7 +721,7 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) if (match->type == ZEBRA_ROUTE_CONNECT) return match; - for (ALL_NEXTHOPS(match->nexthop, nexthop)) + for (ALL_NEXTHOPS(match->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) return match; @@ -743,9 +745,10 @@ int rib_lookup_ipv4_route(struct prefix_ipv4 *p, union sockunion *qgate, { struct route_table *table; struct route_node *rn; - struct route_entry *match; + struct route_entry *match = NULL; struct nexthop *nexthop; int nexthops_active; + rib_dest_t *dest; /* Lookup table. */ table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id); @@ -761,15 +764,13 @@ int rib_lookup_ipv4_route(struct prefix_ipv4 *p, union sockunion *qgate, /* Unlock node. */ route_unlock_node(rn); + dest = rib_dest_from_rnode(rn); /* Find out if a "selected" RR for the discovered RIB entry exists ever. */ - RNODE_FOREACH_RE (rn, match) { - if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED)) - continue; - if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB)) - break; - } + if (dest && dest->selected_fib && + !CHECK_FLAG(dest->selected_fib->status, ROUTE_ENTRY_REMOVED)) + match = dest->selected_fib; /* None such found :( */ if (!match) @@ -780,7 +781,7 @@ int rib_lookup_ipv4_route(struct prefix_ipv4 *p, union sockunion *qgate, /* Ok, we have a cood candidate, let's check it's nexthop list... */ nexthops_active = 0; - for (ALL_NEXTHOPS(match->nexthop, nexthop)) + for (ALL_NEXTHOPS(match->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { nexthops_active = 1; if (nexthop->gate.ipv4.s_addr == sockunion2ip(qgate)) @@ -837,7 +838,7 @@ static unsigned nexthop_active_check(struct route_node *rn, family = 0; switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id); + ifp = if_lookup_by_index(nexthop->ifindex, re->nh_vrf_id); if (ifp && if_is_operative(ifp)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -846,7 +847,9 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, set, rn)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN_RVTEP)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else if (nexthop_active(AFI_IP, re, nexthop, set, rn)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -863,7 +866,8 @@ static unsigned nexthop_active_check(struct route_node *rn, if (rn->p.family != AF_INET) family = AFI_IP6; if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { - ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id); + ifp = if_lookup_by_index(nexthop->ifindex, + re->nh_vrf_id); if (ifp && if_is_operative(ifp)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -906,7 +910,7 @@ static unsigned nexthop_active_check(struct route_node *rn, memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); /* It'll get set if required inside */ - ret = zebra_route_map_check(family, re->type, p, nexthop, re->vrf_id, + ret = zebra_route_map_check(family, re->type, p, nexthop, re->nh_vrf_id, re->tag); if (ret == RMAP_DENYMATCH) { if (IS_ZEBRA_DEBUG_RIB) { @@ -914,7 +918,8 @@ static unsigned nexthop_active_check(struct route_node *rn, zlog_debug( "%u:%s: Filtering out with NH out %s due to route map", re->vrf_id, buf, - ifindex2ifname(nexthop->ifindex, re->vrf_id)); + ifindex2ifname(nexthop->ifindex, + re->nh_vrf_id)); } UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } @@ -942,7 +947,7 @@ static int nexthop_active_update(struct route_node *rn, struct route_entry *re, re->nexthop_active_num = 0; UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { /* No protocol daemon provides src and so we're skipping * tracking it */ prev_src = nexthop->rmap_src; @@ -986,20 +991,56 @@ int zebra_rib_labeled_unicast(struct route_entry *re) if (re->type != ZEBRA_ROUTE_BGP) return 0; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) if (!nexthop->nh_label || !nexthop->nh_label->num_labels) return 0; return 1; } +void kernel_route_rib_pass_fail(struct prefix *p, struct route_entry *re, + enum southbound_results res) +{ + struct nexthop *nexthop; + char buf[PREFIX_STRLEN]; + + switch (res) { + case SOUTHBOUND_INSTALL_SUCCESS: + for (ALL_NEXTHOPS(re->ng, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + zsend_route_notify_owner(re->type, re->instance, re->vrf_id, + p, ZAPI_ROUTE_INSTALLED); + break; + case SOUTHBOUND_INSTALL_FAILURE: + zsend_route_notify_owner(re->type, re->instance, re->vrf_id, + p, ZAPI_ROUTE_FAIL_INSTALL); + zlog_warn("%u:%s: Route install failed", re->vrf_id, + prefix2str(p, buf, sizeof(buf))); + break; + case SOUTHBOUND_DELETE_SUCCESS: + for (ALL_NEXTHOPS(re->ng, nexthop)) + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + break; + case SOUTHBOUND_DELETE_FAILURE: + zlog_warn("%u:%s: Route Deletion failure", re->vrf_id, + prefix2str(p, buf, sizeof(buf))); + break; + } +} + /* Update flag indicates whether this is a "replace" or not. Currently, this * is only used for IPv4. */ -int rib_install_kernel(struct route_node *rn, struct route_entry *re, - struct route_entry *old) +void rib_install_kernel(struct route_node *rn, struct route_entry *re, + struct route_entry *old) { - int ret = 0; struct nexthop *nexthop; rib_table_info_t *info = srcdest_rnode_table_info(rn); struct prefix *p, *src_p; @@ -1008,15 +1049,15 @@ int rib_install_kernel(struct route_node *rn, struct route_entry *re, srcdest_rnode_prefixes(rn, &p, &src_p); if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - return ret; + return; } else { struct nexthop *prev; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_DUPLICATE); - for (ALL_NEXTHOPS(re->nexthop, prev)) { + for (ALL_NEXTHOPS(re->ng, prev)) { if (prev == nexthop) break; if (nexthop_same_firsthop (nexthop, prev)) @@ -1042,33 +1083,15 @@ int rib_install_kernel(struct route_node *rn, struct route_entry *re, * the kernel. */ hook_call(rib_update, rn, "installing in kernel"); - ret = kernel_route_rib(p, src_p, old, re); + kernel_route_rib(p, src_p, old, re); zvrf->installs++; - /* If install succeeds, update FIB flag for nexthops. */ - if (!ret) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - zsend_route_notify_owner(re->type, re->instance, re->vrf_id, - p, ZAPI_ROUTE_INSTALLED); - } else - zsend_route_notify_owner(re->type, re->instance, re->vrf_id, - p, ZAPI_ROUTE_FAIL_INSTALL); - - return ret; + return; } /* Uninstall the route from kernel. */ -int rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) +void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { - int ret = 0; struct nexthop *nexthop; rib_table_info_t *info = srcdest_rnode_table_info(rn); struct prefix *p, *src_p; @@ -1077,9 +1100,9 @@ int rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) srcdest_rnode_prefixes(rn, &p, &src_p); if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - return ret; + return; } /* @@ -1087,21 +1110,19 @@ int rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) * the kernel. */ hook_call(rib_update, rn, "uninstalling from kernel"); - ret = kernel_route_rib(p, src_p, re, NULL); + kernel_route_rib(p, src_p, re, NULL); zvrf->removals++; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - - return ret; + return; } /* Uninstall the route from kernel. */ static void rib_uninstall(struct route_node *rn, struct route_entry *re) { rib_table_info_t *info = srcdest_rnode_table_info(rn); + rib_dest_t *dest = rib_dest_from_rnode(rn); - if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) { + if (dest && dest->selected_fib == re) { if (info->safi == SAFI_UNICAST) hook_call(rib_update, rn, "rib_uninstall"); @@ -1112,7 +1133,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) if (zebra_rib_labeled_unicast(re)) zebra_mpls_lsp_uninstall(info->zvrf, rn, re); - UNSET_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB); + dest->selected_fib = NULL; } if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) { @@ -1186,6 +1207,8 @@ int rib_gc_dest(struct route_node *rn) static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *new) { + rib_dest_t *dest = rib_dest_from_rnode(rn); + hook_call(rib_update, rn, "new route selected"); /* Update real nexthop. This may actually determine if nexthop is active @@ -1195,7 +1218,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, return; } - SET_FLAG(new->status, ROUTE_ENTRY_SELECTED_FIB); + dest->selected_fib = new; if (IS_ZEBRA_DEBUG_RIB) { char buf[SRCDEST2STR_BUFFER]; srcdest_rnode2str(rn, buf, sizeof(buf)); @@ -1207,14 +1230,8 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, if (zebra_rib_labeled_unicast(new)) zebra_mpls_lsp_install(zvrf, rn, new); - if (!RIB_SYSTEM_ROUTE(new)) { - if (rib_install_kernel(rn, new, NULL)) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_warn("%u:%s: Route install failed", zvrf_id(zvrf), - buf); - } - } + if (!RIB_SYSTEM_ROUTE(new)) + rib_install_kernel(rn, new, NULL); UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); } @@ -1222,6 +1239,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *old) { + rib_dest_t *dest = rib_dest_from_rnode(rn); hook_call(rib_update, rn, "removing existing route"); /* Uninstall from kernel. */ @@ -1239,7 +1257,7 @@ static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, if (!RIB_SYSTEM_ROUTE(old)) rib_uninstall_kernel(rn, old); - UNSET_FLAG(old->status, ROUTE_ENTRY_SELECTED_FIB); + dest->selected_fib = NULL; /* Update nexthop for route, reset changed flag. */ nexthop_active_update(rn, old, 1); @@ -1254,6 +1272,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, struct nexthop *nexthop = NULL; int nh_active = 0; int installed = 1; + rib_dest_t *dest = rib_dest_from_rnode(rn); /* * We have to install or update if a new route has been selected or @@ -1301,13 +1320,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (zebra_rib_labeled_unicast(new)) zebra_mpls_lsp_install(zvrf, rn, new); - if (rib_install_kernel(rn, new, old)) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); - installed = 0; - zlog_warn("%u:%s: Route install failed", - zvrf_id(zvrf), buf); - } + rib_install_kernel(rn, new, old); } /* If install succeeded or system route, cleanup flags @@ -1317,7 +1330,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (!RIB_SYSTEM_ROUTE(old)) rib_uninstall_kernel(rn, old); } else { - for (nexthop = old->nexthop; nexthop; + for (nexthop = old->ng.nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1326,7 +1339,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, /* Update for redistribution. */ if (installed) - SET_FLAG(new->status, ROUTE_ENTRY_SELECTED_FIB); + dest->selected_fib = new; } /* @@ -1361,7 +1374,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (!RIB_SYSTEM_ROUTE(old)) rib_uninstall_kernel(rn, old); - UNSET_FLAG(new->status, ROUTE_ENTRY_SELECTED_FIB); + dest->selected_fib = NULL; } } else { /* @@ -1376,7 +1389,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, if (!RIB_SYSTEM_ROUTE(new)) { int in_fib = 0; - for (ALL_NEXTHOPS(new->nexthop, nexthop)) + for (ALL_NEXTHOPS(new->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { in_fib = 1; @@ -1389,8 +1402,6 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, /* Update prior route. */ if (new != old) { - UNSET_FLAG(old->status, ROUTE_ENTRY_SELECTED_FIB); - /* Set real nexthop. */ nexthop_active_update(rn, old, 1); UNSET_FLAG(old->status, ROUTE_ENTRY_CHANGED); @@ -1472,6 +1483,15 @@ static void rib_process(struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug("%u:%s: Processing rn %p", vrf_id, buf, rn); + /* + * we can have rn's that have a NULL info pointer + * (dest). As such let's not let the deref happen + * additionally we know RNODE_FOREACH_RE_SAFE + * will not iterate so we are ok. + */ + if (dest) + old_fib = dest->selected_fib; + RNODE_FOREACH_RE_SAFE (rn, re, next) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( @@ -1487,11 +1507,6 @@ static void rib_process(struct route_node *rn) assert(old_selected == NULL); old_selected = re; } - /* Currently in fib */ - if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) { - assert(old_fib == NULL); - old_fib = re; - } /* Skip deleted entries from selection */ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) @@ -1606,13 +1621,16 @@ static void rib_process(struct route_node *rn) /* Redistribute SELECTED entry */ if (old_selected != new_selected || selected_changed) { - struct nexthop *nexthop; + struct nexthop *nexthop = NULL; /* Check if we have a FIB route for the destination, otherwise, * don't redistribute it */ - for (ALL_NEXTHOPS(new_fib ? new_fib->nexthop : NULL, nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { - break; + if (new_fib) { + for (ALL_NEXTHOPS(new_fib->ng, nexthop)) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB)) { + break; + } } } if (!nexthop) @@ -2042,8 +2060,8 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) } /* free RE and nexthops */ - zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, rn); - nexthops_free(re->nexthop); + zebra_deregister_rnh_static_nexthops(re->vrf_id, re->ng.nexthop, rn); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); } @@ -2107,7 +2125,7 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", func, re->nexthop_num, re->nexthop_active_num); - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { inet_ntop(p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN); zlog_debug("%s: %s %s[%u] with flags %s%s%s", func, (nexthop->rparent ? " NH" : "NH"), straddr, @@ -2180,8 +2198,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; struct route_node *rn; - struct route_entry *re; unsigned changed = 0; + rib_dest_t *dest; if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) { zlog_err("%s: zebra_vrf_table() returned NULL", __func__); @@ -2195,6 +2213,7 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* Unlock node. */ route_unlock_node(rn); + dest = rib_dest_from_rnode(rn); /* Check all RE entries. In case any changes have to be done, requeue * the RN into RIBQ head. If the routing message about the new connected * route (generated by the IP address we are going to assign very soon) @@ -2203,20 +2222,17 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) * revalidation * of the rest of the RE. */ - RNODE_FOREACH_RE (rn, re) { - if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB) - && !RIB_SYSTEM_ROUTE(re)) { - changed = 1; - if (IS_ZEBRA_DEBUG_RIB) { - char buf[PREFIX_STRLEN]; - zlog_debug( - "%u:%s: freeing way for connected prefix", - re->vrf_id, - prefix2str(&rn->p, buf, sizeof(buf))); - route_entry_dump(&rn->p, NULL, re); - } - rib_uninstall(rn, re); + if (dest->selected_fib && !RIB_SYSTEM_ROUTE(dest->selected_fib)) { + changed = 1; + if (IS_ZEBRA_DEBUG_RIB) { + char buf[PREFIX_STRLEN]; + + zlog_debug("%u:%s: freeing way for connected prefix", + dest->selected_fib->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf))); + route_entry_dump(&rn->p, NULL, dest->selected_fib); } + rib_uninstall(rn, dest->selected_fib); } if (changed) rib_queue_add(rn); @@ -2284,7 +2300,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, /* If this route is kernel route, set FIB flag to the route. */ if (RIB_SYSTEM_ROUTE(re)) - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); /* Link new re to node.*/ @@ -2313,7 +2329,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - u_int32_t table_id, u_int32_t metric, bool fromkernel) + u_int32_t table_id, u_int32_t metric, bool fromkernel, + struct ethaddr *rmac) { struct route_table *table; struct route_node *rn; @@ -2322,6 +2339,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, struct route_entry *same = NULL; struct nexthop *rtnh; char buf2[INET6_ADDRSTRLEN]; + rib_dest_t *dest; assert(!src_p || afi == AFI_IP6); @@ -2354,14 +2372,14 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, return; } + dest = rib_dest_from_rnode(rn); + fib = dest->selected_fib; + /* Lookup same type route. */ RNODE_FOREACH_RE (rn, re) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) continue; - if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) - fib = re; - if (re->type != type) continue; if (re->instance != instance) @@ -2369,7 +2387,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric) continue; - if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->nexthop) + if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng.nexthop) && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) { if (rtnh->ifindex != nh->ifindex) continue; @@ -2382,7 +2400,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, same = re; break; } - for (ALL_NEXTHOPS(re->nexthop, rtnh)) + for (ALL_NEXTHOPS(re->ng, rtnh)) if (nexthop_same_no_recurse(rtnh, nh)) { same = re; break; @@ -2419,13 +2437,12 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, } if (allow_delete) { /* Unset flags. */ - for (rtnh = fib->nexthop; rtnh; + for (rtnh = fib->ng.nexthop; rtnh; rtnh = rtnh->next) UNSET_FLAG(rtnh->flags, NEXTHOP_FLAG_FIB); - UNSET_FLAG(fib->status, - ROUTE_ENTRY_SELECTED_FIB); + dest->selected_fib = NULL; } else { /* This means someone else, other than Zebra, * has deleted @@ -2466,6 +2483,22 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, return; } + + if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { + struct nexthop *tmp_nh; + + for (ALL_NEXTHOPS(re->ng, tmp_nh)) { + struct ipaddr vtep_ip; + + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), + &(tmp_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac, + &vtep_ip, p); + } + } rib_delnode(rn, same); } @@ -2474,10 +2507,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, } -int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, - int flags, struct prefix *p, struct prefix_ipv6 *src_p, - const struct nexthop *nh, u_int32_t table_id, u_int32_t metric, - u_int32_t mtu, uint8_t distance) +int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, vrf_id_t nh_vrf_id, + int type, u_short instance, int flags, struct prefix *p, + struct prefix_ipv6 *src_p, const struct nexthop *nh, + u_int32_t table_id, u_int32_t metric, + u_int32_t mtu, uint8_t distance, route_tag_t tag) { struct route_entry *re; struct nexthop *nexthop; @@ -2492,8 +2526,10 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, re->mtu = mtu; re->table = table_id; re->vrf_id = vrf_id; + re->nh_vrf_id = nh_vrf_id; re->nexthop_num = 0; re->uptime = time(NULL); + re->tag = tag; /* Add nexthop. */ nexthop = nexthop_new(); @@ -2549,7 +2585,7 @@ static void rib_update_table(struct route_table *table, continue; } - for (nh = re->nexthop; nh; nh = nh->next) + for (nh = re->ng.nexthop; nh; nh = nh->next) if (!(nh->type == NEXTHOP_TYPE_IPV4 || nh->type == NEXTHOP_TYPE_IPV6)) break; @@ -2633,7 +2669,6 @@ static void rib_sweep_table(struct route_table *table) struct route_entry *re; struct route_entry *next; struct nexthop *nexthop; - int ret = 0; if (!table) return; @@ -2667,12 +2702,11 @@ static void rib_sweep_table(struct route_table *table) * to a different spot (ie startup ) * this decision needs to be revisited */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - ret = rib_uninstall_kernel(rn, re); - if (!ret) - rib_delnode(rn, re); + rib_uninstall_kernel(rn, re); + rib_delnode(rn, re); } } } @@ -2739,24 +2773,24 @@ void rib_close_table(struct route_table *table) { struct route_node *rn; rib_table_info_t *info; - struct route_entry *re; + rib_dest_t *dest; if (!table) return; info = table->info; - for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) - RNODE_FOREACH_RE (rn, re) { - if (!CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) - continue; + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) { + dest = rib_dest_from_rnode(rn); + if (dest && dest->selected_fib) { if (info->safi == SAFI_UNICAST) hook_call(rib_update, rn, NULL); - if (!RIB_SYSTEM_ROUTE(re)) - rib_uninstall_kernel(rn, re); + if (!RIB_SYSTEM_ROUTE(dest->selected_fib)) + rib_uninstall_kernel(rn, dest->selected_fib); } + } } /* Routing information base initialize. */ diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 33d0b3a641ef..b1cb67c13ca9 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -355,7 +355,7 @@ static int zebra_rnh_apply_nht_rmap(int family, struct route_node *prn, rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6; if (prn && re) { - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { ret = zebra_nht_route_map_check(rmap_family, proto, &prn->p, re, nexthop); if (ret != RMAP_DENYMATCH) { @@ -430,7 +430,7 @@ static void zebra_rnh_eval_import_check_entry(vrf_id_t vrfid, int family, struct nexthop *nexthop; if (re && (rnh->state == NULL)) { - for (ALL_NEXTHOPS(re->nexthop, nexthop)) + for (ALL_NEXTHOPS(re->ng, nexthop)) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { state_changed = 1; break; @@ -561,7 +561,7 @@ static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family, * be having multiple. We care here only about * registered nexthops. */ - for (nexthop = sre->nexthop; nexthop; + for (nexthop = sre->ng.nexthop; nexthop; nexthop = nexthop->next) { switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -659,7 +659,7 @@ static struct route_entry *zebra_rnh_resolve_nexthop_entry(vrf_id_t vrfid, * match route to be exact if so specified */ if (is_default_prefix(&rn->p) && - !nh_resolve_via_default(rn->p.family)) + !rnh_resolve_via_default(rn->p.family)) return NULL; /* Identify appropriate route entry. */ @@ -676,7 +676,7 @@ static struct route_entry *zebra_rnh_resolve_nexthop_entry(vrf_id_t vrfid, if (re->type == ZEBRA_ROUTE_NHRP) { struct nexthop *nexthop; - for (nexthop = re->nexthop; + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if (nexthop->type @@ -929,8 +929,8 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, return; /* free RE and nexthops */ - zebra_deregister_rnh_static_nexthops(vrf_id, re->nexthop, rn); - nexthops_free(re->nexthop); + zebra_deregister_rnh_static_nexthops(vrf_id, re->ng.nexthop, rn); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); } @@ -951,8 +951,10 @@ static void copy_state(struct rnh *rnh, struct route_entry *re, state->type = re->type; state->distance = re->distance; state->metric = re->metric; + state->vrf_id = re->vrf_id; + state->nh_vrf_id = re->vrf_id; - route_entry_copy_nexthops(state, re->nexthop); + route_entry_copy_nexthops(state, re->ng.nexthop); rnh->state = state; } @@ -1000,7 +1002,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, s = client->obuf; stream_reset(s); - zserv_create_header(s, cmd, vrf_id); + zclient_create_header(s, cmd, vrf_id); stream_putw(s, rn->p.family); switch (rn->p.family) { @@ -1023,7 +1025,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, num = 0; nump = stream_get_endp(s); stream_putc(s, 0); - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if ((CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) || CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -1121,7 +1123,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty) if (rnh->state) { vty_out(vty, " resolved via %s\n", zebra_route_string(rnh->state->type)); - for (nexthop = rnh->state->nexthop; nexthop; + for (nexthop = rnh->state->ng.nexthop; nexthop; nexthop = nexthop->next) print_nh(nexthop, vty); } else diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 7e183684da40..bd121ec83c6e 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -54,6 +54,15 @@ typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; extern int zebra_rnh_ip_default_route; extern int zebra_rnh_ipv6_default_route; +static inline int rnh_resolve_via_default(int family) +{ + if (((family == AF_INET) && zebra_rnh_ip_default_route) + || ((family == AF_INET6) && zebra_rnh_ipv6_default_route)) + return 1; + else + return 0; +} + extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type); extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 61af60b5da1e..89cb2fc4881e 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -1329,7 +1329,7 @@ route_map_result_t zebra_nht_route_map_check(int family, int client_proto, struct nh_rmap_obj nh_obj; nh_obj.nexthop = nexthop; - nh_obj.vrf_id = re->vrf_id; + nh_obj.vrf_id = re->nh_vrf_id; nh_obj.source_protocol = re->type; nh_obj.metric = re->metric; nh_obj.tag = re->tag; diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index 1f95c7f83c7b..60bf7c3f5941 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -22,6 +22,7 @@ #ifndef __ZEBRA_ROUTEMAP_H__ #define __ZEBRA_ROUTEMAP_H__ +extern void zebra_route_map_init(void); extern void zebra_routemap_config_write_protocol(struct vty *vty); extern char *zebra_get_import_table_route_map(afi_t afi, uint32_t table); extern void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name, diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index 48218effd109..3ab208d30b54 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -285,8 +285,8 @@ static void check_replace(struct route_node *np2, struct route_entry *re2, return; } - if (in_addr_cmp((u_char *)&(*re)->nexthop->gate.ipv4, - (u_char *)&re2->nexthop->gate.ipv4) + if (in_addr_cmp((u_char *)&(*re)->ng.nexthop->gate.ipv4, + (u_char *)&re2->ng.nexthop->gate.ipv4) <= 0) return; @@ -370,9 +370,9 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], for (*np = route_top(table); *np; *np = route_next(*np)) { if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest)) { RNODE_FOREACH_RE (*np, *re) { - if (!in_addr_cmp((u_char *)&(*re) - ->nexthop->gate - .ipv4, + if (!in_addr_cmp( + (u_char *)&(*re) + ->ng.nexthop->gate.ipv4, (u_char *)&nexthop)) if (proto == proto_trans((*re)->type)) @@ -404,9 +404,10 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], if ((policy < policy2) || ((policy == policy2) && (proto < proto2)) || ((policy == policy2) && (proto == proto2) - && (in_addr_cmp((u_char *)&re2->nexthop - ->gate.ipv4, - (u_char *)&nexthop) + && (in_addr_cmp( + (u_char *)&re2->ng.nexthop + ->gate.ipv4, + (u_char *)&nexthop) >= 0))) check_replace(np2, re2, np, re); } @@ -430,7 +431,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], { struct nexthop *nexthop; - nexthop = (*re)->nexthop; + nexthop = (*re)->ng.nexthop; if (nexthop) { pnt = (u_char *)&nexthop->gate.ipv4; for (i = 0; i < 4; i++) @@ -459,7 +460,7 @@ static u_char *ipFwTable(struct variable *v, oid objid[], size_t *objid_len, if (!np) return NULL; - nexthop = re->nexthop; + nexthop = re->ng.nexthop; if (!nexthop) return NULL; diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 5927ba9d750e..b5c33069cc8d 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -91,7 +91,7 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, nh_p.family = AF_INET; nh_p.prefixlen = IPV4_MAX_BITLEN; nh_p.u.prefix4 = si->addr.ipv4; - zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); + zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn); break; case STATIC_IPV4_GATEWAY_IFNAME: nexthop = route_entry_nexthop_ipv4_ifindex_add( @@ -111,7 +111,7 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, nh_p.family = AF_INET6; nh_p.prefixlen = IPV6_MAX_BITLEN; nh_p.u.prefix6 = si->addr.ipv6; - zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); + zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn); break; case STATIC_IPV6_GATEWAY_IFNAME: nexthop = route_entry_nexthop_ipv6_ifindex_add( @@ -141,7 +141,7 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, */ if (si->type == STATIC_IPV4_GATEWAY || si->type == STATIC_IPV6_GATEWAY) - zebra_evaluate_rnh(si->vrf_id, nh_p.family, 1, + zebra_evaluate_rnh(si->nh_vrf_id, nh_p.family, 1, RNH_NEXTHOP_TYPE, &nh_p); else rib_queue_add(rn); @@ -155,8 +155,9 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, re->metric = 0; re->mtu = 0; re->vrf_id = si->vrf_id; + re->nh_vrf_id = si->nh_vrf_id; re->table = - si->vrf_id + (si->vrf_id != VRF_DEFAULT) ? (zebra_vrf_lookup_by_id(si->vrf_id))->table_id : zebrad.rtm_table_default; re->nexthop_num = 0; @@ -169,7 +170,7 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, nh_p.family = AF_INET; nh_p.prefixlen = IPV4_MAX_BITLEN; nh_p.u.prefix4 = si->addr.ipv4; - zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); + zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn); break; case STATIC_IPV4_GATEWAY_IFNAME: nexthop = route_entry_nexthop_ipv4_ifindex_add( @@ -189,7 +190,7 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, nh_p.family = AF_INET6; nh_p.prefixlen = IPV6_MAX_BITLEN; nh_p.u.prefix6 = si->addr.ipv6; - zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn); + zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn); break; case STATIC_IPV6_GATEWAY_IFNAME: nexthop = route_entry_nexthop_ipv6_ifindex_add( @@ -221,7 +222,7 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, if (si->type == STATIC_IPV4_GATEWAY || si->type == STATIC_IPV6_GATEWAY) { rib_addnode(rn, re, 0); - zebra_evaluate_rnh(si->vrf_id, nh_p.family, 1, + zebra_evaluate_rnh(si->nh_vrf_id, nh_p.family, 1, RNH_NEXTHOP_TYPE, &nh_p); } else rib_addnode(rn, re, 1); @@ -298,7 +299,7 @@ void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p, } /* Lookup nexthop. */ - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if (static_nexthop_same(nexthop, si)) break; @@ -331,11 +332,12 @@ void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p, } UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + rib_dest_t *dest = rib_dest_from_rnode(rn); + /* If there are other active nexthops, do an update. */ if (re->nexthop_active_num > 1) { /* Update route in kernel if it's in fib */ - if (CHECK_FLAG(re->status, - ROUTE_ENTRY_SELECTED_FIB)) + if (dest->selected_fib) rib_install_kernel(rn, re, re); /* Update redistribution if it's selected */ if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) @@ -350,8 +352,7 @@ void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p, p, (struct prefix *)src_p, re); /* Remove from kernel if fib route becomes * inactive */ - if (CHECK_FLAG(re->status, - ROUTE_ENTRY_SELECTED_FIB)) + if (dest->selected_fib) rib_uninstall_kernel(rn, re); } } @@ -378,6 +379,7 @@ int static_add_route(afi_t afi, safi_t safi, u_char type, struct prefix *p, struct prefix_ipv6 *src_p, union g_addr *gate, const char *ifname, enum static_blackhole_type bh_type, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, + struct zebra_vrf *nh_zvrf, struct static_nh_label *snh_label) { struct route_node *rn; @@ -439,6 +441,8 @@ int static_add_route(afi_t afi, safi_t safi, u_char type, struct prefix *p, si->bh_type = bh_type; si->tag = tag; si->vrf_id = zvrf_id(zvrf); + si->nh_vrf_id = zvrf_id(nh_zvrf); + if (ifname) strlcpy(si->ifname, ifname, sizeof(si->ifname)); si->ifindex = IFINDEX_INTERNAL; @@ -493,7 +497,7 @@ int static_add_route(afi_t afi, safi_t safi, u_char type, struct prefix *p, else { struct interface *ifp; - ifp = if_lookup_by_name(ifname, zvrf_id(zvrf)); + ifp = if_lookup_by_name(ifname, zvrf_id(nh_zvrf)); if (ifp && ifp->ifindex != IFINDEX_INTERNAL) { si->ifindex = ifp->ifindex; static_install_route(afi, safi, p, src_p, si); diff --git a/zebra/zebra_static.h b/zebra/zebra_static.h index 68fe73b0a348..234e3e403606 100644 --- a/zebra/zebra_static.h +++ b/zebra/zebra_static.h @@ -54,6 +54,7 @@ struct static_route { /* VRF identifier. */ vrf_id_t vrf_id; + vrf_id_t nh_vrf_id; /* Administrative distance. */ u_char distance; @@ -89,6 +90,7 @@ extern int static_add_route(afi_t, safi_t safi, u_char type, struct prefix *p, const char *ifname, enum static_blackhole_type bh_type, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, + struct zebra_vrf *nh_zvrf, struct static_nh_label *snh_label); extern int static_delete_route(afi_t, safi_t safi, u_char type, diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 0d0a8dd747e9..95426683a8c8 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -250,6 +250,10 @@ static int zebra_vrf_delete(struct vrf *vrf) route_table_finish(zvrf->rnh_table[afi]); route_table_finish(zvrf->import_check_table[afi]); } + + /* cleanup evpn states for vrf */ + zebra_vxlan_vrf_delete(zvrf); + list_delete_all_node(zvrf->rid_all_sorted_list); list_delete_all_node(zvrf->rid_lo_sorted_list); XFREE(MTYPE_ZEBRA_VRF, zvrf); @@ -472,10 +476,18 @@ static int vrf_config_write(struct vty *vty) if (!zvrf) continue; - if (strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) { + if (vrf->vrf_id != VRF_DEFAULT) vty_out(vty, "vrf %s\n", zvrf_name(zvrf)); + + static_config(vty, zvrf, AFI_IP, SAFI_UNICAST, "ip route"); + static_config(vty, zvrf, AFI_IP, SAFI_MULTICAST, "ip mroute"); + static_config(vty, zvrf, AFI_IP6, SAFI_UNICAST, "ipv6 route"); + + if (vrf->vrf_id != VRF_DEFAULT && zvrf->l3vni) + vty_out(vty, " vni %u\n", zvrf->l3vni); + + if (vrf->vrf_id != VRF_DEFAULT) vty_out(vty, "!\n"); - } } return 0; } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 1dfc0b3eb8b1..c7a0717ee829 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@ #include #include +#include /* MPLS (Segment Routing) global block */ typedef struct mpls_srgb_t_ { @@ -114,6 +115,9 @@ struct zebra_vrf { */ int advertise_gw_macip; + /* l3-vni info */ + vni_t l3vni; + /* Route Installs */ uint64_t installs; uint64_t removals; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index c2c7075671cc..a51efe9126e6 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -47,6 +47,9 @@ #include "zebra/zebra_vty_clippy.c" #endif #include "zebra/zserv.h" +#include "zebra/router-id.h" +#include "zebra/ipforward.h" +#include "zebra/zebra_vxlan_private.h" extern int allow_delete; @@ -63,17 +66,28 @@ static void vty_show_ip_route_summary(struct vty *vty, static void vty_show_ip_route_summary_prefix(struct vty *vty, struct route_table *table); +/* + * special macro to allow us to get the correct zebra_vrf + */ +#define ZEBRA_DECLVAR_CONTEXT(A, B) \ + struct vrf *A = VTY_GET_CONTEXT(vrf); \ + struct zebra_vrf *B = \ + (vrf) ? vrf->info : NULL; \ + /* VNI range as per RFC 7432 */ #define CMD_VNI_RANGE "(1-16777215)" /* General function for static route. */ -static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, - const char *negate, const char *dest_str, - const char *mask_str, const char *src_str, - const char *gate_str, const char *ifname, - const char *flag_str, const char *tag_str, - const char *distance_str, const char *vrf_id_str, - const char *label_str) +static int zebra_static_route_leak(struct vty *vty, + struct zebra_vrf *zvrf, + struct zebra_vrf *nh_zvrf, + afi_t afi, safi_t safi, + const char *negate, const char *dest_str, + const char *mask_str, const char *src_str, + const char *gate_str, const char *ifname, + const char *flag_str, const char *tag_str, + const char *distance_str, + const char *label_str) { int ret; u_char distance; @@ -84,7 +98,6 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, struct in_addr mask; enum static_blackhole_type bh_type = 0; route_tag_t tag = 0; - struct zebra_vrf *zvrf; u_char type; struct static_nh_label snh_label; @@ -134,14 +147,6 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, if (tag_str) tag = strtoul(tag_str, NULL, 10); - /* VRF id */ - zvrf = zebra_vrf_lookup_by_name(vrf_id_str); - - if (!zvrf) { - vty_out(vty, "%% vrf %s is not defined\n", vrf_id_str); - return CMD_WARNING_CONFIG_FAILED; - } - /* Labels */ memset(&snh_label, 0, sizeof(struct static_nh_label)); if (label_str) { @@ -228,7 +233,8 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, if (!negate) static_add_route(afi, safi, type, &p, src_p, gatep, ifname, - bh_type, tag, distance, zvrf, &snh_label); + bh_type, tag, distance, zvrf, nh_zvrf, + &snh_label); else static_delete_route(afi, safi, type, &p, src_p, gatep, ifname, tag, distance, zvrf, &snh_label); @@ -236,6 +242,31 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, return CMD_SUCCESS; } +static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, + const char *negate, const char *dest_str, + const char *mask_str, const char *src_str, + const char *gate_str, const char *ifname, + const char *flag_str, const char *tag_str, + const char *distance_str, const char *vrf_id_str, + const char *label_str) +{ + struct zebra_vrf *zvrf; + + /* VRF id */ + zvrf = zebra_vrf_lookup_by_name(vrf_id_str); + + if (!zvrf) { + vty_out(vty, "%% vrf %s is not defined\n", vrf_id_str); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, zvrf, afi, safi, + negate, dest_str, mask_str, src_str, + gate_str, ifname, flag_str, tag_str, + distance_str, label_str); +} + + /* Static unicast routes for multicast RPF lookup. */ DEFPY (ip_mroute_dist, ip_mroute_dist_cmd, @@ -376,6 +407,37 @@ DEFPY(ip_route_blackhole, tag_str, distance_str, vrf, label); } +DEFPY(ip_route_blackhole_vrf, + ip_route_blackhole_vrf_cmd, + "[no] ip route\ + \ + $flag \ + [{ \ + tag (1-4294967295) \ + |(1-255)$distance \ + |label WORD \ + }]", + NO_STR IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + MPLS_LABEL_HELPSTR) +{ + VTY_DECLVAR_CONTEXT(vrf, vrf); + struct zebra_vrf *zvrf = vrf->info; + + return zebra_static_route_leak(vty, zvrf, zvrf, + AFI_IP, SAFI_UNICAST, no, prefix, + mask_str, NULL, NULL, NULL, flag, + tag_str, distance_str, label); +} + DEFPY(ip_route_address_interface, ip_route_address_interface_cmd, "[no] ip route\ @@ -387,6 +449,7 @@ DEFPY(ip_route_address_interface, |(1-255)$distance \ |vrf NAME \ |label WORD \ + |nexthop-vrf NAME \ }]", NO_STR IP_STR "Establish static routes\n" @@ -400,16 +463,85 @@ DEFPY(ip_route_address_interface, "Tag value\n" "Distance value for this route\n" VRF_CMD_HELP_STR - MPLS_LABEL_HELPSTR) + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) { + struct zebra_vrf *zvrf; + struct zebra_vrf *nh_zvrf; + const char *flag = NULL; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix, - mask_str, NULL, gate_str, ifname, flag, - tag_str, distance_str, vrf, label); + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + zvrf = zebra_vrf_lookup_by_name(vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP, SAFI_UNICAST, no, prefix, + mask_str, NULL, gate_str, ifname, flag, + tag_str, distance_str, label); +} + +DEFPY(ip_route_address_interface_vrf, + ip_route_address_interface_vrf_cmd, + "[no] ip route\ + \ + A.B.C.D$gate \ + INTERFACE$ifname \ + [{ \ + tag (1-4294967295) \ + |(1-255)$distance \ + |label WORD \ + |nexthop-vrf NAME \ + }]", + NO_STR IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name. Specify 'Null0' (case-insensitive) for a \ + null route.\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) +{ + VTY_DECLVAR_CONTEXT(vrf, vrf); + const char *flag = NULL; + struct zebra_vrf *zvrf = vrf->info; + struct zebra_vrf *nh_zvrf; + + if (ifname && !strncasecmp(ifname, "Null0", 5)) { + flag = "Null0"; + ifname = NULL; + } + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP, SAFI_UNICAST, no, prefix, + mask_str, NULL, gate_str, ifname, flag, + tag_str, distance_str, label); } DEFPY(ip_route, @@ -422,6 +554,7 @@ DEFPY(ip_route, |(1-255)$distance \ |vrf NAME \ |label WORD \ + |nexthop-vrf NAME \ }]", NO_STR IP_STR "Establish static routes\n" @@ -434,16 +567,83 @@ DEFPY(ip_route, "Tag value\n" "Distance value for this route\n" VRF_CMD_HELP_STR - MPLS_LABEL_HELPSTR) + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) { + struct zebra_vrf *zvrf; + struct zebra_vrf *nh_zvrf; const char *flag = NULL; + if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix, - mask_str, NULL, gate_str, ifname, flag, - tag_str, distance_str, vrf, label); + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + zvrf = zebra_vrf_lookup_by_name(vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP, SAFI_UNICAST, no, prefix, + mask_str, NULL, gate_str, ifname, flag, + tag_str, distance_str, label); +} + +DEFPY(ip_route_vrf, + ip_route_vrf_cmd, + "[no] ip route\ + \ + \ + [{ \ + tag (1-4294967295) \ + |(1-255)$distance \ + |label WORD \ + |nexthop-vrf NAME \ + }]", + NO_STR IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) +{ + VTY_DECLVAR_CONTEXT(vrf, vrf); + struct zebra_vrf *zvrf = vrf->info; + struct zebra_vrf *nh_zvrf; + + const char *flag = NULL; + if (ifname && !strncasecmp(ifname, "Null0", 5)) { + flag = "Null0"; + ifname = NULL; + } + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP, SAFI_UNICAST, no, prefix, + mask_str, NULL, gate_str, ifname, flag, + tag_str, distance_str, label); } /* New RIB. Detailed information for IPv4 route. */ @@ -472,8 +672,13 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, vty_out(vty, "\""); vty_out(vty, ", distance %u, metric %u", re->distance, re->metric); - if (re->tag) + if (re->tag) { vty_out(vty, ", tag %u", re->tag); +#if defined(SUPPORT_REALMS) + if (re->tag > 0 && re->tag <= 255) + vty_out(vty, "(realm)"); +#endif + } if (re->mtu) vty_out(vty, ", mtu %u", re->mtu); if (re->vrf_id != VRF_DEFAULT) { @@ -505,7 +710,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, tm->tm_hour); vty_out(vty, " ago\n"); - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { char addrstr[32]; vty_out(vty, " %c%s", @@ -523,7 +728,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, if (nexthop->ifindex) vty_out(vty, ", via %s", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -533,12 +738,12 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, if (nexthop->ifindex) vty_out(vty, ", via %s", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); break; case NEXTHOP_TYPE_IFINDEX: vty_out(vty, " directly connected, %s", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); break; case NEXTHOP_TYPE_BLACKHOLE: vty_out(vty, " unreachable"); @@ -560,6 +765,14 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, default: break; } + + if (re->vrf_id != re->nh_vrf_id) { + struct vrf *vrf = + vrf_lookup_by_id(re->nh_vrf_id); + + vty_out(vty, "(vrf %s)", vrf->name); + } + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) vty_out(vty, " (duplicate nexthop removed)"); @@ -672,7 +885,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add(json_route, "uptime", buf); - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { json_nexthop = json_object_new_object(); if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) @@ -699,7 +912,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add( json_nexthop, "interfaceName", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); } break; case NEXTHOP_TYPE_IPV6: @@ -718,7 +931,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add( json_nexthop, "interfaceName", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); } break; @@ -731,7 +944,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add( json_nexthop, "interfaceName", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); break; case NEXTHOP_TYPE_BLACKHOLE: json_object_boolean_true_add(json_nexthop, @@ -758,6 +971,14 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, break; } + if (re->nh_vrf_id != re->vrf_id) { + struct vrf *vrf = + vrf_lookup_by_id(re->nh_vrf_id); + + json_object_string_add(json_nexthop, + "vrf", + vrf->name); + } if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) json_object_boolean_true_add(json_nexthop, "duplicate"); @@ -829,8 +1050,8 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, } /* Nexthop information. */ - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { - if (nexthop == re->nexthop) { + for (ALL_NEXTHOPS(re->ng, nexthop)) { + if (nexthop == re->ng.nexthop) { /* Prefix information. */ len = vty_out(vty, "%c", zebra_route_char(re->type)); if (re->instance) @@ -865,7 +1086,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, if (nexthop->ifindex) vty_out(vty, ", %s", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -875,12 +1096,13 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, if (nexthop->ifindex) vty_out(vty, ", %s", ifindex2ifname(nexthop->ifindex, - re->vrf_id)); + re->nh_vrf_id)); break; case NEXTHOP_TYPE_IFINDEX: vty_out(vty, " is directly connected, %s", - ifindex2ifname(nexthop->ifindex, re->vrf_id)); + ifindex2ifname(nexthop->ifindex, + re->nh_vrf_id)); break; case NEXTHOP_TYPE_BLACKHOLE: vty_out(vty, " unreachable"); @@ -901,6 +1123,14 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, default: break; } + + if (re->nh_vrf_id != re->vrf_id) { + struct vrf *vrf = + vrf_lookup_by_id(re->nh_vrf_id); + + vty_out(vty, "(vrf %s)", vrf->name); + } + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out(vty, " inactive"); @@ -961,6 +1191,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, u_short ospf_instance_id) { struct route_table *table; + rib_dest_t *dest; struct route_node *rn; struct route_entry *re; int first = 1; @@ -998,10 +1229,11 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, /* Show all routes. */ for (rn = route_top(table); rn; rn = route_next(rn)) { + dest = rib_dest_from_rnode(rn); + RNODE_FOREACH_RE (rn, re) { if (use_fib - && !CHECK_FLAG(re->status, - ROUTE_ENTRY_SELECTED_FIB)) + && re != dest->selected_fib) continue; if (tag && re->tag != tag) @@ -1163,7 +1395,7 @@ DEFUN (ip_nht_default_route, return CMD_SUCCESS; zebra_rnh_ip_default_route = 1; - zebra_evaluate_rnh(0, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL); + zebra_evaluate_rnh(VRF_DEFAULT, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1179,7 +1411,7 @@ DEFUN (no_ip_nht_default_route, return CMD_SUCCESS; zebra_rnh_ip_default_route = 0; - zebra_evaluate_rnh(0, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL); + zebra_evaluate_rnh(VRF_DEFAULT, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1194,7 +1426,7 @@ DEFUN (ipv6_nht_default_route, return CMD_SUCCESS; zebra_rnh_ipv6_default_route = 1; - zebra_evaluate_rnh(0, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL); + zebra_evaluate_rnh(VRF_DEFAULT, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1210,7 +1442,7 @@ DEFUN (no_ipv6_nht_default_route, return CMD_SUCCESS; zebra_rnh_ipv6_default_route = 0; - zebra_evaluate_rnh(0, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL); + zebra_evaluate_rnh(VRF_DEFAULT, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1536,7 +1768,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, * In case of ECMP, count only once. */ cnt = 0; - for (nexthop = re->nexthop; (!cnt && nexthop); + for (nexthop = re->ng.nexthop; (!cnt && nexthop); nexthop = nexthop->next) { cnt++; rib_cnt[ZEBRA_ROUTE_TOTAL]++; @@ -1585,97 +1817,98 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, } /* Write static route configuration. */ -static int static_config(struct vty *vty, afi_t afi, safi_t safi, - const char *cmd) +int static_config(struct vty *vty, struct zebra_vrf *zvrf, + afi_t afi, safi_t safi, const char *cmd) { + char spacing[100]; struct route_node *rn; struct static_route *si; struct route_table *stable; - struct vrf *vrf; - struct zebra_vrf *zvrf; char buf[SRCDEST2STR_BUFFER]; int write = 0; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (!(zvrf = vrf->info)) - continue; - if ((stable = zvrf->stable[afi][safi]) == NULL) - continue; + if ((stable = zvrf->stable[afi][safi]) == NULL) + return write; - for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) - for (si = rn->info; si; si = si->next) { - vty_out(vty, "%s %s", cmd, - srcdest_rnode2str(rn, buf, sizeof buf)); + sprintf(spacing, "%s%s", + (zvrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", + cmd); - switch (si->type) { - case STATIC_IPV4_GATEWAY: - vty_out(vty, " %s", - inet_ntoa(si->addr.ipv4)); - break; - case STATIC_IPV6_GATEWAY: - vty_out(vty, " %s", - inet_ntop(AF_INET6, - &si->addr.ipv6, buf, - sizeof buf)); - break; - case STATIC_IFNAME: - vty_out(vty, " %s", si->ifname); - break; - case STATIC_BLACKHOLE: - switch (si->bh_type) { - case STATIC_BLACKHOLE_DROP: - vty_out(vty, " blackhole"); - break; - case STATIC_BLACKHOLE_NULL: - vty_out(vty, " Null0"); - break; - case STATIC_BLACKHOLE_REJECT: - vty_out(vty, " reject"); - break; - } + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) + for (si = rn->info; si; si = si->next) { + vty_out(vty, "%s %s", spacing, + srcdest_rnode2str(rn, buf, sizeof buf)); + + switch (si->type) { + case STATIC_IPV4_GATEWAY: + vty_out(vty, " %s", + inet_ntoa(si->addr.ipv4)); + break; + case STATIC_IPV6_GATEWAY: + vty_out(vty, " %s", + inet_ntop(AF_INET6, + &si->addr.ipv6, buf, + sizeof buf)); + break; + case STATIC_IFNAME: + vty_out(vty, " %s", si->ifname); + break; + case STATIC_BLACKHOLE: + switch (si->bh_type) { + case STATIC_BLACKHOLE_DROP: + vty_out(vty, " blackhole"); break; - case STATIC_IPV4_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET, - &si->addr.ipv4, buf, - sizeof buf), - si->ifname); + case STATIC_BLACKHOLE_NULL: + vty_out(vty, " Null0"); break; - case STATIC_IPV6_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET6, - &si->addr.ipv6, buf, - sizeof buf), - si->ifname); + case STATIC_BLACKHOLE_REJECT: + vty_out(vty, " reject"); break; } + break; + case STATIC_IPV4_GATEWAY_IFNAME: + vty_out(vty, " %s %s", + inet_ntop(AF_INET, + &si->addr.ipv4, buf, + sizeof buf), + si->ifname); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + vty_out(vty, " %s %s", + inet_ntop(AF_INET6, + &si->addr.ipv6, buf, + sizeof buf), + si->ifname); + break; + } - if (si->tag) - vty_out(vty, " tag %" ROUTE_TAG_PRI, - si->tag); + if (si->tag) + vty_out(vty, " tag %" ROUTE_TAG_PRI, + si->tag); - if (si->distance - != ZEBRA_STATIC_DISTANCE_DEFAULT) - vty_out(vty, " %d", si->distance); + if (si->distance + != ZEBRA_STATIC_DISTANCE_DEFAULT) + vty_out(vty, " %d", si->distance); - if (si->vrf_id != VRF_DEFAULT) - vty_out(vty, " vrf %s", - zvrf_name(zvrf)); + if (si->nh_vrf_id != si->vrf_id) { + struct vrf *vrf; - /* Label information */ - if (si->snh_label.num_labels) - vty_out(vty, " label %s", - mpls_label2str( - si->snh_label - .num_labels, - si->snh_label.label, - buf, sizeof buf, 0)); + vrf = vrf_lookup_by_id(si->nh_vrf_id); + vty_out(vty, " nexthop-vrf %s", + (vrf) ? vrf->name : "Unknown"); + } - vty_out(vty, "\n"); + /* Label information */ + if (si->snh_label.num_labels) + vty_out(vty, " label %s", + mpls_label2str(si->snh_label.num_labels, + si->snh_label.label, + buf, sizeof buf, 0)); - write = 1; - } - } + vty_out(vty, "\n"); + + write = 1; + } return write; } @@ -1709,6 +1942,38 @@ DEFPY(ipv6_route_blackhole, tag_str, distance_str, vrf, label); } +DEFPY(ipv6_route_blackhole_vrf, + ipv6_route_blackhole_vrf_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ + $flag \ + [{ \ + tag (1-4294967295) \ + |(1-255)$distance \ + |label WORD \ + }]", + NO_STR + IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "Null interface\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + MPLS_LABEL_HELPSTR) +{ + VTY_DECLVAR_CONTEXT(vrf, vrf); + struct zebra_vrf *zvrf = vrf->info; + + return zebra_static_route_leak(vty, zvrf, zvrf, + AFI_IP6, SAFI_UNICAST, no, prefix_str, + NULL, from_str, NULL, NULL, flag, + tag_str, distance_str, label); +} + DEFPY(ipv6_route_address_interface, ipv6_route_address_interface_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ @@ -1719,6 +1984,7 @@ DEFPY(ipv6_route_address_interface, |(1-255)$distance \ |vrf NAME \ |label WORD \ + |nexthop-vrf NAME \ }]", NO_STR IPV6_STR @@ -1732,11 +1998,72 @@ DEFPY(ipv6_route_address_interface, "Tag value\n" "Distance value for this prefix\n" VRF_CMD_HELP_STR - MPLS_LABEL_HELPSTR) + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) { - return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str, - NULL, from_str, gate_str, ifname, NULL, - tag_str, distance_str, vrf, label); + struct zebra_vrf *zvrf; + struct zebra_vrf *nh_zvrf; + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + zvrf = zebra_vrf_lookup_by_name(vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP6, SAFI_UNICAST, no, prefix_str, + NULL, from_str, gate_str, ifname, NULL, + tag_str, distance_str, label); +} + +DEFPY(ipv6_route_address_interface_vrf, + ipv6_route_address_interface_vrf_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ + X:X::X:X$gate \ + INTERFACE$ifname \ + [{ \ + tag (1-4294967295) \ + |(1-255)$distance \ + |label WORD \ + |nexthop-vrf NAME \ + }]", + NO_STR + IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) +{ + VTY_DECLVAR_CONTEXT(vrf, vrf); + struct zebra_vrf *zvrf = vrf->info; + struct zebra_vrf *nh_zvrf; + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP6, SAFI_UNICAST, no, prefix_str, + NULL, from_str, gate_str, ifname, NULL, + tag_str, distance_str, label); } DEFPY(ipv6_route, @@ -1748,6 +2075,7 @@ DEFPY(ipv6_route, |(1-255)$distance \ |vrf NAME \ |label WORD \ + |nexthop-vrf NAME \ }]", NO_STR IPV6_STR @@ -1761,19 +2089,79 @@ DEFPY(ipv6_route, "Tag value\n" "Distance value for this prefix\n" VRF_CMD_HELP_STR - MPLS_LABEL_HELPSTR) + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) { - return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str, - NULL, from_str, gate_str, ifname, NULL, - tag_str, distance_str, vrf, label); + struct zebra_vrf *zvrf; + struct zebra_vrf *nh_zvrf; + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + zvrf = zebra_vrf_lookup_by_name(vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP6, SAFI_UNICAST, no, prefix_str, + NULL, from_str, gate_str, ifname, NULL, + tag_str, distance_str, label); } -/* - * Show IPv6 mroute command.Used to dump - * the Multicast routing table. - */ -DEFUN (show_ipv6_mroute, - show_ipv6_mroute_cmd, +DEFPY(ipv6_route_vrf, + ipv6_route_vrf_cmd, + "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ + \ + [{ \ + tag (1-4294967295) \ + |(1-255)$distance \ + |label WORD \ + |nexthop-vrf NAME \ + }]", + NO_STR + IPV6_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 source-dest route\n" + "IPv6 source prefix\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + MPLS_LABEL_HELPSTR + VRF_CMD_HELP_STR) +{ + VTY_DECLVAR_CONTEXT(vrf, vrf); + struct zebra_vrf *zvrf = vrf->info; + struct zebra_vrf *nh_zvrf; + + nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf); + if (!nh_zvrf) { + vty_out(vty, "%% nexthop vrf %s is not defined\n", + nexthop_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + + return zebra_static_route_leak(vty, zvrf, nh_zvrf, + AFI_IP6, SAFI_UNICAST, no, prefix_str, + NULL, from_str, gate_str, ifname, NULL, + tag_str, distance_str, label); +} + +/* + * Show IPv6 mroute command.Used to dump + * the Multicast routing table. + */ +DEFUN (show_ipv6_mroute, + show_ipv6_mroute_cmd, "show ipv6 mroute [vrf NAME]", SHOW_STR IP_STR @@ -1872,7 +2260,7 @@ DEFUN (show_vrf, RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { if (!(zvrf = vrf->info)) continue; - if (!zvrf_id(zvrf)) + if (zvrf_id(zvrf) == VRF_DEFAULT) continue; vty_out(vty, "vrf %s ", zvrf_name(zvrf)); @@ -1887,6 +2275,110 @@ DEFUN (show_vrf, return CMD_SUCCESS; } +DEFUN (vrf_vni_mapping, + vrf_vni_mapping_cmd, + "vni " CMD_VNI_RANGE, + "VNI\n" + "VNI-ID\n") +{ + int ret = 0; + + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + vni_t vni = strtoul(argv[1]->arg, NULL, 10); + char err[ERR_STR_SZ]; + + assert(vrf); + assert(zvrf); + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1); + if (ret != 0) { + vty_out(vty, "%s\n", err); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_vrf_vni_mapping, + no_vrf_vni_mapping_cmd, + "no vni " CMD_VNI_RANGE, + NO_STR + "VNI\n" + "VNI-ID") +{ + int ret = 0; + char err[ERR_STR_SZ]; + vni_t vni = strtoul(argv[2]->arg, NULL, 10); + + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + assert(vrf); + assert(zvrf); + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0); + if (ret != 0) { + vty_out(vty, "%s\n", err); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* show vrf */ +DEFUN (show_vrf_vni, + show_vrf_vni_cmd, + "show vrf vni [json]", + SHOW_STR + "VRF\n" + "VNI\n" + JSON_STR) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + json_object *json = NULL; + json_object *json_vrfs = NULL; + u_char uj = use_json(argc, argv); + + if (uj) { + json = json_object_new_object(); + json_vrfs = json_object_new_array(); + } + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + zvrf = vrf->info; + if (!zvrf) + continue; + + if (!zvrf->l3vni) + continue; + + if (!uj) { + vty_out(vty, "vrf: %s VNI: %u", + zvrf_name(zvrf), + zvrf->l3vni); + vty_out(vty, "\n"); + } else { + json_object *json_vrf = NULL; + + json_vrf = json_object_new_object(); + json_object_string_add(json_vrf, "vrf", + zvrf_name(zvrf)); + json_object_int_add(json_vrf, "l3vni", + zvrf->l3vni); + json_object_array_add(json_vrfs, json_vrf); + } + } + + if (uj) { + json_object_object_add(json, "vrfs", json_vrfs); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + DEFUN (show_evpn_vni, show_evpn_vni_cmd, "show evpn vni [json]", @@ -1922,6 +2414,161 @@ DEFUN (show_evpn_vni_vni, return CMD_SUCCESS; } +DEFUN (show_evpn_l3vni, + show_evpn_l3vni_cmd, + "show evpn l3vni [json]", + SHOW_STR + "EVPN\n" + "L3 VNI\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_l3vnis(vty, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_l3vni_vni, + show_evpn_l3vni_vni_cmd, + "show evpn l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "L3 VxLAN Network Identifier\n" + "VNI number\n" + JSON_STR) +{ + vni_t vni; + u_char uj = use_json(argc, argv); + + vni = strtoul(argv[3]->arg, NULL, 10); + zebra_vxlan_print_l3vni(vty, vni, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni_mac, + show_evpn_rmac_l3vni_mac_cmd, + "show evpn rmac l3vni " CMD_VNI_RANGE " mac WORD [json]", + SHOW_STR + "EVPN\n" + "RMAC\n" + "L3-VNI\n" + "VNI number\n" + "MAC\n" + "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n" + JSON_STR) +{ + vni_t l3vni = 0; + struct ethaddr mac; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + if (!prefix_str2mac(argv[6]->arg, &mac)) { + vty_out(vty, "%% Malformed MAC address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_rmac_l3vni(vty, l3vni, &mac, uj); + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni, + show_evpn_rmac_l3vni_cmd, + "show evpn rmac l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "RMAC\n" + "L3-VNI\n" + "VNI number\n" + JSON_STR) +{ + vni_t l3vni = 0; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + zebra_vxlan_print_rmacs_l3vni(vty, l3vni, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_rmac_l3vni_all, + show_evpn_rmac_l3vni_all_cmd, + "show evpn rmac l3vni all [json]", + SHOW_STR + "EVPN\n" + "RMAC addresses\n" + "L3-VNI\n" + "All VNIs\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_rmacs_all_l3vni(vty, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni_ip, + show_evpn_nh_l3vni_ip_cmd, + "show evpn next-hops l3vni " CMD_VNI_RANGE " ip WORD [json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "L3-VNI\n" + "VNI number\n" + "Ip address\n" + "Host address (ipv4 or ipv6)\n" + JSON_STR) +{ + vni_t l3vni; + struct ipaddr ip; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + if (str2ipaddr(argv[6]->arg, &ip) != 0) { + if (!uj) + vty_out(vty, "%% Malformed Neighbor address\n"); + return CMD_WARNING; + } + zebra_vxlan_print_specific_nh_l3vni(vty, l3vni, &ip, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni, + show_evpn_nh_l3vni_cmd, + "show evpn next-hops l3vni " CMD_VNI_RANGE "[json]", + SHOW_STR + "EVPN\n" + "Remote Vteps\n" + "L3-VNI\n" + "VNI number\n" + JSON_STR) +{ + vni_t l3vni; + u_char uj = use_json(argc, argv); + + l3vni = strtoul(argv[4]->arg, NULL, 10); + zebra_vxlan_print_nh_l3vni(vty, l3vni, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_evpn_nh_l3vni_all, + show_evpn_nh_l3vni_all_cmd, + "show evpn next-hops l3vni all [json]", + SHOW_STR + "EVPN\n" + "Remote VTEPs\n" + "L3-VNI\n" + "All VNIs\n" + JSON_STR) +{ + u_char uj = use_json(argc, argv); + + zebra_vxlan_print_nh_all_l3vni(vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_evpn_mac_vni, show_evpn_mac_vni_cmd, "show evpn mac vni " CMD_VNI_RANGE "[json]", @@ -2142,11 +2789,8 @@ static int zebra_ip_config(struct vty *vty) { int write = 0; - write += static_config(vty, AFI_IP, SAFI_UNICAST, "ip route"); - write += static_config(vty, AFI_IP, SAFI_MULTICAST, "ip mroute"); - write += static_config(vty, AFI_IP6, SAFI_UNICAST, "ipv6 route"); - write += zebra_import_table_config(vty); + return write; } @@ -2325,13 +2969,254 @@ static int config_write_protocol(struct vty *vty) return 1; } +#ifdef HAVE_NETLINK +/* Display default rtm_table for all clients. */ +DEFUN (show_table, + show_table_cmd, + "show table", + SHOW_STR + "default routing table to use for all clients\n") +{ + vty_out(vty, "table %d\n", zebrad.rtm_table_default); + return CMD_SUCCESS; +} + +DEFUN (config_table, + config_table_cmd, + "table TABLENO", + "Configure target kernel routing table\n" + "TABLE integer\n") +{ + zebrad.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10); + return CMD_SUCCESS; +} + +DEFUN (no_config_table, + no_config_table_cmd, + "no table [TABLENO]", + NO_STR + "Configure target kernel routing table\n" + "TABLE integer\n") +{ + zebrad.rtm_table_default = 0; + return CMD_SUCCESS; +} +#endif + +DEFUN (show_zebra, + show_zebra_cmd, + "show zebra", + SHOW_STR + ZEBRA_STR) +{ + struct vrf *vrf; + + vty_out(vty, + " Route Route Neighbor LSP LSP\n"); + vty_out(vty, + "VRF Installs Removals Updates Installs Removals\n"); + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + struct zebra_vrf *zvrf = vrf->info; + + vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 + " %10" PRIu64 " %10" PRIu64 "\n", + vrf->name, zvrf->installs, zvrf->removals, + zvrf->neigh_updates, zvrf->lsp_installs, + zvrf->lsp_removals); + } + + return CMD_SUCCESS; +} + +DEFUN (ip_forwarding, + ip_forwarding_cmd, + "ip forwarding", + IP_STR + "Turn on IP forwarding\n") +{ + int ret; + + ret = ipforward(); + if (ret == 0) + ret = ipforward_on(); + + if (ret == 0) { + vty_out(vty, "Can't turn on IP forwarding\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_forwarding, + no_ip_forwarding_cmd, + "no ip forwarding", + NO_STR + IP_STR + "Turn off IP forwarding\n") +{ + int ret; + + ret = ipforward(); + if (ret != 0) + ret = ipforward_off(); + + if (ret != 0) { + vty_out(vty, "Can't turn off IP forwarding\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +/* Only display ip forwarding is enabled or not. */ +DEFUN (show_ip_forwarding, + show_ip_forwarding_cmd, + "show ip forwarding", + SHOW_STR + IP_STR + "IP forwarding status\n") +{ + int ret; + + ret = ipforward(); + + if (ret == 0) + vty_out(vty, "IP forwarding is off\n"); + else + vty_out(vty, "IP forwarding is on\n"); + return CMD_SUCCESS; +} + +/* Only display ipv6 forwarding is enabled or not. */ +DEFUN (show_ipv6_forwarding, + show_ipv6_forwarding_cmd, + "show ipv6 forwarding", + SHOW_STR + "IPv6 information\n" + "Forwarding status\n") +{ + int ret; + + ret = ipforward_ipv6(); + + switch (ret) { + case -1: + vty_out(vty, "ipv6 forwarding is unknown\n"); + break; + case 0: + vty_out(vty, "ipv6 forwarding is %s\n", "off"); + break; + case 1: + vty_out(vty, "ipv6 forwarding is %s\n", "on"); + break; + default: + vty_out(vty, "ipv6 forwarding is %s\n", "off"); + break; + } + return CMD_SUCCESS; +} + +DEFUN (ipv6_forwarding, + ipv6_forwarding_cmd, + "ipv6 forwarding", + IPV6_STR + "Turn on IPv6 forwarding\n") +{ + int ret; + + ret = ipforward_ipv6(); + if (ret == 0) + ret = ipforward_ipv6_on(); + + if (ret == 0) { + vty_out(vty, "Can't turn on IPv6 forwarding\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_forwarding, + no_ipv6_forwarding_cmd, + "no ipv6 forwarding", + NO_STR + IPV6_STR + "Turn off IPv6 forwarding\n") +{ + int ret; + + ret = ipforward_ipv6(); + if (ret != 0) + ret = ipforward_ipv6_off(); + + if (ret != 0) { + vty_out(vty, "Can't turn off IPv6 forwarding\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +/* Table configuration write function. */ +static int config_write_table(struct vty *vty) +{ + if (zebrad.rtm_table_default) + vty_out(vty, "table %d\n", zebrad.rtm_table_default); + return 0; +} + +/* IPForwarding configuration write function. */ +static int config_write_forwarding(struct vty *vty) +{ + /* FIXME: Find better place for that. */ + router_id_write(vty); + + if (!ipforward()) + vty_out(vty, "no ip forwarding\n"); + if (!ipforward_ipv6()) + vty_out(vty, "no ipv6 forwarding\n"); + vty_out(vty, "!\n"); + return 0; +} + /* IP node for static routes. */ static struct cmd_node ip_node = {IP_NODE, "", 1}; static struct cmd_node protocol_node = {PROTOCOL_NODE, "", 1}; +/* table node for routing tables. */ +static struct cmd_node table_node = {TABLE_NODE, + "", /* This node has no interface. */ + 1}; +static struct cmd_node forwarding_node = {FORWARDING_NODE, + "", /* This node has no interface. */ + 1}; /* Route VTY. */ void zebra_vty_init(void) { + /* Install configuration write function. */ + install_node(&table_node, config_write_table); + install_node(&forwarding_node, config_write_forwarding); + + install_element(VIEW_NODE, &show_ip_forwarding_cmd); + install_element(CONFIG_NODE, &ip_forwarding_cmd); + install_element(CONFIG_NODE, &no_ip_forwarding_cmd); + install_element(ENABLE_NODE, &show_zebra_cmd); + +#ifdef HAVE_NETLINK + install_element(VIEW_NODE, &show_table_cmd); + install_element(CONFIG_NODE, &config_table_cmd); + install_element(CONFIG_NODE, &no_config_table_cmd); +#endif /* HAVE_NETLINK */ + + install_element(VIEW_NODE, &show_ipv6_forwarding_cmd); + install_element(CONFIG_NODE, &ipv6_forwarding_cmd); + install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd); + + /* Route-map */ + zebra_route_map_init(); + install_node(&ip_node, zebra_ip_config); install_node(&protocol_node, config_write_protocol); @@ -2341,8 +3226,11 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &ip_multicast_mode_cmd); install_element(CONFIG_NODE, &no_ip_multicast_mode_cmd); install_element(CONFIG_NODE, &ip_route_blackhole_cmd); + install_element(VRF_NODE, &ip_route_blackhole_vrf_cmd); install_element(CONFIG_NODE, &ip_route_address_interface_cmd); + install_element(VRF_NODE, &ip_route_address_interface_vrf_cmd); install_element(CONFIG_NODE, &ip_route_cmd); + install_element(VRF_NODE, &ip_route_vrf_cmd); install_element(CONFIG_NODE, &ip_zebra_import_table_distance_cmd); install_element(CONFIG_NODE, &no_ip_zebra_import_table_cmd); install_element(CONFIG_NODE, &zebra_workqueue_timer_cmd); @@ -2351,6 +3239,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); install_element(VIEW_NODE, &show_vrf_cmd); + install_element(VIEW_NODE, &show_vrf_vni_cmd); install_element(VIEW_NODE, &show_route_cmd); install_element(VIEW_NODE, &show_route_detail_cmd); install_element(VIEW_NODE, &show_route_summary_cmd); @@ -2363,8 +3252,11 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_ip_rpf_addr_cmd); install_element(CONFIG_NODE, &ipv6_route_blackhole_cmd); + install_element(VRF_NODE, &ipv6_route_blackhole_vrf_cmd); install_element(CONFIG_NODE, &ipv6_route_address_interface_cmd); + install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd); install_element(CONFIG_NODE, &ipv6_route_cmd); + install_element(VRF_NODE, &ipv6_route_vrf_cmd); install_element(CONFIG_NODE, &ip_nht_default_route_cmd); install_element(CONFIG_NODE, &no_ip_nht_default_route_cmd); install_element(CONFIG_NODE, &ipv6_nht_default_route_cmd); @@ -2376,6 +3268,14 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_l3vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_mac_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_rmac_l3vni_all_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_ip_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_cmd); + install_element(VIEW_NODE, &show_evpn_nh_l3vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd); @@ -2385,4 +3285,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd); + + install_element(CONFIG_NODE, &no_vrf_vni_mapping_cmd); + install_element(VRF_NODE, &vrf_vni_mapping_cmd); + install_element(VRF_NODE, &no_vrf_vni_mapping_cmd); + } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 9c70b55a1a4e..1690079f68f7 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -48,7 +48,9 @@ #include "zebra/zebra_l2.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); @@ -61,6 +63,10 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, void **args); +static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, + json_object *json); +static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, + json_object *json); static void zvni_print_mac(zebra_mac_t *mac, void *ctxt); static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt); @@ -91,11 +97,46 @@ static int zvni_neigh_send_del_to_client(vni_t vni, struct ethaddr *macaddr, u_char flags); static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); -static zebra_vni_t *zvni_map_svi(struct interface *ifp, +static zebra_vni_t *zvni_from_svi(struct interface *ifp, struct interface *br_if); static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); +/* l3-vni next-hop neigh related APIs */ +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + struct ipaddr *ip); +static void *zl3vni_nh_alloc(void *p); +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct ethaddr *rmac); +static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); +static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); +static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); + +/* l3-vni rmac related APIs */ +static void zl3vni_print_rmac_hash(struct hash_backet *, void *); +static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac); +static void *zl3vni_rmac_alloc(void *p); +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac); +static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); +static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); +static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac); + +/* l3-vni related APIs*/ +static int is_vni_l3(vni_t); +static zebra_l3vni_t *zl3vni_lookup(vni_t vni); +static void *zl3vni_alloc(void *p); +static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id); +static int zl3vni_del(zebra_l3vni_t *zl3vni); +static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t); +static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); +static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); +static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); +static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); + static unsigned int mac_hash_keymake(void *p); static int mac_cmp(const void *p1, const void *p2); static void *zvni_mac_alloc(void *p); @@ -385,6 +426,80 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, json_object_object_add(json, vni_str, json_vni); } +/* print a specific next hop for an l3vni */ +static void zl3vni_print_nh(zebra_neigh_t *n, + struct vty *vty, + json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct listnode *node = NULL; + struct prefix *p = NULL; + json_object *json_hosts = NULL; + + if (!json) { + vty_out(vty, "Ip: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " RMAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Host-List:\n"); + for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + vty_out(vty, " %s\n", + prefix2str(p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add(json, "ip", + ipaddr2str(&(n->ip), buf2, + sizeof(buf2))); + json_object_string_add(json, "rmac", + prefix_mac2str(&n->emac, buf2, + sizeof(buf2))); + for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + json_object_array_add(json_hosts, + json_object_new_string( + prefix2str(p, buf2, + sizeof(buf2)))); + json_object_object_add(json, "hosts", json_hosts); + } +} + +/* Print a specific RMAC entry */ +static void zl3vni_print_rmac(zebra_mac_t *zrmac, + struct vty *vty, + json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX_STRLEN]; + struct listnode *node = NULL; + struct prefix *p = NULL; + json_object *json_hosts = NULL; + + if (!json) { + vty_out(vty, "MAC: %s\n", + prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + vty_out(vty, " Host-List:\n"); + for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + vty_out(vty, " %s\n", + prefix2str(p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add(json, "Rmac", + prefix_mac2str(&zrmac->macaddr, + buf1, + sizeof(buf1))); + json_object_string_add(json, "vtep-ip", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + json_object_array_add(json_hosts, + json_object_new_string( + prefix2str(p, buf2, + sizeof(buf2)))); + json_object_object_add(json, "hosts", json_hosts); + } +} + /* * Print a specific MAC entry. */ @@ -603,6 +718,234 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) } } +static void zl3vni_print_nh_hash(struct hash_backet *backet, + void *ctx) +{ + struct nh_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json_vni = NULL; + struct json_object *json_nh = NULL; + zebra_neigh_t *n = NULL; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + wctx = (struct nh_walk_ctx *)ctx; + vty = wctx->vty; + json_vni = wctx->json; + if (json_vni) + json_nh = json_object_new_object(); + n = (zebra_neigh_t *)backet->data; + if (!n) + return; + + if (!json_vni) { + vty_out(vty, "%-15s %-17s %6d\n", + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + prefix_mac2str(&n->emac, buf1, sizeof(buf1)), + listcount(n->host_list)); + } else { + json_object_string_add(json_nh, "nexthop-ip", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + json_object_string_add(json_nh, "rmac", + prefix_mac2str(&n->emac, buf1, + sizeof(buf1))); + json_object_int_add(json_nh, "refCnt", listcount(n->host_list)); + json_object_object_add(json_vni, + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + json_nh); + } +} + +static void zl3vni_print_nh_hash_all_vni(struct hash_backet *backet, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + uint32_t num_nh = 0; + struct nh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + + num_nh = hashcount(zl3vni->nh_table); + if (!num_nh) + return; + + if (json) { + json_vni = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } + + if (json == NULL) { + vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", + zl3vni->vni, num_nh); + vty_out(vty, "%-15s %-17s %6s\n", "IP", + "RMAC", "Refcnt"); + } else + json_object_int_add(json_vni, "numNh", num_nh); + + memset(&wctx, 0, sizeof(struct nh_walk_ctx)); + wctx.vty = vty; + wctx.json = json_vni; + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zl3vni_print_rmac_hash_all_vni(struct hash_backet *backet, + void **args) +{ + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + u_int32_t num_rmacs; + struct rmac_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + + num_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; + + if (json) { + json_vni = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } + + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs %u\n\n", + zl3vni->vni, num_rmacs); + vty_out(vty, "%-17s %-21s %-6s\n", "MAC", + "Remote VTEP", "Refcnt"); + } else + json_object_int_add(json_vni, "numRmacs", num_rmacs); + + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. + */ + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); + wctx.vty = vty; + wctx.json = json_vni; + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zl3vni_print_rmac_hash(struct hash_backet *backet, + void *ctx) +{ + zebra_mac_t *zrmac = NULL; + struct rmac_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json = NULL; + struct json_object *json_rmac = NULL; + char buf[ETHER_ADDR_STRLEN]; + + wctx = (struct rmac_walk_ctx *)ctx; + vty = wctx->vty; + json = wctx->json; + if (json) + json_rmac = json_object_new_object(); + zrmac = (zebra_mac_t *)backet->data; + if (!zrmac) + return; + + if (!json) { + vty_out(vty, "%-17s %-21s %-6d\n", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + inet_ntoa(zrmac->fwd_info.r_vtep_ip), + listcount(zrmac->host_list)); + } else { + json_object_string_add(json_rmac, "rmac", + prefix_mac2str(&zrmac->macaddr, buf, + sizeof(buf))); + json_object_string_add(json_rmac, "vtep-ip", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + json_object_int_add(json_rmac, "refcnt", + listcount(zrmac->host_list)); + json_object_object_add(json, + prefix_mac2str(&zrmac->macaddr, buf, + sizeof(buf)), + json_rmac); + } +} + +/* print a specific L3 VNI entry */ +static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) +{ + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + zebra_vni_t *zvni = NULL; + json_object *json_vni_list = NULL; + struct listnode *node = NULL, *nnode = NULL; + + vty = ctx[0]; + json = ctx[1]; + + if (!json) { + vty_out(vty, "VNI: %u\n", zl3vni->vni); + vty_out(vty, " Local Vtep Ip: %s", + inet_ntoa(zl3vni->local_vtep_ip)); + vty_out(vty, " Vxlan-Intf: %s\n", + zl3vni_vxlan_if_name(zl3vni)); + vty_out(vty, " SVI-If: %s\n", + zl3vni_svi_if_name(zl3vni)); + vty_out(vty, " State: %s\n", + zl3vni_state2str(zl3vni)); + vty_out(vty, " Vrf: %s\n", + zl3vni_vrf_name(zl3vni)); + vty_out(vty, " Rmac: %s\n", + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + vty_out(vty, " L2-VNIs: "); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) + vty_out(vty, "%u ", zvni->vni); + vty_out(vty, "\n"); + } else { + json_vni_list = json_object_new_array(); + json_object_int_add(json, "vni", zl3vni->vni); + json_object_string_add(json, "local-vtep-ip", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json, "vxlan-intf", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json, "svi-if", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json, "state", + zl3vni_state2str(zl3vni)); + json_object_string_add(json, "vrf", + zl3vni_vrf_name(zl3vni)); + json_object_string_add(json, "rmac", + zl3vni_rmac2str(zl3vni, buf, + sizeof(buf))); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { + json_object_array_add(json_vni_list, + json_object_new_int(zvni->vni)); + } + json_object_object_add(json, "l2-vnis", json_vni_list); + } +} + /* * Print a specific VNI entry. */ @@ -619,10 +962,14 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) vty = ctxt[0]; json = ctxt[1]; - if (json == NULL) + if (json == NULL) { vty_out(vty, "VNI: %u\n", zvni->vni); - else + vty_out(vty, " VRF: %s\n", vrf_id_to_name(zvni->vrf_id)); + } else { json_object_int_add(json, "vni", zvni->vni); + json_object_string_add(json, "vrf", + vrf_id_to_name(zvni->vrf_id)); + } if (!zvni->vxlan_if) { // unexpected if (json == NULL) @@ -682,6 +1029,56 @@ static void zvni_print(zebra_vni_t *zvni, void **ctxt) } } +/* print a L3 VNI hash entry */ +static void zl3vni_print_hash(struct hash_backet *backet, + void *ctx[]) +{ + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_vni = NULL; + zebra_l3vni_t *zl3vni = NULL; + + vty = (struct vty *)ctx[0]; + json = (json_object *)ctx[1]; + + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) + return; + + if (!json) { + vty_out(vty, "%-10u %-15s %-20s %-20s %-5s %-37s %-18s\n", + zl3vni->vni, + inet_ntoa(zl3vni->local_vtep_ip), + zl3vni_vxlan_if_name(zl3vni), + zl3vni_svi_if_name(zl3vni), + zl3vni_state2str(zl3vni), + zl3vni_vrf_name(zl3vni), + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + } else { + char vni_str[VNI_STR_LEN]; + + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "vni", zl3vni->vni); + json_object_string_add(json_vni, "local-ip", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json_vni, "vxlan-if", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json_vni, "svi-if", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json_vni, "state", + zl3vni_state2str(zl3vni)); + json_object_string_add(json_vni, "vrf", + zl3vni_vrf_name(zl3vni)); + json_object_string_add(json_vni, "rmac", + zl3vni_rmac2str(zl3vni, buf, + sizeof(buf))); + json_object_object_add(json, vni_str, json_vni); + } + +} + /* * Print a VNI hash entry - called for display of all VNIs. */ @@ -714,10 +1111,12 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]) num_macs = num_valid_macs(zvni); num_neigh = hashcount(zvni->neigh_table); if (json == NULL) - vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni, + vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u %-37s\n", + zvni->vni, zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, - num_vteps); + num_vteps, + vrf_id_to_name(zvni->vrf_id)); else { char vni_str[VNI_STR_LEN]; snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); @@ -753,11 +1152,11 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ipaddr *ip, u_char flags, u_int16_t cmd) { - struct zserv *client; - struct stream *s; - int ipa_len; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + int ipa_len; + struct zserv *client = NULL; + struct stream *s = NULL; client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ @@ -767,7 +1166,7 @@ static int zvni_macip_send_msg_to_client(vni_t vni, s = client->obuf; stream_reset(s); - zserv_create_header(s, cmd, VRF_DEFAULT); + zclient_create_header(s, cmd, VRF_DEFAULT); stream_putl(s, vni); stream_put(s, macaddr->octet, ETH_ALEN); if (ip) { @@ -785,12 +1184,13 @@ static int zvni_macip_send_msg_to_client(vni_t vni, stream_putc(s, flags); /* sticky mac/gateway mac */ + /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s", + "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s", (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, prefix_mac2str(macaddr, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2)), vni, @@ -1000,7 +1400,7 @@ static void zvni_process_neigh_on_local_mac_add(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_INACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u is now ACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1040,7 +1440,7 @@ static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_ACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u is now INACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1077,7 +1477,7 @@ static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_ACTIVE(n)) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "neigh %s (MAC %s) on VNI %u INACTIVE", + "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE", ipaddr2str(&n->ip, buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf, @@ -1302,12 +1702,12 @@ static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, struct ethaddr *macaddr, struct ipaddr *ip) { - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; zif = zvni->vxlan_if->info; if (!zif) @@ -1353,7 +1753,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP", + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP", ifp->name, ifp->ifindex, zvni->vni, prefix_mac2str(macaddr, buf, sizeof(buf)), ipaddr2str(ip, buf2, sizeof(buf2))); @@ -1370,10 +1770,10 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, struct ipaddr *ip) { - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; /* If the neigh entry is not present nothing to do*/ n = zvni_neigh_lookup(zvni, ip); @@ -1395,7 +1795,7 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s del to BGP", ifp->name, ifp->ifindex, zvni->vni, prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), ipaddr2str(ip, buf2, sizeof(buf2))); @@ -1747,7 +2147,8 @@ static zebra_vni_t *zvni_map_vlan(struct interface *ifp, * Map SVI and associated bridge to a VNI. This is invoked upon getting * neighbor notifications, to see if they are of interest. */ -static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if) +static zebra_vni_t *zvni_from_svi(struct interface *ifp, + struct interface *br_if) { struct zebra_ns *zns; struct route_node *rn; @@ -2130,16 +2531,18 @@ static int zvni_send_add_to_client(zebra_vni_t *zvni) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT); + zclient_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT); stream_putl(s, zvni->vni); stream_put_in_addr(s, &zvni->local_vtep_ip); + stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_ADD %u %s to %s", + zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni, inet_ntoa(zvni->local_vtep_ip), + vrf_id_to_name(zvni->vrf_id), zebra_route_string(client->proto)); client->vniadd_cnt++; @@ -2162,7 +2565,7 @@ static int zvni_send_del_to_client(vni_t vni) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_VNI_DEL, VRF_DEFAULT); + zclient_create_header(s, ZEBRA_VNI_DEL, VRF_DEFAULT); stream_putl(s, vni); /* Write packet size. */ @@ -2189,10 +2592,9 @@ static void zvni_build_hash_table() /* Walk VxLAN interfaces and create VNI hash. */ zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + vni_t vni; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; - zebra_vni_t *zvni; - vni_t vni; ifp = (struct interface *)rn->info; if (!ifp) @@ -2200,39 +2602,83 @@ static void zvni_build_hash_table() zif = ifp->info; if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) continue; - vxl = &zif->l2info.vxl; + vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Create VNI hash for intf %s(%u) VNI %u local IP %s", - ifp->name, ifp->ifindex, vni, - inet_ntoa(vxl->vtep_ip)); + if (is_vni_l3(vni)) { + zebra_l3vni_t *zl3vni = NULL; - /* VNI hash entry is not expected to exist. */ - zvni = zvni_lookup(vni); - if (zvni) { - zlog_err( - "VNI hash already present for IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - continue; - } + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("create L3-VNI hash for Intf %s(%u) L3-VNI %u", + ifp->name, ifp->ifindex, vni); - zvni = zvni_add(vni); - if (!zvni) { - zlog_err( - "Failed to add VNI hash, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return; - } + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return; + } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; - /* Inform BGP if interface is up and mapped to bridge. */ - if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + /* + * we need to associate with SVI. + * we can associate with svi-if only after association + * with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %s", + ifp->name, ifp->ifindex, vni, + inet_ntoa(vxl->vtep_ip)); + + /* VNI hash entry is not expected to exist. */ + zvni = zvni_lookup(vni); + if (zvni) { + zlog_err( + "VNI hash already present for IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + continue; + } + + zvni = zvni_add(vni); + if (!zvni) { + zlog_err( + "Failed to add VNI hash, IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + return; + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + + /* Inform BGP if intf is up and mapped to bridge. */ + if (if_is_operative(ifp) && zif->brslave_info.br_if) + zvni_send_add_to_client(zvni); + } } } @@ -2351,12 +2797,18 @@ static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip) */ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) { - zebra_vni_t *zvni; + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; zvni = (zebra_vni_t *)backet->data; if (!zvni) return; + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zvni->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zvni); + /* Free up all neighbors and MACs, if any. */ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); @@ -2368,63 +2820,1342 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf) zvni_del(zvni); } +/* cleanup L3VNI */ +static void zl3vni_cleanup_all(struct hash_backet *backet, + void *args) +{ + zebra_l3vni_t *zl3vni = NULL; -/* Public functions */ + zl3vni = (zebra_l3vni_t *)backet->data; + if (!zl3vni) + return; -/* - * Display Neighbors for a VNI (VTY command handler). - */ -void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, u_char use_json) + zebra_vxlan_process_l3vni_oper_down(zl3vni); +} + +static int is_host_present_in_host_list(struct list *list, + struct prefix *host) { - zebra_vni_t *zvni; - u_int32_t num_neigh; - struct neigh_walk_ctx wctx; - json_object *json = NULL; + struct listnode *node = NULL; + struct prefix *p = NULL; - if (!is_evpn_enabled()) - return; - zvni = zvni_lookup(vni); - if (!zvni) { - if (use_json) - vty_out(vty, "{}\n"); - else - vty_out(vty, "%% VNI %u does not exist\n", vni); - return; + for (ALL_LIST_ELEMENTS_RO(list, node, p)) { + if (prefix_same(p, host)) + return 1; } - num_neigh = hashcount(zvni->neigh_table); - if (!num_neigh) - return; - - if (use_json) - json = json_object_new_object(); + return 0; +} - /* Since we have IPv6 addresses to deal with which can vary widely in - * size, we try to be a bit more elegant in display by first computing - * the maximum width. - */ - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - wctx.vty = vty; - wctx.addr_width = 15; - wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); +static void host_list_add_host(struct list *list, + struct prefix *host) +{ + struct prefix *p = NULL; - if (!use_json) { - vty_out(vty, - "Number of ARPs (local and remote) known for this VNI: %u\n", - num_neigh); - vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", - "Type", "MAC", "Remote VTEP"); - } else - json_object_int_add(json, "numArpNd", num_neigh); + p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix)); + memcpy(p, host, sizeof(struct prefix)); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); - if (use_json) { - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); - } + listnode_add_sort(list, p); +} + +static void host_list_delete_host(struct list *list, + struct prefix *host) +{ + struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; + struct prefix *p = NULL; + + for (ALL_LIST_ELEMENTS(list, node, nnode, p)) { + if (prefix_same(p, host)) { + XFREE(MTYPE_HOST_PREFIX, p); + node_to_del = node; + } + } + + if (node_to_del) + list_delete_node(list, node_to_del); +} + +/* + * Look up MAC hash entry. + */ +static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, rmac, ETH_ALEN); + pmac = hash_lookup(zl3vni->rmac_table, &tmp); + + return pmac; +} + +/* + * Callback to allocate RMAC hash entry. + */ +static void *zl3vni_rmac_alloc(void *p) +{ + const zebra_mac_t *tmp_rmac = p; + zebra_mac_t *zrmac; + + zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + *zrmac = *tmp_rmac; + + return ((void *)zrmac); +} + +/* + * Add RMAC entry to l3-vni + */ +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + zebra_mac_t tmp_rmac; + zebra_mac_t *zrmac = NULL; + + memset(&tmp_rmac, 0, sizeof(zebra_mac_t)); + memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN); + zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc); + assert(zrmac); + + zrmac->host_list = list_new(); + zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + + SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE); + SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC); + + return zrmac; +} + +/* + * Delete MAC entry. + */ +static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + zebra_mac_t *tmp_rmac; + + if (zrmac->host_list) + list_delete_and_null(&zrmac->host_list); + zrmac->host_list = NULL; + + tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); + if (tmp_rmac) + XFREE(MTYPE_MAC, tmp_rmac); + + return 0; +} + +/* + * Install remote RMAC into the kernel. + */ +static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || + !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) + return 0; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan, + &zrmac->macaddr, + zrmac->fwd_info.r_vtep_ip, 0); +} + +/* + * Uninstall remote RMAC from the kernel. + */ +static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, + zebra_mac_t *zrmac) +{ + char buf[ETHER_ADDR_STRLEN]; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || + !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) + return 0; + + if (!zl3vni->vxlan_if) { + zlog_err( + "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + zl3vni->vni, zl3vni); + return -1; + } + + zif = zl3vni->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0); +} + +/* handle rmac add */ +static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_mac_t *zrmac = NULL; + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) { + + zrmac = zl3vni_rmac_add(zl3vni, rmac); + if (!zrmac) { + zlog_warn( + "Failed to add RMAC %s L3VNI %u Remote VTEP %s", + prefix_mac2str(rmac, buf, + sizeof(buf)), + zl3vni->vni, ipaddr2str(vtep_ip, buf1, + sizeof(buf1))); + return -1; + } + memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info)); + zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4; + + /* install rmac in kernel */ + zl3vni_rmac_install(zl3vni, zrmac); + } + + if (!is_host_present_in_host_list(zrmac->host_list, host_prefix)) + host_list_add_host(zrmac->host_list, host_prefix); + return 0; +} + + +/* handle rmac delete */ +static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac, + struct prefix *host_prefix) +{ + zebra_mac_t *zrmac = NULL; + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) + return -1; + + host_list_delete_host(zrmac->host_list, host_prefix); + if (list_isempty(zrmac->host_list)) { + + /* uninstall from kernel */ + zl3vni_rmac_uninstall(zl3vni, zrmac); + + /* del the rmac entry */ + zl3vni_rmac_del(zl3vni, zrmac); + } + return 0; +} + +/* + * Look up nh hash entry on a l3-vni. + */ +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + struct ipaddr *ip) +{ + zebra_neigh_t tmp; + zebra_neigh_t *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zl3vni->nh_table, &tmp); + + return n; +} + + +/* + * Callback to allocate NH hash entry on L3-VNI. + */ +static void *zl3vni_nh_alloc(void *p) +{ + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); +} + +/* + * Add neighbor entry. + */ +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *ip, + struct ethaddr *mac) +{ + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; + + memset(&tmp_n, 0, sizeof(zebra_neigh_t)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc); + assert(n); + + n->host_list = list_new(); + n->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + + memcpy(&n->emac, mac, ETH_ALEN); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE_NH); + + return n; +} + +/* + * Delete neighbor entry. + */ +static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + zebra_neigh_t *tmp_n; + + if (n->host_list) + list_delete_and_null(&n->host_list); + n->host_list = NULL; + + tmp_n = hash_release(zl3vni->nh_table, n); + if (tmp_n) + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +/* + * Install remote nh as neigh into the kernel. + */ +static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + if (!is_l3vni_oper_up(zl3vni)) + return -1; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE) || + !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) + return 0; + + return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac); +} + +/* + * Uninstall remote nh from the kernel. + */ +static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, + zebra_neigh_t *n) +{ + if (!is_l3vni_oper_up(zl3vni)) + return -1; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE) || + !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) + return 0; + + return kernel_del_neigh(zl3vni->svi_if, &n->ip); +} + +/* add remote vtep as a neigh entry */ +static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct ethaddr *rmac, + struct prefix *host_prefix) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_neigh_t *nh = NULL; + + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) { + nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac); + if (!nh) { + + zlog_warn( + "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)", + ipaddr2str(vtep_ip, buf1, + sizeof(buf1)), + prefix_mac2str(rmac, buf, + sizeof(buf)), + zl3vni->vni); + return -1; + } + + /* install the nh neigh in kernel */ + zl3vni_nh_install(zl3vni, nh); + } + + if (!is_host_present_in_host_list(nh->host_list, host_prefix)) + host_list_add_host(nh->host_list, host_prefix); + + return 0; +} + +/* handle nh neigh delete */ +static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_neigh_t *nh = NULL; + + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) + return -1; + + host_list_delete_host(nh->host_list, host_prefix); + if (list_isempty(nh->host_list)) { + + /* uninstall from kernel */ + zl3vni_nh_uninstall(zl3vni, nh); + + /* delete the nh entry */ + zl3vni_nh_del(zl3vni, nh); + } + + return 0; +} + +/* handle neigh update from kernel - the only thing of interest is to + * readd stale entries. + */ +static int zl3vni_local_nh_add_update(zebra_l3vni_t *zl3vni, + struct ipaddr *ip, u_int16_t state) +{ +#ifdef GNU_LINUX + zebra_neigh_t *n = NULL; + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) + return 0; + + /* all next hop neigh are remote and installed by frr. + * If the kernel has aged this entry, re-install. + */ + if (state & NUD_STALE) + zl3vni_nh_install(zl3vni, n); +#endif + return 0; +} + +/* handle neigh delete from kernel */ +static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni, + struct ipaddr *ip) +{ + zebra_neigh_t *n = NULL; + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) + return 0; + + /* all next hop neigh are remote and installed by frr. + * If we get an age out notification for these neigh entries, we have to + * install it back + */ + zl3vni_nh_install(zl3vni, n); + + return 0; +} + +/* + * Hash function for L3 VNI. + */ +static unsigned int l3vni_hash_keymake(void *p) +{ + const zebra_l3vni_t *zl3vni = p; + + return jhash_1word(zl3vni->vni, 0); +} + +/* + * Compare 2 L3 VNI hash entries. + */ +static int l3vni_hash_cmp(const void *p1, const void *p2) +{ + const zebra_l3vni_t *zl3vni1 = p1; + const zebra_l3vni_t *zl3vni2 = p2; + + return (zl3vni1->vni == zl3vni2->vni); +} + +/* + * Callback to allocate L3 VNI hash entry. + */ +static void *zl3vni_alloc(void *p) +{ + zebra_l3vni_t *zl3vni = NULL; + const zebra_l3vni_t *tmp_l3vni = p; + + zl3vni = XCALLOC(MTYPE_ZL3VNI, sizeof(zebra_l3vni_t)); + zl3vni->vni = tmp_l3vni->vni; + return ((void *)zl3vni); +} + +/* + * Look up L3 VNI hash entry. + */ +static zebra_l3vni_t *zl3vni_lookup(vni_t vni) +{ + struct zebra_ns *zns; + zebra_l3vni_t tmp_l3vni; + zebra_l3vni_t *zl3vni = NULL; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t)); + tmp_l3vni.vni = vni; + zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni); + + return zl3vni; +} + +/* + * Add L3 VNI hash entry. + */ +static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id) +{ + zebra_l3vni_t tmp_zl3vni; + struct zebra_ns *zns = NULL; + zebra_l3vni_t *zl3vni = NULL; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t)); + tmp_zl3vni.vni = vni; + + zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc); + assert(zl3vni); + + zl3vni->vrf_id = vrf_id; + zl3vni->svi_if = NULL; + zl3vni->vxlan_if = NULL; + zl3vni->l2vnis = list_new(); + zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp; + + /* Create hash table for remote RMAC */ + zl3vni->rmac_table = + hash_create(mac_hash_keymake, mac_cmp, + "Zebra L3-VNI RMAC-Table"); + + /* Create hash table for neighbors */ + zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp, + "Zebra L3-VNI next-hop table"); + + return zl3vni; +} + +/* + * Delete L3 VNI hash entry. + */ +static int zl3vni_del(zebra_l3vni_t *zl3vni) +{ + struct zebra_ns *zns; + zebra_l3vni_t *tmp_zl3vni; + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + /* free the list of l2vnis */ + list_delete_and_null(&zl3vni->l2vnis); + zl3vni->l2vnis = NULL; + + /* Free the rmac table */ + hash_free(zl3vni->rmac_table); + zl3vni->rmac_table = NULL; + + /* Free the nh table */ + hash_free(zl3vni->nh_table); + zl3vni->nh_table = NULL; + + /* Free the VNI hash entry and allocated memory. */ + tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni); + if (tmp_zl3vni) + XFREE(MTYPE_ZL3VNI, tmp_zl3vni); + + return 0; +} + +static int is_vni_l3(vni_t vni) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_lookup(vni); + if (zl3vni) + return 1; + return 0; +} + +static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_ns *zns = NULL; + struct route_node *rn = NULL; + struct interface *ifp = NULL; + + /* loop through all vxlan-interface */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + ifp = (struct interface *)rn->info; + if (!ifp) + continue; + + zif = ifp->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + + vxl = &zif->l2info.vxl; + if (vxl->vni == zl3vni->vni) { + zl3vni->local_vtep_ip = vxl->vtep_ip; + return ifp; + } + } + + return NULL; +} + +static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */ + struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */ + + if (!zl3vni->vxlan_if) + return NULL; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return NULL; + + vxl = &zif->l2info.vxl; + + return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); +} + +static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) +{ + struct zebra_vrf *zvrf = NULL; + + zvrf = zebra_vrf_lookup_by_id(vrf_id); + if (!zvrf) + return NULL; + + return zl3vni_lookup(zvrf->l3vni); +} + +/* + * Map SVI and associated bridge to a VNI. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, + struct interface *br_if) +{ + int found = 0; + vlanid_t vid = 0; + u_char bridge_vlan_aware = 0; + zebra_l3vni_t *zl3vni = NULL; + struct zebra_ns *zns = NULL; + struct route_node *rn = NULL; + struct zebra_if *zif = NULL; + struct interface *tmp_if = NULL; + struct zebra_l2info_bridge *br = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + if (!br_if) + return NULL; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE(br_if)) + return NULL; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + if (bridge_vlan_aware) { + struct zebra_l2info_vlan *vl; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zif = ifp->info; + assert(zif); + vl = &zif->l2info.vl; + vid = vl->vid; + } + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zl3vni = zl3vni_lookup(vxl->vni); + return zl3vni; +} + +/* + * Inform BGP about l3-vni. + */ +static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni) +{ + struct stream *s = NULL; + struct zserv *client = NULL; + struct ethaddr rmac; + char buf[ETHER_ADDR_STRLEN]; + + client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + /* get the rmac */ + memset(&rmac, 0, sizeof(struct ethaddr)); + zl3vni_get_rmac(zl3vni, &rmac); + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_L3VNI_ADD, + zl3vni_vrf_id(zl3vni)); + stream_putl(s, zl3vni->vni); + stream_put(s, &rmac, sizeof(struct ethaddr)); + stream_put_in_addr(s, &zl3vni->local_vtep_ip); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s", + zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + prefix_mac2str(&rmac, buf, sizeof(buf)), + inet_ntoa(zl3vni->local_vtep_ip), + zebra_route_string(client->proto)); + + client->l3vniadd_cnt++; + return zebra_server_send_message(client); +} + +/* + * Inform BGP about local l3-VNI deletion. + */ +static int zl3vni_send_del_to_client(zebra_l3vni_t *zl3vni) +{ + struct stream *s = NULL; + struct zserv *client = NULL; + + client = zebra_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_L3VNI_DEL, + zl3vni_vrf_id(zl3vni)); + stream_putl(s, zl3vni->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send L3_VNI_DEL %u VRF %s to %s", + zl3vni->vni, + vrf_id_to_name(zl3vni_vrf_id(zl3vni)), + zebra_route_string(client->proto)); + + client->l3vnidel_cnt++; + return zebra_server_send_message(client); +} + +static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3-VNI %u is UP - send add to BGP", + zl3vni->vni); + + /* send l3vni add to BGP */ + zl3vni_send_add_to_client(zl3vni); +} + +static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3-VNI %u is Down - Send del to BGP", + zl3vni->vni); + + /* send l3-vni del to BGP*/ + zl3vni_send_del_to_client(zl3vni); +} + +static void zvni_add_to_l3vni_list(struct hash_backet *backet, + void *ctxt) +{ + zebra_vni_t *zvni = (zebra_vni_t *) backet->data; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *) ctxt; + + if (zvni->vrf_id == zl3vni_vrf_id(zl3vni)) + listnode_add_sort(zl3vni->l2vnis, zvni); +} + +/* + * handle transition of vni from l2 to l3 and vice versa + */ +static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, + vni_t vni, int add) +{ + zebra_vni_t *zvni = NULL; + + /* There is a possibility that VNI notification was already received + * from kernel and we programmed it as L2-VNI + * In such a case we need to delete this L2-VNI first, so + * that it can be reprogrammed as L3-VNI in the system. It is also + * possible that the vrf-vni mapping is removed from FRR while the vxlan + * interface is still present in kernel. In this case to keep it + * symmetric, we will delete the l3-vni and reprogram it as l2-vni + */ + if (add) { + /* Locate hash entry */ + zvni = zvni_lookup(vni); + if (!zvni) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u - transition to L3-VNI", + vni); + + /* Delete VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del(zvni)) { + zlog_err("Failed to del VNI hash %p, VNI %u", + zvni, zvni->vni); + return -1; + } + } else { + /* TODO_MITESH: This needs to be thought through. We don't have + * enough information at this point to reprogram the vni as + * l2-vni. One way is to store the required info in l3-vni and + * used it solely for this purpose + */ + } + + return 0; +} + +/* delete and uninstall rmac hash entry */ +static void zl3vni_del_rmac_hash_entry(struct hash_backet *backet, + void *ctx) +{ + zebra_mac_t *zrmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + zrmac = (zebra_mac_t *)backet->data; + zl3vni = (zebra_l3vni_t *)ctx; + zl3vni_rmac_uninstall(zl3vni, zrmac); + zl3vni_rmac_del(zl3vni, zrmac); +} + +/* delete and uninstall nh hash entry */ +static void zl3vni_del_nh_hash_entry(struct hash_backet *backet, + void *ctx) +{ + zebra_neigh_t *n = NULL; + zebra_l3vni_t *zl3vni = NULL; + + n = (zebra_neigh_t *)backet->data; + zl3vni = (zebra_l3vni_t *)ctx; + zl3vni_nh_uninstall(zl3vni, n); + zl3vni_nh_del(zl3vni, n); +} + +/* Public functions */ + +/* handle evpn route in vrf table */ +void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return; + + /* add the next hop neighbor */ + zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix); + + /* add the rmac */ + zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix); +} + +/* handle evpn vrf route delete */ +void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni) + return; + + /* delete the next hop entry */ + zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix); + + /* delete the rmac entry */ + zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix); +} + +void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, + vni_t l3vni, + struct ethaddr *rmac, + u_char use_json) +{ + zebra_l3vni_t *zl3vni = NULL; + zebra_mac_t *zrmac = NULL; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u doesnt exist\n", + l3vni); + return; + } + + zrmac = zl3vni_rmac_lookup(zl3vni, rmac); + if (!zrmac) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, + "%% Requested RMAC doesnt exist in L3-VNI %u", + l3vni); + return; + } + + zl3vni_print_rmac(zrmac, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, + vni_t l3vni, + u_char use_json) +{ + zebra_l3vni_t *zl3vni; + u_int32_t num_rmacs; + struct rmac_walk_ctx wctx; + json_object *json = NULL; + + if (!is_evpn_enabled()) + return; + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + num_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; + + if (use_json) + json = json_object_new_object(); + + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); + wctx.vty = vty; + wctx.json = json; + if (!use_json) { + vty_out(vty, + "Number of Remote RMACs known for this VNI: %u\n", + num_rmacs); + vty_out(vty, "%-17s %-21s %-6s\n", "MAC", + "Remote VTEP", "Refcnt"); + } else + json_object_int_add(json, "numRmacs", num_rmacs); + + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, + u_char use_json) +{ + struct zebra_ns *zns = NULL; + json_object *json = NULL; + void *args[2]; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_rmac_hash_all_vni, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, + vni_t l3vni, + struct ipaddr *ip, + u_char use_json) +{ + zebra_l3vni_t *zl3vni = NULL; + zebra_neigh_t *n = NULL; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) + json = json_object_new_object(); + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + + n = zl3vni_nh_lookup(zl3vni, ip); + if (!n) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, + "%% Requested next-hop not present for L3-VNI %u", + l3vni); + return; + } + + zl3vni_print_nh(n, vty, json); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_nh_l3vni(struct vty *vty, + vni_t l3vni, + u_char use_json) +{ + u_int32_t num_nh; + struct nh_walk_ctx wctx; + json_object *json = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (!is_evpn_enabled()) + return; + + zl3vni = zl3vni_lookup(l3vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); + return; + } + + num_nh = hashcount(zl3vni->nh_table); + if (!num_nh) + return; + + if (use_json) + json = json_object_new_object(); + + wctx.vty = vty; + wctx.json = json; + if (!use_json) { + vty_out(vty, + "Number of NH Neighbors known for this VNI: %u\n", + num_nh); + vty_out(vty, "%-15s %-17s %6s\n", "IP", + "RMAC", "Refcnt"); + } else + json_object_int_add(json, "numNh", num_nh); + + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, + u_char use_json) +{ + struct zebra_ns *zns = NULL; + json_object *json = NULL; + void *args[2]; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return; + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_nh_hash_all_vni, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return; +} + +/* + * Display L3 VNI information (VTY command handler). + */ +void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, u_char use_json) +{ + void *args[2]; + json_object *json = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + zl3vni_print(zl3vni, (void *)args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* + * Display L3 VNI hash table (VTY command handler). + */ +void zebra_vxlan_print_l3vnis(struct vty *vty, u_char use_json) +{ + u_int32_t num_vnis; + void *args[2]; + json_object *json = NULL; + struct zebra_ns *zns = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + zns = zebra_ns_lookup(NS_DEFAULT); + assert(zns); + + num_vnis = hashcount(zns->l3vni_table); + if (!num_vnis) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + + if (use_json) { + json = json_object_new_object(); + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Number of L3 VNIs: %u\n", num_vnis); + vty_out(vty, "%-10s %-15s %-20s %-20s %-5s %-37s %-18s\n", + "VNI", "Local-ip", "Vx-intf", "L3-SVI", "State", + "VRF", "Rmac"); + } + + args[0] = vty; + args[1] = json; + hash_iterate(zns->l3vni_table, + (void (*)(struct hash_backet *, void *))zl3vni_print_hash, + args); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* + * Display Neighbors for a VNI (VTY command handler). + */ +void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, + vni_t vni, u_char use_json) +{ + zebra_vni_t *zvni; + u_int32_t num_neigh; + struct neigh_walk_ctx wctx; + json_object *json = NULL; + + if (!is_evpn_enabled()) + return; + zvni = zvni_lookup(vni); + if (!zvni) { + if (use_json) + vty_out(vty, "{}\n"); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + num_neigh = hashcount(zvni->neigh_table); + if (!num_neigh) + return; + + if (use_json) + json = json_object_new_object(); + + /* Since we have IPv6 addresses to deal with which can vary widely in + * size, we try to be a bit more elegant in display by first computing + * the maximum width. + */ + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zvni = zvni; + wctx.vty = vty; + wctx.addr_width = 15; + wctx.json = json; + hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + + if (!use_json) { + vty_out(vty, + "Number of ARPs (local and remote) known for this VNI: %u\n", + num_neigh); + vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", + "Type", "MAC", "Remote VTEP"); + } else + json_object_int_add(json, "numArpNd", num_neigh); + + hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } /* @@ -2789,9 +4520,9 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, vty_out(vty, "Advertise gateway mac-ip: %s\n", zvrf->advertise_gw_macip ? "Yes" : "No"); vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", + vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s %-37s\n", "VNI", "VxLAN IF", "VTEP IP", "# MACs", "# ARPs", - "# Remote VTEPs"); + "# Remote VTEPs", "VRF"); } args[0] = vty; args[1] = json; @@ -2816,18 +4547,27 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, int zebra_vxlan_local_neigh_del(struct interface *ifp, struct interface *link_if, struct ipaddr *ip) { - zebra_vni_t *zvni; - zebra_neigh_t *n; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; - zebra_mac_t *zmac; + zebra_neigh_t *n = NULL; + zebra_vni_t *zvni = NULL; + zebra_mac_t *zmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* check if this is a remote neigh entry corresponding to remote + * next-hop + */ + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) + return zl3vni_local_nh_del(zl3vni, ip); /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_map_svi(ifp, link_if); + zvni = zvni_from_svi(ifp, link_if); if (!zvni) return 0; + if (!zvni->vxlan_if) { zlog_err( "VNI %u hash %p doesn't have intf upon local neighbor DEL", @@ -2836,7 +4576,7 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, } if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del neighbor %s intf %s(%u) -> VNI %u", + zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u", ipaddr2str(ip, buf, sizeof(buf)), ifp->name, ifp->ifindex, zvni->vni); @@ -2891,23 +4631,30 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, struct ethaddr *macaddr, u_int16_t state, u_char ext_learned) { - zebra_vni_t *zvni; - zebra_neigh_t *n; - zebra_mac_t *zmac, *old_zmac; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + zebra_vni_t *zvni = NULL; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL, *old_zmac = NULL; + zebra_l3vni_t *zl3vni = NULL; + + /* check if this is a remote neigh entry corresponding to remote + * next-hop + */ + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) + return zl3vni_local_nh_add_update(zl3vni, ip, state); /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_map_svi(ifp, link_if); + zvni = zvni_from_svi(ifp, link_if); if (!zvni) return 0; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x " - "%s-> VNI %u", + "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s-> L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, state, ext_learned ? "ext-learned " : "", @@ -3015,12 +4762,12 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, /* Inform BGP. */ if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("neigh %s (MAC %s) is now ACTIVE on VNI %u", + zlog_debug("neigh %s (MAC %s) is now ACTIVE on L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), zvni->vni); - ZEBRA_NEIGH_SET_ACTIVE(n); + return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, 0); } @@ -3045,6 +4792,10 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, u_short length, struct interface *ifp = NULL; struct zebra_if *zif = NULL; + memset(&macaddr, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&vtep_ip, 0, sizeof(struct in_addr)); + s = client->ibuf; while (l < length) { @@ -3189,6 +4940,10 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, u_short length, struct interface *ifp = NULL; struct zebra_if *zif = NULL; + memset(&macaddr, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); + memset(&vtep_ip, 0, sizeof(struct in_addr)); + if (!EVPN_ENABLED(zvrf)) { zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback", __PRETTY_FUNCTION__); @@ -3898,14 +5653,14 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); - zvni = zvni_map_svi(svi_if, svi_if_link); + zvni = zvni_from_svi(svi_if, svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { /* * If it is a vlan unaware bridge then svi is the bridge * itself */ - zvni = zvni_map_svi(svi_if, svi_if); + zvni = zvni_from_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = @@ -3917,9 +5672,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); if (svi_if_zif && svi_if_link) - zvni = zvni_map_svi(ifp, svi_if_link); + zvni = zvni_from_svi(ifp, svi_if_link); } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zvni = zvni_map_svi(ifp, ifp); + zvni = zvni_from_svi(ifp, ifp); } if (!zvni) @@ -3958,56 +5713,108 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, } /* - * Handle SVI interface going down. At this point, this is a NOP since - * the kernel deletes the neighbor entries on this SVI (if any). + * Handle SVI interface going down. + * SVI can be associated to either L3-VNI or L2-VNI. + * For L2-VNI: At this point, this is a NOP since + * the kernel deletes the neighbor entries on this SVI (if any). + * We only need to update the vrf corresponding to zvni. + * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete + * from bgp */ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) { + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) { + + /* process l3-vni down */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* remove association with svi-if */ + zl3vni->svi_if = NULL; + } else { + zebra_vni_t *zvni = NULL; + + /* since we dont have svi corresponding to zvni, we associate it + * to default vrf. Note: the corresponding neigh entries on the + * SVI would have already been deleted */ + zvni = zvni_from_svi(ifp, link_if); + if (zvni) { + zvni->vrf_id = VRF_DEFAULT; + + /* update the tenant vrf in BGP */ + zvni_send_add_to_client(zvni); + } + } return 0; } /* - * Handle SVI interface coming up. This may or may not be of interest, - * but if this is a SVI on a VxLAN bridge, we need to install any remote - * neighbor entries (which will be used for EVPN ARP suppression). + * Handle SVI interface coming up. + * SVI can be associated to L3-VNI (l3vni vxlan interface) or L2-VNI (l2-vni + * vxlan intf). + * For L2-VNI: we need to install any remote neighbors entried (used for + * apr-suppression) + * For L3-VNI: SVI will be used to get the rmac to be used with L3-VNI */ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { - zebra_vni_t *zvni; - struct neigh_walk_ctx n_wctx; + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; - zvni = zvni_map_svi(ifp, link_if); - if (!zvni) - return 0; + zl3vni = zl3vni_from_svi(ifp, link_if); + if (zl3vni) { - if (!zvni->vxlan_if) { - zlog_err("VNI %u hash %p doesn't have intf upon SVI up", - zvni->vni, zvni); - return -1; - } + /* associate with svi */ + zl3vni->svi_if = ifp; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors", - ifp->name, ifp->ifindex, zvni->vni); + /* process oper-up */ + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process SVI up for l2-vni */ + struct neigh_walk_ctx n_wctx; + + zvni = zvni_from_svi(ifp, link_if); + if (!zvni) + return 0; + + if (!zvni->vxlan_if) { + zlog_err("VNI %u hash %p doesn't have intf upon SVI up", + zvni->vni, zvni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("SVI %s(%u) VNI %u VRF %s is UP, installing neighbors", + ifp->name, ifp->ifindex, zvni->vni, + vrf_id_to_name(ifp->vrf_id)); - /* Install any remote neighbors for this VNI. */ - memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx); + /* update the vrf information for l2-vni and inform bgp */ + zvni->vrf_id = ifp->vrf_id; + zvni_send_add_to_client(zvni); + + /* Install any remote neighbors for this VNI. */ + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.zvni = zvni; + hash_iterate(zvni->neigh_table, + zvni_install_neigh_hash, + &n_wctx); + } return 0; } /* - * Handle VxLAN interface down - update BGP if required, and do - * internal cleanup. + * Handle VxLAN interface down */ int zebra_vxlan_if_down(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4018,31 +5825,55 @@ int zebra_vxlan_if_down(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) VNI %u is DOWN", - ifp->name, ifp->ifindex, vni); - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + if (is_vni_l3(vni)) { - assert(zvni->vxlan_if == ifp); + /* process-if-down for l3-vni */ + zebra_l3vni_t *zl3vni = NULL; - /* Delete this VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is DOWN", + ifp->name, ifp->ifindex, vni); - /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + } else { + /* process if-down for l2-vni */ + zebra_vni_t *zvni; + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is DOWN", + ifp->name, ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zvni->vxlan_if == ifp); + + /* Delete this VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MACs, if any. */ + zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 1); + } return 0; } @@ -4051,10 +5882,9 @@ int zebra_vxlan_if_down(struct interface *ifp) */ int zebra_vxlan_if_up(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4065,26 +5895,66 @@ int zebra_vxlan_if_up(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) VNI %u is UP", - ifp->name, ifp->ifindex, vni); + if (is_vni_l3(vni)) { - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + /* Handle L3-VNI add */ + zebra_l3vni_t *zl3vni = NULL; - assert(zvni->vxlan_if == ifp); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is UP", + ifp->name, ifp->ifindex, vni); - /* If part of a bridge, inform BGP about this VNI. */ - /* Also, read and populate local MACs and neighbors. */ - if (zif->brslave_info.br_if) { - zvni_send_add_to_client(zvni); - zvni_read_mac_neigh(zvni, ifp); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + /* we need to associate with SVI, if any, we can associate with + * svi-if only after association with vxlan-intf is complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + /* Handle L2-VNI add */ + + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L2-VNI %u is UP", + ifp->name, ifp->ifindex, vni); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + assert(zvni->vxlan_if == ifp); + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + /* If part of a bridge, inform BGP about this VNI. */ + /* Also, read and populate local MACs and neighbors. */ + if (zif->brslave_info.br_if) { + zvni_send_add_to_client(zvni); + zvni_read_mac_neigh(zvni, ifp); + } } return 0; @@ -4096,10 +5966,9 @@ int zebra_vxlan_if_up(struct interface *ifp) */ int zebra_vxlan_if_del(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4110,34 +5979,69 @@ int zebra_vxlan_if_del(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Del VNI %u intf %s(%u)", - vni, ifp->name, ifp->ifindex); + if (is_vni_l3(vni)) { - /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to locate VNI hash at del, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return 0; - } + /* process if-del for l3-vni */ + zebra_l3vni_t *zl3vni = NULL; - /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L3-VNI %u intf %s(%u)", + vni, ifp->name, ifp->ifindex); - /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + /* process oper-down for l3-vni */ + zebra_vxlan_process_l3vni_oper_down(zl3vni); - /* Delete the hash entry. */ - if (zvni_del(zvni)) { - zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u", - zvni, ifp->name, ifp->ifindex, zvni->vni); - return -1; + /* remove the association with vxlan_if */ + memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr)); + zl3vni->vxlan_if = NULL; + } else { + + /* process if-del for l2-vni*/ + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Del L2-VNI %u intf %s(%u)", + vni, ifp->name, ifp->ifindex); + + /* Locate hash entry; it is expected to exist. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to locate VNI hash at del, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return 0; + } + + /* remove from l3-vni list */ + zl3vni = zl3vni_from_vrf(zvni->vrf_id); + if (zl3vni) + listnode_delete(zl3vni->l2vnis, zvni); + + /* Delete VNI from BGP. */ + zvni_send_del_to_client(zvni->vni); + + /* Free up all neighbors and MAC, if any. */ + zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zvni_vtep_del_all(zvni, 0); + + /* Delete the hash entry. */ + if (zvni_del(zvni)) { + zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u", + zvni, ifp->name, ifp->ifindex, zvni->vni); + return -1; + } } return 0; @@ -4148,10 +6052,9 @@ int zebra_vxlan_if_del(struct interface *ifp) */ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4162,79 +6065,129 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) vxl = &zif->l2info.vxl; vni = vxl->vni; - /* Update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_err( - "Failed to find VNI hash on update, IF %s(%u) VNI %u", - ifp->name, ifp->ifindex, vni); - return -1; - } + if (is_vni_l3(vni)) { + zebra_l3vni_t *zl3vni = NULL; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Update VNI %u intf %s(%u) VLAN %u local IP %s " - "master %u chg 0x%x", - vni, ifp->name, ifp->ifindex, - vxl->access_vlan, inet_ntoa(vxl->vtep_ip), - zif->brslave_info.bridge_ifindex, chgflags); - - /* Removed from bridge? Cleanup and return */ - if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { - /* Delete from client, remove all remote VTEPs */ - /* Also, free up all MACs and neighbors. */ - zvni_send_del_to_client(zvni->vni); - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - zvni_vtep_del_all(zvni, 1); - return 0; - } + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + zlog_err( + "Failed to find L3-VNI hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex, chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + return 0; + } - /* Handle other changes. */ - if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - /* Remove all existing local neighbors and MACs for this VNI - * (including from BGP) + /* access-vlan change - process oper down, associate with new + * svi_if and then process oper up again */ - zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - } + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + if (if_is_operative(ifp)) { + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni->svi_if = NULL; + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up( + zl3vni); + } + } - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; + /* if we have a valid new master, process l3-vni oper up */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } + } else { + zebra_vni_t *zvni = NULL; - /* Take further actions needed. Note that if we are here, there is a - * change of interest. - */ - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return 0; + /* Update VNI hash. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zlog_err( + "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } - /* Inform BGP, if there is a change of interest. */ - if (chgflags - & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) - zvni_send_add_to_client(zvni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Update L2-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex, chgflags); + + /* Removed from bridge? Cleanup and return */ + if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { + /* Delete from client, remove all remote VTEPs */ + /* Also, free up all MACs and neighbors. */ + zvni_send_del_to_client(zvni->vni); + zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); + zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zvni_vtep_del_all(zvni, 1); + return 0; + } - /* If there is a valid new master or a VLAN mapping change, read and - * populate local MACs and neighbors. Also, reinstall any remote MACs - * and neighbors for this VNI (based on new VLAN). - */ - if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - zvni_read_mac_neigh(zvni, ifp); - else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { - struct mac_walk_ctx m_wctx; - struct neigh_walk_ctx n_wctx; + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + /* Remove all existing local neigh and MACs for this VNI + * (including from BGP) + */ + zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + } - zvni_read_mac_neigh(zvni, ifp); + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; - memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx); + /* Take further actions needed. + * Note that if we are here, there is a change of interest. + */ + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; - memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, - &n_wctx); + /* Inform BGP, if there is a change of interest. */ + if (chgflags + & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE)) + zvni_send_add_to_client(zvni); + + /* If there is a valid new master or a VLAN mapping change, + * read and populate local MACs and neighbors. + * Also, reinstall any remote MACs and neighbors + * for this VNI (based on new VLAN). + */ + if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) + zvni_read_mac_neigh(zvni, ifp); + else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + zvni_read_mac_neigh(zvni, ifp); + + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + hash_iterate(zvni->mac_table, + zvni_install_mac_hash, + &m_wctx); + + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.zvni = zvni; + hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, + &n_wctx); + } } return 0; @@ -4245,10 +6198,9 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) */ int zebra_vxlan_if_add(struct interface *ifp) { - struct zebra_if *zif; - zebra_vni_t *zvni; - struct zebra_l2info_vxlan *vxl; vni_t vni; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -4259,37 +6211,200 @@ int zebra_vxlan_if_add(struct interface *ifp) vxl = &zif->l2info.vxl; vni = vxl->vni; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Add VNI %u intf %s(%u) VLAN %u local IP %s master %u", - vni, ifp->name, ifp->ifindex, - vxl->access_vlan, inet_ntoa(vxl->vtep_ip), - zif->brslave_info.bridge_ifindex); + if (is_vni_l3(vni)) { - /* Create or update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zvni = zvni_add(vni); - if (!zvni) { + /* process if-add for l3-vni*/ + zebra_l3vni_t *zl3vni = NULL; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u", + vni, ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex); + + /* + * we expect the l3-vni has entry to be present here. + * The only place l3-vni is created in zebra is vrf-vni mapping + * command. This might change when we have the switchd support + * for l3-vxlan interface. + */ + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { zlog_err( - "Failed to add VNI hash, IF %s(%u) VNI %u", + "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); + return 0; + } + + /* associate with vxlan_if */ + zl3vni->local_vtep_ip = vxl->vtep_ip; + zl3vni->vxlan_if = ifp; + + /* Associate with SVI, if any. We can associate with svi-if only + * after association with vxlan_if is complete */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } else { + + /* process if-add for l2-vni */ + zebra_vni_t *zvni = NULL; + zebra_l3vni_t *zl3vni = NULL; + struct interface *vlan_if = NULL; + + /* Create or update VNI hash. */ + zvni = zvni_lookup(vni); + if (!zvni) { + zvni = zvni_add(vni); + if (!zvni) { + zlog_err( + "Failed to add VNI hash, IF %s(%u) VNI %u", + ifp->name, ifp->ifindex, vni); + return -1; + } + } + + zvni->local_vtep_ip = vxl->vtep_ip; + zvni->vxlan_if = ifp; + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zvni->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort(zl3vni->l2vnis, zvni); + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u", + vni, + vlan_if ? vrf_id_to_name(vlan_if->vrf_id) : + "Default", + ifp->name, ifp->ifindex, + vxl->access_vlan, inet_ntoa(vxl->vtep_ip), + zif->brslave_info.bridge_ifindex); + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + + /* Inform BGP */ + zvni_send_add_to_client(zvni); + + /* Read and populate local MACs and neighbors */ + zvni_read_mac_neigh(zvni, ifp); + } + + return 0; +} + +int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, + vni_t vni, + char *err, int err_str_sz, + int add) +{ + zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf_default = NULL; + + zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT); + if (!zvrf_default) + return -1; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("vrf %s vni %u %s", + zvrf_name(zvrf), + vni, + add ? "ADD" : "DEL"); + + if (add) { + + zebra_vxlan_handle_vni_transition(zvrf, vni, add); + + /* check if the vni is already present under zvrf */ + if (zvrf->l3vni) { + snprintf(err, err_str_sz, + "VNI is already configured under the vrf"); + return -1; + } + + /* check if this VNI is already present in the system */ + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + snprintf(err, err_str_sz, + "VNI is already configured as L3-VNI"); + return -1; + } + + /* add the L3-VNI to the global table */ + zl3vni = zl3vni_add(vni, zvrf_id(zvrf)); + if (!zl3vni) { + snprintf(err, err_str_sz, + "Could not add L3-VNI"); + return -1; + } + + /* associate the vrf with vni */ + zvrf->l3vni = vni; + + /* associate with vxlan-intf; + * we need to associate with the vxlan-intf first + */ + zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni); + + /* associate with corresponding SVI interface, we can associate + * with svi-if only after vxlan interface association is + * complete + */ + zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + + /* formulate l2vni list */ + hash_iterate(zvrf_default->vni_table, + zvni_add_to_l3vni_list, zl3vni); + + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + + } else { + zl3vni = zl3vni_lookup(vni); + if (!zl3vni) { + snprintf(err, err_str_sz, "VNI doesn't exist"); return -1; } + + zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* delete and uninstall all rmacs */ + hash_iterate(zl3vni->rmac_table, + zl3vni_del_rmac_hash_entry, + zl3vni); + + /* delete and uninstall all next-hops */ + hash_iterate(zl3vni->nh_table, + zl3vni_del_nh_hash_entry, + zl3vni); + + zvrf->l3vni = 0; + zl3vni_del(zl3vni); + + zebra_vxlan_handle_vni_transition(zvrf, vni, add); } + return 0; +} - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->vxlan_if = ifp; +int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf) +{ + zebra_l3vni_t *zl3vni = NULL; - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + zl3vni = zl3vni_from_vrf(zvrf_id(zvrf)); + if (!zl3vni) return 0; - /* Inform BGP */ - zvni_send_add_to_client(zvni); - - /* Read and populate local MACs and neighbors */ - zvni_read_mac_neigh(zvni, ifp); + zebra_vxlan_process_l3vni_oper_down(zl3vni); + zl3vni_del(zl3vni); + zebra_vxlan_handle_vni_transition(zvrf, zl3vni->vni, 0); return 0; } @@ -4410,8 +6525,9 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, u_short length, int zebra_vxlan_advertise_all_vni(struct zserv *client, u_short length, struct zebra_vrf *zvrf) { - struct stream *s; - int advertise; + struct stream *s = NULL; + int advertise = 0; + struct zebra_ns *zns = NULL; if (zvrf_id(zvrf) != VRF_DEFAULT) { zlog_err("EVPN VNI Adv for non-default VRF %u", @@ -4449,6 +6565,13 @@ int zebra_vxlan_advertise_all_vni(struct zserv *client, * kernel and free entries. */ hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + + /* cleanup all l3vnis */ + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return -1; + + hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL); } stream_failure: @@ -4475,3 +6598,28 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); hash_free(zvrf->vni_table); } + +/* init the l3vni table */ +void zebra_vxlan_ns_init(struct zebra_ns *zns) +{ + zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, + "Zebra VRF L3 VNI table"); +} + +/* free l3vni table */ +void zebra_vxlan_ns_disable(struct zebra_ns *zns) +{ + hash_free(zns->l3vni_table); +} + +/* get the l3vni svi ifindex */ +ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) +{ + zebra_l3vni_t *zl3vni = NULL; + + zl3vni = zl3vni_from_vrf(vrf_id); + if (!zl3vni || !is_l3vni_oper_up(zl3vni)) + return 0; + + return zl3vni->svi_if->ifindex; +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 290d19bcf330..b7def6acf8cb 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -51,6 +51,13 @@ is_evpn_enabled() #define VNI_STR_LEN 32 +extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); +extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf); +extern void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, + struct ipaddr *ip, u_char uj); +extern void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, + struct ethaddr *rmac, + u_char use_json); extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, @@ -84,6 +91,16 @@ extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, u_char use_json); extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, u_char use_json); +extern void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t vni, u_char + use_json); +extern void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, u_char use_json); +extern void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t vni, u_char + use_json); +extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, u_char use_json); +extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, + u_char use_json); +extern void zebra_vxlan_print_l3vnis(struct vty *vty, + u_char use_json); extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, int add); @@ -129,7 +146,20 @@ extern int zebra_vxlan_advertise_gw_macip(struct zserv *client, extern int zebra_vxlan_advertise_all_vni(struct zserv *client, u_short length, struct zebra_vrf *zvrf); +extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, + char *err, + int err_str_sz, int add); extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf); extern void zebra_vxlan_close_tables(struct zebra_vrf *); +extern void zebra_vxlan_ns_init(struct zebra_ns *zns); +extern void zebra_vxlan_ns_disable(struct zebra_ns *zns); +extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *ip, + struct prefix *host_prefix); +extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, + struct ethaddr *rmac, + struct ipaddr *vtep_ip, + struct prefix *host_prefix); #endif /* _ZEBRA_VXLAN_H */ diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c index bbed5ddb05e9..db828c337e23 100644 --- a/zebra/zebra_vxlan_null.c +++ b/zebra/zebra_vxlan_null.c @@ -83,6 +83,30 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf) { } +void zebra_vxlan_print_rmacs_l3vni(struct vty*, vni_t, u_char) +{ +} + +void zebra_vxlan_print_rmacs_all_l3vni(struct vty*, u_char) +{ +} + +void zebra_vxlan_print_nh_l3vni(struct vty*, vni_t, u_char) +{ +} + +void zebra_vxlan_print_nh_all_l3vni(struct vty*, u_char) +{ +} + +void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni) +{ +} + +void zebra_vxlan_print_l3vnis(struct vty *vty) +{ +} + int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { return 0; diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index fa7d0e94578f..ef6f9b99cb76 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -30,12 +30,16 @@ #include "if.h" #include "linklist.h" +#include "zebra_vxlan.h" + +#define ERR_STR_SZ 256 /* definitions */ typedef struct zebra_vni_t_ zebra_vni_t; typedef struct zebra_vtep_t_ zebra_vtep_t; typedef struct zebra_mac_t_ zebra_mac_t; typedef struct zebra_neigh_t_ zebra_neigh_t; +typedef struct zebra_l3vni_t_ zebra_l3vni_t; /* * VTEP info @@ -75,6 +79,9 @@ struct zebra_vni_t_ { /* Local IP */ struct in_addr local_vtep_ip; + /* tenant VRF, if any */ + vrf_id_t vrf_id; + /* List of local or remote MAC */ struct hash *mac_table; @@ -82,6 +89,128 @@ struct zebra_vni_t_ { struct hash *neigh_table; }; +/* L3 VNI hash table */ +struct zebra_l3vni_t_ { + + /* VNI key */ + vni_t vni; + + /* vrf_id */ + vrf_id_t vrf_id; + + /* Local IP */ + struct in_addr local_vtep_ip; + + /* kernel interface for l3vni */ + struct interface *vxlan_if; + + /* SVI interface corresponding to the l3vni */ + struct interface *svi_if; + + /* list of L2 VNIs associated with the L3 VNI */ + struct list *l2vnis; + + /* list of remote router-macs */ + struct hash *rmac_table; + + /* list of remote vtep-ip neigh */ + struct hash *nh_table; +}; + +/* get the vx-intf name for l3vni */ +static inline const char *zl3vni_vxlan_if_name(zebra_l3vni_t *zl3vni) +{ + return zl3vni->vxlan_if ? zl3vni->vxlan_if->name : "None"; +} + +/* get the svi intf name for l3vni */ +static inline const char *zl3vni_svi_if_name(zebra_l3vni_t *zl3vni) +{ + return zl3vni->svi_if ? zl3vni->svi_if->name : "None"; +} + +/* get the vrf name for l3vni */ +static inline const char *zl3vni_vrf_name(zebra_l3vni_t *zl3vni) +{ + return vrf_id_to_name(zl3vni->vrf_id); +} + +/* get the rmac string */ +static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf, + int size) +{ + char *ptr; + + if (!buf) + ptr = (char *)XMALLOC(MTYPE_TMP, + ETHER_ADDR_STRLEN * sizeof(char)); + else { + assert(size >= ETHER_ADDR_STRLEN); + ptr = buf; + } + + if (zl3vni->svi_if) + snprintf(ptr, (ETHER_ADDR_STRLEN), + "%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)zl3vni->svi_if->hw_addr[0], + (uint8_t)zl3vni->svi_if->hw_addr[1], + (uint8_t)zl3vni->svi_if->hw_addr[2], + (uint8_t)zl3vni->svi_if->hw_addr[3], + (uint8_t)zl3vni->svi_if->hw_addr[4], + (uint8_t)zl3vni->svi_if->hw_addr[5]); + else + snprintf(ptr, ETHER_ADDR_STRLEN, "None"); + + return ptr; +} + +/* + * l3-vni is oper up when: + * 0. if EVPN is enabled (advertise-all-vni cfged) + * 1. it is associated to a vxlan-intf + * 2. Associated vxlan-intf is oper up + * 3. it is associated to an SVI + * 4. associated SVI is oper up + */ +static inline int is_l3vni_oper_up(zebra_l3vni_t *zl3vni) +{ + return (is_evpn_enabled() && zl3vni && + (zl3vni->vrf_id != VRF_UNKNOWN) && + zl3vni->vxlan_if && if_is_operative(zl3vni->vxlan_if) && + zl3vni->svi_if && if_is_operative(zl3vni->svi_if)); +} + +static inline const char *zl3vni_state2str(zebra_l3vni_t *zl3vni) +{ + if (!zl3vni) + return NULL; + + if (is_l3vni_oper_up(zl3vni)) + return "Up"; + else + return "Down"; + + return NULL; +} + +static inline vrf_id_t zl3vni_vrf_id(zebra_l3vni_t *zl3vni) +{ + return zl3vni->vrf_id; +} + +static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + if (!zl3vni) + return; + + if (!is_l3vni_oper_up(zl3vni)) + return; + + if (zl3vni->svi_if && if_is_operative(zl3vni->svi_if)) + memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); +} + /* * MAC hash table. * @@ -103,6 +232,7 @@ struct zebra_mac_t_ { #define ZEBRA_MAC_REMOTE 0x02 #define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ #define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ +#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ /* Local or remote info. */ union { @@ -116,6 +246,9 @@ struct zebra_mac_t_ { /* List of neigh associated with this mac */ struct list *neigh_list; + + /* list of hosts pointing to this remote RMAC */ + struct list *host_list; }; /* @@ -141,6 +274,11 @@ struct mac_walk_ctx { struct json_object *json; /* Used for JSON Output */ }; +struct rmac_walk_ctx { + struct vty *vty; + struct json_object *json; +}; + enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; #define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE @@ -175,11 +313,15 @@ struct zebra_neigh_t_ { u_int32_t flags; #define ZEBRA_NEIGH_LOCAL 0x01 #define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ enum zebra_neigh_state state; /* Remote VTEP IP - applicable only for remote neighbors. */ struct in_addr r_vtep_ip; + + /* list of hosts pointing to this remote NH entry */ + struct list *host_list; }; /* @@ -206,4 +348,18 @@ struct neigh_walk_ctx { struct json_object *json; /* Used for JSON Output */ }; +/* context for neigh hash walk - update l3vni and rmac */ +struct neigh_l3info_walk_ctx { + + zebra_vni_t *zvni; + zebra_l3vni_t *zl3vni; + int add; +}; + +struct nh_walk_ctx { + + struct vty *vty; + struct json_object *json; +}; + #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index b6d70084c0c3..858ec7ad2466 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -48,7 +48,6 @@ #include "zebra/router-id.h" #include "zebra/redistribute.h" #include "zebra/debug.h" -#include "zebra/ipforward.h" #include "zebra/zebra_rnh.h" #include "zebra/rt_netlink.h" #include "zebra/interface.h" @@ -150,16 +149,6 @@ int zebra_server_send_message(struct zserv *client) return 0; } -void zserv_create_header(struct stream *s, uint16_t cmd, vrf_id_t vrf_id) -{ - /* length placeholder, caller can update */ - stream_putw(s, ZEBRA_HEADER_SIZE); - stream_putc(s, ZEBRA_HEADER_MARKER); - stream_putc(s, ZSERV_VERSION); - stream_putw(s, vrf_id); - stream_putw(s, cmd); -} - static void zserv_encode_interface(struct stream *s, struct interface *ifp) { /* Interface information. */ @@ -222,7 +211,7 @@ int zsend_interface_add(struct zserv *client, struct interface *ifp) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_INTERFACE_ADD, ifp->vrf_id); + zclient_create_header(s, ZEBRA_INTERFACE_ADD, ifp->vrf_id); zserv_encode_interface(s, ifp); client->ifadd_cnt++; @@ -237,7 +226,7 @@ int zsend_interface_delete(struct zserv *client, struct interface *ifp) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_INTERFACE_DELETE, ifp->vrf_id); + zclient_create_header(s, ZEBRA_INTERFACE_DELETE, ifp->vrf_id); zserv_encode_interface(s, ifp); client->ifdel_cnt++; @@ -251,7 +240,7 @@ int zsend_vrf_add(struct zserv *client, struct zebra_vrf *zvrf) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_VRF_ADD, zvrf_id(zvrf)); + zclient_create_header(s, ZEBRA_VRF_ADD, zvrf_id(zvrf)); zserv_encode_vrf(s, zvrf); client->vrfadd_cnt++; @@ -266,7 +255,7 @@ int zsend_vrf_delete(struct zserv *client, struct zebra_vrf *zvrf) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_VRF_DELETE, zvrf_id(zvrf)); + zclient_create_header(s, ZEBRA_VRF_DELETE, zvrf_id(zvrf)); zserv_encode_vrf(s, zvrf); client->vrfdel_cnt++; @@ -286,7 +275,7 @@ int zsend_interface_link_params(struct zserv *client, struct interface *ifp) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_INTERFACE_LINK_PARAMS, ifp->vrf_id); + zclient_create_header(s, ZEBRA_INTERFACE_LINK_PARAMS, ifp->vrf_id); /* Add Interface Index */ stream_putl(s, ifp->ifindex); @@ -349,7 +338,7 @@ int zsend_interface_address(int cmd, struct zserv *client, s = client->obuf; stream_reset(s); - zserv_create_header(s, cmd, ifp->vrf_id); + zclient_create_header(s, cmd, ifp->vrf_id); stream_putl(s, ifp->ifindex); /* Interface address flag. */ @@ -394,7 +383,7 @@ static int zsend_interface_nbr_address(int cmd, struct zserv *client, s = client->obuf; stream_reset(s); - zserv_create_header(s, cmd, ifp->vrf_id); + zclient_create_header(s, cmd, ifp->vrf_id); stream_putl(s, ifp->ifindex); /* Prefix information. */ @@ -505,11 +494,11 @@ int zsend_interface_vrf_update(struct zserv *client, struct interface *ifp, s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_INTERFACE_VRF_UPDATE, ifp->vrf_id); + zclient_create_header(s, ZEBRA_INTERFACE_VRF_UPDATE, ifp->vrf_id); /* Fill in the ifIndex of the interface and its new VRF (id) */ stream_putl(s, ifp->ifindex); - stream_putw(s, vrf_id); + stream_putl(s, vrf_id); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -582,7 +571,7 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp) s = client->obuf; stream_reset(s); - zserv_create_header(s, cmd, ifp->vrf_id); + zclient_create_header(s, cmd, ifp->vrf_id); zserv_encode_interface(s, ifp); if (cmd == ZEBRA_INTERFACE_UP) @@ -603,6 +592,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, memset(&api, 0, sizeof(api)); api.vrf_id = re->vrf_id; + api.nh_vrf_id = re->nh_vrf_id; api.type = re->type; api.instance = re->instance; api.flags = re->flags; @@ -619,7 +609,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = re->nexthop_active_num; } - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; @@ -958,7 +948,7 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, stream_reset(s); /* Fill in result. */ - zserv_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, zvrf_id(zvrf)); + zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, zvrf_id(zvrf)); stream_put_in_addr(s, &addr); if (re) { @@ -972,7 +962,7 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, * we * are looking up. Therefore, we will just iterate over the top * chain of nexthops. */ - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) num += zsend_write_nexthop(s, nexthop); @@ -1010,7 +1000,7 @@ int zsend_route_notify_owner(u_char proto, u_short instance, s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id); + zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id); stream_put(s, ¬e, sizeof(note)); @@ -1040,7 +1030,7 @@ int zsend_router_id_update(struct zserv *client, struct prefix *p, stream_reset(s); /* Message type. */ - zserv_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id); + zclient_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id); /* Prefix information. */ stream_putc(s, p->family); @@ -1064,7 +1054,7 @@ int zsend_pw_update(struct zserv *client, struct zebra_pw *pw) s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id); + zclient_create_header(s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id); stream_write(s, pw->ifname, IF_NAMESIZE); stream_putl(s, pw->ifindex); stream_putl(s, pw->status); @@ -1133,23 +1123,27 @@ static int zread_route_add(struct zserv *client, u_short length, struct route_entry *re; struct nexthop *nexthop = NULL; int i, ret; + vrf_id_t vrf_id = 0; s = client->ibuf; if (zapi_route_decode(s, &api) < 0) return -1; /* Allocate new route. */ + vrf_id = zvrf_id(zvrf); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = api.type; re->instance = api.instance; re->flags = api.flags; re->uptime = time(NULL); - re->vrf_id = zvrf_id(zvrf); + re->vrf_id = vrf_id; + re->nh_vrf_id = api.nh_vrf_id; re->table = zvrf->table_id; if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; + ifindex_t ifindex = 0; switch (api_nh->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1160,11 +1154,42 @@ static int zread_route_add(struct zserv *client, u_short length, nexthop = route_entry_nexthop_ipv4_add( re, &api_nh->gate.ipv4, NULL); break; - case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: { + + struct ipaddr vtep_ip; + + memset(&vtep_ip, 0, sizeof(struct ipaddr)); + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_ROUTE)) { + ifindex = + get_l3vni_svi_ifindex(vrf_id); + } else { + ifindex = api_nh->ifindex; + } + nexthop = route_entry_nexthop_ipv4_ifindex_add( re, &api_nh->gate.ipv4, NULL, - api_nh->ifindex); + ifindex); + + /* if this an EVPN route entry, + program the nh as neigh + */ + if (CHECK_FLAG(api.flags, + ZEBRA_FLAG_EVPN_ROUTE)) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_EVPN_RVTEP); + vtep_ip.ipa_type = IPADDR_V4; + memcpy(&(vtep_ip.ipaddr_v4), + &(api_nh->gate.ipv4), + sizeof(struct in_addr)); + zebra_vxlan_evpn_vrf_route_add( + vrf_id, + &api.rmac, + &vtep_ip, + &api.prefix); + } break; + } case NEXTHOP_TYPE_IPV6: nexthop = route_entry_nexthop_ipv6_add( re, &api_nh->gate.ipv6); @@ -1183,7 +1208,7 @@ static int zread_route_add(struct zserv *client, u_short length, if (!nexthop) { zlog_warn("%s: Nexthops Specified: %d but we failed to properly create one", __PRETTY_FUNCTION__, api.nexthop_num); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1215,7 +1240,7 @@ static int zread_route_add(struct zserv *client, u_short length, if (afi != AFI_IP6 && CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) { zlog_warn("%s: Received SRC Prefix but afi is not v6", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1266,7 +1291,7 @@ static int zread_route_del(struct zserv *client, u_short length, rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, api.flags, &api.prefix, src_p, NULL, zvrf->table_id, - api.metric, false); + api.metric, false, &api.rmac); /* Stats */ switch (api.prefix.family) { @@ -1339,6 +1364,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, /* VRF ID */ re->vrf_id = zvrf_id(zvrf); + re->nh_vrf_id = zvrf_id(zvrf); /* Nexthop parse. */ if (CHECK_FLAG(message, ZAPI_MESSAGE_NEXTHOP)) { @@ -1380,7 +1406,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, case NEXTHOP_TYPE_IPV6: zlog_warn("%s: Please use ZEBRA_ROUTE_ADD if you want to pass v6 nexthops", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; break; @@ -1390,7 +1416,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, default: zlog_warn("%s: Specified nexthop type: %d does not exist", __PRETTY_FUNCTION__, nexthop_type); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1430,7 +1456,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, return 0; stream_failure: - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1467,7 +1493,7 @@ static int zread_ipv4_delete(struct zserv *client, u_short length, table_id = zvrf->table_id; rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, NULL, NULL, table_id, 0, false); + api.flags, &p, NULL, NULL, table_id, 0, false, NULL); client->v4_route_del_cnt++; stream_failure: @@ -1548,6 +1574,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, /* VRF ID */ re->vrf_id = zvrf_id(zvrf); + re->nh_vrf_id = zvrf_id(zvrf); /* We need to give nh-addr, nh-ifindex with the same next-hop object * to the re to ensure that IPv6 multipathing works; need to coalesce @@ -1594,7 +1621,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, default: zlog_warn("%s: Please use ZEBRA_ROUTE_ADD if you want to pass non v6 nexthops", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1656,7 +1683,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, return 0; stream_failure: - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1783,7 +1810,7 @@ static int zread_ipv6_add(struct zserv *client, u_short length, default: zlog_warn("%s: Please use ZEBRA_ROUTE_ADD if you want to pass non v6 nexthops", __PRETTY_FUNCTION__); - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; } @@ -1833,6 +1860,8 @@ static int zread_ipv6_add(struct zserv *client, u_short length, /* VRF ID */ re->vrf_id = zvrf_id(zvrf); + re->nh_vrf_id = zvrf_id(zvrf); + re->table = zvrf->table_id; ret = rib_add_multipath(AFI_IP6, safi, &p, src_pp, re); @@ -1845,7 +1874,7 @@ static int zread_ipv6_add(struct zserv *client, u_short length, return 0; stream_failure: - nexthops_free(re->nexthop); + nexthops_free(re->ng.nexthop); XFREE(MTYPE_RE, re); return -1; @@ -1885,7 +1914,8 @@ static int zread_ipv6_delete(struct zserv *client, u_short length, src_pp = NULL; rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, src_pp, NULL, client->rtm_table, 0, false); + api.flags, &p, src_pp, NULL, client->rtm_table, 0, false, + NULL); client->v6_route_del_cnt++; @@ -2055,7 +2085,7 @@ static int zsend_label_manager_connect_response(struct zserv *client, s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id); + zclient_create_header(s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id); /* result */ stream_putc(s, result); @@ -2117,7 +2147,7 @@ static int zsend_assign_label_chunk_response(struct zserv *client, s = client->obuf; stream_reset(s); - zserv_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id); + zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id); if (lmc) { /* keep */ @@ -2438,11 +2468,11 @@ static int zread_interface_set_master(struct zserv *client, int ifindex; vrf_id_t vrf_id; - STREAM_GETW(s, vrf_id); + STREAM_GETL(s, vrf_id); STREAM_GETL(s, ifindex); master = if_lookup_by_index(ifindex, vrf_id); - STREAM_GETW(s, vrf_id); + STREAM_GETL(s, vrf_id); STREAM_GETL(s, ifindex); slave = if_lookup_by_index(ifindex, vrf_id); @@ -2680,7 +2710,7 @@ static int zebra_client_read(struct thread *thread) STREAM_GETW(client->ibuf, length); STREAM_GETC(client->ibuf, marker); STREAM_GETC(client->ibuf, version); - STREAM_GETW(client->ibuf, vrf_id); + STREAM_GETL(client->ibuf, vrf_id); STREAM_GETW(client->ibuf, command); if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) { @@ -2980,6 +3010,8 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt); vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt); vty_out(vty, "VNI delete notifications: %d\n", client->vnidel_cnt); + vty_out(vty, "L3-VNI add notifications: %d\n", client->l3vniadd_cnt); + vty_out(vty, "L3-VNI delete notifications: %d\n", client->l3vnidel_cnt); vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt); vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt); @@ -3017,105 +3049,6 @@ struct zserv *zebra_find_client(u_char proto, u_short instance) return NULL; } -#ifdef HAVE_NETLINK -/* Display default rtm_table for all clients. */ -DEFUN (show_table, - show_table_cmd, - "show table", - SHOW_STR - "default routing table to use for all clients\n") -{ - vty_out(vty, "table %d\n", zebrad.rtm_table_default); - return CMD_SUCCESS; -} - -DEFUN (config_table, - config_table_cmd, - "table TABLENO", - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zebrad.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10); - return CMD_SUCCESS; -} - -DEFUN (no_config_table, - no_config_table_cmd, - "no table [TABLENO]", - NO_STR - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zebrad.rtm_table_default = 0; - return CMD_SUCCESS; -} -#endif - -DEFUN (ip_forwarding, - ip_forwarding_cmd, - "ip forwarding", - IP_STR - "Turn on IP forwarding\n") -{ - int ret; - - ret = ipforward(); - if (ret == 0) - ret = ipforward_on(); - - if (ret == 0) { - vty_out(vty, "Can't turn on IP forwarding\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (no_ip_forwarding, - no_ip_forwarding_cmd, - "no ip forwarding", - NO_STR - IP_STR - "Turn off IP forwarding\n") -{ - int ret; - - ret = ipforward(); - if (ret != 0) - ret = ipforward_off(); - - if (ret != 0) { - vty_out(vty, "Can't turn off IP forwarding\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (show_zebra, - show_zebra_cmd, - "show zebra", - SHOW_STR - ZEBRA_STR) -{ - struct vrf *vrf; - - vty_out(vty, - " Route Route Neighbor LSP LSP\n"); - vty_out(vty, - "VRF Installs Removals Updates Installs Removals\n"); - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - struct zebra_vrf *zvrf = vrf->info; - vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 - " %10" PRIu64 " %10" PRIu64 "\n", - vrf->name, zvrf->installs, zvrf->removals, - zvrf->neigh_updates, zvrf->lsp_installs, - zvrf->lsp_removals); - } - - return CMD_SUCCESS; -} - /* This command is for debugging purpose. */ DEFUN (show_zebra_client, show_zebra_client_cmd, @@ -3157,127 +3090,6 @@ DEFUN (show_zebra_client_summary, return CMD_SUCCESS; } -/* Table configuration write function. */ -static int config_write_table(struct vty *vty) -{ - if (zebrad.rtm_table_default) - vty_out(vty, "table %d\n", zebrad.rtm_table_default); - return 0; -} - -/* table node for routing tables. */ -static struct cmd_node table_node = {TABLE_NODE, - "", /* This node has no interface. */ - 1}; - -/* Only display ip forwarding is enabled or not. */ -DEFUN (show_ip_forwarding, - show_ip_forwarding_cmd, - "show ip forwarding", - SHOW_STR - IP_STR - "IP forwarding status\n") -{ - int ret; - - ret = ipforward(); - - if (ret == 0) - vty_out(vty, "IP forwarding is off\n"); - else - vty_out(vty, "IP forwarding is on\n"); - return CMD_SUCCESS; -} - -/* Only display ipv6 forwarding is enabled or not. */ -DEFUN (show_ipv6_forwarding, - show_ipv6_forwarding_cmd, - "show ipv6 forwarding", - SHOW_STR - "IPv6 information\n" - "Forwarding status\n") -{ - int ret; - - ret = ipforward_ipv6(); - - switch (ret) { - case -1: - vty_out(vty, "ipv6 forwarding is unknown\n"); - break; - case 0: - vty_out(vty, "ipv6 forwarding is %s\n", "off"); - break; - case 1: - vty_out(vty, "ipv6 forwarding is %s\n", "on"); - break; - default: - vty_out(vty, "ipv6 forwarding is %s\n", "off"); - break; - } - return CMD_SUCCESS; -} - -DEFUN (ipv6_forwarding, - ipv6_forwarding_cmd, - "ipv6 forwarding", - IPV6_STR - "Turn on IPv6 forwarding\n") -{ - int ret; - - ret = ipforward_ipv6(); - if (ret == 0) - ret = ipforward_ipv6_on(); - - if (ret == 0) { - vty_out(vty, "Can't turn on IPv6 forwarding\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_forwarding, - no_ipv6_forwarding_cmd, - "no ipv6 forwarding", - NO_STR - IPV6_STR - "Turn off IPv6 forwarding\n") -{ - int ret; - - ret = ipforward_ipv6(); - if (ret != 0) - ret = ipforward_ipv6_off(); - - if (ret != 0) { - vty_out(vty, "Can't turn off IPv6 forwarding\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -/* IPForwarding configuration write function. */ -static int config_write_forwarding(struct vty *vty) -{ - /* FIXME: Find better place for that. */ - router_id_write(vty); - - if (!ipforward()) - vty_out(vty, "no ip forwarding\n"); - if (!ipforward_ipv6()) - vty_out(vty, "no ipv6 forwarding\n"); - vty_out(vty, "!\n"); - return 0; -} - -/* table node for routing tables. */ -static struct cmd_node forwarding_node = {FORWARDING_NODE, - "", /* This node has no interface. */ - 1}; - #if defined(HANDLE_ZAPI_FUZZING) void zserv_read_file(char *input) { @@ -3298,34 +3110,12 @@ void zserv_read_file(char *input) } #endif -/* Initialisation of zebra and installation of commands. */ -void zebra_init(void) +void zserv_init(void) { /* Client list init. */ zebrad.client_list = list_new(); zebrad.client_list->del = (void (*)(void *))zebra_client_free; - /* Install configuration write function. */ - install_node(&table_node, config_write_table); - install_node(&forwarding_node, config_write_forwarding); - - install_element(VIEW_NODE, &show_ip_forwarding_cmd); - install_element(CONFIG_NODE, &ip_forwarding_cmd); - install_element(CONFIG_NODE, &no_ip_forwarding_cmd); - install_element(ENABLE_NODE, &show_zebra_cmd); install_element(ENABLE_NODE, &show_zebra_client_cmd); install_element(ENABLE_NODE, &show_zebra_client_summary_cmd); - -#ifdef HAVE_NETLINK - install_element(VIEW_NODE, &show_table_cmd); - install_element(CONFIG_NODE, &config_table_cmd); - install_element(CONFIG_NODE, &no_config_table_cmd); -#endif /* HAVE_NETLINK */ - - install_element(VIEW_NODE, &show_ipv6_forwarding_cmd); - install_element(CONFIG_NODE, &ipv6_forwarding_cmd); - install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd); - - /* Route-map */ - zebra_route_map_init(); } diff --git a/zebra/zserv.h b/zebra/zserv.h index 6077dc105a42..4b3b0041b845 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -110,6 +110,8 @@ struct zserv { u_int32_t bfd_client_reg_cnt; u_int32_t vniadd_cnt; u_int32_t vnidel_cnt; + u_int32_t l3vniadd_cnt; + u_int32_t l3vnidel_cnt; u_int32_t macipadd_cnt; u_int32_t macipdel_cnt; @@ -148,22 +150,8 @@ extern struct zebra_t zebrad; extern unsigned int multipath_num; /* Prototypes. */ -extern void zebra_init(void); -extern void zebra_if_init(void); +extern void zserv_init(void); extern void zebra_zserv_socket_init(char *path); -extern void hostinfo_get(void); -extern void rib_init(void); -extern void interface_list(struct zebra_ns *); -extern void route_read(struct zebra_ns *); -extern void macfdb_read(struct zebra_ns *); -extern void macfdb_read_for_bridge(struct zebra_ns *, struct interface *, - struct interface *); -extern void neigh_read(struct zebra_ns *); -extern void neigh_read_for_vlan(struct zebra_ns *, struct interface *); -extern void kernel_init(struct zebra_ns *); -extern void kernel_terminate(struct zebra_ns *); -extern void zebra_route_map_init(void); -extern void zebra_vty_init(void); extern int zsend_vrf_add(struct zserv *, struct zebra_vrf *); extern int zsend_vrf_delete(struct zserv *, struct zebra_vrf *); @@ -189,10 +177,6 @@ extern int zsend_route_notify_owner(u_char proto, u_short instance, vrf_id_t vrf_id, struct prefix *p, enum zapi_route_notify_owner note); -extern pid_t pid; - -extern void zserv_create_header(struct stream *s, uint16_t cmd, - vrf_id_t vrf_id); extern void zserv_nexthop_num_warn(const char *, const struct prefix *, const unsigned int); extern int zebra_server_send_message(struct zserv *client);