diff options
139 files changed, 3304 insertions, 1411 deletions
diff --git a/COMMUNITY.md b/COMMUNITY.md deleted file mode 100644 index fa00310851..0000000000 --- a/COMMUNITY.md +++ /dev/null @@ -1 +0,0 @@ -Moved to doc/developer/workflow.rst diff --git a/Makefile.am b/Makefile.am index 6cac1a7ba3..4316964303 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,8 +83,6 @@ rc_SCRIPTS = \ endif EXTRA_DIST += \ - REPORTING-BUGS \ - SERVICES \ aclocal.m4 \ update-autotools \ m4/README.txt \ @@ -5,11 +5,11 @@ Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, RIPv2, RIPng, IS-IS, PIM-SM/MSDP, LDP and Babel as well as very early support for EIGRP and NHRP. -See the file REPORTING-BUGS to report bugs. +See doc/user/bugs.rst for information on how to report bugs. -See COMMUNITY.md for information on contributing. +See doc/developer/workflow.rst for information on contributing. -Free RRRouting is free software. See the file COPYING for copying conditions. +See the file COPYING for copying conditions. Public email discussion can be found at https://lists.frrouting.org/listinfo diff --git a/README.NetBSD b/README.NetBSD deleted file mode 100755 index c97e3bcb38..0000000000 --- a/README.NetBSD +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -# This file is helpful for building FRR from cvs on NetBSD, and -# probably on any system using pkgsrc. -# One should have readline installed already (pkgsrc/devel/readline). - -MAKE=make -# FRR is currently documented not to require GNU make, but sometimes -# BSD make fails. Enable this if statement as a workaround. -if false; then - MAKE=gmake - echo "WARNING: using gmake to work around nonportable makefiles" -fi - -# Use /usr/frr to be independent, and /usr/pkg to overwrite pkgsrc. -PREFIX=/usr/pkg - -case $1 in - - build) - # Omitted because it is now default: - # --enable-opaque-lsa - ./bootstrap.sh - LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" CPPFLAGS="-I/usr/pkg/include" \ - ./configure --prefix=${PREFIX} \ - --sysconfdir=/etc/zebra --localstatedir=/var/run/zebra \ - --enable-exampledir=${PREFIX}/share/examples/zebra \ - --enable-pkgsrcrcdir=${PREFIX}/etc/rc.d \ - --enable-vtysh - ${MAKE} - ;; - - install) - ${MAKE} install - ;; - - clean) - ${MAKE} clean - ;; - - *) - echo "Usage: README.NetBSD (build|install|clean)" - exit 1 - ;; - -esac diff --git a/REPORTING-BUGS b/REPORTING-BUGS deleted file mode 100644 index 339ebc24d5..0000000000 --- a/REPORTING-BUGS +++ /dev/null @@ -1,35 +0,0 @@ -This file describes the procedure for reporting FRRouting bugs. You are not -obliged to follow this format, but it would be great help for FRRouting developers -if you report a bug as described below. - -Bugs submitted with woefully incomplete information may be summarily -closed. Submitters of bugs against old versions may be asked to -retest against the latest release. Submitters may be asked for -additional information. Bugs may be closed after 30 days of -non-response to requests to reconfirm or supply additional -information. - -Report bugs on Github Issue Tracker at - https://github.com/frrouting/frr/issues - -Please supply the following information: -1. Your FRRouting version or if it is from git then the commit reference. - Please try to report bugs against git master or the latest release. -2. FRR daemons you run e.g. bgpd or ripd and full name of your OS. Any - specific options you compiled FRR with. -3. Problem description. Copy and paste relative commands and their output to - describe your network setup e.g. "zebra>show ip route". - Please, also give your simple network layout and output of relative OS - commands (e.g., ifconfig (BSD) or ip (Linux)). -4. All FRR configuration files you use. If you don't want to publish your - network numbers change 2 middle bytes in IPv4 address to be XXX (e.g. - 192.XXX.XXX.32/24). Similar could be done with IPv6. -5. If any FRR daemon core dumped, please, supply stack trace using the - following commands: host> gdb exec_file core_file , (gdb) bt . -6. Run all FRR daemons with full debugging on (see documentation on - debugging) and send _only_ part of logs which are relative to your problem. -7. If the problem is difficult to reproduce please send a shell script to - reproduce it. -8. Patches, workarounds, fixes are always welcome. - -Thank You. diff --git a/SERVICES b/SERVICES deleted file mode 100644 index f7f92a736b..0000000000 --- a/SERVICES +++ /dev/null @@ -1,24 +0,0 @@ -# As long as this software is in alpha testing it is not yet included -# in /etc/services files. This means that you may need to add the following -# lines into your /etc/services file on your hosts. -# -# --- Please add this to your /etc/services --- - -# -# GNU Zebra services -# - -zebrasrv 2600/tcp -zebra 2601/tcp -ripd 2602/tcp -ripng 2603/tcp -ospfd 2604/tcp -bgpd 2605/tcp -ospf6d 2606/tcp -ospfapi 2607/tcp -isisd 2608/tcp -babeld 2609/tcp -nhrpd 2610/tcp -pimd 2611/tcp -ldpd 2612/tcp -eigrpd 2613/tcp diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b6e9ee3cb2..276a7054e3 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2021,36 +2021,32 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ return 0; } -/* Prefix SID attribute - * draft-ietf-idr-bgp-prefix-sid-05 +/* + * Read an individual SID value returning how much data we have read + * Returns 0 if there was an error that needs to be passed up the stack */ -static bgp_attr_parse_ret_t -bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, - struct bgp_nlri *mp_update) +static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, + int32_t length, + struct bgp_attr_parser_args *args, + struct bgp_nlri *mp_update) { struct peer *const peer = args->peer; struct attr *const attr = args->attr; - int type; - int length; uint32_t label_index; struct in6_addr ipv6_sid; uint32_t srgb_base; uint32_t srgb_range; int srgb_count; - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); - - type = stream_getc(peer->curr); - length = stream_getw(peer->curr); - if (type == BGP_PREFIX_SID_LABEL_INDEX) { if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { zlog_err( - "Prefix SID label index length is %d instead of %d", - length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH); - return bgp_attr_malformed( - args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - args->total); + "Prefix SID label index length is %d instead of %d", + length, + BGP_PREFIX_SID_LABEL_INDEX_LENGTH); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); } /* Ignore flags and reserved */ @@ -2060,9 +2056,8 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, /* Fetch the label index and see if it is valid. */ label_index = stream_getl(peer->curr); if (label_index == BGP_INVALID_LABEL_INDEX) - return bgp_attr_malformed( - args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, - args->total); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); /* Store label index; subsequently, we'll check on * address-family */ @@ -2083,9 +2078,9 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, if (length != BGP_PREFIX_SID_IPV6_LENGTH) { zlog_err("Prefix SID IPv6 length is %d instead of %d", length, BGP_PREFIX_SID_IPV6_LENGTH); - return bgp_attr_malformed( - args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - args->total); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); } /* Ignore reserved */ @@ -2122,6 +2117,47 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, return BGP_ATTR_PARSE_PROCEED; } +/* Prefix SID attribute + * draft-ietf-idr-bgp-prefix-sid-05 + */ +bgp_attr_parse_ret_t +bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, + struct bgp_nlri *mp_update) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + bgp_attr_parse_ret_t ret; + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); + + while (tlength) { + int32_t type, length; + + type = stream_getc(peer->curr); + length = stream_getw(peer->curr); + + ret = bgp_attr_psid_sub(type, length, args, mp_update); + + if (ret != BGP_ATTR_PARSE_PROCEED) + return ret; + /* + * Subtract length + the T and the L + * since length is the Vector portion + */ + tlength -= length + 3; + + if (tlength < 0) { + zlog_err("Prefix SID internal length %d causes us to read beyond the total Prefix SID length", + length); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + } + + return BGP_ATTR_PARSE_PROCEED; +} + /* PMSI tunnel attribute (RFC 6514) * Basic validation checks done here. */ @@ -2498,7 +2534,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, startp); break; case BGP_ATTR_PREFIX_SID: - ret = bgp_attr_prefix_sid(&attr_args, mp_update); + ret = bgp_attr_prefix_sid(length, + &attr_args, mp_update); break; case BGP_ATTR_PMSI_TUNNEL: ret = bgp_attr_pmsi_tunnel(&attr_args); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index d59eae5f14..f17c2a68e4 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -308,6 +308,9 @@ extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, struct bgp_nlri *); extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args, struct bgp_nlri *); +extern bgp_attr_parse_ret_t +bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, + struct bgp_nlri *mp_update); extern struct bgp_attr_encap_subtlv * encap_tlv_dup(struct bgp_attr_encap_subtlv *orig); diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index d2a61b93fe..14ff01ada5 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -227,16 +227,18 @@ extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag, dst->family = AF_EVPN; p_evpn_p->route_type = evpn_type; if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) { - p_evpn_p->eth_tag = eth_tag; - p_evpn_p->ip_prefix_length = p2.prefixlen; + p_evpn_p->prefix_addr.eth_tag = eth_tag; + p_evpn_p->prefix_addr.ip_prefix_length = p2.prefixlen; if (src->family == AF_INET) { - SET_IPADDR_V4(&p_evpn_p->ip); - memcpy(&p_evpn_p->ip.ipaddr_v4, &src->u.prefix4, + SET_IPADDR_V4(&p_evpn_p->prefix_addr.ip); + memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v4, + &src->u.prefix4, sizeof(struct in_addr)); dst->prefixlen = (uint8_t)PREFIX_LEN_ROUTE_TYPE_5_IPV4; } else { - SET_IPADDR_V6(&p_evpn_p->ip); - memcpy(&p_evpn_p->ip.ipaddr_v6, &src->u.prefix6, + SET_IPADDR_V6(&p_evpn_p->prefix_addr.ip); + memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v6, + &src->u.prefix6, sizeof(struct in6_addr)); dst->prefixlen = (uint8_t)PREFIX_LEN_ROUTE_TYPE_5_IPV6; } diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index d1bc7f6e53..7cf1477549 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -512,7 +512,7 @@ static int lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) if (com == NULL || com->size == 0) str = ""; else - str = lcommunity_str(com); + str = lcommunity_str(com, false); /* Regular expression match. */ if (regexec(reg, str, 0, NULL, 0) == 0) @@ -986,13 +986,8 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, entry->any = (str ? 0 : 1); entry->u.lcom = lcom; entry->reg = regex; - if (lcom) - entry->config = lcommunity_lcom2str( - lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST); - else if (regex) - entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str); - else - entry->config = NULL; + entry->config = + (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL); /* Do not put duplicated community entry. */ if (community_list_dup_check(list, entry)) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 85b9ffd8ca..99fe80f055 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -52,6 +52,11 @@ struct ecommunity *ecommunity_new(void) sizeof(struct ecommunity)); } +void ecommunity_strfree(char **s) +{ + XFREE(MTYPE_ECOMMUNITY_STR, *s); +} + /* Allocate ecommunities. */ void ecommunity_free(struct ecommunity **ecom) { diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 88bdb5e2ae..2f59308d65 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -156,6 +156,7 @@ extern void ecommunity_unintern(struct ecommunity **); extern unsigned int ecommunity_hash_make(void *); extern struct ecommunity *ecommunity_str2com(const char *, int, int); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); +extern void ecommunity_strfree(char **s); extern int ecommunity_match(const struct ecommunity *, const struct ecommunity *); extern char *ecommunity_str(struct ecommunity *); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 49808e7cdd..8394c3a7b7 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -327,6 +327,12 @@ static int evpn_route_target_cmp(struct ecommunity *ecom1, return strcmp(ecom1->str, ecom2->str); } +static void evpn_xxport_delete_ecomm(void *val) +{ + struct ecommunity *ecomm = val; + ecommunity_free(&ecomm); +} + /* * Mask off global-admin field of specified extended community (RT), * just retain the local-admin field. @@ -452,6 +458,8 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) struct ecommunity_val eval; struct ecommunity *ecomadd; + if (bgp->advertise_autort_rfc8365) + vni |= EVPN_AUTORT_VXLAN; encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecomadd = ecommunity_new(); @@ -510,15 +518,15 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL, bgp->vrf_id); stream_putl(s, vpn->vni); - stream_put(s, &p->prefix.mac.octet, ETH_ALEN); /* Mac Addr */ + stream_put(s, &p->prefix.macip_addr.mac.octet, ETH_ALEN); /* Mac Addr */ /* IP address length and IP address, if any. */ - if (IS_EVPN_PREFIX_IPADDR_NONE(p)) + if (is_evpn_prefix_ipaddr_none(p)) stream_putl(s, 0); else { - ipa_len = IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BYTELEN + ipa_len = is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; stream_putl(s, ipa_len); - stream_put(s, &p->prefix.ip.ip.addr, ipa_len); + stream_put(s, &p->prefix.macip_addr.ip.ip.addr, ipa_len); } stream_put_in_addr(s, &remote_vtep_ip); @@ -532,8 +540,10 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, zlog_debug( "Tx %s MACIP, VNI %u MAC %s IP %s (flags: 0x%x) remote VTEP %s", add ? "ADD" : "DEL", vpn->vni, - prefix_mac2str(&p->prefix.mac, buf1, sizeof(buf1)), - ipaddr2str(&p->prefix.ip, buf3, sizeof(buf3)), flags, + prefix_mac2str(&p->prefix.macip_addr.mac, + buf1, sizeof(buf1)), + ipaddr2str(&p->prefix.macip_addr.ip, + buf3, sizeof(buf3)), flags, inet_ntop(AF_INET, &remote_vtep_ip, buf2, sizeof(buf2))); @@ -563,9 +573,9 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, s, add ? ZEBRA_REMOTE_VTEP_ADD : ZEBRA_REMOTE_VTEP_DEL, bgp->vrf_id); stream_putl(s, vpn->vni); - if (IS_EVPN_PREFIX_IPADDR_V4(p)) - stream_put_in_addr(s, &p->prefix.ip.ipaddr_v4); - else if (IS_EVPN_PREFIX_IPADDR_V6(p)) { + if (is_evpn_prefix_ipaddr_v4(p)) + stream_put_in_addr(s, &p->prefix.imet_addr.ip.ipaddr_v4); + else if (is_evpn_prefix_ipaddr_v6(p)) { zlog_err( "Bad remote IP when trying to %s remote VTEP for VNI %u", add ? "ADD" : "DEL", vpn->vni); @@ -577,7 +587,7 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, if (bgp_debug_zebra(NULL)) zlog_debug("Tx %s Remote VTEP, VNI %u remote VTEP %s", add ? "ADD" : "DEL", vpn->vni, - inet_ntoa(p->prefix.ip.ipaddr_v4)); + inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4)); return zclient_send_message(zclient); } @@ -1293,8 +1303,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * these routes. */ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && - (IS_EVPN_PREFIX_IPADDR_V4(p) || - !IN6_IS_ADDR_LINKLOCAL(&p->prefix.ip.ipaddr_v6)) && + (is_evpn_prefix_ipaddr_v4(p) || + !IN6_IS_ADDR_LINKLOCAL(&p->prefix.macip_addr.ip.ipaddr_v6)) && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) add_l3_ecomm = 1; @@ -1539,8 +1549,8 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - if (IS_EVPN_PREFIX_IPADDR_V6(evp) && - IN6_IS_ADDR_LINKLOCAL(&evp->prefix.ip.ipaddr_v6)) + if (is_evpn_prefix_ipaddr_v6(evp) && + IN6_IS_ADDR_LINKLOCAL(&evp->prefix.macip_addr.ip.ipaddr_v6)) update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr_ip6_ll, 0, 1, &ri, 0); else { @@ -1792,10 +1802,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, char buf1[PREFIX_STRLEN]; memset(pp, 0, sizeof(struct prefix)); - if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - ip_prefix_from_type2_prefix(evp, pp); - else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) - ip_prefix_from_type5_prefix(evp, pp); + ip_prefix_from_evpn_prefix(evp, pp); if (bgp_debug_zebra(NULL)) { zlog_debug( @@ -1807,11 +1814,11 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Create (or fetch) route within the VRF. */ /* NOTE: There is no RD here. */ - if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + if (is_evpn_prefix_ipaddr_v4(evp)) { afi = AFI_IP; safi = SAFI_UNICAST; rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); - } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + } else if (is_evpn_prefix_ipaddr_v6(evp)) { afi = AFI_IP6; safi = SAFI_UNICAST; rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); @@ -1820,12 +1827,15 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* EVPN routes currently only support a IPv4 next hop which corresponds * to the remote VTEP. When importing into a VRF, if it is IPv6 host - * route, we have to convert the next hop to an IPv4-mapped address - * for the rest of the code to flow through. + * or prefix route, we have to convert the next hop to an IPv4-mapped + * address for the rest of the code to flow through. In the case of IPv4, + * make sure to set the flag for next hop attribute. */ bgp_attr_dup(&attr, parent_ri->attr); if (afi == AFI_IP6) evpn_convert_nexthop_to_ipv6(&attr); + else + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); /* Check if route entry is already present. */ for (ri = rn->info; ri; ri = ri->next) @@ -1842,7 +1852,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, parent_ri->peer, attr_new, rn); SET_FLAG(ri->flags, BGP_INFO_VALID); bgp_info_extra_get(ri); - ri->extra->parent = parent_ri; + ri->extra->parent = bgp_info_lock(parent_ri); if (parent_ri->extra) { memcpy(&ri->extra->label, &parent_ri->extra->label, sizeof(ri->extra->label)); @@ -1914,7 +1924,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, parent_ri->peer, attr_new, rn); SET_FLAG(ri->flags, BGP_INFO_VALID); bgp_info_extra_get(ri); - ri->extra->parent = parent_ri; + ri->extra->parent = bgp_info_lock(parent_ri); if (parent_ri->extra) { memcpy(&ri->extra->label, &parent_ri->extra->label, sizeof(ri->extra->label)); @@ -1970,10 +1980,7 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, char buf1[PREFIX_STRLEN]; memset(pp, 0, sizeof(struct prefix)); - if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - ip_prefix_from_type2_prefix(evp, pp); - else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) - ip_prefix_from_type5_prefix(evp, pp); + ip_prefix_from_evpn_prefix(evp, pp); if (bgp_debug_zebra(NULL)) { zlog_debug( @@ -1985,7 +1992,7 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Locate route within the VRF. */ /* NOTE: There is no RD here. */ - if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + if (is_evpn_prefix_ipaddr_v4(evp)) { afi = AFI_IP; safi = SAFI_UNICAST; rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); @@ -2232,8 +2239,8 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) continue; /* if not a mac+ip route skip this route */ - if (!(IS_EVPN_PREFIX_IPADDR_V4(evp) - || IS_EVPN_PREFIX_IPADDR_V6(evp))) + if (!(is_evpn_prefix_ipaddr_v4(evp) + || is_evpn_prefix_ipaddr_v6(evp))) continue; for (ri = rn->info; ri; ri = ri->next) { @@ -2423,8 +2430,8 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, /* if it is type-2 route and not a mac+ip route skip this route */ if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - && !(IS_EVPN_PREFIX_IPADDR_V4(evp) - || IS_EVPN_PREFIX_IPADDR_V6(evp))) + && !(is_evpn_prefix_ipaddr_v4(evp) + || is_evpn_prefix_ipaddr_v6(evp))) return 0; for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) { @@ -2850,7 +2857,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, /* Copy Ethernet Tag */ memcpy(ð_tag, pfx, 4); - p.prefix.eth_tag = ntohl(eth_tag); + p.prefix.macip_addr.eth_tag = ntohl(eth_tag); pfx += 4; /* Get the MAC Addr len */ @@ -2858,7 +2865,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, /* Get the MAC Addr */ if (macaddr_len == (ETH_ALEN * 8)) { - memcpy(&p.prefix.mac.octet, pfx, ETH_ALEN); + memcpy(&p.prefix.macip_addr.mac.octet, pfx, ETH_ALEN); pfx += ETH_ALEN; } else { zlog_err( @@ -2880,10 +2887,10 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, if (ipaddr_len) { ipaddr_len /= 8; /* Convert to bytes. */ - p.prefix.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN) + p.prefix.macip_addr.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; - memcpy(&p.prefix.ip.ip.addr, pfx, ipaddr_len); + memcpy(&p.prefix.macip_addr.ip.ip.addr, pfx, ipaddr_len); } pfx += ipaddr_len; @@ -2965,14 +2972,14 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, /* Copy Ethernet Tag */ memcpy(ð_tag, pfx, 4); - p.prefix.eth_tag = ntohl(eth_tag); + p.prefix.imet_addr.eth_tag = ntohl(eth_tag); pfx += 4; /* Get the IP. */ ipaddr_len = *pfx++; if (ipaddr_len == IPV4_MAX_BITLEN) { - p.prefix.ip.ipa_type = IPADDR_V4; - memcpy(&p.prefix.ip.ip.addr, pfx, IPV4_MAX_BYTELEN); + p.prefix.imet_addr.ip.ipa_type = IPADDR_V4; + memcpy(&p.prefix.imet_addr.ip.ip.addr, pfx, IPV4_MAX_BYTELEN); } else { zlog_err( "%u:%s - Rx EVPN Type-3 NLRI with unsupported IP address length %d", @@ -3039,7 +3046,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, /* Fetch Ethernet Tag. */ memcpy(ð_tag, pfx, 4); - p.prefix.eth_tag = ntohl(eth_tag); + p.prefix.prefix_addr.eth_tag = ntohl(eth_tag); pfx += 4; /* Fetch IP prefix length. */ @@ -3050,21 +3057,21 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, peer->bgp->vrf_id, peer->host, ippfx_len); return -1; } - p.prefix.ip_prefix_length = ippfx_len; + p.prefix.prefix_addr.ip_prefix_length = ippfx_len; /* Determine IPv4 or IPv6 prefix */ /* Since the address and GW are from the same family, this just becomes * a simple check on the total size. */ if (psize == 34) { - SET_IPADDR_V4(&p.prefix.ip); - memcpy(&p.prefix.ip.ipaddr_v4, pfx, 4); + SET_IPADDR_V4(&p.prefix.prefix_addr.ip); + memcpy(&p.prefix.prefix_addr.ip.ipaddr_v4, pfx, 4); pfx += 4; memcpy(&evpn.gw_ip.ipv4, pfx, 4); pfx += 4; } else { - SET_IPADDR_V6(&p.prefix.ip); - memcpy(&p.prefix.ip.ipaddr_v6, pfx, 16); + SET_IPADDR_V6(&p.prefix.prefix_addr.ip); + memcpy(&p.prefix.prefix_addr.ip.ipaddr_v6, pfx, 16); pfx += 16; memcpy(&evpn.gw_ip.ipv6, pfx, 16); pfx += 16; @@ -3109,7 +3116,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, /* len denites the total len of IP and GW-IP in the route IP and GW-IP have to be both ipv4 or ipv6 */ - if (IS_IPADDR_V4(&p_evpn_p->ip)) + if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip)) len = 8; /* IP and GWIP are both ipv4 */ else len = 32; /* IP and GWIP are both ipv6 */ @@ -3120,20 +3127,20 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, stream_put(s, &(attr->evpn_overlay.eth_s_id), 10); else stream_put(s, &temp, 10); - stream_putl(s, p_evpn_p->eth_tag); - stream_putc(s, p_evpn_p->ip_prefix_length); - if (IS_IPADDR_V4(&p_evpn_p->ip)) - stream_put_ipv4(s, p_evpn_p->ip.ipaddr_v4.s_addr); + stream_putl(s, p_evpn_p->prefix_addr.eth_tag); + stream_putc(s, p_evpn_p->prefix_addr.ip_prefix_length); + if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip)) + stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr); else - stream_put(s, &p_evpn_p->ip.ipaddr_v6, 16); + stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16); if (attr) { - if (IS_IPADDR_V4(&p_evpn_p->ip)) + if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip)) stream_put_ipv4(s, attr->evpn_overlay.gw_ip.ipv4.s_addr); else stream_put(s, &(attr->evpn_overlay.gw_ip.ipv6), 16); } else { - if (IS_IPADDR_V4(&p_evpn_p->ip)) + if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip)) stream_put_ipv4(s, 0); else stream_put(s, &temp, 16); @@ -3234,6 +3241,35 @@ static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) } /* + * Handle autort change for a given VNI. + */ +static void update_autort_vni(struct hash_backet *backet, struct bgp *bgp) +{ + struct bgpevpn *vpn = backet->data; + + if (!vpn) { + zlog_warn("%s: VNI hash entry for VNI not found", __PRETTY_FUNCTION__); + return; + } + + if (!is_import_rt_configured(vpn)) { + if (is_vni_live(vpn)) + bgp_evpn_uninstall_routes(bgp, vpn); + bgp_evpn_unmap_vni_from_its_rts(bgp, vpn); + list_delete_all_node(vpn->import_rtl); + bgp_evpn_derive_auto_rt_import(bgp, vpn); + if (is_vni_live(vpn)) + bgp_evpn_install_routes(bgp, vpn); + } + if (!is_export_rt_configured(vpn)) { + list_delete_all_node(vpn->export_rtl); + bgp_evpn_derive_auto_rt_export(bgp, vpn); + if (is_vni_live(vpn)) + bgp_evpn_handle_export_rt_change(bgp, vpn); + } +} + +/* * Public functions. */ @@ -3344,6 +3380,8 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) struct ecommunity *ecom, *ecom_auto; struct ecommunity_val eval; + if (bgp->advertise_autort_rfc8365) + vni |= EVPN_AUTORT_VXLAN; encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecom_auto = ecommunity_new(); @@ -3504,6 +3542,17 @@ void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw) } /* + * Handle change to auto-RT algorithm - update and advertise local routes. + */ +void bgp_evpn_handle_autort_change(struct bgp *bgp) +{ + hash_iterate(bgp->vnihash, + (void (*)(struct hash_backet *, + void*))update_autort_vni, + bgp); +} + +/* * Handle change to export RT - update and advertise local routes. */ int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn) @@ -3583,44 +3632,49 @@ void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json) if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) { json_object_int_add(json, "routeType", p->prefix.route_type); - json_object_int_add(json, "ethTag", p->prefix.eth_tag); + json_object_int_add(json, "ethTag", + p->prefix.imet_addr.eth_tag); json_object_int_add(json, "ipLen", - IS_EVPN_PREFIX_IPADDR_V4(p) + is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN); json_object_string_add(json, "ip", - inet_ntoa(p->prefix.ip.ipaddr_v4)); + inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4)); } else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { - if (IS_EVPN_PREFIX_IPADDR_NONE(p)) { + if (is_evpn_prefix_ipaddr_none(p)) { json_object_int_add(json, "routeType", p->prefix.route_type); - json_object_int_add(json, "ethTag", p->prefix.eth_tag); + json_object_int_add(json, "ethTag", + p->prefix.macip_addr.eth_tag); json_object_int_add(json, "macLen", 8 * ETH_ALEN); json_object_string_add(json, "mac", - prefix_mac2str(&p->prefix.mac, + prefix_mac2str(&p->prefix.macip_addr.mac, buf1, sizeof(buf1))); } else { uint8_t family; - family = IS_EVPN_PREFIX_IPADDR_V4(p) ? AF_INET + family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6; json_object_int_add(json, "routeType", p->prefix.route_type); - json_object_int_add(json, "ethTag", p->prefix.eth_tag); + json_object_int_add(json, "ethTag", + p->prefix.macip_addr.eth_tag); json_object_int_add(json, "macLen", 8 * ETH_ALEN); json_object_string_add(json, "mac", - prefix_mac2str(&p->prefix.mac, + prefix_mac2str(&p->prefix.macip_addr.mac, buf1, sizeof(buf1))); json_object_int_add(json, "ipLen", - IS_EVPN_PREFIX_IPADDR_V4(p) + is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN); json_object_string_add( json, "ip", - inet_ntop(family, &p->prefix.ip.ip.addr, buf2, + inet_ntop(family, + &p->prefix.macip_addr.ip.ip.addr, + buf2, PREFIX2STR_BUFFER)); } } else { @@ -3639,42 +3693,44 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) { snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type, - p->prefix.eth_tag, - IS_EVPN_PREFIX_IPADDR_V4(p) ? IPV4_MAX_BITLEN + p->prefix.imet_addr.eth_tag, + is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, - inet_ntoa(p->prefix.ip.ipaddr_v4)); + inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4)); } else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { - if (IS_EVPN_PREFIX_IPADDR_NONE(p)) + if (is_evpn_prefix_ipaddr_none(p)) snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type, - p->prefix.eth_tag, + p->prefix.macip_addr.eth_tag, 8 * ETH_ALEN, - prefix_mac2str(&p->prefix.mac, buf1, + prefix_mac2str(&p->prefix.macip_addr.mac, buf1, sizeof(buf1))); else { uint8_t family; - family = IS_EVPN_PREFIX_IPADDR_V4(p) ? AF_INET + family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6; snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]:[%d]:[%s]", p->prefix.route_type, - p->prefix.eth_tag, + p->prefix.macip_addr.eth_tag, 8 * ETH_ALEN, - prefix_mac2str(&p->prefix.mac, buf1, + prefix_mac2str(&p->prefix.macip_addr.mac, buf1, sizeof(buf1)), family == AF_INET ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, - inet_ntop(family, &p->prefix.ip.ip.addr, buf2, + inet_ntop(family, + &p->prefix.macip_addr.ip.ip.addr, + buf2, PREFIX2STR_BUFFER)); } } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { snprintf(buf, len, "[%d]:[%d]:[%d]:[%s]", p->prefix.route_type, - p->prefix.eth_tag, - p->prefix.ip_prefix_length, - IS_EVPN_PREFIX_IPADDR_V4(p) - ? inet_ntoa(p->prefix.ip.ipaddr_v4) - : inet6_ntoa(p->prefix.ip.ipaddr_v6)); + p->prefix.prefix_addr.eth_tag, + p->prefix.prefix_addr.ip_prefix_length, + is_evpn_prefix_ipaddr_v4(p) + ? inet_ntoa(p->prefix.prefix_addr.ip.ipaddr_v4) + : inet6_ntoa(p->prefix.prefix_addr.ip.ipaddr_v6)); } else { /* For EVPN route types not supported yet. */ snprintf(buf, len, "(unsupported route type %d)", @@ -3703,9 +3759,9 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, switch (evp->prefix.route_type) { case BGP_EVPN_MAC_IP_ROUTE: - if (IS_EVPN_PREFIX_IPADDR_V4(evp)) + if (is_evpn_prefix_ipaddr_v4(evp)) ipa_len = IPV4_MAX_BYTELEN; - else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) + else if (is_evpn_prefix_ipaddr_v6(evp)) ipa_len = IPV6_MAX_BYTELEN; /* RD, ESI, EthTag, MAC+len, IP len, [IP], 1 VNI */ len = 8 + 10 + 4 + 1 + 6 + 1 + ipa_len + 3; @@ -3717,12 +3773,13 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, stream_put(s, &attr->evpn_overlay.eth_s_id, ESI_LEN); else stream_put(s, 0, 10); - stream_putl(s, evp->prefix.eth_tag); /* Ethernet Tag ID */ + stream_putl(s, evp->prefix.macip_addr.eth_tag); /* Ethernet Tag ID */ stream_putc(s, 8 * ETH_ALEN); /* Mac Addr Len - bits */ - stream_put(s, evp->prefix.mac.octet, 6); /* Mac Addr */ - stream_putc(s, 8 * ipa_len); /* IP address Length */ - if (ipa_len) /* IP */ - stream_put(s, &evp->prefix.ip.ip.addr, ipa_len); + stream_put(s, evp->prefix.macip_addr.mac.octet, 6); /* Mac Addr */ + stream_putc(s, 8 * ipa_len); /* IP address Length */ + if (ipa_len) /* IP */ + stream_put(s, &evp->prefix.macip_addr.ip.ip.addr, + ipa_len); /* 1st label is the L2 VNI */ stream_put(s, label, BGP_LABEL_BYTES); /* Include 2nd label (L3 VNI) if advertising MAC+IP */ @@ -3733,10 +3790,10 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, case BGP_EVPN_IMET_ROUTE: stream_putc(s, 17); // TODO: length - assumes IPv4 address stream_put(s, prd->val, 8); /* RD */ - stream_putl(s, evp->prefix.eth_tag); /* Ethernet Tag ID */ + stream_putl(s, evp->prefix.imet_addr.eth_tag); /* Ethernet Tag ID */ stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */ /* Originating Router's IP Addr */ - stream_put_in_addr(s, &evp->prefix.ip.ipaddr_v4); + stream_put_in_addr(s, &evp->prefix.imet_addr.ip.ipaddr_v4); break; case BGP_EVPN_IP_PREFIX_ROUTE: @@ -4043,8 +4100,10 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; + vpn->import_rtl->del = evpn_xxport_delete_ecomm; vpn->export_rtl = list_new(); vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; + vpn->export_rtl->del = evpn_xxport_delete_ecomm; bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); @@ -4421,8 +4480,10 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) /* Locate VNI hash */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - zlog_warn("%u: VNI hash entry for VNI %u not found at DEL", - bgp->vrf_id, vni); + if (bgp_debug_zebra(NULL)) + zlog_warn( + "%u: VNI hash entry for VNI %u not found at DEL", + bgp->vrf_id, vni); return 0; } @@ -4592,10 +4653,11 @@ void bgp_evpn_init(struct bgp *bgp) bgp->vrf_import_rtl = list_new(); bgp->vrf_import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - + bgp->vrf_import_rtl->del = evpn_xxport_delete_ecomm; bgp->vrf_export_rtl = list_new(); bgp->vrf_export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; + bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp; } diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index dd8d6e2e8e..8e954159c0 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -25,6 +25,7 @@ #include "bgpd.h" #define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */ +#define EVPN_AUTORT_VXLAN 0x10000000 static inline int is_evpn_enabled(void) { diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 1eecb9ecf7..39a08e7036 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -272,15 +272,15 @@ static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp, struct prefix *ip) { memset(ip, 0, sizeof(struct prefix)); - if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + if (is_evpn_prefix_ipaddr_v4(evp)) { ip->family = AF_INET; - ip->prefixlen = evp->prefix.ip_prefix_length; - memcpy(&(ip->u.prefix4), &(evp->prefix.ip.ip), + ip->prefixlen = evp->prefix.prefix_addr.ip_prefix_length; + memcpy(&(ip->u.prefix4), &(evp->prefix.prefix_addr.ip.ip), IPV4_MAX_BYTELEN); - } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + } else if (is_evpn_prefix_ipaddr_v6(evp)) { ip->family = AF_INET6; - ip->prefixlen = evp->prefix.ip_prefix_length; - memcpy(&(ip->u.prefix6), &(evp->prefix.ip.ip), + ip->prefixlen = evp->prefix.prefix_addr.ip_prefix_length; + memcpy(&(ip->u.prefix6), &(evp->prefix.prefix_addr.ip.ip), IPV6_MAX_BYTELEN); } } @@ -290,26 +290,36 @@ static inline int is_evpn_prefix_default(struct prefix *evp) if (evp->family != AF_EVPN) return 0; - return ((evp->u.prefix_evpn.ip_prefix_length == 0) ? 1 : 0); + return ((evp->u.prefix_evpn.prefix_addr.ip_prefix_length == 0) ? + 1 : 0); } static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp, struct prefix *ip) { memset(ip, 0, sizeof(struct prefix)); - if (IS_EVPN_PREFIX_IPADDR_V4(evp)) { + if (is_evpn_prefix_ipaddr_v4(evp)) { ip->family = AF_INET; ip->prefixlen = IPV4_MAX_BITLEN; - memcpy(&(ip->u.prefix4), &(evp->prefix.ip.ip), + memcpy(&(ip->u.prefix4), &(evp->prefix.macip_addr.ip.ip), IPV4_MAX_BYTELEN); - } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) { + } else if (is_evpn_prefix_ipaddr_v6(evp)) { ip->family = AF_INET6; ip->prefixlen = IPV6_MAX_BITLEN; - memcpy(&(ip->u.prefix6), &(evp->prefix.ip.ip), + memcpy(&(ip->u.prefix6), &(evp->prefix.macip_addr.ip.ip), IPV6_MAX_BYTELEN); } } +static inline void ip_prefix_from_evpn_prefix(struct prefix_evpn *evp, + struct prefix *ip) +{ + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + ip_prefix_from_type2_prefix(evp, ip); + else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) + ip_prefix_from_type5_prefix(evp, ip); +} + static inline void build_evpn_type2_prefix(struct prefix_evpn *p, struct ethaddr *mac, struct ipaddr *ip) @@ -318,10 +328,10 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, p->family = AF_EVPN; p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN; p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; - memcpy(&p->prefix.mac.octet, mac->octet, ETH_ALEN); - p->prefix.ip.ipa_type = IPADDR_NONE; + memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN); + p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE; if (ip) - memcpy(&p->prefix.ip, ip, sizeof(*ip)); + memcpy(&p->prefix.macip_addr.ip, ip, sizeof(*ip)); } static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, @@ -343,10 +353,10 @@ static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, memset(evp, 0, sizeof(struct prefix_evpn)); evp->family = AF_EVPN; evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN; - evp->prefix.ip_prefix_length = ip_prefix->prefixlen; evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE; - evp->prefix.ip.ipa_type = ip.ipa_type; - memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr)); + evp->prefix.prefix_addr.ip_prefix_length = ip_prefix->prefixlen; + evp->prefix.prefix_addr.ip.ipa_type = ip.ipa_type; + memcpy(&evp->prefix.prefix_addr.ip, &ip, sizeof(struct ipaddr)); } static inline void build_evpn_type3_prefix(struct prefix_evpn *p, @@ -356,8 +366,8 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p, p->family = AF_EVPN; p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN; p->prefix.route_type = BGP_EVPN_IMET_ROUTE; - p->prefix.ip.ipa_type = IPADDR_V4; - p->prefix.ip.ipaddr_v4 = originator_ip; + p->prefix.imet_addr.ip.ipa_type = IPADDR_V4; + p->prefix.imet_addr.ip.ipaddr_v4 = originator_ip; } static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi, @@ -385,6 +395,7 @@ 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_autort_change(struct bgp *bgp); 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); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index c74a1bfb7c..e920bbb212 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -311,7 +311,7 @@ static void bgp_evpn_show_route_rd_header(struct vty *vty, } static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, - json_object *json) + uint64_t tbl_ver, json_object *json) { char ri_header[] = " Network Next Hop Metric LocPrf Weight Path\n"; @@ -319,9 +319,8 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, if (json) return; - - vty_out(vty, "BGP table version is 0, local router ID is %s\n", - inet_ntoa(bgp->router_id)); + vty_out(vty, "BGP table version is %" PRIu64 ", local router ID is %s\n", + tbl_ver, inet_ntoa(bgp->router_id)); vty_out(vty, "Status codes: s suppressed, d damped, h history, " "* valid, > best, i - internal\n"); @@ -494,12 +493,16 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, { struct bgp_node *rn; struct bgp_info *ri; + struct bgp_table *table; int header = 1; + uint64_t tbl_ver; uint32_t prefix_cnt, path_cnt; prefix_cnt = path_cnt = 0; - for (rn = bgp_table_top(vpn->route_table); rn; + table = vpn->route_table; + tbl_ver = table->version; + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; int add_prefix_to_json = 0; @@ -519,7 +522,8 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, if (rn->info) { /* Overall header/legend displayed once. */ if (header) { - bgp_evpn_show_route_header(vty, bgp, json); + bgp_evpn_show_route_header(vty, bgp, + tbl_ver, json); header = 0; } @@ -603,9 +607,9 @@ static void show_vni_routes_hash(struct hash_backet *backet, void *arg) static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object *json) { - json_object *json_vni; - json_object *json_import_rtl; - json_object *json_export_rtl; + json_object *json_vni = NULL; + json_object *json_import_rtl = NULL; + json_object *json_export_rtl = NULL; char buf1[10]; char buf2[INET6_ADDRSTRLEN]; char rt_buf[25]; @@ -862,6 +866,8 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, for (rn = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); rn; rn = bgp_route_next(rn)) { + uint64_t tbl_ver; + if (use_json) continue; /* XXX json TODO */ @@ -872,6 +878,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, continue; rd_header = 1; + tbl_ver = table->version; for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) for (ri = rm->info; ri; ri = ri->next) { @@ -891,7 +898,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, json_object_int_add( json, "bgpTableVersion", - 0); + tbl_ver); json_object_string_add( json, "bgpLocalRouterId", @@ -917,7 +924,8 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, V4_HEADER_OVERLAY); else { vty_out(vty, - "BGP table version is 0, local router ID is %s\n", + "BGP table version is %" PRIu64 ", local router ID is %s\n", + tbl_ver, inet_ntoa( bgp->router_id)); vty_out(vty, @@ -2201,11 +2209,13 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, char rd_str[RD_ADDRSTRLEN]; json_object *json_rd = NULL; /* contains routes for an RD */ int add_rd_to_json = 0; + uint64_t tbl_ver; table = (struct bgp_table *)rd_rn->info; if (table == NULL) continue; + tbl_ver = table->version; prefix_rd2str((struct prefix_rd *)&rd_rn->p, rd_str, sizeof(rd_str)); @@ -2236,6 +2246,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, /* Overall header/legend displayed once. */ if (header) { bgp_evpn_show_route_header(vty, bgp, + tbl_ver, json); header = 0; } @@ -2502,6 +2513,24 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp) bgp_zebra_advertise_all_vni(bgp, bgp->advertise_all_vni); bgp_evpn_cleanup_on_disable(bgp); } + +/* + * EVPN - use RFC8365 to auto-derive RT + */ +static void evpn_set_advertise_autort_rfc8365(struct bgp *bgp) +{ + bgp->advertise_autort_rfc8365 = 1; + bgp_evpn_handle_autort_change(bgp); +} + +/* + * EVPN - don't use RFC8365 to auto-derive RT + */ +static void evpn_unset_advertise_autort_rfc8365(struct bgp *bgp) +{ + bgp->advertise_autort_rfc8365 = 0; + bgp_evpn_handle_autort_change(bgp); +} #endif /* HAVE_CUMULUS */ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) @@ -2655,6 +2684,35 @@ DEFUN (no_bgp_evpn_advertise_all_vni, return CMD_SUCCESS; } +DEFUN (bgp_evpn_advertise_autort_rfc8365, + bgp_evpn_advertise_autort_rfc8365_cmd, + "autort rfc8365-compatible", + "Auto-derivation of RT\n" + "Auto-derivation of RT using RFC8365\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (!bgp) + return CMD_WARNING; + evpn_set_advertise_autort_rfc8365(bgp); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_evpn_advertise_autort_rfc8365, + no_bgp_evpn_advertise_autort_rfc8365_cmd, + "no autort rfc8365-compatible", + NO_STR + "Auto-derivation of RT\n" + "Auto-derivation of RT using RFC8365\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + + if (!bgp) + return CMD_WARNING; + evpn_unset_advertise_autort_rfc8365(bgp); + return CMD_SUCCESS; +} + DEFUN (bgp_evpn_default_originate, bgp_evpn_default_originate_cmd, "default-originate <ipv4 | ipv6>", @@ -2873,7 +2931,7 @@ DEFUN (no_bgp_evpn_advertise_type5, if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi); - UNSET_FLAG(bgp_vrf->vrf_flags, + UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN], BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST); } } @@ -4378,16 +4436,29 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->advertise_all_vni) vty_out(vty, " advertise-all-vni\n"); + if (bgp->advertise_autort_rfc8365) + vty_out(vty, " autort rfc8365-compatible\n"); + if (bgp->advertise_gw_macip) vty_out(vty, " advertise-default-gw\n"); if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) - vty_out(vty, " advertise ipv4 unicast\n"); + BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) { + if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name) + vty_out(vty, " advertise ipv4 unicast route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name); + else + vty_out(vty, " advertise ipv4 unicast\n"); + } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) - vty_out(vty, " advertise ipv6 unicast\n"); + BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) { + if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name) + vty_out(vty, " advertise ipv6 unicast route-map %s\n", + bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name); + else + vty_out(vty, " advertise ipv6 unicast\n"); + } if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN], BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4)) @@ -4455,6 +4526,8 @@ void bgp_ethernetvpn_init(void) #if defined(HAVE_CUMULUS) install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_autort_rfc8365_cmd); + install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_autort_rfc8365_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); diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 5db7e37089..6eb1e39884 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -166,6 +166,9 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, BGP_FLOWSPEC_NLRI_STRING_MAX, "EC{%s}", s == NULL ? "none" : s); + + if (s) + ecommunity_strfree(&s); } snprintf(local_string, BGP_FLOWSPEC_NLRI_STRING_MAX, "FS Rx %s %s %s %s", withdraw ? diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index a11a4f78f7..3f5ff12cbc 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -52,6 +52,7 @@ #include "bgpd/bgp_memory.h" #include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_io.h" +#include "bgpd/bgp_zebra.h" DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) DEFINE_HOOK(peer_established, (struct peer * peer), (peer)) @@ -1256,7 +1257,8 @@ static int bgp_connect_check(struct thread *thread) /* If getsockopt is fail, this is fatal error. */ if (ret < 0) { - zlog_info("can't get sockopt for nonblocking connect"); + zlog_info("can't get sockopt for nonblocking connect: %d(%s)", + errno, safe_strerror(errno)); BGP_EVENT_ADD(peer, TCP_fatal_error); return -1; } @@ -1267,8 +1269,8 @@ static int bgp_connect_check(struct thread *thread) return 1; } else { if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [Event] Connect failed (%s)", peer->host, - safe_strerror(errno)); + zlog_debug("%s [Event] Connect failed %d(%s)", + peer->host, status, safe_strerror(status)); BGP_EVENT_ADD(peer, TCP_connection_open_failed); return 0; } @@ -1397,13 +1399,14 @@ int bgp_start(struct peer *peer) if (!bgp_find_or_add_nexthop(peer->bgp, peer->bgp, family2afi(peer->su.sa.sa_family), NULL, peer, connected)) { -#if defined(HAVE_CUMULUS) - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [FSM] Waiting for NHT", peer->host); + if (bgp_zebra_num_connects()) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [FSM] Waiting for NHT", + peer->host); - BGP_EVENT_ADD(peer, TCP_connection_open_failed); - return 0; -#endif + BGP_EVENT_ADD(peer, TCP_connection_open_failed); + return 0; + } } assert(!peer->t_write); diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index 3882ff8b56..69c92e829c 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -179,7 +179,7 @@ static int bgp_process_reads(struct thread *thread) peer = THREAD_ARG(thread); - if (peer->fd < 0) + if (peer->fd < 0 || bm->terminating) return -1; struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO); diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 09b3a8718a..33f4d139b8 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -160,15 +160,6 @@ struct lcommunity *lcommunity_dup(struct lcommunity *lcom) return new; } -/* Retrun string representation of communities attribute. */ -char *lcommunity_str(struct lcommunity *lcom) -{ - if (!lcom->str) - lcom->str = - lcommunity_lcom2str(lcom, LCOMMUNITY_FORMAT_DISPLAY); - return lcom->str; -} - /* Merge two Large Communities Attribute structure. */ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, struct lcommunity *lcom2) @@ -186,6 +177,80 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, return lcom1; } +static void set_lcommunity_string(struct lcommunity *lcom, bool make_json) +{ + int i; + int len; + bool first = 1; + char *str_buf; + char *str_pnt; + uint8_t *pnt; + uint32_t global, local1, local2; + json_object *json_lcommunity_list = NULL; + json_object *json_string = NULL; + +#define LCOMMUNITY_STR_DEFAULT_LEN 32 + + if (!lcom) + return; + + if (make_json) { + lcom->json = json_object_new_object(); + json_lcommunity_list = json_object_new_array(); + } + + if (lcom->size == 0) { + str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1); + str_buf[0] = '\0'; + + if (make_json) { + json_object_string_add(lcom->json, "string", ""); + json_object_object_add(lcom->json, "list", + json_lcommunity_list); + } + + lcom->str = str_buf; + return; + } + + str_buf = str_pnt = + XMALLOC(MTYPE_LCOMMUNITY_STR, + (LCOMMUNITY_STR_DEFAULT_LEN * lcom->size) + 1); + + for (i = 0; i < lcom->size; i++) { + if (first) + first = 0; + else + *str_pnt++ = ' '; + + pnt = lcom->val + (i * LCOMMUNITY_SIZE); + pnt = ptr_get_be32(pnt, &global); + pnt = ptr_get_be32(pnt, &local1); + pnt = ptr_get_be32(pnt, &local2); + (void)pnt; + + len = sprintf(str_pnt, "%u:%u:%u", global, local1, local2); + if (make_json) { + json_string = json_object_new_string(str_pnt); + json_object_array_add(json_lcommunity_list, + json_string); + } + + str_pnt += len; + } + + str_buf = + XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, str_pnt - str_buf + 1); + + if (make_json) { + json_object_string_add(lcom->json, "string", str_buf); + json_object_object_add(lcom->json, "list", + json_lcommunity_list); + } + + lcom->str = str_buf; +} + /* Intern Large Communities Attribute. */ struct lcommunity *lcommunity_intern(struct lcommunity *lcom) { @@ -201,8 +266,7 @@ struct lcommunity *lcommunity_intern(struct lcommunity *lcom) find->refcnt++; if (!find->str) - find->str = - lcommunity_lcom2str(find, LCOMMUNITY_FORMAT_DISPLAY); + set_lcommunity_string(find, false); return find; } @@ -225,6 +289,21 @@ void lcommunity_unintern(struct lcommunity **lcom) } } +/* Retrun string representation of communities attribute. */ +char *lcommunity_str(struct lcommunity *lcom, bool make_json) +{ + if (!lcom) + return NULL; + + if (make_json && !lcom->json && lcom->str) + XFREE(MTYPE_LCOMMUNITY_STR, lcom->str); + + if (!lcom->str) + set_lcommunity_string(lcom, make_json); + + return lcom->str; +} + /* Utility function to make hash key. */ unsigned int lcommunity_hash_make(void *arg) { @@ -388,59 +467,6 @@ int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr) return 0; } -/* Convert large community attribute to string. - The large coms will be in 65535:65531:0 format. -*/ -char *lcommunity_lcom2str(struct lcommunity *lcom, int format) -{ - int i; - uint8_t *pnt; -#define LCOMMUNITY_STR_DEFAULT_LEN 40 - int str_size; - int str_pnt; - char *str_buf; - int len = 0; - int first = 1; - uint32_t globaladmin, localdata1, localdata2; - - if (lcom->size == 0) { - str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1); - str_buf[0] = '\0'; - return str_buf; - } - - /* Prepare buffer. */ - str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1); - str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1; - str_pnt = 0; - - for (i = 0; i < lcom->size; i++) { - /* Make it sure size is enough. */ - while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size) { - str_size *= 2; - str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, - str_size); - } - - /* Space between each value. */ - if (!first) - str_buf[str_pnt++] = ' '; - - pnt = lcom->val + (i * LCOMMUNITY_SIZE); - - pnt = ptr_get_be32(pnt, &globaladmin); - pnt = ptr_get_be32(pnt, &localdata1); - pnt = ptr_get_be32(pnt, &localdata2); - (void)pnt; /* consume value */ - - len = sprintf(str_buf + str_pnt, "%u:%u:%u", globaladmin, - localdata1, localdata2); - str_pnt += len; - first = 0; - } - return str_buf; -} - int lcommunity_match(const struct lcommunity *lcom1, const struct lcommunity *lcom2) { diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h index 78841accf3..c88a016396 100644 --- a/bgpd/bgp_lcommunity.h +++ b/bgpd/bgp_lcommunity.h @@ -21,10 +21,7 @@ #ifndef _QUAGGA_BGP_LCOMMUNITY_H #define _QUAGGA_BGP_LCOMMUNITY_H -/* Extended communities attribute string format. */ -#define LCOMMUNITY_FORMAT_ROUTE_MAP 0 -#define LCOMMUNITY_FORMAT_COMMUNITY_LIST 1 -#define LCOMMUNITY_FORMAT_DISPLAY 2 +#include "lib/json.h" /* Large Communities value is twelve octets long. */ #define LCOMMUNITY_SIZE 12 @@ -40,6 +37,9 @@ struct lcommunity { /* Large Communities value. */ uint8_t *val; + /* Large Communities as a json object */ + json_object *json; + /* Human readable format string. */ char *str; }; @@ -65,10 +65,9 @@ extern void lcommunity_unintern(struct lcommunity **); extern unsigned int lcommunity_hash_make(void *); extern struct hash *lcommunity_hash(void); extern struct lcommunity *lcommunity_str2com(const char *); -extern char *lcommunity_lcom2str(struct lcommunity *, int); extern int lcommunity_match(const struct lcommunity *, const struct lcommunity *); -extern char *lcommunity_str(struct lcommunity *); +extern char *lcommunity_str(struct lcommunity *, bool make_json); extern int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr); extern void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr); #endif /* _QUAGGA_BGP_LCOMMUNITY_H */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 5158717b5d..004bdd90a2 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -143,6 +143,8 @@ void sighup(void) __attribute__((__noreturn__)) void sigint(void) { zlog_notice("Terminating on signal"); + assert(bm->terminating == false); + bm->terminating = true; /* global flag that shutting down */ if (!retain_mode) bgp_terminate(); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 64d12cf607..bcc448119c 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -565,11 +565,12 @@ leak_update( if (nexthop_self_flag) bgp_info_set_flag(bn, new, BGP_INFO_ANNC_NH_SELF); + bgp_info_extra_get(new); + if (num_labels) setlabels(new, label, num_labels); - bgp_info_extra_get(new); - new->extra->parent = parent; + new->extra->parent = bgp_info_lock(parent); if (bgp_orig) new->extra->bgp_orig = bgp_orig; @@ -582,7 +583,7 @@ leak_update( struct bgp *bgp_nexthop = bgp; int nh_valid; - if (new->extra && new->extra->bgp_orig) + if (new->extra->bgp_orig) bgp_nexthop = new->extra->bgp_orig; /* diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 4802a4caa5..7bfeee9669 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -225,6 +225,11 @@ struct bgp_info *bgp_info_lock(struct bgp_info *binfo) struct bgp_info *bgp_info_unlock(struct bgp_info *binfo) { + /* unlink reference to parent, if any. */ + if (binfo->extra && binfo->extra->parent) { + bgp_info_unlock((struct bgp_info *)binfo->extra->parent); + binfo->extra->parent = NULL; + } assert(binfo && binfo->lock > 0); binfo->lock--; @@ -1336,6 +1341,8 @@ static void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr) } if (family == AF_INET6) memset(&attr->mp_nexthop_global, 0, IPV6_MAX_BYTELEN); + if (family == AF_EVPN) + memset(&attr->mp_nexthop_global_in, 0, BGP_ATTR_NHLEN_IPV4); } int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri, @@ -2336,10 +2343,18 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, if (new_select && new_select->type == ZEBRA_ROUTE_BGP && (new_select->sub_type == BGP_ROUTE_NORMAL || new_select->sub_type == BGP_ROUTE_AGGREGATE - || new_select->sub_type == BGP_ROUTE_IMPORTED)) + || new_select->sub_type == BGP_ROUTE_IMPORTED)) { + + /* if this is an evpn imported type-5 prefix, + * we need to withdraw the route first to clear + * the nh neigh and the RMAC entry. + */ + if (old_select && + is_route_parent_evpn(old_select)) + bgp_zebra_withdraw(p, old_select, bgp, safi); bgp_zebra_announce(rn, p, new_select, bgp, afi, safi); - else { + } else { /* Withdraw the route from the kernel. */ if (old_select && old_select->type == ZEBRA_ROUTE_BGP && (old_select->sub_type == BGP_ROUTE_NORMAL @@ -2353,12 +2368,28 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* advertise/withdraw type-5 routes */ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (advertise_type5_routes(bgp, afi) && new_select && - (!new_select->extra || !new_select->extra->parent)) - bgp_evpn_advertise_type5_route(bgp, &rn->p, - new_select->attr, - afi, safi); - else if (advertise_type5_routes(bgp, afi) && old_select && - (!old_select->extra || !old_select->extra->parent)) + (!new_select->extra || !new_select->extra->parent)) { + + /* apply the route-map */ + if (bgp->adv_cmd_rmap[afi][safi].map) { + int ret = 0; + + ret = route_map_apply( + bgp->adv_cmd_rmap[afi][safi].map, + &rn->p, RMAP_BGP, new_select); + if (ret == RMAP_MATCH) + bgp_evpn_advertise_type5_route( + bgp, &rn->p, new_select->attr, + afi, safi); + } else { + bgp_evpn_advertise_type5_route(bgp, + &rn->p, + new_select->attr, + afi, safi); + + } + } else if (advertise_type5_routes(bgp, afi) && old_select && + (!old_select->extra || !old_select->extra->parent)) bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi); } @@ -4626,7 +4657,7 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, if (bgp_static->encap_tunneltype == BGP_ENCAP_TYPE_VXLAN) { struct bgp_encap_type_vxlan bet; memset(&bet, 0, sizeof(struct bgp_encap_type_vxlan)); - bet.vnid = p->u.prefix_evpn.eth_tag; + bet.vnid = p->u.prefix_evpn.prefix_addr.eth_tag; bgp_encap_type_vxlan_to_tlv(&bet, &attr); } if (bgp_static->router_mac) { @@ -5101,10 +5132,10 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, return CMD_WARNING_CONFIG_FAILED; } if ((gw_ip.family == AF_INET - && IS_EVPN_PREFIX_IPADDR_V6( + && is_evpn_prefix_ipaddr_v6( (struct prefix_evpn *)&p)) || (gw_ip.family == AF_INET6 - && IS_EVPN_PREFIX_IPADDR_V4( + && is_evpn_prefix_ipaddr_v4( (struct prefix_evpn *)&p))) { vty_out(vty, "%% GatewayIp family differs with IP prefix\n"); @@ -6478,7 +6509,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, ? true : false; bool nexthop_othervrf = false; - vrf_id_t nexthop_vrfid; + vrf_id_t nexthop_vrfid = VRF_DEFAULT; const char *nexthop_vrfname = "Default"; if (json_paths) @@ -7129,10 +7160,10 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, vty_out(vty, "%s", str); XFREE(MTYPE_TMP, str); - if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)) { + if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)p)) { vty_out(vty, "/%s", inet_ntoa(attr->evpn_overlay.gw_ip.ipv4)); - } else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p)) { + } else if (is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)p)) { vty_out(vty, "/%s", inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6), buf, @@ -7383,7 +7414,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_object *json_cluster_list = NULL; json_object *json_cluster_list_list = NULL; json_object *json_ext_community = NULL; - json_object *json_lcommunity = NULL; json_object *json_last_update = NULL; json_object *json_pmsi = NULL; json_object *json_nexthop_global = NULL; @@ -8010,13 +8040,12 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, /* Line 6 display Large community */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) { if (json_paths) { - json_lcommunity = json_object_new_object(); - json_object_string_add(json_lcommunity, - "string", - attr->lcommunity->str); + if (!attr->lcommunity->json) + lcommunity_str(attr->lcommunity, true); + json_object_lock(attr->lcommunity->json); json_object_object_add(json_path, "largeCommunity", - json_lcommunity); + attr->lcommunity->json); } else { vty_out(vty, " Large Community: %s\n", attr->lcommunity->str); @@ -11398,14 +11427,15 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); if (p->u.prefix_evpn.route_type == 5) { char local_buf[PREFIX_STRLEN]; - uint8_t family = IS_EVPN_PREFIX_IPADDR_V4(( + uint8_t family = is_evpn_prefix_ipaddr_v4(( struct prefix_evpn *)p) ? AF_INET : AF_INET6; - inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, + inet_ntop(family, + &p->u.prefix_evpn.prefix_addr.ip.ip.addr, local_buf, PREFIX_STRLEN); sprintf(buf, "%s/%u", local_buf, - p->u.prefix_evpn.ip_prefix_length); + p->u.prefix_evpn.prefix_addr.ip_prefix_length); } else { prefix2str(p, buf, sizeof(buf)); } @@ -11417,7 +11447,8 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, sizeof(buf2)); vty_out(vty, " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n", - buf, rdbuf, p->u.prefix_evpn.eth_tag, + buf, rdbuf, + p->u.prefix_evpn.prefix_addr.eth_tag, decode_label(&bgp_static->label), esi, buf2, macrouter); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 4cc889286e..8c92f7ff32 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -338,9 +338,8 @@ static route_map_result_t route_match_ip_address(void *rule, void *object) { struct access_list *alist; - /* struct prefix_ipv4 match; */ - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET) { alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) return RMAP_NOMATCH; @@ -382,7 +381,7 @@ static route_map_result_t route_match_ip_next_hop(void *rule, struct bgp_info *bgp_info; struct prefix_ipv4 p; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET) { bgp_info = object; p.family = AF_INET; p.prefix = bgp_info->attr->nexthop; @@ -430,7 +429,7 @@ static route_map_result_t route_match_ip_route_source(void *rule, struct peer *peer; struct prefix_ipv4 p; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET) { bgp_info = object; peer = bgp_info->peer; @@ -478,7 +477,7 @@ route_match_ip_address_prefix_list(void *rule, struct prefix *prefix, { struct prefix_list *plist; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET) { plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) return RMAP_NOMATCH; @@ -515,7 +514,7 @@ route_match_ip_next_hop_prefix_list(void *rule, struct prefix *prefix, struct bgp_info *bgp_info; struct prefix_ipv4 p; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET) { bgp_info = object; p.family = AF_INET; p.prefix = bgp_info->attr->nexthop; @@ -558,7 +557,7 @@ route_match_ip_route_source_prefix_list(void *rule, struct prefix *prefix, struct peer *peer; struct prefix_ipv4 p; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET) { bgp_info = object; peer = bgp_info->peer; @@ -635,7 +634,7 @@ static route_map_result_t route_match_mac_address(void *rule, p.family = AF_ETHERNET; p.prefixlen = ETH_ALEN * 8; - p.u.prefix_eth = prefix->u.prefix_evpn.mac; + p.u.prefix_eth = prefix->u.prefix_evpn.macip_addr.mac; return (access_list_apply(alist, &p) == FILTER_DENY ? RMAP_NOMATCH @@ -2239,7 +2238,7 @@ static route_map_result_t route_match_ipv6_address(void *rule, { struct access_list *alist; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET6) { alist = access_list_lookup(AFI_IP6, (char *)rule); if (alist == NULL) return RMAP_NOMATCH; @@ -2326,7 +2325,7 @@ route_match_ipv6_address_prefix_list(void *rule, struct prefix *prefix, { struct prefix_list *plist; - if (type == RMAP_BGP) { + if (type == RMAP_BGP && prefix->family == AF_INET6) { plist = prefix_list_lookup(AFI_IP6, (char *)rule); if (plist == NULL) return RMAP_NOMATCH; diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 75ba1609d0..a23d5d03c4 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -25,6 +25,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* If rtrlib compiled with ssh support, don`t fail build */ +#define LIBSSH_LEGACY_0_4 + #include <zebra.h> #include <pthread.h> #include <time.h> diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 2c7e4e0435..241b23a62d 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -356,20 +356,19 @@ static struct peer *peer_lookup_addr_ipv4(struct in_addr *src) struct bgp *bgp; struct peer *peer; struct listnode *node; - struct in_addr addr; - int ret; bgp = bgp_get_default(); if (!bgp) return NULL; for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { - ret = inet_pton(AF_INET, peer->host, &addr); - if (ret > 0) { - if (IPV4_ADDR_SAME(&addr, src)) - return peer; - } + if (sockunion_family(&peer->su) != AF_INET) + continue; + + if (sockunion2ip(&peer->su) == src->s_addr) + return peer; } + return NULL; } @@ -377,28 +376,31 @@ static struct peer *bgp_peer_lookup_next(struct in_addr *src) { struct bgp *bgp; struct peer *peer; + struct peer *next_peer = NULL; struct listnode *node; - struct in_addr *p; - union sockunion su; - int ret; - - sockunion_init(&su); bgp = bgp_get_default(); if (!bgp) return NULL; for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { - ret = inet_pton(AF_INET, peer->host, &su.sin.sin_addr); - if (ret > 0) { - p = &su.sin.sin_addr; - - if (ntohl(p->s_addr) > ntohl(src->s_addr)) { - src->s_addr = p->s_addr; - return peer; - } + if (sockunion_family(&peer->su) != AF_INET) + continue; + if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr)) + continue; + + if (!next_peer + || ntohl(sockunion2ip(&next_peer->su)) + > ntohl(sockunion2ip(&peer->su))) { + next_peer = peer; } } + + if (next_peer) { + src->s_addr = sockunion2ip(&next_peer->su); + return next_peer; + } + return NULL; } diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 8ba7902a5f..cabd5b5cbd 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -467,13 +467,12 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, nh_modified = 1; } else if ( peer->sort == BGP_PEER_EBGP - && paf->safi != SAFI_EVPN && (bgp_multiaccess_check_v4(v4nh, peer) == 0) && !CHECK_FLAG( vec->flags, BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) && !peer_af_flag_check( - peer, nhafi, paf->safi, + peer, paf->afi, paf->safi, PEER_FLAG_NEXTHOP_UNCHANGED)) { /* NOTE: not handling case where NH has new AFI */ diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index e1b050bf59..f4f4e63264 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -553,39 +553,46 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, const char *arg) { int ret; + bool found = false; struct peer *peer; struct listnode *node, *nnode; /* Clear all neighbors. */ /* * Pass along pointer to next node to peer_clear() when walking all - * nodes - * on the BGP instance as that may get freed if it is a doppelganger + * nodes on the BGP instance as that may get freed if it is a + * doppelganger */ if (sort == clear_all) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (!peer->afc[afi][safi]) + continue; + if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, &nnode); - else if (peer->afc[afi][safi]) - ret = peer_clear_soft(peer, afi, safi, stype); else - ret = 0; + ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } /* This is to apply read-only mode on this clear. */ if (stype == BGP_CLEAR_SOFT_NONE) bgp->update_delay_over = 0; + if (!found) + vty_out(vty, "%%BGP: No %s peer configured", + afi_safi_print(afi, safi)); + return CMD_SUCCESS; } - /* Clear specified neighbors. */ + /* Clear specified neighbor. */ if (sort == clear_peer) { union sockunion su; - int ret; /* Make sockunion for lookup. */ ret = str2sockunion(arg, &su); @@ -610,7 +617,9 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } } - if (stype == BGP_CLEAR_SOFT_NONE) + if (!peer->afc[afi][safi]) + ret = BGP_ERR_AF_UNCONFIGURED; + else if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, NULL); else ret = peer_clear_soft(peer, afi, safi, stype); @@ -621,7 +630,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, return CMD_SUCCESS; } - /* Clear all peer-group members. */ + /* Clear all neighbors belonging to a specific peer-group. */ if (sort == clear_group) { struct peer_group *group; @@ -632,27 +641,37 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (stype == BGP_CLEAR_SOFT_NONE) { - peer_clear(peer, NULL); - continue; - } - if (!peer->afc[afi][safi]) continue; - ret = peer_clear_soft(peer, afi, safi, stype); + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear(peer, NULL); + else + ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } + + if (!found) + vty_out(vty, + "%%BGP: No %s peer belonging to peer-group %s is configured\n", + afi_safi_print(afi, safi), arg); + return CMD_SUCCESS; } + /* Clear all external (eBGP) neighbors. */ if (sort == clear_external) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (peer->sort == BGP_PEER_IBGP) continue; + if (!peer->afc[afi][safi]) + continue; + if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, &nnode); else @@ -660,33 +679,44 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } + + if (!found) + vty_out(vty, + "%%BGP: No external %s peer is configured\n", + afi_safi_print(afi, safi)); + return CMD_SUCCESS; } + /* Clear all neighbors belonging to a specific AS. */ if (sort == clear_as) { - as_t as; - int find = 0; - - as = strtoul(arg, NULL, 10); + as_t as = strtoul(arg, NULL, 10); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (peer->as != as) continue; - find = 1; - if (stype == BGP_CLEAR_SOFT_NONE) + if (!peer->afc[afi][safi]) + ret = BGP_ERR_AF_UNCONFIGURED; + else if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, &nnode); else ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } - if (!find) + + if (!found) vty_out(vty, - "%%BGP: No peer is configured with AS %s\n", - arg); + "%%BGP: No %s peer is configured with AS %s\n", + afi_safi_print(afi, safi), arg); + return CMD_SUCCESS; } @@ -10859,7 +10889,7 @@ static void lcommunity_show_all_iterator(struct hash_backet *backet, lcom = (struct lcommunity *)backet->data; vty_out(vty, "[%p] (%ld) %s\n", (void *)lcom, lcom->refcnt, - lcommunity_str(lcom)); + lcommunity_str(lcom, false)); } /* Show BGP's community internal data. */ @@ -12153,7 +12183,6 @@ static void bgp_ac_neighbor(vector comps, struct cmd_token *token) { struct bgp *bgp; struct peer *peer; - struct peer_group *group; struct listnode *lnbgp, *lnpeer; for (ALL_LIST_ELEMENTS_RO(bm->bgp, lnbgp, bgp)) { @@ -12177,11 +12206,6 @@ static void bgp_ac_neighbor(vector comps, struct cmd_token *token) vector_set(comps, XSTRDUP(MTYPE_COMPLETION, name)); } - - if (token->type == VARIABLE_TKN) - for (ALL_LIST_ELEMENTS_RO(bgp->group, lnpeer, group)) - vector_set(comps, XSTRDUP(MTYPE_COMPLETION, - group->name)); } } @@ -12191,9 +12215,27 @@ static const struct cmd_variable_handler bgp_var_neighbor[] = { {.varname = "peer", .completions = bgp_ac_neighbor}, {.completions = NULL}}; +static void bgp_ac_peergroup(vector comps, struct cmd_token *token) +{ + struct bgp *bgp; + struct peer_group *group; + struct listnode *lnbgp, *lnpeer; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, lnbgp, bgp)) { + for (ALL_LIST_ELEMENTS_RO(bgp->group, lnpeer, group)) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, + group->name)); + } +} + +static const struct cmd_variable_handler bgp_var_peergroup[] = { + {.tokenname = "PGNAME", .completions = bgp_ac_peergroup}, + {.completions = NULL} }; + void bgp_vty_init(void) { cmd_variable_handler_register(bgp_var_neighbor); + cmd_variable_handler_register(bgp_var_peergroup); /* Install bgp top node. */ install_node(&bgp_node, bgp_config_write); @@ -12575,6 +12617,8 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &no_neighbor_nexthop_self_cmd); install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_nexthop_self_cmd); + install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_cmd); /* "neighbor next-hop-self force" commands. */ install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd); @@ -13592,6 +13636,24 @@ DEFUN (no_ip_community_list_expanded_all, return CMD_SUCCESS; } +/* Return configuration string of community-list entry. */ +static const char *community_list_config_str(struct community_entry *entry) +{ + const char *str; + + if (entry->any) + str = ""; + else { + if (entry->style == COMMUNITY_LIST_STANDARD) + str = community_str(entry->u.com, false); + else if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) + str = lcommunity_str(entry->u.lcom, false); + else + str = entry->config; + } + return str; +} + static void community_list_show(struct vty *vty, struct community_list *list) { struct community_entry *entry; @@ -13617,9 +13679,7 @@ static void community_list_show(struct vty *vty, struct community_list *list) else vty_out(vty, " %s %s\n", community_direct_str(entry->direct), - entry->style == COMMUNITY_LIST_STANDARD - ? community_str(entry->u.com, false) - : entry->config); + community_list_config_str(entry)); } } @@ -13971,9 +14031,7 @@ static void lcommunity_list_show(struct vty *vty, struct community_list *list) else vty_out(vty, " %s %s\n", community_direct_str(entry->direct), - entry->style == EXTCOMMUNITY_LIST_STANDARD - ? entry->u.ecom->str - : entry->config); + community_list_config_str(entry)); } } @@ -14208,9 +14266,7 @@ static void extcommunity_list_show(struct vty *vty, struct community_list *list) else vty_out(vty, " %s %s\n", community_direct_str(entry->direct), - entry->style == EXTCOMMUNITY_LIST_STANDARD - ? entry->u.ecom->str - : entry->config); + community_list_config_str(entry)); } } @@ -14261,22 +14317,6 @@ DEFUN (show_ip_extcommunity_list_arg, return CMD_SUCCESS; } -/* Return configuration string of community-list entry. */ -static const char *community_list_config_str(struct community_entry *entry) -{ - const char *str; - - if (entry->any) - str = ""; - else { - if (entry->style == COMMUNITY_LIST_STANDARD) - str = community_str(entry->u.com, false); - else - str = entry->config; - } - return str; -} - /* Display community-list and extcommunity-list configuration. */ static int community_list_config_write(struct vty *vty) { diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 93a509c219..4a909d8cdc 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1207,7 +1207,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, /* Make Zebra API structure. */ memset(&api, 0, sizeof(api)); - memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; @@ -1288,17 +1287,11 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, } } - if (bgp->table_map[afi][safi].name || nh_othervrf) { + if (bgp->table_map[afi][safi].name) { /* Copy info and attributes, so the route-map apply doesn't modify the BGP route info. */ local_attr = *mpinfo->attr; mpinfo_cp->attr = &local_attr; - if (nh_othervrf) { - /* allow route-map to modify */ - local_attr.nexthop = - info->extra->nexthop_orig.u - .prefix4; - } } if (bgp->table_map[afi][safi].name) { @@ -1323,19 +1316,11 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, ifindex_t ifindex; struct in6_addr *nexthop; - if (bgp->table_map[afi][safi].name || nh_othervrf) { + if (bgp->table_map[afi][safi].name) { /* Copy info and attributes, so the route-map apply doesn't modify the BGP route info. */ local_attr = *mpinfo->attr; mpinfo_cp->attr = &local_attr; - if (nh_othervrf) { - /* allow route-map to modify */ - local_attr.mp_nexthop_global = - info->extra->nexthop_orig.u - .prefix6; - local_attr.mp_nexthop_len = - BGP_ATTR_NHLEN_IPV6_GLOBAL; - } } if (bgp->table_map[afi][safi].name) { @@ -1376,6 +1361,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh->label_num = 1; api_nh->labels[0] = label; } + memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), + sizeof(struct ethaddr)); valid_nh_count++; } @@ -1500,7 +1487,6 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, } memset(&api, 0, sizeof(api)); - memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; @@ -1913,7 +1899,7 @@ int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni) zclient_create_header(s, ZEBRA_ADVERTISE_DEFAULT_GW, bgp->vrf_id); stream_putc(s, advertise); - stream_put3(s, vni); + stream_putl(s, vni); stream_putw_at(s, 0, stream_get_endp(s)); return zclient_send_message(zclient); @@ -2405,12 +2391,25 @@ static void bgp_zebra_process_label_chunk( uint8_t response_keep; uint32_t first; uint32_t last; + uint8_t proto; + unsigned short instance; s = zclient->ibuf; + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); STREAM_GETC(s, response_keep); STREAM_GETL(s, first); STREAM_GETL(s, last); + if (zclient->redist_default != proto) { + zlog_err("Got LM msg with wrong proto %u", proto); + return; + } + if (zclient->instance != instance) { + zlog_err("Got LM msg with wrong instance %u", proto); + return; + } + if (first > last || first < MPLS_LABEL_UNRESERVED_MIN || last > MPLS_LABEL_UNRESERVED_MAX) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a331fad5d4..71707b6afa 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1174,6 +1174,11 @@ struct peer *peer_new(struct bgp *bgp) } peer->orf_plist[afi][safi] = NULL; } + + /* set nexthop-unchanged for l2vpn evpn by default */ + SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN], + PEER_FLAG_NEXTHOP_UNCHANGED); + SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); /* Create buffers. */ @@ -4072,6 +4077,35 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, return 0; } + /* + * For EVPN we implicitly set the NEXTHOP_UNCHANGED flag, + * if we are setting/unsetting flags which conflict with this flag + * handle accordingly + */ + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + if (set) { + + /* + * if we are setting NEXTHOP_SELF, we need to unset the + * NEXTHOP_UNCHANGED flag + */ + if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) || + CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF)) + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_UNCHANGED); + } else { + + /* + * if we are unsetting NEXTHOP_SELF, we need to set the + * NEXTHOP_UNCHANGED flag to reset the defaults for EVPN + */ + if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) || + CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF)) + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_UNCHANGED); + } + } + if (set) SET_FLAG(peer->af_flags[afi][safi], flag); else @@ -5947,39 +5981,63 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, struct peer_group *group; struct listnode *node, *nnode; + /* apply configuration and set flags */ SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); - peer->pmax[afi][safi] = max; - peer->pmax_threshold[afi][safi] = threshold; - peer->pmax_restart[afi][safi] = restart; if (warning) SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); else UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = max; + peer->pmax_threshold[afi][safi] = threshold; + peer->pmax_restart[afi][safi] = restart; + /* if handling a peer-group, apply to all children */ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX); - peer->pmax[afi][safi] = max; - peer->pmax_threshold[afi][safi] = threshold; - peer->pmax_restart[afi][safi] = restart; - if (warning) + /* + * If peer configuration is user-set, it overrides + * peer-group config. + */ + if (!CHECK_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX)) { SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - else - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); + PEER_FLAG_MAX_PREFIX); + peer->pmax[afi][safi] = max; + peer->pmax_threshold[afi][safi] = threshold; + peer->pmax_restart[afi][safi] = restart; + } + if (!CHECK_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING)) { + if (warning) + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); + else + UNSET_FLAG( + peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); + } if ((peer->status == Established) && (peer->afc[afi][safi])) bgp_maximum_prefix_overflow(peer, afi, safi, 1); } } else { + /* if not handling a peer-group, set the override flags */ if ((peer->status == Established) && (peer->afc[afi][safi])) bgp_maximum_prefix_overflow(peer, afi, safi, 1); + + SET_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX); + + if (warning) + SET_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); + else + UNSET_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); } return 0; @@ -5990,49 +6048,49 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) struct peer_group *group; struct listnode *node, *nnode; - /* apply peer-group config */ - if (peer_group_active(peer)) { - if (CHECK_FLAG(peer->group->conf->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX)) - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX); - else - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX); - - if (CHECK_FLAG(peer->group->conf->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING)) - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - else - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - - peer->pmax[afi][safi] = peer->group->conf->pmax[afi][safi]; - peer->pmax_threshold[afi][safi] = - peer->group->conf->pmax_threshold[afi][safi]; - peer->pmax_restart[afi][safi] = - peer->group->conf->pmax_restart[afi][safi]; - return 0; - } - UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); peer->pmax[afi][safi] = 0; peer->pmax_threshold[afi][safi] = 0; peer->pmax_restart[afi][safi] = 0; - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + /* if not handling a peer-group, unset override flags */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + UNSET_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX); + UNSET_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); + /* if peer is part of a peer-group, apply peer-group config */ + if (peer_group_active(peer)) { + peer->pmax[afi][safi] = + peer->group->conf->pmax[afi][safi]; + peer->pmax_threshold[afi][safi] = + peer->group->conf->pmax_threshold[afi][safi]; + peer->pmax_restart[afi][safi] = + peer->group->conf->pmax_restart[afi][safi]; + } + return 0; + } + /* + * If this peer is a peer-group, set all peers in the group unless they + * have overrides for our config. + */ group = peer->group; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - peer->pmax[afi][safi] = 0; - peer->pmax_threshold[afi][safi] = 0; - peer->pmax_restart[afi][safi] = 0; + if (!CHECK_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING)) + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); + if (!CHECK_FLAG(peer->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX)) { + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX); + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = 0; + peer->pmax_restart[afi][safi] = 0; + } } return 0; } @@ -7109,7 +7167,9 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, /* atribute-unchanged. */ if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) - || peer_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_UNCHANGED) + || (safi != SAFI_EVPN + && peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { if (!peer_group_active(peer) @@ -7559,6 +7619,7 @@ void bgp_master_init(struct thread_master *master) bm->start_time = bgp_clock(); bm->t_rmap_update = NULL; bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER; + bm->terminating = false; bgp_process_queue_init(); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 470fd10850..340851e8d9 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -144,6 +144,7 @@ struct bgp_master { /* dynamic mpls label allocation pool */ struct labelpool labelpool; + bool terminating; /* global flag that sigint terminate seen */ QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp_master) @@ -480,6 +481,9 @@ struct bgp { /* EVPN enable - advertise local VNIs and their MACs etc. */ int advertise_all_vni; + /* EVPN - use RFC 8365 to auto-derive RT */ + int advertise_autort_rfc8365; + /* Hash table of Import RTs to EVIs */ struct hash *import_rt_hash; @@ -836,7 +840,28 @@ struct peer { /* NSF mode (graceful restart) */ uint8_t nsf[AFI_MAX][SAFI_MAX]; - /* Per AF configuration flags. */ + /* Peer Per AF flags */ + /* + * Parallel array to af_flags that indicates whether each flag + * originates from a peer-group or if it is config that is specific to + * this individual peer. If a flag is set independent of the + * peer-group the same bit should be set here. If this peer is a + * peer-group, this memory region should be all zeros. The assumption + * is that the default state for all flags is unset. + * + * Notes: + * - if a flag for an individual peer is unset, the corresponding + * override flag is unset and the peer is considered to be back in + * sync with the peer-group. + * - This does *not* contain the flag values, rather it contains + * whether the flag at the same position in af_flags is + * *peer-specific*. + */ + uint32_t af_flags_override[AFI_MAX][SAFI_MAX]; + /* + * Effective flags, computed by applying peer-group flags and then + * overriding with individual flags + */ uint32_t af_flags[AFI_MAX][SAFI_MAX]; #define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */ #define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */ diff --git a/configure.ac b/configure.ac index 7662f2a4e5..8b49295444 100755 --- a/configure.ac +++ b/configure.ac @@ -1913,8 +1913,13 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile bgpd/Makefile vtysh/Makefile - doc/Makefile tests/Makefile - bgpd/rfp-example/rfptest/Makefile bgpd/rfp-example/librfp/Makefile + doc/Makefile + doc/user/Makefile + doc/manpages/Makefile + doc/developer/Makefile + tests/Makefile + bgpd/rfp-example/rfptest/Makefile + bgpd/rfp-example/librfp/Makefile redhat/frr.spec debianpkg/Makefile debianpkg/changelog diff --git a/debianpkg/README.deb_build.md b/debianpkg/README.deb_build.md deleted file mode 100644 index c90ca13394..0000000000 --- a/debianpkg/README.deb_build.md +++ /dev/null @@ -1,127 +0,0 @@ -Building your own FRRouting Debian Package -========================================== -(Tested on Ubuntu 12.04, 14.04, 16.04, 17.10, 18.04, Debian 8 and 9) - -**Note:** If you try to build for a different distro, then it will most likely -fail because of the missing backport. See debianpkg/backports/README about -adding a new backport. - -1. Follow the package installation as outlined in doc/Building_on_XXXX.md - (XXXX refers your OS Distribution) to install the required build packages - -2. Install the following additional packages: - - on Ubuntu 12.04, 14.04, 16.04, 17.10, Debian 8 and 9: - - apt-get install realpath equivs groff fakeroot debhelper devscripts - - on Ubuntu 18.04: (realpath is now part of preinstalled by coreutils) - - apt-get install equivs groff fakeroot debhelper devscripts - -3. Checkout FRR under a **unpriviledged** user account - - git clone https://github.com/frrouting/frr.git frr - cd frr - # git checkout <branch> - if different branch than master - -4. Run Bootstrap and make distribution tar.gz - - ./bootstrap.sh - ./configure --with-pkg-extra-version=-MyDebPkgVersion - make dist - - Note: configure parameters are not important for the Debian Package - building - except the `with-pkg-extra-version` if you want to give the - Debian Package a specific name to mark your own unoffical build - -5. Edit `debianpkg/rules` and set the configuration as needed - - Look for section `dh_auto_configure` to modify the configure - options as needed. Options might be different between main `rules` and - `backports/XXXX/debian/rules`. Please adjust as needed on all files - -6. Create backports debian sources - - Move the `debianpkg` to `debian` and create the backports - (Debian requires to not ship a `debian` directory inside the source - directory to avoid build conflicts with the reserved `debian` subdirectory - name during the build) - - mv debianpkg debian - make -f debian/rules backports - - This will create a `frr_*.orig.tar.gz` with the source (same as dist tar), - and multiple `frr_*.debian.tar.xz` and `frr_*.dsc` for the debian package - source on each backport supported distribution - -7. Create a new directory to build the package and populate with package src - - mkdir frrpkg - cd frrpkg - tar xf ~/frr/frr_*.orig.tar.gz - cd frr* - . /etc/os-release - tar xf ~/frr/frr_*${ID}${VERSION_ID}*.debian.tar.xz - -8. Build Debian Package Dependencies and install them as needed - - sudo mk-build-deps --install debian/control - -9. Build Debian Package - - Building with standard options: - - debuild -b -uc -us - - Or change some options: - (see `rules` file for available options) - - debuild --set-envvar=WANT_BGP_VNC=1 --set-envvar=WANT_CUMULUS_MODE=1 -b -uc -us - - To build with RPKI, download the librtr packages from - https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact - install librtr-dev on the build server and build the packages as - debuild --set-envvar=WANT_RPKI=1 -b -uc -us - RPKI packages have an additonal dependency of librtr0 which can be - found at the same URL - -DONE. - -If all works correctly, then you should end up with the Debian packages under -`frrpkg`. If distributed, please make sure you distribute it together with -the sources (`frr_*.orig.tar.gz`, `frr_*.debian.tar.xz` and `frr_*.dsc`) - - -Enabling daemons after installation of the package: ---------------------------------------------------- - -1. Edit `/etc/frr/daemons` and enable required routing daemons (Zebra is -probably needed for most deployments, so make sure to enable it.) - -2. Check your firewall / IPtables to make sure the routing protocols are -allowed. - -3. Enable FRR at startup - - - On `init.d` based systems (Ubuntu 12.04) - - sudo update-rc.d frr defaults - - - On `systemd` based systems (Debian 8 and later, Ubuntu 14.04 and later) - - sudo systemctl enable frr - -4. Start/Restart the daemons (or reboot) - - - On `init.d` based systems (Ubuntu 12.04) - - sudo invoke-rc.d frr start - - - on `systemd` based systems (Debian 8 and later, Ubuntu 14.04 and later) - - sudo systemctl start frr - - -Configuration is stored in `/etc/frr/*.conf` files and daemon selection -is stored in `/etc/frr/daemons`. diff --git a/debianpkg/backports/ubuntu14.04/debian/frr.postinst b/debianpkg/backports/ubuntu14.04/debian/frr.postinst index b1d463a33d..5a14e510cd 100644 --- a/debianpkg/backports/ubuntu14.04/debian/frr.postinst +++ b/debianpkg/backports/ubuntu14.04/debian/frr.postinst @@ -18,7 +18,7 @@ chgrp ${frrvtygid} /etc/frr/vtysh* chmod 644 /etc/frr/* ENVIRONMENTFILE=/etc/environment -if ! grep --quiet VTYSH_PAGER=/bin/cat ${ENVIRONMENTFILE}; then +if ! egrep --quiet '^VTYSH_PAGER=' ${ENVIRONMENTFILE}; then echo "VTYSH_PAGER=/bin/cat" >> ${ENVIRONMENTFILE} fi ################################################## diff --git a/debianpkg/frr-doc.docs b/debianpkg/frr-doc.docs index 4720a3b920..d2218d00f9 100644 --- a/debianpkg/frr-doc.docs +++ b/debianpkg/frr-doc.docs @@ -1,6 +1,5 @@ AUTHORS NEWS README -REPORTING-BUGS doc/user/*.rst doc/figures/*.png diff --git a/debianpkg/frr.postinst b/debianpkg/frr.postinst index 972f8c0500..32af741c98 100644 --- a/debianpkg/frr.postinst +++ b/debianpkg/frr.postinst @@ -19,7 +19,7 @@ chgrp ${frrvtygid} /etc/frr/vtysh* chmod 644 /etc/frr/* ENVIRONMENTFILE=/etc/environment -if ! grep --quiet VTYSH_PAGER=/bin/cat ${ENVIRONMENTFILE}; then +if ! egrep --quiet '^VTYSH_PAGER=' ${ENVIRONMENTFILE}; then echo "VTYSH_PAGER=/bin/cat" >> ${ENVIRONMENTFILE} fi ################################################## diff --git a/doc/Makefile.am b/doc/Makefile.am index 053842283e..19561c031d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -14,7 +14,7 @@ .NOTPARALLEL: SUBDIRS = manpages user -AM_MAKEFLAGS = DESTDIR=${DESTDIR} infodir=${infodir} doczdir=${abs_srcdir} +AM_MAKEFLAGS = DESTDIR=${DESTDIR} infodir=${infodir} MANPAGE_BUILDDIR = manpages/_build/man @@ -198,6 +198,7 @@ EXTRA_DIST = frr-sphinx.mk \ user/conf.py \ user/eigrpd.rst \ user/filter.rst \ + user/getting-started.rst \ user/glossary.rst \ user/index.rst \ user/installation.rst \ @@ -217,6 +218,7 @@ EXTRA_DIST = frr-sphinx.mk \ user/routemap.rst \ user/routeserver.rst \ user/rpki.rst \ + user/setup.rst \ user/sharp.rst \ user/snmp.rst \ user/snmptrap.rst \ diff --git a/doc/developer/.gitignore b/doc/developer/.gitignore index 0505537159..2e7d8573f1 100644 --- a/doc/developer/.gitignore +++ b/doc/developer/.gitignore @@ -1,3 +1,3 @@ /_templates /_build -!/Makefile +!/Makefile.in diff --git a/doc/developer/Makefile b/doc/developer/Makefile deleted file mode 100644 index 9807a750bf..0000000000 --- a/doc/developer/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../frr-sphinx.mk diff --git a/doc/developer/Makefile.in b/doc/developer/Makefile.in new file mode 100644 index 0000000000..76758f9242 --- /dev/null +++ b/doc/developer/Makefile.in @@ -0,0 +1,8 @@ +# This is necessary to support VPATH builds. +srcdir = @srcdir@ +VPATH = @srcdir@ + +# This variable is used as the documentation source location in frr-sphinx.mk +SOURCESDIR = @srcdir@ + +include @srcdir@/../frr-sphinx.mk diff --git a/doc/developer/building-frr-on-openbsd6.rst b/doc/developer/building-frr-on-openbsd6.rst index 1f3aec8d92..46db25a025 100644 --- a/doc/developer/building-frr-on-openbsd6.rst +++ b/doc/developer/building-frr-on-openbsd6.rst @@ -14,7 +14,7 @@ Add packages: :: - pkg_add git autoconf-2.69p2 automake-1.15p0 libtool bison + pkg_add git autoconf-2.69p2 automake-1.15.1 libtool bison pkg_add gmake gawk dejagnu openssl json-c py-test py-sphinx Select Python2.7 as default (required for pytest) diff --git a/doc/developer/building-frr-on-ubuntu1804.rst b/doc/developer/building-frr-on-ubuntu1804.rst index 17edb7ef4f..d9fc37c172 100644 --- a/doc/developer/building-frr-on-ubuntu1804.rst +++ b/doc/developer/building-frr-on-ubuntu1804.rst @@ -1,17 +1,56 @@ Ubuntu 18.04LTS =============================================== -Install required packages +Install dependencies ------------------------- +Required packages +^^^^^^^^^^^^^^^^^ -Add packages: +:: + + sudo apt-get install \ + git \ + autoconf \ + automake \ + libtool \ + make \ + gawk \ + libreadline-dev \ + texinfo \ + pkg-config \ + libpam0g-dev \ + libjson-c-dev \ + bison \ + flex \ + python-pytest \ + libc-ares-dev \ + python3-dev \ + libsystemd-dev \ + python-ipaddr \ + python3-sphinx + +Optional packages +^^^^^^^^^^^^^^^^^ + +Dependencies for additional functionality can be installed as-desired. + +Protobuf +~~~~~~~~ :: - apt-get install git autoconf automake libtool make gawk libreadline-dev \ - texinfo pkg-config libpam0g-dev libjson-c-dev bison flex \ - python-pytest libc-ares-dev python3-dev libsystemd-dev python-ipaddr \ - python3-sphinx + sudo apt-get install \ + protobuf-c-compiler \ + libprotobuf-c-dev + +ZeroMQ +~~~~~~ + +:: + + sudo apt-get install \ + libzmq5 \ + libzmq3-dev Get FRR, compile it and install it (from Git) --------------------------------------------- @@ -30,15 +69,21 @@ Add frr groups and user --gecos "FRR suite" --shell /sbin/nologin frr sudo usermod -a -G frrvty frr -Download Source, configure and compile it +Download source ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -(You may prefer different options on configure statement. These are just -an example.) - :: git clone https://github.com/frrouting/frr.git frr + +Configure +^^^^^^^^^ +Options below are provided as an example. + +.. seealso:: *Installation* section of user guide + +:: + cd frr ./bootstrap.sh ./configure \ @@ -62,6 +107,23 @@ an example.) --enable-systemd=yes \ --with-pkg-git-version \ --with-pkg-extra-version=-MyOwnFRRVersion + +If optional packages were installed, the associated feature may now be +enabled. + +.. option:: --enable-protobuf + +Enable support for protobuf transport + +.. option:: --enable-zeromq + +Enable support for ZeroMQ transport + +Compile +^^^^^^^ + +:: + make make check sudo make install @@ -69,6 +131,21 @@ an example.) Create empty FRR configuration files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Although not strictly necessary, it's good practice to create empty +configuration files _before_ starting FRR. This assures that the permissions +are correct. If the files are not already present, FRR will create them. + +It's also important to consider _which_ files to create. FRR supports writing +configuration to a monolithic file, ``/etc/frr/frr.conf``, which is not +recommended +.. seealso:: *VTYSH* section of user guide + +The presence of ``/etc/frr/frr.conf`` on startup implicitly configures FRR to +ignore daemon-specific configuration files. + +Daemon-specific configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + :: sudo install -m 755 -o frr -g frr -d /var/log/frr @@ -83,7 +160,15 @@ Create empty FRR configuration files sudo install -m 640 -o frr -g frr /dev/null /etc/frr/pimd.conf sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ldpd.conf sudo install -m 640 -o frr -g frr /dev/null /etc/frr/nhrpd.conf - sudo install -m 640 -o frr -g frrvty /dev/null /etc/frr/vtysh.conf + +Monolithic configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + sudo install -m 755 -o frr -g frr -d /var/log/frr + sudo install -m 775 -o frr -g frrvty -d /etc/frr + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/frr.conf Enable IPv4 & IPv6 forwarding ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -101,7 +186,22 @@ other settings) # based on Router Advertisements for this host net.ipv6.conf.all.forwarding=1 -Enable MPLS Forwarding (with Linux Kernel >= 4.5) +Add MPLS kernel modules +^^^^^^^^^^^^^^^^^^^^^^^ + +Ubuntu 18.04 ships with kernel 4.15. MPLS modules are present by default. +To enable, add the following lines to ``/etc/modules-load.d/modules.conf``: + +:: + + # Load MPLS Kernel Modules + mpls_router + mpls_iptunnel + +**Reboot** or use ``sysctl -p`` to apply the same config to the running +system + +Enable MPLS Forwarding ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a @@ -116,20 +216,6 @@ MPLS net.mpls.conf.eth2.input=1 net.mpls.platform_labels=100000 -Add MPLS kernel modules -^^^^^^^^^^^^^^^^^^^^^^^ - -Add the following lines to ``/etc/modules-load.d/modules.conf``: - -:: - - # Load MPLS Kernel Modules - mpls-router - mpls-iptunnel - -**Reboot** or use ``sysctl -p`` to apply the same config to the running -system - Install the systemd service (if rebooted from last step, change directory back to frr directory) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/developer/building.rst b/doc/developer/building.rst index 92fd1bb63a..051611a65d 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -1,3 +1,5 @@ +.. _building: + ************ Building FRR ************ diff --git a/doc/developer/conf.py b/doc/developer/conf.py index a3968b60ff..61253c4b2f 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -342,6 +342,14 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False +# contents of ../extra/frrlexer.py. +# This is read here to support VPATH build. Since this section is execfile()'d +# with the file location, we can safely use a relative path here to save the +# contents of the lexer file for later use even if our relative path changes +# due to VPATH. +with open('../extra/frrlexer.py', 'rb') as lex: + frrlexerpy = lex.read() + # custom extensions here def setup(app): # object type for FRR CLI commands, can be extended to document parent CLI @@ -357,5 +365,5 @@ def setup(app): # # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer") custom_namespace = {} - exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace) + exec(frrlexerpy, custom_namespace) lexers['frr'] = custom_namespace['FRRLexer']() diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 2f4b96bc1f..42192db637 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -1,11 +1,12 @@ -Welcome to FRR's documentation! -=============================== +FRRouting Developer's Guide +=========================== .. toctree:: :maxdepth: 2 workflow building + packaging process-architecture library bgpd diff --git a/doc/developer/library.rst b/doc/developer/library.rst index c5ce1f5982..f6efa33051 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -1,3 +1,5 @@ +.. _libfrr: + *************************** Library Facilities (libfrr) *************************** diff --git a/doc/developer/packaging-debian.rst b/doc/developer/packaging-debian.rst new file mode 100644 index 0000000000..66339b6d1f --- /dev/null +++ b/doc/developer/packaging-debian.rst @@ -0,0 +1,173 @@ +Debian +====== + +(Tested on Ubuntu 12.04, 14.04, 16.04, 17.10, 18.04, Debian 8 and 9) + +.. note:: + + If you try to build for a different distro, then it will most likely fail + because of the missing backport. See :ref:`deb-backports` about adding a new + backport. + +1. Install build dependencies for your platform as outlined in :ref:`building`. + +2. Install the following additional packages: + + - on Ubuntu 12.04, 14.04, 16.04, 17.10, Debian 8 and 9: + + .. code-block:: shell + + apt-get install realpath equivs groff fakeroot debhelper devscripts + + - on Ubuntu 18.04: (realpath is now part of preinstalled by coreutils) + + .. code-block:: shell + + apt-get install equivs groff fakeroot debhelper devscripts + +3. Checkout FRR under a **unprivileged** user account: + + .. code-block:: shell + + git clone https://github.com/frrouting/frr.git frr + cd frr + + If you wish to build a package for a branch other than master: + + .. code-block:: shell + + git checkout <branch> + +4. Run ``bootstrap.sh`` and make a dist tarball: + + .. code-block:: shell + + ./bootstrap.sh + ./configure --with-pkg-extra-version=-MyDebPkgVersion + make dist + + .. note:: + + Configure parameters are not important for the Debian Package building - + except the `with-pkg-extra-version` if you want to give the Debian + package a specific name to mark your own unoffical build. + +5. Edit :file:`debianpkg/rules` and set the configuration as needed. + + Look for section ``dh_auto_configure`` to modify the configure options as + needed. Options might be different between the top-level ``rules``` and + :file:`backports/XXXX/debian/rules`. Please adjust as needed on all files. + +6. Create backports debian sources + + Rename the :file:`debianpkg` directory to :file:`debian` and create the + backports (Debian requires to not ship a :file:`debian` directory inside the + source directory to avoid build conflicts with the reserved ``debian`` + subdirectory name during the build): + + .. code-block:: shell + + mv debianpkg debian + make -f debian/rules backports + + This will create a :file:`frr_*.orig.tar.gz` with the source (same as the + dist tarball), as well as multiple :file:`frr_*.debian.tar.xz` and + :file:`frr_*.dsc` corresponding to each distribution for which a backport is + available. + +7. Create a new directory to build the package and populate with package + source. + + .. code-block:: shell + + mkdir frrpkg + cd frrpkg + tar xf ~/frr/frr_*.orig.tar.gz + cd frr* + . /etc/os-release + tar xf ~/frr/frr_*${ID}${VERSION_ID}*.debian.tar.xz + +8. Build Debian package dependencies and install them as needed. + + .. code-block:: shell + + sudo mk-build-deps --install debian/control + +9. Build Debian Package + + Building with standard options: + + .. code-block:: shell + + debuild -b -uc -us + + Or change some options (see `rules` file for available options): + + .. code-block:: shell + + debuild --set-envvar=WANT_BGP_VNC=1 --set-envvar=WANT_CUMULUS_MODE=1 -b -uc -us + + To build with RPKI: + + - Download the librtr packages from + https://ci1.netdef.org/browse/RPKI-RTRLIB/latestSuccessful/artifact + + - install librtr-dev on the build server + + Then build with: + + .. code-block:: shell + + debuild --set-envvar=WANT_RPKI=1 -b -uc -us + + RPKI packages have an additonal dependency of ``librtr0`` which can be found + at the same URL. + +10. Done! + +If all worked correctly, then you should end up with the Debian packages under +:file:`frrpkg`. If distributed, please make sure you distribute it together +with the sources (``frr_*.orig.tar.gz``, ``frr_*.debian.tar.xz`` and +``frr_*.dsc``) + +.. _deb-backports: + +Debian Backports +---------------- + +The :file:`debianpkg/backports` directory contains the Debian directories for +backports to other Debian platforms. These are built via the ``3.0 (custom)`` +source format, which allows one to build a source package directly out of +tarballs (e.g. an orig.tar.gz tarball and a debian.tar.gz file), at which point +the format can be changed to a real format (e.g. ``3.0 (quilt)``). + +Source packages are assembled via targets of the same name as the system to +which the backport is done (e.g. ``precise``), included in :file:`debian/rules`. + +To create a new Debian backport: + +- Add its name to ``KNOWN_BACKPORTS``, defined in :file:`debian/rules`. +- Create a directory of the same name in :file:`debian/backports`. +- Add the files ``exclude``, ``versionext``, and ``debian/source/format`` under + this directory. + +For the last point, these files should contain the following: + +``exclude`` + Contains whitespace-separated paths (relative to the root of the source dir) + that should be excluded from the source package (e.g. + :file:`debian/patches`). + +``versionext`` + Contains the suffix added to the version number for this backport's build. + Distributions often have guidelines for what this should be. If left empty, + no new :file:`debian/changelog` entry is created. + +``debian/source/format`` + Contains the source format of the resulting source package. As of of the + writing of this document the only supported format is ``3.0 (quilt)``. + +- Add appropriate files under the :file:`debian/` subdirectory. These will be + included in the source package, overriding any top-level :file:`debian/` + files with equivalent paths. + diff --git a/doc/developer/packaging.rst b/doc/developer/packaging.rst new file mode 100644 index 0000000000..e9bb3a5409 --- /dev/null +++ b/doc/developer/packaging.rst @@ -0,0 +1,8 @@ +********* +Packaging +********* + +.. toctree:: + :maxdepth: 2 + + packaging-debian diff --git a/doc/developer/process-architecture.rst b/doc/developer/process-architecture.rst index 514b5becdd..806afa644c 100644 --- a/doc/developer/process-architecture.rst +++ b/doc/developer/process-architecture.rst @@ -32,6 +32,8 @@ explicitly named, they will be formatted ``like this`` to differentiate from the conceptual names. When speaking of kernel threads, the term used will be "pthread" since FRR's kernel threading implementation is POSIX threads. +.. This should be broken into its document under :ref:`libfrr` +.. _event-architecture: Event Architecture ------------------ @@ -67,7 +69,8 @@ are given by integer macros in :file:`thread.h` and are: ``THREAD_EVENT`` Generic task that executes with high priority and carries an arbitrary - integer indicating the event type to its handler. + integer indicating the event type to its handler. These are commonly used to + implement the finite state machines typically found in routing protocols. ``THREAD_READY`` Type used internally for tasks on the ready queue. @@ -110,6 +113,26 @@ highest priority, followed by expired timers and finally I/O tasks (type arbitrary argument are provided. The task returned from ``thread_fetch()`` is then executed with ``thread_call()``. +The following diagram illustrates a simplified version of this infrastructure. + +.. todo: replace these with SVG +.. figure:: ../figures/threadmaster-single.png + :align: center + + Lifecycle of a program using a single threadmaster. + +The series of "task" boxes represents the current ready task queue. The various +other queues for other types are not shown. The fetch-execute loop is +illustrated at the bottom. + +Mapping the general names used in the figure to specific FRR functions: + +- ``task`` is ``struct thread *`` +- ``fetch`` is ``thread_fetch()`` +- ``exec()`` is ``thread_call`` +- ``cancel()`` is ``thread_cancel()`` +- ``schedule()`` is any of the various task-specific ``thread_add_*`` functions + Adding tasks is done with various task-specific function-like macros. These macros wrap underlying functions in :file:`thread.c` to provide additional information added at compile time, such as the line number the task was @@ -134,10 +157,6 @@ There are some gotchas to keep in mind: call is the responsibility of the caller. -.. todo: include this when its more complete -.. .. figure:: ../figures/threadmaster.svg - - Kernel Thread Architecture -------------------------- Efforts have begun to introduce kernel threads into FRR to improve performance @@ -158,6 +177,27 @@ the currently existing task and context primitives. In this way the familiar execution model of FRR gains the ability to execute tasks simultaneously while preserving the existing model for concurrency. +The following figure illustrates the architecture with multiple pthreads, each +running their own ``threadmaster``-based event loop. + +.. todo: replace these with SVG +.. figure:: ../figures/threadmaster-multiple.png + :align: center + + Lifecycle of a program using multiple pthreads, each running their own + ``threadmaster`` + +Each roundrect represents a single pthread running the same event loop +described under :ref:`event-architecture`. Note the arrow from the ``exec()`` +box on the right to the ``schedule()`` box in the middle pthread. This +illustrates code running in one pthread scheduling a task onto another +pthread's threadmaster. A global lock for each ``threadmaster`` is used to +synchronize these operations. The pthread names are examples. + + +.. This should be broken into its document under :ref:`libfrr` +.. _kernel-thread-wrapper: + Kernel Thread Wrapper ^^^^^^^^^^^^^^^^^^^^^ The basis for the integration of pthreads and the event system is a lightweight @@ -184,7 +224,9 @@ passing, where the messages are the regular task events as used in the event-driven model. The only difference is thread cancellation, which requires calling ``thread_cancel_async()`` instead of ``thread_cancel`` to cancel a task currently scheduled on a ``threadmaster`` belonging to a different pthread. -This is necessary +This is necessary to avoid race conditions in the specific case where one +pthread wants to guarantee that a task on another pthread is cancelled before +proceeding. In addition, the existing commands to show statistics and other information for tasks within the event driven model have been expanded to handle multiple diff --git a/doc/figures/threadmaster-multiple.png b/doc/figures/threadmaster-multiple.png Binary files differnew file mode 100644 index 0000000000..2ded50c4cb --- /dev/null +++ b/doc/figures/threadmaster-multiple.png diff --git a/doc/figures/threadmaster-single.png b/doc/figures/threadmaster-single.png Binary files differnew file mode 100644 index 0000000000..a068389b2a --- /dev/null +++ b/doc/figures/threadmaster-single.png diff --git a/doc/frr-sphinx.mk b/doc/frr-sphinx.mk index df4056760d..3e4c67d374 100644 --- a/doc/frr-sphinx.mk +++ b/doc/frr-sphinx.mk @@ -10,6 +10,11 @@ SPHINXBUILD ?= sphinx-build PAPER ?= BUILDDIR = _build +# This is a custom FRR variable just for this docs subdirectory used to support +# VPATH builds. Makefiles which include this file should override it to point +# to the correct sources path. +SOURCESDIR ?= . + # User-friendly check for sphinx-build ifneq ($(MAKECMDGOALS), clean) ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) @@ -23,9 +28,9 @@ endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCESDIR) # the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCESDIR) .PHONY: help help: diff --git a/doc/manpages/.gitignore b/doc/manpages/.gitignore index 0505537159..2e7d8573f1 100644 --- a/doc/manpages/.gitignore +++ b/doc/manpages/.gitignore @@ -1,3 +1,3 @@ /_templates /_build -!/Makefile +!/Makefile.in diff --git a/doc/manpages/Makefile b/doc/manpages/Makefile.in index ebbbc31009..f28746cee6 100644 --- a/doc/manpages/Makefile +++ b/doc/manpages/Makefile.in @@ -1,4 +1,11 @@ -include ../frr-sphinx.mk +# This is necessary to support VPATH builds. +srcdir = @srcdir@ +VPATH = @srcdir@ + +# This variable is used as the documentation source location in frr-sphinx.mk +SOURCESDIR = @srcdir@ + +include @srcdir@/../frr-sphinx.mk # ----------------------------------------------------------------------------- # Automake requires that 3rd-party Makefiles recognize these targets. diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index ae622bb699..6d3f3aae55 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -3,9 +3,6 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to FRR's documentation! -=============================== - .. toctree:: :maxdepth: 2 diff --git a/doc/user/.gitignore b/doc/user/.gitignore index 0505537159..2e7d8573f1 100644 --- a/doc/user/.gitignore +++ b/doc/user/.gitignore @@ -1,3 +1,3 @@ /_templates /_build -!/Makefile +!/Makefile.in diff --git a/doc/user/Makefile b/doc/user/Makefile.in index 223f8a64a3..77c6abf917 100644 --- a/doc/user/Makefile +++ b/doc/user/Makefile.in @@ -1,4 +1,11 @@ -include ../frr-sphinx.mk +# This is necessary to support VPATH builds. +srcdir = @srcdir@ +VPATH = @srcdir@ + +# This variable is used as the documentation source location in frr-sphinx.mk +SOURCESDIR = @srcdir@ + +include @srcdir@/../frr-sphinx.mk # ----------------------------------------------------------------------------- # Automake requires that 3rd-party Makefiles recognize these targets. diff --git a/doc/user/basic.rst b/doc/user/basic.rst index f134133da4..b861444e88 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -55,18 +55,23 @@ Basic Config Commands Set hostname of the router. -.. index:: password PASSWORD +.. index:: + single: no password PASSWORD + single: password PASSWORD -.. clicmd:: password PASSWORD +.. clicmd:: [no] password PASSWORD - Set password for vty interface. If there is no password, a vty won't - accept connections. + Set password for vty interface. The ``no`` form of the command deletes the + password. If there is no password, a vty won't accept connections. -.. index:: enable password PASSWORD +.. index:: + single: no enable password PASSWORD + single: enable password PASSWORD -.. clicmd:: enable password PASSWORD +.. clicmd:: [no] enable password PASSWORD - Set enable password. + Set enable password. The ``no`` form of the command deletes the enable + password. .. index:: single: no log trap [LEVEL] diff --git a/doc/user/bugs.rst b/doc/user/bugs.rst new file mode 100644 index 0000000000..60ddf612db --- /dev/null +++ b/doc/user/bugs.rst @@ -0,0 +1,67 @@ +.. _bugs: + +************** +Reporting Bugs +************** + +This file describes the procedure for reporting FRRouting bugs. You are asked +to follow this format when submitting bug reports. + +Bugs submitted with woefully incomplete information will receive little +attention and are likely to be closed. If you hit a suspected bug in an older +version, you may be asked to test with a later version in your environment. + +Often you may be asked for additional information to help solve the bug. Bugs +may be closed after 30 days of non-response to requests to reconfirm or supply +additional information. + +Please report bugs on the project GitHub issue tracker at +https://github.com/frrouting/frr/issues + +Report Format & Requested Information +===================================== + +When reporting a bug, please provide the following information. + +#. Your FRR version if it is a release build, or the commit hash if you built + from source. + +#. If you compiled from source, please provide your ``./configure`` line, + including all option flags. + +#. A full list of the FRR daemons you run. + +#. Your platform name and version, e.g. ``Ubuntu 18.04``. + +#. Problem description. + + - Provide as much information as possible. + - Copy and paste relevant commands and their output to describe your network + setup. + - Topology diagrams are helpful when reporting bugs involving more than one + box. + - Platform routing tables and interface configurations are useful if you are + reporting a routing issue. + + *Please be sure to review the provided information and censor any sensitive + material.* + +#. All FRR configuration files you use. Again, please be sure to censor any + sensitive information. For sensitive v4 / v6 addresses, we ask that you + censor the inner octets; e.g., ``192.XXX.XXX.32/24``. + +#. If you are reporting a crash and have a core file, please supply a stack + trace using GDB: + + :: + + $ gdb exec_file core_file + (gdb) bt . + +#. Run all FRR daemons with full debugging on and send *only* the portion of + logs which are relevant to your problem. + +#. Patches, workarounds, and fixes are always welcome. + +.. seealso:: :ref:`basic-config-commands` + diff --git a/doc/user/conf.py b/doc/user/conf.py index 886403b69d..3fced11024 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -342,6 +342,14 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False +# contents of ../extra/frrlexer.py. +# This is read here to support VPATH build. Since this section is execfile()'d +# with the file location, we can safely use a relative path here to save the +# contents of the lexer file for later use even if our relative path changes +# due to VPATH. +with open('../extra/frrlexer.py', 'rb') as lex: + frrlexerpy = lex.read() + # custom extensions here def setup(app): # object type for FRR CLI commands, can be extended to document parent CLI @@ -357,5 +365,5 @@ def setup(app): # # frrlexer = pygments.lexers.load_lexer_from_file('../extra/frrlexer.py', lexername="FRRLexer") custom_namespace = {} - exec(open('../extra/frrlexer.py', 'rb').read(), custom_namespace) + exec(frrlexerpy, custom_namespace) lexers['frr'] = custom_namespace['FRRLexer']() diff --git a/doc/user/getting-started.rst b/doc/user/getting-started.rst new file mode 100644 index 0000000000..4ef293c711 --- /dev/null +++ b/doc/user/getting-started.rst @@ -0,0 +1,9 @@ +*************** +Getting Started +*************** + +.. toctree:: + :maxdepth: 2 + + installation + setup diff --git a/doc/user/index.rst b/doc/user/index.rst index 28a85da8aa..7118022df7 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -1,11 +1,11 @@ -Welcome to FRR's documentation! -=============================== +FRRouting User Guide +==================== .. toctree:: :maxdepth: 2 overview - installation + getting-started basic vtysh filter @@ -27,6 +27,7 @@ Welcome to FRR's documentation! ripngd sharp vnc + bugs glossary appendix diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 8501054fdb..7a430fdf98 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -1,8 +1,7 @@ .. _installation: -************ Installation -************ +============ .. index:: How to install FRR .. index:: Installation @@ -10,46 +9,73 @@ Installation .. index:: Building the system .. index:: Making FRR -Several distributions provide packages for FRR. Check your distribution's -repositories to find out if a suitable version is available. +This section covers the basics of building, installing and setting up FRR. -FRR depends on various libraries depending on your operating system. +From Packages +------------- -After installing these dependencies, change to the frr source directory and -issue the following commands: +The project publishes packages for Red Hat, Centos, Debian and Ubuntu on the +`GitHub releases <https://github.com/FRRouting/frr/releases>`_. page. External +contributors offer packages for many other platforms including \*BSD, Alpine, +Gentoo, Docker, and others. There is currently no documentation on how to use +those but we hope to add it soon. -:: +From Snapcraft +-------------- - $ ./bootstrap.sh - $ ./configure - $ make - $ make install +In addition to traditional packages the project also builds and publishes +universal Snap images, available at https://snapcraft.io/frr. +From Source +----------- -.. _configure-the-software: +Building FRR from source is the best way to ensure you have the latest features +and bug fixes. Details for each supported platform, including dependency +package listings, permissions, and other gotchas, are in the developer's +documentation. This section provides a brief overview on the process. -Configure the Software -====================== +Getting the Source +^^^^^^^^^^^^^^^^^^ +FRR's source is available on the project +`GitHub page <https://github.com/FRRouting/frr>`_. -.. _the-configure-script: +.. code-block:: shell -The Configure Script --------------------- + git clone https://github.com/FRRouting/frr.git -.. index:: Configuration options +When building from Git there are several branches to choose from. The +``master`` branch is the primary development branch. It should be considered +unstable. Each release has its own branch named ``stable/X.X``, where ``X.X`` +is the release version. -.. index:: Options for configuring +In addition, release tarballs are published on the GitHub releases page +`here <https://github.com/FRRouting/frr/releases>`_. -.. index:: Build options +Configuration +^^^^^^^^^^^^^ +.. index:: Configuration options +.. index:: Options for configuring +.. index:: Build options .. index:: Distribution configuration - .. index:: Options to `./configure` -FRR has an excellent configure script which automatically detects most -host configurations. There are several additional configure options to -customize the build to include or exclude specific features and dependencies. +FRR has an excellent configure script which automatically detects most host +configurations. There are several additional configure options to customize the +build to include or exclude specific features and dependencies. + +First, update the build system. Change into your FRR source directory and issue: + +.. code-block:: shell + + ./bootstrap.sh + +This will install any missing build scripts and update the Autotools +configuration. Once this is done you can move on to choosing your configuration +options from the list below. + +.. _frr-configuration: .. program:: configure @@ -117,11 +143,6 @@ customize the build to include or exclude specific features and dependencies. Enable Traffic Engineering Extension for ISIS (RFC5305) -.. option:: --enable-multipath <ARG> - - Enable support for Equal Cost Multipath. `ARG` is the maximum number - of ECMP paths to allow, set to 0 to allow unlimited number of paths. - .. option:: --enable-realms Enable the support of Linux Realms. Convert tag values from 1-255 into a @@ -176,6 +197,14 @@ customize the build to include or exclude specific features and dependencies. With this option, we provide a way to strip out these characters for APK dev package builds. +.. option:: --enable-multipath=X + + Compile FRR with up to X way ECMP supported. This number can be from 0-999. + For backwards compatability with older configure options when setting X = 0, + we will build FRR with 64 way ECMP. This is needed because there are + hardcoded arrays that FRR builds towards, so we need to know how big to + make these arrays at build time. + You may specify any combination of the above options to the configure script. By default, the executables are placed in :file:`/usr/local/sbin` and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/` @@ -199,10 +228,9 @@ options to the configuration script. .. _least-privilege-support: Least-Privilege Support ------------------------ +""""""""""""""""""""""" .. index:: FRR Least-Privileges - .. index:: FRR Privileges Additionally, you may configure zebra to drop its elevated privileges @@ -237,112 +265,54 @@ only Linux), FRR will retain only minimal capabilities required and will only raise these capabilities for brief periods. On systems without libcap, FRR will run as the user specified and only raise its UID to 0 for brief periods. -.. _linux-notes: - Linux Notes ------------ - -.. index:: Configuring FRR +""""""""""" .. index:: Building on Linux boxes - .. index:: Linux configurations -There are several options available only to GNU/Linux systems [#]_. -If you use GNU/Linux, make sure that the current kernel configuration is what -you want. FRR will run with any kernel configuration but some recommendations -do exist. - - -- :makevar:`CONFIG_NETLINK` - Kernel/User Netlink socket. This is a brand new feature which enables an - advanced interface between the Linux kernel and zebra (:ref:`kernel-interface`). -- :makevar:`CONFIG_RTNETLINK` - Routing messages. - This makes it possible to receive Netlink routing messages. If you - specify this option, *zebra* can detect routing information - updates directly from the kernel (:ref:`kernel-interface`). -- :makevar:`CONFIG_IP_MULTICAST` - IP: multicasting. - This option should be specified when you use *ripd* (:ref:`rip`) or - *ospfd* (:ref:`ospfv2`) because these protocols use multicast. - -IPv6 support has been added in GNU/Linux kernel version 2.2. If you -try to use the FRR IPv6 feature on a GNU/Linux kernel, please -make sure the following libraries have been installed. Please note that -these libraries will not be needed when you uses GNU C library 2.1 -or upper. - -- inet6-apps - - The `inet6-apps` package includes basic IPv6 related libraries such - as `inet_ntop` and `inet_pton`. Some basic IPv6 programs such - as *ping*, *ftp*, and *inetd* are also - included. The `inet-apps` can be found at - `ftp://ftp.inner.net/pub/ipv6/ <ftp://ftp.inner.net/pub/ipv6/>`_. - -- net-tools - - The `net-tools` package provides an IPv6 enabled interface and routing - utility. It contains *ifconfig*, *route*, *netstat*, and other tools. - `net-tools` may be found at http://www.tazenda.demon.co.uk/phil/net-tools/. - -.. _build-the-software: - -Build the Software -================== - -After configuring the software, you will need to compile it for your system. -Simply issue the command *make* in the root of the source directory and the -software will be compiled. Cliff Notes versions of different compilation -examples can be found in the Developer's Manual Appendix. If you have *any* -problems at this stage, please send a bug report :ref:`bug-reports`. - -:: - - $ ./bootstrap.sh - $ ./configure <appropriate to your system> - $ make - +There are several options available only to GNU/Linux systems. If you use +GNU/Linux, make sure that the current kernel configuration is what you want. +FRR will run with any kernel configuration but some recommendations do exist. -Install the Software -==================== +:makevar:`CONFIG_NETLINK` + Kernel/User Netlink socket. This is a enables an advanced interface between + the Linux kernel and *zebra* (:ref:`kernel-interface`). -Installing the software to your system consists of copying the compiled -programs and supporting files to a standard location. After the -installation process has completed, these files have been copied -from your work directory to :file:`/usr/local/bin`, and :file:`/usr/local/etc`. +:makevar:`CONFIG_RTNETLINK` + This makes it possible to receive Netlink routing messages. If you specify + this option, *zebra* can detect routing information updates directly from + the kernel (:ref:`kernel-interface`). -To install the FRR suite, issue the following command at your shell -prompt::: +:makevar:`CONFIG_IP_MULTICAST` + This option enables IP multicast and should be specified when you use *ripd* + (:ref:`rip`) or *ospfd* (:ref:`ospfv2`) because these protocols use + multicast. - $ make install +Building +^^^^^^^^ -FRR daemons have their own terminal interface or VTY. After -installation, you have to setup each beast's port number to connect to -them. Please add the following entries to :file:`/etc/services`. +Once you have chosen your configure options, run the configure script and pass +the options you chose: -:: +.. code-block:: shell - zebrasrv 2600/tcp # zebra service - zebra 2601/tcp # zebra vty - ripd 2602/tcp # RIPd vty - ripngd 2603/tcp # RIPngd vty - ospfd 2604/tcp # OSPFd vty - bgpd 2605/tcp # BGPd vty - ospf6d 2606/tcp # OSPF6d vty - ospfapi 2607/tcp # ospfapi - isisd 2608/tcp # ISISd vty - nhrpd 2610/tcp # nhrpd vty - pimd 2611/tcp # PIMd vty + ./configure \ + --prefix=/usr \ + --enable-exampledir=/usr/share/doc/frr/examples/ \ + --localstatedir=/var/run/frr \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --enable-pimd \ + --enable-watchfrr \ + ... +After configuring the software, you are ready to build and install it for your +system. -If you use a FreeBSD newer than 2.2.8, the above entries are already -added to :file:`/etc/services` so there is no need to add it. If you -specify a port number when starting the daemon, these entries may not be -needed. +.. code-block:: shell -You may need to make changes to the config files in -|INSTALL_PREFIX_ETC|. :ref:`config-commands`. + make && sudo make install -.. [#] GNU/Linux has very flexible kernel configuration features. +If everything finishes successfully, FRR should be installed. You should now +skip to the section on :ref:`basic-setup`. diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 56f95e64b0..71bc047720 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -101,7 +101,7 @@ OSPF6 interface .. index:: ipv6 ospf6 hello-interval HELLOINTERVAL .. clicmd:: ipv6 ospf6 hello-interval HELLOINTERVAL - Sets interface's Hello Interval. Default 40 + Sets interface's Hello Interval. Default 10 .. index:: ipv6 ospf6 dead-interval DEADINTERVAL .. clicmd:: ipv6 ospf6 dead-interval DEADINTERVAL diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 38d55d68ad..eb24970bee 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -20,6 +20,8 @@ can use FRR library as your program's client user interface. FRR is distributed under the GNU General Public License. +FRR is a fork of `Quagga <http://www.quagga.net/>`_. + .. _about-frr: About FRR @@ -235,7 +237,8 @@ How to get FRR The official FRR website is located at |PACKAGE_URL| and contains further information, as well as links to additional resources. -FRR is a fork of `Quagga <http://www.quagga.net/>`_. +Several distributions provide packages for FRR. Check your distribution's +repositories to find out if a suitable version is available. Mailing Lists ============= diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index eec575cf3f..aa48a3cd4f 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -57,7 +57,7 @@ against incoming packets. If matched the nexthop-group or nexthop is used to forward the packets to the end destination .. index:: pbr-map -.. clicmd:: pbr-map NAME seq (1-1000) +.. clicmd:: pbr-map NAME seq (1-700) Create a pbr-map with NAME and sequence number specified. This command puts you into a new submode for pbr-map specification. To exit this mode type diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index bddf2ba26d..8080e001f1 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -180,6 +180,18 @@ Route Map Match Command interface name specified if the neighbor was specified in this manner. +.. index:: match source-protocol PROTOCOL_NAME +.. clicmd:: match source-protocol PROTOCOL_NAME + + This is a ZEBRA specific match command. Matches the + originating protocol specified. + +.. index:: match source-instance NUMBER +.. clicmd:: match source-instance NUMBER + + This is a ZEBRA specific match command. The number is a range from (0-255). + Matches the originating protocols instance specified. + .. _route-map-set-command: Route Map Set Command diff --git a/doc/user/setup.rst b/doc/user/setup.rst new file mode 100644 index 0000000000..9971c43c5e --- /dev/null +++ b/doc/user/setup.rst @@ -0,0 +1,155 @@ +.. _basic-setup: + +Basic Setup +============ + +After installing FRR, some basic configuration must be completed before it is +ready to use. + +Daemons File +------------ +After a fresh install, starting FRR will do nothing. This is because daemons +must be explicitly enabled by editing a file in your configuration directory. +This file is usually located at :file:`/etc/frr/daemons` and determines which +daemons are activated when issuing a service start / stop command via init or +systemd. The file initially looks like this: + +:: + + zebra=no + bgpd=no + ospfd=no + ospf6d=no + ripd=no + ripngd=no + isisd=no + pimd=no + ldpd=no + nhrpd=no + eigrpd=no + babeld=no + sharpd=no + pbrd=no + +To enable a particular daemon, simply change the corresponding 'no' to 'yes'. +Subsequent service restarts should start the daemon. + +Daemons Configuration File +-------------------------- +There is another file that controls the default options passed to daemons when +starting FRR as a service. This file is located in your configuration +directory, usually at :file:`/etc/frr/daemons.conf`. + +This file has several parts. Here is an example: + +:: + + # + # If this option is set the /etc/init.d/frr script automatically loads + # the config via "vtysh -b" when the servers are started. + # Check /etc/pam.d/frr if you intend to use "vtysh"! + # + vtysh_enable=yes + zebra_options=" -r -s 90000000 --daemon -A 127.0.0.1" + bgpd_options=" --daemon -A 127.0.0.1" + ospfd_options=" --daemon -A 127.0.0.1" + ospf6d_options=" --daemon -A ::1" + ripd_options=" --daemon -A 127.0.0.1" + ripngd_options=" --daemon -A ::1" + isisd_options=" --daemon -A 127.0.0.1" + pimd_options=" --daemon -A 127.0.0.1" + ldpd_options=" --daemon -A 127.0.0.1" + 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) + + # 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. + valgrind_enable=no + valgrind=/usr/bin/valgrind + +Breaking this file down: + +:: + + vtysh_enable=yes + +As the comment says, this causes :ref:`VTYSH <vty-shell>` to apply +configuration when starting the daemons. This is useful for a variety of +reasons touched on in the VTYSH documentation and should generally be enabled. + +:: + + zebra_options=" -r -s 90000000 --daemon -A 127.0.0.1" + bgpd_options=" --daemon -A 127.0.0.1" + ... + +The next set of lines controls what options are passed to daemons when started +from the service script. Usually daemons will have ``--daemon`` and ``-A +<address>`` specified in order to daemonize and listen for VTY commands on a +particular address. + +:: + + # 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) + +Options for the ``watchfrr``, the watchdog daemon. + +:: + + valgrind_enable=no + valgrind=/usr/bin/valgrind + +Whether or not to start FRR daemons under Valgrind. This is primarily useful +for gathering information for bug reports and for developers. +``valgrind_enable`` should be ``no`` for production use. + +Services +-------- +FRR daemons have their own terminal interface or VTY. After installation, it's +a good idea to setup each daemon's port number to connect to them. To do this +add the following entries to :file:`/etc/services`. + +:: + + zebrasrv 2600/tcp # zebra service + zebra 2601/tcp # zebra vty + ripd 2602/tcp # RIPd vty + ripngd 2603/tcp # RIPngd vty + ospfd 2604/tcp # OSPFd vty + bgpd 2605/tcp # BGPd vty + ospf6d 2606/tcp # OSPF6d vty + ospfapi 2607/tcp # ospfapi + isisd 2608/tcp # ISISd vty + babeld 2609/tcp # BABELd vty + nhrpd 2610/tcp # nhrpd vty + pimd 2611/tcp # PIMd vty + ldpd 2612/tcp # LDPd vty + eigprd 2613/tcp # EIGRPd vty + + +If you use a FreeBSD newer than 2.2.8, the above entries are already added to +:file:`/etc/services` so there is no need to add it. If you specify a port +number when starting the daemon, these entries may not be needed. + +You may need to make changes to the config files in |INSTALL_PREFIX_ETC|. + +systemd +------- +Although not installed when installing from source, FRR provides a service file +for use with ``systemd``. It is located in :file:`tools/frr.service` in the Git +repository. If ``systemctl status frr.service`` indicates that the FRR service +is not found, copy the service file from the Git repository into your preferred +location. A good place is usually ``/etc/systemd/system/``. + +After issuing a ``systemctl daemon-reload``, you should be able to start the +FRR service via ``systemctl start frr``. If this fails, or no daemons are +started. check the ``journalctl`` logs for an indication of what went wrong. diff --git a/doc/user/snmp.rst b/doc/user/snmp.rst index 1a24d56cb7..38918ff250 100644 --- a/doc/user/snmp.rst +++ b/doc/user/snmp.rst @@ -43,6 +43,9 @@ master SNMP agent (snmpd) and each of the FRR daemons must be configured. In In each of the FRR daemons, ``agentx`` command will enable AgentX support. :file:`/etc/snmp/snmpd.conf`: + +:: + # # example access restrictions setup # @@ -113,6 +116,9 @@ using the password "frr_ospfd". For testing it is recommending to take exactly the below snmpd.conf as wrong access restrictions can be hard to debug. :file:`/etc/snmp/snmpd.conf`: + +:: + # # example access restrictions setup # @@ -126,6 +132,9 @@ the below snmpd.conf as wrong access restrictions can be hard to debug. smuxpeer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd :file:`/etc/frr/ospf`: + +:: + ! ... the rest of ospfd.conf has been omitted for clarity ... ! smux peer .1.3.6.1.4.1.3317.1.2.5 frr_ospfd diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 0927a4dbe9..9d927359ba 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -31,7 +31,21 @@ Besides the common invocation options (:ref:`common-invocation-options`), the When program terminates, retain routes added by zebra. -.. program:: configure +.. option:: -e X, --ecmp X + + Run zebra with a limited ecmp ability compared to what it is compiled to. + If you are running zebra on hardware limited functionality you can + force zebra to limit the maximum ecmp allowed to X. This number + is bounded by what you compiled FRR with as the maximum number. + +.. option:: -n, --vrfwnetns + + When *Zebra* starts with this option, the VRF backend is based on Linux + network namespaces. That implies that all network namespaces discovered by + ZEBRA will create an associated VRF. The other daemons will operate on the VRF + VRF defined by *Zebra*, as usual. + + .. seealso:: :ref:`vrf` .. _interface-commands: @@ -47,6 +61,10 @@ Standard Commands .. clicmd:: interface IFNAME +.. index:: interface IFNAME vrf VRF + +.. clicmd:: interface IFNAME vrf VRF + .. index:: shutdown .. clicmd:: shutdown @@ -227,8 +245,8 @@ Link Parameters Commands Static Route Commands ===================== -Static routing is a very fundamental feature of routing technology. It -defines static prefix and gateway. +Static routing is a very fundamental feature of routing technology. It defines +static prefix and gateway. .. index:: ip route NETWORK GATEWAY .. clicmd:: ip route NETWORK GATEWAY @@ -246,16 +264,16 @@ defines static prefix and gateway. ip route 10.0.0.0/8 ppp0 ip route 10.0.0.0/8 null0 - First example defines 10.0.0.0/8 static route with gateway 10.0.0.2. - Second one defines the same prefix but with gateway to interface ppp0. The - third install a blackhole route. + First example defines 10.0.0.0/8 static route with gateway 10.0.0.2. Second + one defines the same prefix but with gateway to interface ppp0. The third + install a blackhole route. .. index:: ip route NETWORK NETMASK GATEWAY .. clicmd:: ip route NETWORK NETMASK GATEWAY - This is alternate version of above command. When NETWORK is - A.B.C.D format, user must define NETMASK value with A.B.C.D - format. GATEWAY is same option as above command. + This is alternate version of above command. When NETWORK is A.B.C.D format, + user must define NETMASK value with A.B.C.D format. GATEWAY is same option + as above command. .. code-block:: frr @@ -338,16 +356,139 @@ default) should the specified gateways not be reachable. E.g.: on Linux operating systems only, and perform AND matching on packet's destination and source addresses in the kernel's forwarding path. Note that destination longest-prefix match is "more important" than source LPM, e.g. - *"2001:db8:1::/64 from 2001:db8::/48"* will win over - *"2001:db8::/48 from 2001:db8:1::/64"* if both match. + ``2001:db8:1::/64 from 2001:db8::/48`` will win over + ``2001:db8::/48 from 2001:db8:1::/64`` if both match. .. index:: table TABLENO .. clicmd:: table TABLENO - Select the primary kernel routing table to be used. This only works - for kernels supporting multiple routing tables (like GNU/Linux 2.2.x - and later). After setting TABLENO with this command, - static routes defined after this are added to the specified table. + Select the primary kernel routing table to be used. This only works for + kernels supporting multiple routing tables (like GNU/Linux 2.2.x and later). + After setting TABLENO with this command, static routes defined after this + are added to the specified table. + +.. _vrf: + +Virtual Routing and Forwarding +============================== + +FRR supports :abbr:`VRF (Virtual Routing and Forwarding)`. VRF is a way to +separate networking contexts on the same machine. Those networking contexts are +associated with separate interfaces, thus making it possible to associate one +interface with a specific VRF. + +VRF can be used, for example, when instantiating per enterprise networking +services, without having to instantiate the physical host machine or the +routing management daemons for each enterprise. As a result, interfaces are +separate for each set of VRF, and routing daemons can have their own context +for each VRF. + +This conceptual view introduces the *Default VRF* case. If the user does not +configure any specific VRF, then by default, FRR uses the *Default VRF*. + +In the context of *Zebra*, this is done automatically when configuring a static +route with, for example, :clicmd:`ip route NETWORK GATEWAY`: + +.. code-block:: frr + + # case without VRF + configure terminal + ip route 10.0.0.0 255.255.255.0 10.0.0.2 + exit + +Configuring VRF networking contexts can be done in various ways on FRR. The VRF +interfaces can be configured by entering in interface configuration mode +:clicmd:`interface IFNAME vrf VRF`. Also, if the user wants to configure a +static route for a specific VRF, then a specific VRF configuration mode is +available. After entering into that mode with :clicmd:`vrf VRF` the user can +enter the same route command as before, but this time, the route command will +apply to the VRF. + +.. code-block:: frr + + # case with VRF + configure terminal + vrf r1-cust1 + ip route 10.0.0.0 255.255.255.0 10.0.0.2 + exit-vrf + +A VRF backend mode is chosen when running *Zebra*. + +If no option is chosen, then the *Linux VRF* implementation as references in +https://www.kernel.org/doc/Documentation/networking/vrf.txt will be mapped over +the *Zebra* VRF. The routing table associated to that VRF is a Linux table +identifier located in the same *Linux network namespace* where *Zebra* started. + +If the :option:`-n` option is chosen, then the *Linux network namespace* will +be mapped over the *Zebra* VRF. That implies that *Zebra* is able to configure +several *Linux network namespaces*. The routing table associated to that VRF +is the whole routing tables located in that namespace. For instance, this mode +matches OpenStack Network Namespaces. It matches also OpenFastPath. The default +behavior remains Linux VRF which is supported by the Linux kernel community, +see https://www.kernel.org/doc/Documentation/networking/vrf.txt. + +Because of that difference, there are some subtle differences when running some +commands in relationship to VRF. Here is an extract of some of those commands: + +.. index:: vrf VRF +.. clicmd:: vrf VRF + + This command is available on configuration mode. By default, above command + permits accessing the vrf configuration mode. This mode is available for + both VRFs. It is to be noted that *Zebra* does not create *Linux VRF*. + Provisioning this command is used to keep the configuration intact. + +.. index:: netns NAMESPACE +.. clicmd:: netns NAMESPACE + + This command is based on VRF configuration mode. This command is available + when *Zebra* is run in :option:`-n` mode. This command reflects which *Linux + network namespace* is to be mapped with *Zebra* VRF. It is to be noted that + *Zebra* creates and detects added/suppressed VRFs from the Linux environment + (in fact, those managed with iproute2). Provisioning this command is used to + keep the configuration intact. + +.. index:: ip route NETWORK NETMASK GATEWAY NEXTHOPVRF +.. clicmd:: ip route NETWORK NETMASK GATEWAY NEXTHOPVRF + + This command is based on VRF configuration mode or in configuration mode. If + on configuration mode, this applies to default VRF. Otherwise, this command + applies to the VRF of the vrf configuration mode. This command is used to + configure a vrf route leak across 2 VRFs. This command is only available + when *Zebra* is launched without :option:`-n` option. + +.. index:: ip route NETWORK NETMASK GATEWAY table TABLENO +.. clicmd:: ip route NETWORK NETMASK GATEWAY table TABLENO + + This command is based on configuration mode. There, for default VRF, this + command is available for all modes. The ``TABLENO`` configured is one of the + tables from Default *Linux network namespace*. + +.. index:: show ip route vrf VRF +.. clicmd:: show ip route vrf VRF + + The show command permits dumping the routing table associated to the VRF. If + *Zebra* is launched with default settings, this will be the ``TABLENO`` of + the VRF configured on the kernel, thanks to information provided in + https://www.kernel.org/doc/Documentation/networking/vrf.txt. If *Zebra* is + launched with :option:`-n` option, this will be the default routing table of + the *Linux network namespace* ``VRF``. + +.. index:: show ip route vrf VRF table TABLENO +.. clicmd:: show ip route vrf VRF table TABLENO + + The show command is only available with :option:`-n` option. This command + will dump the routing table ``TABLENO`` of the *Linux network namespace* + ``VRF``. + +The usual static route commands are also available in the VRF context. When +entered within the VRF context the static routes are created in the VRF. + +.. code-block:: frr + + ip route 10.0.0.0 255.255.255.0 10.0.0.2 vrf r1-cust1 table 43 + show ip table vrf r1-cust1 table 43 + .. _multicast-rib-commands: @@ -519,6 +660,8 @@ The FIB push interface comprises of a TCP connection between zebra and the FPM. The connection is initiated by zebra -- that is, the FPM acts as the TCP server. +.. program:: configure + The relevant zebra code kicks in when zebra is configured with the :option:`--enable-fpm` flag. Zebra periodically attempts to connect to the well-known FPM port. Once the connection is up, zebra starts diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 9c61512df4..e903dc8c7f 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -184,8 +184,7 @@ static void isis_redist_update_ext_reach(struct isis_area *area, int level, route_map_result_t map_ret; memcpy(&area_info, info, sizeof(area_info)); - if (redist->metric != 0xffffffff) - area_info.metric = redist->metric; + area_info.metric = redist->metric; if (redist->map_name) { map_ret = @@ -540,7 +539,7 @@ DEFUN (isis_redistribute, int afi; int type; int level; - unsigned long metric; + unsigned long metric = 0; const char *routemap = NULL; family = str2family(argv[idx_afi]->text); @@ -567,9 +566,6 @@ DEFUN (isis_redistribute, return CMD_WARNING_CONFIG_FAILED; } - metric = 0xffffffff; - routemap = NULL; - if (argc > idx_metric_rmap + 1) { if (argv[idx_metric_rmap + 1]->arg[0] == '\0') return CMD_WARNING_CONFIG_FAILED; @@ -651,7 +647,7 @@ DEFUN (isis_default_originate, int family; int originate_type = DEFAULT_ORIGINATE; int level; - unsigned long metric = 0xffffffff; + unsigned long metric = 0; const char *routemap = NULL; family = str2family(argv[idx_afi]->text); @@ -748,7 +744,7 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area, continue; vty_out(vty, " redistribute %s %s level-%d", family_str, zebra_route_string(type), level); - if (redist->metric != 0xffffffff) + if (redist->metric) vty_out(vty, " metric %u", redist->metric); if (redist->map_name) vty_out(vty, " route-map %s", redist->map_name); @@ -766,7 +762,7 @@ int isis_redist_config_write(struct vty *vty, struct isis_area *area, family_str, level); if (redist->redist == DEFAULT_ORIGINATE_ALWAYS) vty_out(vty, " always"); - if (redist->metric != 0xffffffff) + if (redist->metric) vty_out(vty, " metric %u", redist->metric); if (redist->map_name) vty_out(vty, " route-map %s", redist->map_name); diff --git a/lib/command.c b/lib/command.c index 2744061b5a..0fa6bde334 100644 --- a/lib/command.c +++ b/lib/command.c @@ -85,6 +85,7 @@ const char *node_names[] = { "keychain", // KEYCHAIN_NODE, "keychain key", // KEYCHAIN_KEY_NODE, "logical-router", // LOGICALROUTER_NODE, + "static ip", // IP_NODE, "vrf", // VRF_NODE, "interface", // INTERFACE_NODE, "nexthop-group", // NH_GROUP_NODE, @@ -119,7 +120,6 @@ const char *node_names[] = { "ldp l2vpn", // LDP_L2VPN_NODE, "ldp", // LDP_PSEUDOWIRE_NODE, "isis", // ISIS_NODE, - "static ip", // IP_NODE, "ipv4 access list", // ACCESS_NODE, "ipv4 prefix list", // PREFIX_NODE, "ipv6 access list", // ACCESS_IPV6_NODE, @@ -529,87 +529,103 @@ static int config_write_host(struct vty *vty) if (cmd_domainname_get()) vty_out(vty, "domainname %s\n", cmd_domainname_get()); - if (host.encrypt) { - if (host.password_encrypt) - vty_out(vty, "password 8 %s\n", host.password_encrypt); - if (host.enable_encrypt) - vty_out(vty, "enable password 8 %s\n", - host.enable_encrypt); - } else { - if (host.password) - vty_out(vty, "password %s\n", host.password); - if (host.enable) - vty_out(vty, "enable password %s\n", host.enable); - } + /* The following are all configuration commands that are not sent to + * watchfrr. For instance watchfrr is hardcoded to log to syslog so + * we would always display 'log syslog informational' in the config + * which would cause other daemons to then switch to syslog when they + * parse frr.conf. + */ + if (strcmp(zlog_default->protoname, "WATCHFRR")) { + if (host.encrypt) { + if (host.password_encrypt) + vty_out(vty, "password 8 %s\n", + host.password_encrypt); + if (host.enable_encrypt) + vty_out(vty, "enable password 8 %s\n", + host.enable_encrypt); + } else { + if (host.password) + vty_out(vty, "password %s\n", host.password); + if (host.enable) + vty_out(vty, "enable password %s\n", + host.enable); + } - if (zlog_default->default_lvl != LOG_DEBUG) { - vty_out(vty, "! N.B. The 'log trap' command is deprecated.\n"); - vty_out(vty, "log trap %s\n", - zlog_priority[zlog_default->default_lvl]); - } + if (zlog_default->default_lvl != LOG_DEBUG) { + vty_out(vty, + "! N.B. The 'log trap' command is deprecated.\n"); + vty_out(vty, "log trap %s\n", + zlog_priority[zlog_default->default_lvl]); + } - if (host.logfile - && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { - vty_out(vty, "log file %s", host.logfile); - if (zlog_default->maxlvl[ZLOG_DEST_FILE] - != zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority - [zlog_default->maxlvl[ZLOG_DEST_FILE]]); - vty_out(vty, "\n"); - } + if (host.logfile + && (zlog_default->maxlvl[ZLOG_DEST_FILE] + != ZLOG_DISABLED)) { + vty_out(vty, "log file %s", host.logfile); + if (zlog_default->maxlvl[ZLOG_DEST_FILE] + != zlog_default->default_lvl) + vty_out(vty, " %s", + zlog_priority + [zlog_default->maxlvl + [ZLOG_DEST_FILE]]); + vty_out(vty, "\n"); + } - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) { - vty_out(vty, "log stdout"); - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] - != zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority[zlog_default->maxlvl - [ZLOG_DEST_STDOUT]]); - vty_out(vty, "\n"); - } + if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) { + vty_out(vty, "log stdout"); + if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] + != zlog_default->default_lvl) + vty_out(vty, " %s", + zlog_priority + [zlog_default->maxlvl + [ZLOG_DEST_STDOUT]]); + vty_out(vty, "\n"); + } - if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) - vty_out(vty, "no log monitor\n"); - else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] - != zlog_default->default_lvl) - vty_out(vty, "log monitor %s\n", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]]); - - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { - vty_out(vty, "log syslog"); - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] - != zlog_default->default_lvl) - vty_out(vty, " %s", + if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out(vty, "no log monitor\n"); + else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] + != zlog_default->default_lvl) + vty_out(vty, "log monitor %s\n", zlog_priority[zlog_default->maxlvl - [ZLOG_DEST_SYSLOG]]); - vty_out(vty, "\n"); - } + [ZLOG_DEST_MONITOR]]); + + if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { + vty_out(vty, "log syslog"); + if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] + != zlog_default->default_lvl) + vty_out(vty, " %s", + zlog_priority[zlog_default->maxlvl + [ZLOG_DEST_SYSLOG]]); + vty_out(vty, "\n"); + } - if (zlog_default->facility != LOG_DAEMON) - vty_out(vty, "log facility %s\n", - facility_name(zlog_default->facility)); + if (zlog_default->facility != LOG_DAEMON) + vty_out(vty, "log facility %s\n", + facility_name(zlog_default->facility)); - if (zlog_default->record_priority == 1) - vty_out(vty, "log record-priority\n"); + if (zlog_default->record_priority == 1) + vty_out(vty, "log record-priority\n"); - if (zlog_default->timestamp_precision > 0) - vty_out(vty, "log timestamp precision %d\n", - zlog_default->timestamp_precision); + if (zlog_default->timestamp_precision > 0) + vty_out(vty, "log timestamp precision %d\n", + zlog_default->timestamp_precision); - if (host.advanced) - vty_out(vty, "service advanced-vty\n"); + if (host.advanced) + vty_out(vty, "service advanced-vty\n"); - if (host.encrypt) - vty_out(vty, "service password-encryption\n"); + if (host.encrypt) + vty_out(vty, "service password-encryption\n"); - if (host.lines >= 0) - vty_out(vty, "service terminal-length %d\n", host.lines); + if (host.lines >= 0) + vty_out(vty, "service terminal-length %d\n", + host.lines); - if (host.motdfile) - vty_out(vty, "banner motd file %s\n", host.motdfile); - else if (!host.motd) - vty_out(vty, "no banner motd\n"); + if (host.motdfile) + vty_out(vty, "banner motd file %s\n", host.motdfile); + else if (!host.motd) + vty_out(vty, "no banner motd\n"); + } if (debug_memstats_at_exit) vty_out(vty, "!\ndebug memstats-at-exit\n"); @@ -1895,7 +1911,7 @@ DEFUN (config_no_hostname, DEFUN (config_password, password_cmd, "password [(8-8)] WORD", - "Assign the terminal connection password\n" + "Modify the terminal connection password\n" "Specifies a HIDDEN password will follow\n" "The password string\n") { @@ -1934,6 +1950,34 @@ DEFUN (config_password, return CMD_SUCCESS; } +/* VTY interface password delete. */ +DEFUN (no_config_password, + no_password_cmd, + "no password", + NO_STR + "Modify the terminal connection password\n") +{ + bool warned = false; + + if (host.password) { + if (!vty_shell_serv(vty)) { + vty_out(vty, NO_PASSWD_CMD_WARNING); + warned = true; + } + XFREE(MTYPE_HOST, host.password); + } + host.password = NULL; + + if (host.password_encrypt) { + if (!warned && !vty_shell_serv(vty)) + vty_out(vty, NO_PASSWD_CMD_WARNING); + XFREE(MTYPE_HOST, host.password_encrypt); + } + host.password_encrypt = NULL; + + return CMD_SUCCESS; +} + /* VTY enable password set. */ DEFUN (config_enable_password, enable_password_cmd, @@ -1995,12 +2039,22 @@ DEFUN (no_config_enable_password, "Modify enable password parameters\n" "Assign the privileged level password\n") { - if (host.enable) + bool warned = false; + + if (host.enable) { + if (!vty_shell_serv(vty)) { + vty_out(vty, NO_PASSWD_CMD_WARNING); + warned = true; + } XFREE(MTYPE_HOST, host.enable); + } host.enable = NULL; - if (host.enable_encrypt) + if (host.enable_encrypt) { + if (!warned && !vty_shell_serv(vty)) + vty_out(vty, NO_PASSWD_CMD_WARNING); XFREE(MTYPE_HOST, host.enable_encrypt); + } host.enable_encrypt = NULL; return CMD_SUCCESS; @@ -2304,7 +2358,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel) #if defined(HAVE_CUMULUS) if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) - zlog_default->maxlvl[ZLOG_DEST_SYSLOG] = ZLOG_DISABLED; + zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); #endif return CMD_SUCCESS; } @@ -2330,6 +2384,16 @@ DEFUN (config_log_file, zlog_default->default_lvl); } +static void disable_log_file(void) +{ + zlog_reset_file(); + + if (host.logfile) + XFREE(MTYPE_HOST, host.logfile); + + host.logfile = NULL; +} + DEFUN (no_config_log_file, no_config_log_file_cmd, "no log file [FILENAME [LEVEL]]", @@ -2339,13 +2403,7 @@ DEFUN (no_config_log_file, "Logging file name\n" "Logging level\n") { - zlog_reset_file(); - - if (host.logfile) - XFREE(MTYPE_HOST, host.logfile); - - host.logfile = NULL; - + disable_log_file(); return CMD_SUCCESS; } @@ -2357,6 +2415,9 @@ DEFUN (config_log_syslog, LOG_LEVEL_DESC) { int idx_log_levels = 2; + + disable_log_file(); + if (argc == 3) { int level; if ((level = level_match(argv[idx_log_levels]->arg)) @@ -2710,6 +2771,7 @@ void cmd_init(int terminal) if (terminal > 0) { install_element(CONFIG_NODE, &password_cmd); + install_element(CONFIG_NODE, &no_password_cmd); install_element(CONFIG_NODE, &enable_password_cmd); install_element(CONFIG_NODE, &no_enable_password_cmd); diff --git a/lib/command.h b/lib/command.h index f18de3417c..8d9c39b0ea 100644 --- a/lib/command.h +++ b/lib/command.h @@ -85,6 +85,7 @@ enum node_type { KEYCHAIN_NODE, /* Key-chain node. */ KEYCHAIN_KEY_NODE, /* Key-chain key node. */ LOGICALROUTER_NODE, /* Logical-Router node. */ + IP_NODE, /* Static ip route node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ NH_GROUP_NODE, /* Nexthop-Group mode node. */ @@ -119,7 +120,6 @@ enum node_type { LDP_L2VPN_NODE, /* LDP L2VPN node */ LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ ISIS_NODE, /* ISIS protocol mode */ - IP_NODE, /* Static ip route node. */ ACCESS_NODE, /* Access list node. */ PREFIX_NODE, /* Prefix list node. */ ACCESS_IPV6_NODE, /* Access list node. */ @@ -376,6 +376,10 @@ struct cmd_node { #define CONF_BACKUP_EXT ".sav" +/* Command warnings. */ +#define NO_PASSWD_CMD_WARNING \ + "Please be aware that removing the password is a security risk and you should think twice about this command.\n" + /* IPv4 only machine should not accept IPv6 address for peer's IP address. So we replace VTY command string like below. */ #define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" diff --git a/lib/ipaddr.h b/lib/ipaddr.h index 33591cb4e7..7f2d06548b 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -102,4 +102,14 @@ static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6, memcpy((char *)in6 + 12, &in, sizeof(struct in_addr)); } +/* + * convert an ipv4 mapped ipv6 address back to ipv4 address + */ +static inline void ipv4_mapped_ipv6_to_ipv4(struct in6_addr *in6, + struct in_addr *in) +{ + memset(in, 0, sizeof(struct in_addr)); + memcpy(in, (char *)in6 + 12, sizeof(struct in_addr)); +} + #endif /* __IPADDR_H__ */ diff --git a/lib/prefix.c b/lib/prefix.c index b38dd94589..05af190e9d 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1206,54 +1206,104 @@ int str2prefix(const char *str, struct prefix *p) return 0; } -static const char *prefixevpn2str(const struct prefix *p, char *str, int size) +static const char *prefixevpn_ead2str(const struct prefix_evpn *p, char *str, + int size) +{ + snprintf(str, size, "Unsupported EVPN prefix"); + return str; +} + +static const char *prefixevpn_macip2str(const struct prefix_evpn *p, char *str, + int size) { uint8_t family; char buf[PREFIX2STR_BUFFER]; char buf2[ETHER_ADDR_STRLEN]; - if (p->u.prefix_evpn.route_type == 2) { - if (IS_EVPN_PREFIX_IPADDR_NONE((struct prefix_evpn *)p)) - snprintf(str, size, "[%d]:[%s]/%d", - p->u.prefix_evpn.route_type, - prefix_mac2str(&p->u.prefix_evpn.mac, buf2, - sizeof(buf2)), - p->prefixlen); - else { - family = IS_EVPN_PREFIX_IPADDR_V4( - (struct prefix_evpn *)p) - ? AF_INET - : AF_INET6; - snprintf(str, size, "[%d]:[%s]:[%s]/%d", - p->u.prefix_evpn.route_type, - prefix_mac2str(&p->u.prefix_evpn.mac, buf2, - sizeof(buf2)), - inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, - buf, PREFIX2STR_BUFFER), - p->prefixlen); - } - } else if (p->u.prefix_evpn.route_type == 3) { - family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p) - ? AF_INET - : AF_INET6; - snprintf(str, size, "[%d]:[%s]/%d", p->u.prefix_evpn.route_type, - inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf, - PREFIX2STR_BUFFER), + if (is_evpn_prefix_ipaddr_none(p)) + snprintf(str, size, "[%d]:[%s]/%d", + p->prefix.route_type, + prefix_mac2str(&p->prefix.macip_addr.mac, + buf2, sizeof(buf2)), p->prefixlen); - } else if (p->u.prefix_evpn.route_type == 5) { - family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p) + else { + family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6; - snprintf(str, size, "[%d]:[%u][%s/%d]/%d", - p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag, - inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf, - PREFIX2STR_BUFFER), - p->u.prefix_evpn.ip_prefix_length, p->prefixlen); - } else { - sprintf(str, "Unsupported EVPN route type %d", - p->u.prefix_evpn.route_type); + snprintf(str, size, "[%d]:[%s]:[%s]/%d", + p->prefix.route_type, + prefix_mac2str(&p->prefix.macip_addr.mac, + buf2, sizeof(buf2)), + inet_ntop(family, + &p->prefix.macip_addr.ip.ip.addr, + buf, PREFIX2STR_BUFFER), + p->prefixlen); } + return str; +} + +static const char *prefixevpn_imet2str(const struct prefix_evpn *p, char *str, + int size) +{ + uint8_t family; + char buf[PREFIX2STR_BUFFER]; + + family = is_evpn_prefix_ipaddr_v4(p) + ? AF_INET + : AF_INET6; + snprintf(str, size, "[%d]:[%s]/%d", p->prefix.route_type, + inet_ntop(family, + &p->prefix.imet_addr.ip.ip.addr, buf, + PREFIX2STR_BUFFER), + p->prefixlen); + return str; +} +static const char *prefixevpn_es2str(const struct prefix_evpn *p, char *str, + int size) +{ + snprintf(str, size, "Unsupported EVPN prefix"); + return str; +} + +static const char *prefixevpn_prefix2str(const struct prefix_evpn *p, char *str, + int size) +{ + uint8_t family; + char buf[PREFIX2STR_BUFFER]; + + family = is_evpn_prefix_ipaddr_v4(p) + ? AF_INET + : AF_INET6; + snprintf(str, size, "[%d]:[%u][%s/%d]/%d", + p->prefix.route_type, + p->prefix.prefix_addr.eth_tag, + inet_ntop(family, + &p->prefix.prefix_addr.ip.ip.addr, buf, + PREFIX2STR_BUFFER), + p->prefix.prefix_addr.ip_prefix_length, + p->prefixlen); + return str; +} + +static const char *prefixevpn2str(const struct prefix_evpn *p, char *str, + int size) +{ + switch (p->prefix.route_type) { + case 1: + return prefixevpn_ead2str(p, str, size); + case 2: + return prefixevpn_macip2str(p, str, size); + case 3: + return prefixevpn_imet2str(p, str, size); + case 4: + return prefixevpn_es2str(p, str, size); + case 5: + return prefixevpn_prefix2str(p, str, size); + default: + snprintf(str, size, "Unsupported EVPN prefix"); + break; + } return str; } @@ -1277,7 +1327,7 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) break; case AF_EVPN: - prefixevpn2str(p, str, size); + prefixevpn2str((const struct prefix_evpn *)p, str, size); break; case AF_FLOWSPEC: diff --git a/lib/prefix.h b/lib/prefix.h index f01c85b811..ab3c05ae74 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -56,26 +56,56 @@ struct ethaddr { #define PREFIX_LEN_ROUTE_TYPE_5_IPV4 (18*8) #define PREFIX_LEN_ROUTE_TYPE_5_IPV6 (30*8) -/* EVPN address (RFC 7432) */ -struct evpn_addr { - uint8_t route_type; +typedef struct esi_t_ { + uint8_t val[10]; +} esi_t; + +struct evpn_ead_addr { + esi_t esi; + uint32_t eth_tag; +}; + +struct evpn_macip_addr { + uint32_t eth_tag; uint8_t ip_prefix_length; struct ethaddr mac; + struct ipaddr ip; +}; + +struct evpn_imet_addr { uint32_t eth_tag; + uint8_t ip_prefix_length; struct ipaddr ip; -#if 0 - union - { - uint8_t addr; - struct in_addr v4_addr; - struct in6_addr v6_addr; - } ip; -#endif }; -#define IS_EVPN_PREFIX_IPADDR_NONE(evp) IS_IPADDR_NONE(&(evp)->prefix.ip) -#define IS_EVPN_PREFIX_IPADDR_V4(evp) IS_IPADDR_V4(&(evp)->prefix.ip) -#define IS_EVPN_PREFIX_IPADDR_V6(evp) IS_IPADDR_V6(&(evp)->prefix.ip) +struct evpn_es_addr { + esi_t esi; + uint8_t ip_prefix_length; + struct ipaddr ip; +}; + +struct evpn_prefix_addr { + uint32_t eth_tag; + uint8_t ip_prefix_length; + struct ipaddr ip; +}; + +/* EVPN address (RFC 7432) */ +struct evpn_addr { + uint8_t route_type; + union { + struct evpn_ead_addr _ead_addr; + struct evpn_macip_addr _macip_addr; + struct evpn_imet_addr _imet_addr; + struct evpn_es_addr _es_addr; + struct evpn_prefix_addr _prefix_addr; + } u; +#define ead_addr u._ead_addr +#define macip_addr u._macip_addr +#define imet_addr u._imet_addr +#define es_addr u._es_addr +#define prefix_addr u._prefix_addr +}; /* * A struct prefix contains an address family, a prefix length, and an @@ -177,6 +207,39 @@ struct prefix_evpn { struct evpn_addr prefix __attribute__((aligned(8))); }; +static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp) +{ + if (evp->prefix.route_type == 2) + return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip); + if (evp->prefix.route_type == 3) + return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip); + if (evp->prefix.route_type == 5) + return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip); + return 0; +} + +static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp) +{ + if (evp->prefix.route_type == 2) + return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip); + if (evp->prefix.route_type == 3) + return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip); + if (evp->prefix.route_type == 5) + return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip); + return 0; +} + +static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp) +{ + if (evp->prefix.route_type == 2) + return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip); + if (evp->prefix.route_type == 3) + return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip); + if (evp->prefix.route_type == 5) + return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip); + return 0; +} + /* Prefix for a generic pointer */ struct prefix_ptr { uint8_t family; diff --git a/lib/stream.c b/lib/stream.c index c4edd3d5bf..aba4c20166 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -21,6 +21,7 @@ #include <zebra.h> #include <stddef.h> +#include <pthread.h> #include "stream.h" #include "memory.h" @@ -1101,6 +1102,7 @@ struct stream_fifo *stream_fifo_new(void) struct stream_fifo *new; new = XCALLOC(MTYPE_STREAM_FIFO, sizeof(struct stream_fifo)); + pthread_mutex_init(&new->mtx, NULL); return new; } @@ -1115,7 +1117,16 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) fifo->tail = s; fifo->tail->next = NULL; - fifo->count++; + atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); +} + +void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s) +{ + pthread_mutex_lock(&fifo->mtx); + { + stream_fifo_push(fifo, s); + } + pthread_mutex_unlock(&fifo->mtx); } /* Delete first stream from fifo. */ @@ -1131,7 +1142,8 @@ struct stream *stream_fifo_pop(struct stream_fifo *fifo) if (fifo->head == NULL) fifo->tail = NULL; - fifo->count--; + atomic_fetch_sub_explicit(&fifo->count, 1, + memory_order_release); /* ensure stream is scrubbed of references to this fifo */ s->next = NULL; @@ -1140,12 +1152,37 @@ struct stream *stream_fifo_pop(struct stream_fifo *fifo) return s; } -/* Return first fifo entry. */ +struct stream *stream_fifo_pop_safe(struct stream_fifo *fifo) +{ + struct stream *ret; + + pthread_mutex_lock(&fifo->mtx); + { + ret = stream_fifo_pop(fifo); + } + pthread_mutex_unlock(&fifo->mtx); + + return ret; +} + struct stream *stream_fifo_head(struct stream_fifo *fifo) { return fifo->head; } +struct stream *stream_fifo_head_safe(struct stream_fifo *fifo) +{ + struct stream *ret; + + pthread_mutex_lock(&fifo->mtx); + { + ret = stream_fifo_head(fifo); + } + pthread_mutex_unlock(&fifo->mtx); + + return ret; +} + void stream_fifo_clean(struct stream_fifo *fifo) { struct stream *s; @@ -1156,11 +1193,26 @@ void stream_fifo_clean(struct stream_fifo *fifo) stream_free(s); } fifo->head = fifo->tail = NULL; - fifo->count = 0; + atomic_store_explicit(&fifo->count, 0, memory_order_release); +} + +void stream_fifo_clean_safe(struct stream_fifo *fifo) +{ + pthread_mutex_lock(&fifo->mtx); + { + stream_fifo_clean(fifo); + } + pthread_mutex_unlock(&fifo->mtx); +} + +size_t stream_fifo_count_safe(struct stream_fifo *fifo) +{ + return atomic_load_explicit(&fifo->count, memory_order_acquire); } void stream_fifo_free(struct stream_fifo *fifo) { stream_fifo_clean(fifo); + pthread_mutex_destroy(&fifo->mtx); XFREE(MTYPE_STREAM_FIFO, fifo); } diff --git a/lib/stream.h b/lib/stream.h index cc74e22a67..e5d325e43e 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -22,6 +22,9 @@ #ifndef _ZEBRA_STREAM_H #define _ZEBRA_STREAM_H +#include <pthread.h> + +#include "frratomic.h" #include "mpls.h" #include "prefix.h" @@ -107,7 +110,11 @@ struct stream { /* First in first out queue structure. */ struct stream_fifo { - size_t count; + /* lock for mt-safe operations */ + pthread_mutex_t mtx; + + /* number of streams in this fifo */ + _Atomic size_t count; struct stream *head; struct stream *tail; @@ -240,12 +247,94 @@ extern int stream_empty(struct stream *); /* is the stream empty? */ /* deprecated */ extern uint8_t *stream_pnt(struct stream *); -/* Stream fifo. */ +/* + * Operations on struct stream_fifo. + * + * Each function has a safe variant, which ensures that the operation performed + * is atomic with respect to the operations performed by all other safe + * variants. In other words, the safe variants lock the stream_fifo's mutex + * before performing their action. These are provided for convenience when + * using stream_fifo in a multithreaded context, to alleviate the need for the + * caller to implement their own synchronization around the stream_fifo. + * + * The following functions do not have safe variants. The caller must ensure + * that these operations are performed safely in a multithreaded context: + * - stream_fifo_new + * - stream_fifo_free + */ + +/* + * Create a new stream_fifo. + * + * Returns: + * newly created stream_fifo + */ extern struct stream_fifo *stream_fifo_new(void); + +/* + * Push a stream onto a stream_fifo. + * + * fifo + * the stream_fifo to push onto + * + * s + * the stream to push onto the stream_fifo + */ extern void stream_fifo_push(struct stream_fifo *fifo, struct stream *s); +extern void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s); + +/* + * Pop a stream off a stream_fifo. + * + * fifo + * the stream_fifo to pop from + * + * Returns: + * the next stream in the stream_fifo + */ extern struct stream *stream_fifo_pop(struct stream_fifo *fifo); +extern struct stream *stream_fifo_pop_safe(struct stream_fifo *fifo); + +/* + * Retrieve the next stream from a stream_fifo without popping it. + * + * fifo + * the stream_fifo to operate on + * + * Returns: + * the next stream that would be returned from stream_fifo_pop + */ extern struct stream *stream_fifo_head(struct stream_fifo *fifo); +extern struct stream *stream_fifo_head_safe(struct stream_fifo *fifo); + +/* + * Remove all streams from a stream_fifo. + * + * fifo + * the stream_fifo to clean + */ extern void stream_fifo_clean(struct stream_fifo *fifo); +extern void stream_fifo_clean_safe(struct stream_fifo *fifo); + +/* + * Retrieve number of streams on a stream_fifo. + * + * fifo + * the stream_fifo to retrieve the count for + * + * Returns: + * the number of streams on the stream_fifo + */ +extern size_t stream_fifo_count_safe(struct stream_fifo *fifo); + +/* + * Free a stream_fifo. + * + * Calls stream_fifo_clean, then deinitializes the stream_fifo and frees it. + * + * fifo + * the stream_fifo to free + */ extern void stream_fifo_free(struct stream_fifo *fifo); /* This is here because "<< 24" is particularly problematic in C. diff --git a/lib/zclient.c b/lib/zclient.c index cb39099fc2..0f7cf350db 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -975,8 +975,6 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putl(s, api->flags); stream_putc(s, api->message); stream_putc(s, api->safi); - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_put(s, &(api->rmac), sizeof(struct ethaddr)); /* Put prefix information. */ stream_putc(s, api->prefix.family); @@ -1061,6 +1059,11 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) api_nh->label_num * sizeof(mpls_label_t)); } + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_put(s, &(api_nh->rmac), + sizeof(struct ethaddr)); } } @@ -1101,8 +1104,6 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETL(s, api->flags); STREAM_GETC(s, api->message); STREAM_GETC(s, api->safi); - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - STREAM_GET(&(api->rmac), s, sizeof(struct ethaddr)); /* Prefix. */ STREAM_GETC(s, api->prefix.family); @@ -1212,6 +1213,11 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) api_nh->label_num * sizeof(mpls_label_t)); } + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_get(&(api_nh->rmac), s, + sizeof(struct ethaddr)); } } @@ -1231,9 +1237,8 @@ stream_failure: return 0; } -static void zapi_encode_prefix(struct stream *s, - struct prefix *p, - uint8_t family) +static void zapi_encode_prefix(struct stream *s, struct prefix *p, + uint8_t family) { struct prefix any; @@ -1248,8 +1253,7 @@ static void zapi_encode_prefix(struct stream *s, stream_put(s, &p->u.prefix, prefix_blen(p)); } -int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, - struct pbr_rule *zrule) +int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule) { stream_reset(s); zclient_create_header(s, cmd, zrule->vrf_id); @@ -1265,11 +1269,11 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, zapi_encode_prefix(s, &(zrule->filter.src_ip), zrule->filter.src_ip.family); - stream_putw(s, zrule->filter.src_port); /* src port */ + stream_putw(s, zrule->filter.src_port); /* src port */ zapi_encode_prefix(s, &(zrule->filter.dst_ip), zrule->filter.src_ip.family); - stream_putw(s, zrule->filter.dst_port); /* dst port */ - stream_putw(s, zrule->filter.fwmark); /* fwmark */ + stream_putw(s, zrule->filter.dst_port); /* dst port */ + stream_putw(s, zrule->filter.fwmark); /* fwmark */ stream_putl(s, zrule->action.table); stream_putl(s, zrule->ifindex); @@ -1317,8 +1321,8 @@ bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, STREAM_GETL(s, ifi); if (zclient_debug) - zlog_debug("%s: %u %u %u %u", __PRETTY_FUNCTION__, - seq, prio, uni, ifi); + zlog_debug("%s: %u %u %u %u", __PRETTY_FUNCTION__, seq, prio, + uni, ifi); *seqno = seq; *priority = prio; *unique = uni; @@ -1330,9 +1334,8 @@ stream_failure: return false; } -bool zapi_ipset_notify_decode(struct stream *s, - uint32_t *unique, - enum zapi_ipset_notify_owner *note) +bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, + enum zapi_ipset_notify_owner *note) { uint32_t uni; @@ -1350,10 +1353,9 @@ stream_failure: return false; } -bool zapi_ipset_entry_notify_decode(struct stream *s, - uint32_t *unique, - char *ipset_name, - enum zapi_ipset_entry_notify_owner *note) +bool zapi_ipset_entry_notify_decode(struct stream *s, uint32_t *unique, + char *ipset_name, + enum zapi_ipset_entry_notify_owner *note) { uint32_t uni; @@ -1361,8 +1363,7 @@ bool zapi_ipset_entry_notify_decode(struct stream *s, STREAM_GETL(s, uni); - STREAM_GET(ipset_name, s, - ZEBRA_IPSET_NAME_SIZE); + STREAM_GET(ipset_name, s, ZEBRA_IPSET_NAME_SIZE); if (zclient_debug) zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); @@ -1408,7 +1409,7 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) */ if (znh->label_num) { nexthop_add_labels(n, ZEBRA_LSP_NONE, znh->label_num, - znh->labels); + znh->labels); } return n; @@ -1469,7 +1470,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) if (nhr->nexthops[i].label_num) STREAM_GET(&nhr->nexthops[i].labels[0], s, nhr->nexthops[i].label_num - * sizeof(mpls_label_t)); + * sizeof(mpls_label_t)); } return true; @@ -1892,8 +1893,9 @@ struct connected *zebra_interface_address_read(int type, struct stream *s, zlog_warn( "warning: interface %s address %s " "with peer flag set, but no peer address!", - ifp->name, prefix2str(ifc->address, buf, - sizeof buf)); + ifp->name, + prefix2str(ifc->address, buf, + sizeof buf)); UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); } } @@ -2059,7 +2061,7 @@ int lm_label_manager_connect(struct zclient *zclient) uint8_t result; if (zclient_debug) - zlog_debug("Connecting to Label Manager"); + zlog_debug("Connecting to Label Manager (LM)"); if (zclient->sock < 0) return -1; @@ -2079,33 +2081,45 @@ int lm_label_manager_connect(struct zclient *zclient) ret = writen(zclient->sock, s->data, stream_get_endp(s)); if (ret < 0) { - zlog_err("%s: can't write to zclient->sock", __func__); + zlog_err("Can't write to zclient sock"); close(zclient->sock); zclient->sock = -1; return -1; } if (ret == 0) { - zlog_err("%s: zclient->sock connection closed", __func__); + zlog_err("Zclient sock closed"); close(zclient->sock); zclient->sock = -1; return -1; } if (zclient_debug) - zlog_debug("%s: Label manager connect request (%d bytes) sent", - __func__, ret); + zlog_debug("LM connect request sent (%d bytes)", ret); /* read response */ if (zclient_read_sync_response(zclient, ZEBRA_LABEL_MANAGER_CONNECT) != 0) return -1; - /* result */ s = zclient->ibuf; + + /* read instance and proto */ + uint8_t proto = stream_getc(s); + uint16_t instance = stream_getw(s); + + /* sanity */ + if (proto != zclient->redist_default) + zlog_err( + "Wrong proto (%u) in LM connect response. Should be %u", + proto, zclient->redist_default); + if (instance != zclient->instance) + zlog_err( + "Wrong instId (%u) in LM connect response. Should be %u", + instance, zclient->instance); + + /* result code */ result = stream_getc(s); if (zclient_debug) - zlog_debug( - "%s: Label Manager connect response received, result %u", - __func__, result); + zlog_debug("LM connect-response received, result %u", result); return (int)result; } @@ -2118,10 +2132,8 @@ int lm_label_manager_connect(struct zclient *zclient) * @param chunk_size Amount of labels requested * @result 0 on success, -1 otherwise */ -int zclient_send_get_label_chunk( - struct zclient *zclient, - uint8_t keep, - uint32_t chunk_size) +int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, + uint32_t chunk_size) { struct stream *s; @@ -2135,6 +2147,10 @@ int zclient_send_get_label_chunk( stream_reset(s); zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT); + /* proto */ + stream_putc(s, zclient->redist_default); + /* instance */ + stream_putw(s, zclient->instance); stream_putc(s, keep); stream_putl(s, chunk_size); @@ -2174,6 +2190,10 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, s = zclient->obuf; stream_reset(s); zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, VRF_DEFAULT); + /* proto */ + stream_putc(s, zclient->redist_default); + /* instance */ + stream_putw(s, zclient->instance); /* keep */ stream_putc(s, keep); /* chunk size */ @@ -2183,26 +2203,39 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, ret = writen(zclient->sock, s->data, stream_get_endp(s)); if (ret < 0) { - zlog_err("%s: can't write to zclient->sock", __func__); + zlog_err("Can't write to zclient sock"); close(zclient->sock); zclient->sock = -1; return -1; } if (ret == 0) { - zlog_err("%s: zclient->sock connection closed", __func__); + zlog_err("Zclient sock closed"); close(zclient->sock); zclient->sock = -1; return -1; } if (zclient_debug) - zlog_debug("%s: Label chunk request (%d bytes) sent", __func__, - ret); + zlog_debug("Label chunk request (%d bytes) sent", ret); /* read response */ if (zclient_read_sync_response(zclient, ZEBRA_GET_LABEL_CHUNK) != 0) return -1; + /* parse response */ s = zclient->ibuf; + + /* read proto and instance */ + uint8_t proto = stream_getc(s); + uint16_t instance = stream_getw(s); + + /* sanities */ + if (proto != zclient->redist_default) + zlog_err("Wrong proto (%u) in get chunk response. Should be %u", + proto, zclient->redist_default); + if (instance != zclient->instance) + zlog_err("Wrong instId (%u) in get chunk response Should be %u", + instance, zclient->instance); + /* keep */ response_keep = stream_getc(s); /* start and end labels */ @@ -2212,19 +2245,18 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, /* not owning this response */ if (keep != response_keep) { zlog_err( - "%s: Invalid Label chunk: %u - %u, keeps mismatch %u != %u", - __func__, *start, *end, keep, response_keep); + "Invalid Label chunk: %u - %u, keeps mismatch %u != %u", + *start, *end, keep, response_keep); } /* sanity */ if (*start > *end || *start < MPLS_LABEL_UNRESERVED_MIN || *end > MPLS_LABEL_UNRESERVED_MAX) { - zlog_err("%s: Invalid Label chunk: %u - %u", __func__, *start, - *end); + zlog_err("Invalid Label chunk: %u - %u", *start, *end); return -1; } if (zclient_debug) - zlog_debug("Label Chunk assign: %u - %u (%u) ", *start, *end, + zlog_debug("Label Chunk assign: %u - %u (%u)", *start, *end, response_keep); return 0; @@ -2245,7 +2277,7 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, struct stream *s; if (zclient_debug) - zlog_debug("Releasing Label Chunk"); + zlog_debug("Releasing Label Chunk %u - %u", start, end); if (zclient->sock < 0) return -1; @@ -2255,6 +2287,10 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, stream_reset(s); zclient_create_header(s, ZEBRA_RELEASE_LABEL_CHUNK, VRF_DEFAULT); + /* proto */ + stream_putc(s, zclient->redist_default); + /* instance */ + stream_putw(s, zclient->instance); /* start */ stream_putl(s, start); /* end */ @@ -2265,13 +2301,13 @@ int lm_release_label_chunk(struct zclient *zclient, uint32_t start, ret = writen(zclient->sock, s->data, stream_get_endp(s)); if (ret < 0) { - zlog_err("%s: can't write to zclient->sock", __func__); + zlog_err("Can't write to zclient sock"); close(zclient->sock); zclient->sock = -1; return -1; } if (ret == 0) { - zlog_err("%s: zclient->sock connection closed", __func__); + zlog_err("Zclient sock connection closed"); close(zclient->sock); zclient->sock = -1; return -1; @@ -2319,8 +2355,7 @@ int tm_table_manager_connect(struct zclient *zclient) return -1; if (zclient_debug) - zlog_debug("%s: Table manager connect request sent", - __func__); + zlog_debug("%s: Table manager connect request sent", __func__); /* read response */ if (zclient_read_sync_response(zclient, ZEBRA_TABLE_MANAGER_CONNECT) @@ -2783,7 +2818,7 @@ static int zclient_read(struct thread *thread) case ZEBRA_GET_LABEL_CHUNK: if (zclient->label_chunk) (*zclient->label_chunk)(command, zclient, length, - vrf_id); + vrf_id); break; case ZEBRA_IPSET_NOTIFY_OWNER: if (zclient->ipset_notify_owner) diff --git a/lib/zclient.h b/lib/zclient.h index 8d26b7fe59..c5eaf9c0fd 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -302,6 +302,8 @@ struct zapi_nexthop { /* MPLS labels for BGP-LU or Segment Routing */ uint8_t label_num; mpls_label_t labels[MPLS_MAX_LABELS]; + + struct ethaddr rmac; }; /* @@ -342,8 +344,6 @@ struct zapi_route { vrf_id_t vrf_id; uint32_t tableid; - - struct ethaddr rmac; }; /* Zebra IPv4 route message API. */ diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index b895b5ad8b..bc1ce621ae 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -684,7 +684,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) { struct prefix prefix, abr_prefix; struct ospf6_route_table *table = NULL; - struct ospf6_route *range, *route, *old = NULL; + struct ospf6_route *range, *route, *old = NULL, *old_route; struct ospf6_route *abr_entry; uint8_t type = 0; char options[3] = {0, 0, 0}; @@ -695,14 +695,15 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) int is_debug = 0; struct ospf6_inter_prefix_lsa *prefix_lsa = NULL; struct ospf6_inter_router_lsa *router_lsa = NULL; - struct ospf6_path *path; + bool old_entry_updated = false; memset(&prefix, 0, sizeof(prefix)); if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) { if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) { is_debug++; - zlog_debug("Examin %s in area %s", lsa->name, oa->name); + zlog_debug("%s: Examin %s in area %s", + __PRETTY_FUNCTION__, lsa->name, oa->name); } prefix_lsa = @@ -720,7 +721,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) } else if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_ROUTER)) { if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER)) { is_debug++; - zlog_debug("Examin %s in area %s", lsa->name, oa->name); + zlog_debug("%s: Examin %s in area %s", + __PRETTY_FUNCTION__, lsa->name, oa->name); } router_lsa = @@ -768,7 +770,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) } if (OSPF6_LSA_IS_MAXAGE(lsa)) { if (is_debug) - zlog_debug("LSA is MaxAge, ignore"); + zlog_debug("%s: LSA %s is MaxAge, ignore", + __PRETTY_FUNCTION__, lsa->name); if (old) ospf6_route_remove(old, table); return; @@ -845,9 +848,24 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) || CHECK_FLAG(abr_entry->flag, OSPF6_ROUTE_REMOVE) || !CHECK_FLAG(abr_entry->path.router_bits, OSPF6_ROUTER_BIT_B)) { if (is_debug) - zlog_debug("ABR router entry does not exist, ignore"); - if (old) - ospf6_route_remove(old, table); + zlog_debug("%s: ABR router entry does not exist, ignore", + __PRETTY_FUNCTION__); + if (old) { + if (old->type == OSPF6_DEST_TYPE_ROUTER && + oa->intra_brouter_calc) { + if (is_debug) + zlog_debug( + "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)", + __PRETTY_FUNCTION__, buf, + (void *)old); + } else { + if (is_debug) + zlog_debug("%s: remove old entry: %s %p ", + __PRETTY_FUNCTION__, buf, + (void *)old); + ospf6_route_remove(old, table); + } + } return; } @@ -902,11 +920,11 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) route->path.type = OSPF6_PATH_TYPE_INTER; route->path.cost = abr_entry->path.cost + cost; - ospf6_route_copy_nexthops(route, abr_entry); - - path = ospf6_path_dup(&route->path); - ospf6_copy_nexthops(path->nh_list, abr_entry->nh_list); - listnode_add_sort(route->paths, path); + /* Inter abr_entry is same as brouter. + * Avoid duplicate nexthops to brouter and its + * learnt route. i.e. use merge nexthops. + */ + ospf6_route_merge_nexthops(route, abr_entry); /* (7) If the routes are identical, copy the next hops over to existing route. ospf6's route table implementation will otherwise string both @@ -915,11 +933,28 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) */ old = ospf6_route_lookup(&prefix, table); - if (old && (ospf6_route_cmp(route, old) == 0)) { - ospf6_route_merge_nexthops(old, route); + for (old_route = old; old_route; old_route = old_route->next) { + if (!ospf6_route_is_same(old_route, route) || + (old_route->type != route->type) || + (old_route->path.type != route->path.type)) + continue; + + if ((ospf6_route_cmp(route, old_route) != 0)) { + if (is_debug) { + prefix2str(&prefix, buf, sizeof(buf)); + zlog_debug("%s: old %p %s cost %u new route cost %u are not same", + __PRETTY_FUNCTION__, + (void *)old_route, buf, + old_route->path.cost, + route->path.cost); + } + continue; + } + old_entry_updated = true; + ospf6_route_merge_nexthops(old, route); if (is_debug) - zlog_debug("%s: Update route: %s old cost %u new cost %u nh count %u", + zlog_debug("%s: Update route: %s old cost %u new cost %u nh %u", __PRETTY_FUNCTION__, buf, old->path.cost, route->path.cost, listcount(route->nh_list)); @@ -930,9 +965,12 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) /* Delete new route */ ospf6_route_delete(route); - } else { + break; + } + + if (old_entry_updated == false) { if (is_debug) - zlog_debug("%s: Install route: %s cost %u nh count %u", + zlog_debug("%s: Install route: %s cost %u nh %u", __PRETTY_FUNCTION__, buf, route->path.cost, listcount(route->nh_list)); /* ospf6_ia_add_nw_route (table, &prefix, route); */ diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index eaf3e5c6de..ba497a168e 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -50,6 +50,9 @@ struct ospf6_area { /* Area type */ int no_summary; + /* Brouter traversal protection */ + int intra_brouter_calc; + /* OSPF interface list */ struct list *if_list; diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 0fe0cada39..8bd0683f14 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -260,12 +260,12 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, listnode_delete(old_route->nh_list, rnh); ospf6_nexthop_delete(rnh); - route_updated = true; } } listnode_delete(old_route->paths, o_path); ospf6_path_free(o_path); + route_updated = true; /* Current route's path (adv_router info) is similar * to route being added. @@ -273,6 +273,19 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, * Update FIB with effective NHs. */ if (listcount(old_route->paths)) { + for (ALL_LIST_ELEMENTS(old_route->paths, + anode, anext, o_path)) { + ospf6_merge_nexthops( + old_route->nh_list, + o_path->nh_list); + } + /* Update RIB/FIB with effective + * nh_list + */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add) + (old_route); + if (old_route->path.origin.id == route->path.origin.id && old_route->path.origin.adv_router @@ -290,23 +303,7 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, old_route->path.origin.adv_router = h_path->origin.adv_router; } - - if (route_updated) { - for (ALL_LIST_ELEMENTS(old_route->paths, - anode, anext, - o_path)) { - ospf6_merge_nexthops( - old_route->nh_list, - o_path->nh_list); - } - /* Update RIB/FIB with effective - * nh_list - */ - if (ospf6->route_table->hook_add) - (*ospf6->route_table->hook_add)( - old_route); - break; - } + break; } else { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { prefix2str(&old_route->prefix, buf, @@ -374,13 +371,6 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, /* 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); @@ -400,46 +390,52 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, listcount(old_route->nh_list)); } } else { - for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, - nnode, nh)) { - for (ALL_LIST_ELEMENTS( - old_route->nh_list, rnode, - rnext, rnh)) { - if (!ospf6_nexthop_is_same(rnh, - nh)) - continue; - - listnode_delete( - old_route->nh_list, - rnh); - ospf6_nexthop_delete(rnh); - } - } 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); + /* Reset nexthop lists, rebuild from brouter table + * for each adv. router. + */ + list_delete_all_node(old_route->nh_list); - 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); + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + struct ospf6_route *asbr_entry; + + asbr_entry = ospf6_route_lookup( + &o_path->ls_prefix, + ospf6->brouter_table); + if (asbr_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN( + AS_EXTERNAL)) { + prefix2str(&old_route->prefix, + buf, sizeof(buf)); + zlog_debug("%s: ls_prfix %s asbr_entry not found.", + __PRETTY_FUNCTION__, + buf); + } + continue; } + ospf6_route_merge_nexthops(old_route, + asbr_entry); + } - /* Update RIB/FIB */ - if (ospf6->route_table->hook_add) - (*ospf6->route_table->hook_add)( - old_route); + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: route %s with effective paths %u nh %u", + __PRETTY_FUNCTION__, buf, + old_route->paths ? + listcount(old_route->paths) : 0, + 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. */ @@ -509,8 +505,9 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) route->path.origin.type = lsa->header->type; route->path.origin.id = lsa->header->id; route->path.origin.adv_router = lsa->header->adv_router; - route->path.prefix_options = external->prefix.prefix_options; + memcpy(&route->path.ls_prefix, &asbr_id, sizeof(struct prefix)); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) { route->path.type = OSPF6_PATH_TYPE_EXTERNAL2; route->path.metric_type = 2; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index b234b10d51..d99541ebad 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -2037,6 +2037,8 @@ static void ospf6_brouter_debug_print(struct ospf6_route *brouter) zlog_info(" options: %s router-bits: %s metric-type: %d metric: %d/%d", options, capa, brouter->path.metric_type, brouter->path.cost, brouter->path.u.cost_e2); + zlog_info(" paths %u nh %u", listcount(brouter->paths), + listcount(brouter->nh_list)); } void ospf6_intra_brouter_calculation(struct ospf6_area *oa) @@ -2047,8 +2049,10 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) uint32_t brouter_id; char brouter_name[16]; - if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id)) - zlog_info("border-router calculation for area %s", oa->name); + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || + IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_info("%s: border-router calculation for area %s", + __PRETTY_FUNCTION__, oa->name); hook_add = oa->ospf6->brouter_table->hook_add; hook_remove = oa->ospf6->brouter_table->hook_remove; @@ -2114,6 +2118,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) for (brouter = ospf6_route_head(oa->ospf6->brouter_table); brouter; brouter = nbrouter) { + /* * brouter may have been "deleted" in the last loop iteration. * If this is the case there is still 1 final refcount lock @@ -2122,6 +2127,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) * skip processing the deleted route. */ if (brouter->lock == 1) { + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + ospf6_brouter_debug_print(brouter); nbrouter = ospf6_route_next(brouter); continue; } else { @@ -2173,8 +2180,14 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) brouter_id) || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( oa->area_id)) - zlog_info("brouter %s disappears via area %s", - brouter_name, oa->name); + zlog_info("%s: brouter %s disappears via area %s", + __PRETTY_FUNCTION__, brouter_name, + oa->name); + /* This is used to protect nbrouter from removed from + * the table. For an example, ospf6_abr_examin_summary, + * removes brouters which are marked for remove. + */ + oa->intra_brouter_calc = 1; ospf6_route_remove(brouter, oa->ospf6->brouter_table); brouter = NULL; } else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD) @@ -2184,8 +2197,9 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) brouter_id) || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( oa->area_id)) - zlog_info("brouter %s appears via area %s", - brouter_name, oa->name); + zlog_info("%s: brouter %s appears via area %s", + __PRETTY_FUNCTION__, brouter_name, + oa->name); /* newly added */ if (hook_add) @@ -2205,11 +2219,14 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD); UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE); } + /* Reset for nbrouter */ + oa->intra_brouter_calc = 0; } - if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id)) - zlog_info("border-router calculation for area %s: done", - oa->name); + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || + IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_info("%s: border-router calculation for area %s: done", + __PRETTY_FUNCTION__, oa->name); } struct ospf6_lsa_handler router_handler = {.lh_type = OSPF6_LSTYPE_ROUTER, diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 39272b3701..15d8eb6cf2 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -925,10 +925,11 @@ struct ospf6_route *ospf6_route_next(struct ospf6_route *route) struct ospf6_route *next = route->next; if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) - zlog_info("%s %p: route next: %p<-[%p]->%p", + zlog_info("%s %p: route next: %p<-[%p]->%p , route ref count %u", ospf6_route_table_name(route->table), (void *)route->table, (void *)route->prev, - (void *)route, (void *)route->next); + (void *)route, (void *)route->next, + route->lock); ospf6_route_unlock(route); if (next) diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index e4a4891526..7bf099fbbf 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -97,7 +97,8 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route) static void ospf6_top_brouter_hook_add(struct ospf6_route *route) { - if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || + IS_OSPF6_DEBUG_BROUTER) { uint32_t brouter_id; char brouter_name[16]; @@ -116,15 +117,17 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route) static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) { - if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || + IS_OSPF6_DEBUG_BROUTER) { uint32_t brouter_id; char brouter_name[16]; brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix); inet_ntop(AF_INET, &brouter_id, brouter_name, sizeof(brouter_name)); - zlog_debug("%s: brouter %s del with nh count %u", - __PRETTY_FUNCTION__, brouter_name, + zlog_debug("%s: brouter %p %s del with adv router %x nh %u", + __PRETTY_FUNCTION__, (void *)route, brouter_name, + route->path.origin.adv_router, listcount(route->nh_list)); } route->flag |= OSPF6_ROUTE_REMOVE; diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 8d6d5b4a26..db61fe087b 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -97,7 +97,7 @@ DEFUN_NOSH (show_debugging_ospf6, DEBUG_STR OSPF6_STR) { - vty_out(vty, "OSPF6 debugging status:"); + vty_out(vty, "OSPF6 debugging status:\n"); config_write_ospf6_debug(vty); diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 52b954d6a1..ea31d8c2ca 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -263,6 +263,9 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp, ospf_opaque_type9_lsa_init(oi); oi->ospf = ospf; + + ospf_if_stream_set(oi); + QOBJ_REG(oi, ospf_interface); if (IS_DEBUG_OSPF_EVENT) @@ -322,6 +325,9 @@ void ospf_if_free(struct ospf_interface *oi) { ospf_if_down(oi); + if (oi->obuf) + ospf_fifo_free(oi->obuf); + assert(oi->state == ISM_Down); ospf_opaque_type9_lsa_term(oi); @@ -496,9 +502,8 @@ void ospf_if_stream_unset(struct ospf_interface *oi) struct ospf *ospf = oi->ospf; if (oi->obuf) { - ospf_fifo_free(oi->obuf); - oi->obuf = NULL; - + /* flush the interface packet queue */ + ospf_fifo_flush(oi->obuf); /*reset protocol stats */ ospf_if_reset_stats(oi); @@ -781,7 +786,6 @@ int ospf_if_up(struct ospf_interface *oi) if (oi->type == OSPF_IFTYPE_LOOPBACK) OSPF_ISM_EVENT_SCHEDULE(oi, ISM_LoopInd); else { - ospf_if_stream_set(oi); OSPF_ISM_EVENT_SCHEDULE(oi, ISM_InterfaceUp); } diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 5be96e86d0..a4a9233f72 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -470,18 +470,6 @@ void pbr_nht_change_group(const char *name) pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); } -/* - * Since we are writing into the name field which is PBR_MAP_NAMELEN - * size, we are expecting this to field to be at max 100 bytes. - * Newer compilers understand that the %s portion may be up to - * 100 bytes( because of the size of the string. The %u portion - * is expected to be 10 bytes. So in `theory` there are situations - * where we might truncate. The reality this is never going to - * happen( who is going to create a nexthop group name that is - * over say 30 characters? ). As such we are expecting the - * calling function to subtract 10 from the size_t l before - * we pass it in to get around this new gcc fun. - */ char *pbr_nht_nexthop_make_name(char *name, size_t l, uint32_t seqno, char *buffer) { @@ -497,7 +485,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) struct pbr_nexthop_cache lookup; memset(&find, 0, sizeof(find)); - pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_MAP_NAMELEN - 10, + pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, pbrms->seqno, find.name); if (!pbrms->internal_nhg_name) pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name); diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index 2cfb076f00..e6fdbfd04c 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -25,8 +25,10 @@ #include "pbr_map.h" +#define PBR_NHC_NAMELEN PBR_MAP_NAMELEN + 10 + struct pbr_nexthop_group_cache { - char name[PBR_MAP_NAMELEN]; + char name[PBR_NHC_NAMELEN]; uint32_t table_id; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index ba5c49ad5c..7e0e8d632b 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -38,7 +38,7 @@ #include "pbrd/pbr_vty_clippy.c" #endif -DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)", +DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert in existing pbr-map entry\n" @@ -54,7 +54,7 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)", return CMD_SUCCESS; } -DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]", +DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" @@ -269,7 +269,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhg) nh = nexthop_exists(pbrms->nhg, &nhop); else { - char buf[PBR_MAP_NAMELEN]; + char buf[PBR_NHC_NAMELEN]; if (no) { vty_out(vty, "No nexthops to delete"); @@ -280,7 +280,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, pbr_nht_nexthop_make_name(pbrms->parent->name, - PBR_MAP_NAMELEN - 10, + PBR_NHC_NAMELEN, pbrms->seqno, buf)); nh = NULL; diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index bc7dd20832..cdacfad4b4 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -479,6 +479,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, stream_putw(s, 0); /* src port */ pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family); stream_putw(s, 0); /* dst port */ + stream_putl(s, 0); /* fwmark */ if (pbrms->nhgrp_name) stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name)); else if (pbrms->nhg) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 6f2e020bd8..141ec62aeb 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -24,6 +24,7 @@ verification commands: show ip igmp groups retransmissions IGMP group retransmission show ip igmp sources IGMP sources information show ip igmp sources retransmissions IGMP source retransmission + show ip igmp statistics IGMP statistics information show ip pim address PIM interface address show ip pim assert PIM interface assert show ip pim assert-internal PIM interface internal assert state diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 35514a85da..81191eb96c 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1294,6 +1294,76 @@ static void pim_show_interfaces_single(struct pim_instance *pim, } } +static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, + const char *ifname, uint8_t uj) +{ + struct interface *ifp; + struct igmp_stats rx_stats; + + igmp_stats_init(&rx_stats); + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp; + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (ifname && strcmp(ifname, ifp->name)) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + igmp_stats_add(&rx_stats, &igmp->rx_stats); + } + } + if (uj) { + json_object *json = NULL; + json_object *json_row = NULL; + + json = json_object_new_object(); + json_row = json_object_new_object(); + + json_object_string_add(json_row, "name", ifname ? ifname : + "global"); + json_object_int_add(json_row, "queryV1", rx_stats.query_v1); + json_object_int_add(json_row, "queryV2", rx_stats.query_v2); + json_object_int_add(json_row, "queryV3", rx_stats.query_v3); + json_object_int_add(json_row, "leaveV3", rx_stats.leave_v2); + json_object_int_add(json_row, "reportV1", rx_stats.report_v1); + json_object_int_add(json_row, "reportV2", rx_stats.report_v2); + json_object_int_add(json_row, "reportV3", rx_stats.report_v3); + json_object_int_add(json_row, "mtraceResponse", + rx_stats.mtrace_rsp); + json_object_int_add(json_row, "mtraceRequest", + rx_stats.mtrace_req); + json_object_int_add(json_row, "unsupported", + rx_stats.unsupported); + json_object_object_add(json, ifname ? ifname : "global", + json_row); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + vty_out(vty, "IGMP RX statistics\n"); + vty_out(vty, "Interface : %s\n", + ifname ? ifname : "global"); + vty_out(vty, "V1 query : %u\n", rx_stats.query_v1); + vty_out(vty, "V2 query : %u\n", rx_stats.query_v2); + vty_out(vty, "V3 query : %u\n", rx_stats.query_v3); + vty_out(vty, "V2 leave : %u\n", rx_stats.leave_v2); + vty_out(vty, "V1 report : %u\n", rx_stats.report_v1); + vty_out(vty, "V2 report : %u\n", rx_stats.report_v2); + vty_out(vty, "V3 report : %u\n", rx_stats.report_v3); + vty_out(vty, "mtrace response : %u\n", rx_stats.mtrace_rsp); + vty_out(vty, "mtrace request : %u\n", rx_stats.mtrace_req); + vty_out(vty, "unsupported : %u\n", rx_stats.unsupported); + } +} + static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, uint8_t uj) { @@ -3527,6 +3597,33 @@ DEFUN (show_ip_igmp_sources_retransmissions, return CMD_SUCCESS; } +DEFUN (show_ip_igmp_statistics, + show_ip_igmp_statistics_cmd, + "show ip igmp [vrf NAME] statistics [interface WORD] [json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + "IGMP statistics\n" + "interface\n" + "IGMP interface\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + uint8_t uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + if (argv_find(argv, argc, "WORD", &idx)) + igmp_show_statistics(vrf->info, vty, argv[idx]->arg, uj); + else + igmp_show_statistics(vrf->info, vty, NULL, uj); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_assert, show_ip_pim_assert_cmd, "show ip pim [vrf NAME] assert", @@ -8644,6 +8741,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); install_element(VIEW_NODE, &show_ip_igmp_sources_cmd); install_element(VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element(VIEW_NODE, &show_ip_igmp_statistics_cmd); install_element(VIEW_NODE, &show_ip_pim_assert_cmd); install_element(VIEW_NODE, &show_ip_pim_assert_internal_cmd); install_element(VIEW_NODE, &show_ip_pim_assert_metric_cmd); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 5996a3ac96..5c4d99ad4d 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -62,7 +62,16 @@ void pim_if_init(struct pim_instance *pim) void pim_if_terminate(struct pim_instance *pim) { - // Nothing to do at this moment + struct interface *ifp; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + pim_if_delete(ifp); + } return; } diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index bb56379c14..5e6b0f10cf 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -124,11 +124,6 @@ static void pim_ifchannel_find_new_children(struct pim_ifchannel *ch) } } -void pim_ifchannel_free(struct pim_ifchannel *ch) -{ - XFREE(MTYPE_PIM_IFCHANNEL, ch); -} - void pim_ifchannel_delete(struct pim_ifchannel *ch) { struct pim_interface *pim_ifp; @@ -198,7 +193,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) zlog_debug("%s: ifchannel entry %s is deleted ", __PRETTY_FUNCTION__, ch->sg_str); - pim_ifchannel_free(ch); + XFREE(MTYPE_PIM_IFCHANNEL, ch); } void pim_ifchannel_delete_all(struct interface *ifp) diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index 0af9ebf0be..b9d4d291d8 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -114,7 +114,6 @@ RB_HEAD(pim_ifchannel_rb, pim_ifchannel); RB_PROTOTYPE(pim_ifchannel_rb, pim_ifchannel, pim_ifp_rb, pim_ifchannel_compare); -void pim_ifchannel_free(struct pim_ifchannel *ch); void pim_ifchannel_delete(struct pim_ifchannel *ch); void pim_ifchannel_delete_all(struct interface *ifp); void pim_ifchannel_membership_clear(struct interface *ifp); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 5e1aecc3a3..c980f5fcba 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -303,6 +303,21 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version, return -1; } + /* Collecting IGMP Rx stats */ + switch (query_version) { + case 1: + igmp->rx_stats.query_v1++; + break; + case 2: + igmp->rx_stats.query_v2++; + break; + case 3: + igmp->rx_stats.query_v3++; + break; + default: + igmp->rx_stats.unsupported++; + } + /* * RFC 3376 defines some guidelines on operating in backwards * compatibility with older versions of IGMP but there are some gaps in @@ -400,6 +415,9 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.report_v1++; + if (PIM_DEBUG_IGMP_TRACE) { zlog_warn("%s %s: FIXME WRITEME", __FILE__, __PRETTY_FUNCTION__); @@ -524,6 +542,9 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); + /* Collecting IGMP Rx stats */ + igmp->rx_stats.unsupported++; + return -1; } @@ -867,6 +888,8 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, pim_ifp->igmp_default_robustness_variable; igmp->sock_creation = pim_time_monotonic_sec(); + igmp_stats_init(&igmp->rx_stats); + if (mtrace_only) { igmp->mtrace_only = mtrace_only; return igmp; diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 561a127d0f..c8b880ddd7 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -25,6 +25,7 @@ #include <zebra.h> #include "vty.h" #include "linklist.h" +#include "pim_igmp_stats.h" /* The following sizes are likely to support @@ -94,6 +95,8 @@ struct igmp_sock { struct list *igmp_group_list; /* list of struct igmp_group */ struct hash *igmp_group_hash; + + struct igmp_stats rx_stats; }; struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index d3ae185709..673e2ca5b8 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -671,6 +671,9 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.mtrace_req++; + if (PIM_DEBUG_MTRACE) mtrace_debug(pim_ifp, mtracep, igmp_msg_len); @@ -881,6 +884,9 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, mtracep->checksum = checksum; + /* Collecting IGMP Rx stats */ + igmp->rx_stats.mtrace_rsp++; + if (PIM_DEBUG_MTRACE) mtrace_debug(pim_ifp, mtracep, igmp_msg_len); diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c new file mode 100644 index 0000000000..428816e1f0 --- /dev/null +++ b/pimd/pim_igmp_stats.c @@ -0,0 +1,42 @@ +/* + * PIM for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pim_igmp_stats.h" + +void igmp_stats_init(struct igmp_stats *stats) +{ + memset(stats, 0, sizeof(struct igmp_stats)); +} + +void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b) +{ + if (!a || !b) + return; + + a->query_v1 += b->query_v1; + a->query_v2 += b->query_v2; + a->query_v3 += b->query_v3; + a->report_v1 += b->report_v1; + a->report_v2 += b->report_v2; + a->report_v3 += b->report_v3; + a->leave_v2 += b->leave_v2; + a->mtrace_rsp += b->mtrace_rsp; + a->mtrace_req += b->mtrace_req; + a->unsupported += b->unsupported; +} diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h new file mode 100644 index 0000000000..57b5cc62f4 --- /dev/null +++ b/pimd/pim_igmp_stats.h @@ -0,0 +1,41 @@ +/* + * PIM for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PIM_IGMP_STATS_H +#define PIM_IGMP_STATS_H + +#include <zebra.h> + +struct igmp_stats { + uint32_t query_v1; + uint32_t query_v2; + uint32_t query_v3; + uint32_t report_v1; + uint32_t report_v2; + uint32_t report_v3; + uint32_t leave_v2; + uint32_t mtrace_rsp; + uint32_t mtrace_req; + uint32_t unsupported; +}; + +void igmp_stats_init(struct igmp_stats *stats); +void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b); + +#endif /* PIM_IGMP_STATS_H */ diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c index dbbe83a965..19c3768813 100644 --- a/pimd/pim_igmpv2.c +++ b/pimd/pim_igmpv2.c @@ -121,6 +121,9 @@ int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.report_v2++; + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); if (PIM_DEBUG_IGMP_PACKETS) { @@ -167,6 +170,9 @@ int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.leave_v2++; + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); if (PIM_DEBUG_IGMP_PACKETS) { diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 3360e36b4a..5ccad39b33 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1900,6 +1900,9 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.report_v3++; + num_groups = ntohs( *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); if (num_groups < 1) { diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index ab4ffc26ae..7e5bb34e31 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -35,13 +35,6 @@ static void pim_instance_terminate(struct pim_instance *pim) { - /* Traverse and cleanup rpf_hash */ - if (pim->rpf_hash) { - hash_clean(pim->rpf_hash, (void *)pim_rp_list_hash_clean); - hash_free(pim->rpf_hash); - pim->rpf_hash = NULL; - } - if (pim->ssm_info) { pim_ssm_terminate(pim->ssm_info); pim->ssm_info = NULL; @@ -54,6 +47,13 @@ static void pim_instance_terminate(struct pim_instance *pim) pim_upstream_terminate(pim); + /* Traverse and cleanup rpf_hash */ + if (pim->rpf_hash) { + hash_clean(pim->rpf_hash, (void *)pim_rp_list_hash_clean); + hash_free(pim->rpf_hash); + pim->rpf_hash = NULL; + } + pim_oil_terminate(pim); pim_if_terminate(pim); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 829c917b0f..7afc37a28e 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1176,8 +1176,7 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp) * Let's make sure we are not running when we delete * the underlying data structure */ - pim_msdp_peer_cr_timer_setup(mp, false); - pim_msdp_peer_ka_timer_setup(mp, false); + pim_msdp_peer_stop_tcp_conn(mp, false); if (mp->ibuf) { stream_free(mp->ibuf); @@ -1190,6 +1189,8 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp) if (mp->mesh_group_name) { XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); } + + mp->pim = NULL; XFREE(MTYPE_PIM_MSDP_PEER, mp); } diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index b5f5f646d4..9329d72ce0 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -140,12 +140,6 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, return NULL; } -void pim_upstream_free(struct pim_upstream *up) -{ - XFREE(MTYPE_PIM_UPSTREAM, up); - up = NULL; -} - static void upstream_channel_oil_detach(struct pim_upstream *up) { if (up->channel_oil) { @@ -161,6 +155,8 @@ static void upstream_channel_oil_detach(struct pim_upstream *up) struct pim_upstream *pim_upstream_del(struct pim_instance *pim, struct pim_upstream *up, const char *name) { + struct listnode *node, *nnode; + struct pim_ifchannel *ch; bool notify_msdp = false; struct prefix nht_p; @@ -200,20 +196,17 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, notify_msdp = true; } - pim_upstream_remove_children(pim, up); - if (up->sources) - list_delete_and_null(&up->sources); - pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); upstream_channel_oil_detach(up); + for (ALL_LIST_ELEMENTS(up->ifchannels, node, nnode, ch)) + pim_ifchannel_delete(ch); list_delete_and_null(&up->ifchannels); - /* - notice that listnode_delete() can't be moved - into pim_upstream_free() because the later is - called by list_delete_all_node() - */ + pim_upstream_remove_children(pim, up); + if (up->sources) + list_delete_and_null(&up->sources); + if (up->parent && up->parent->sources) listnode_delete(up->parent->sources, up); up->parent = NULL; @@ -237,7 +230,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, } pim_delete_tracked_nexthop(pim, &nht_p, up, NULL); - pim_upstream_free(up); + XFREE(MTYPE_PIM_UPSTREAM, up); return NULL; } @@ -1545,8 +1538,15 @@ unsigned int pim_upstream_hash_key(void *arg) void pim_upstream_terminate(struct pim_instance *pim) { - if (pim->upstream_list) + struct listnode *node, *nnode; + struct pim_upstream *up; + + if (pim->upstream_list) { + for (ALL_LIST_ELEMENTS(pim->upstream_list, node, nnode, up)) + pim_upstream_del(pim, up, __PRETTY_FUNCTION__); + list_delete_and_null(&pim->upstream_list); + } if (pim->upstream_hash) hash_free(pim->upstream_hash); @@ -1773,6 +1773,5 @@ void pim_upstream_init(struct pim_instance *pim) pim_upstream_equal, hash_name); pim->upstream_list = list_new(); - pim->upstream_list->del = (void (*)(void *))pim_upstream_free; pim->upstream_list->cmp = pim_upstream_compare; } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index b75a5b9df3..ba133b39dd 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -137,7 +137,6 @@ struct pim_upstream { int64_t state_transition; /* Record current state uptime */ }; -void pim_upstream_free(struct pim_upstream *up); struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct prefix_sg *sg); struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 6cce697202..ae7f61c8eb 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -69,6 +69,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; + struct pim_instance *pim; /* zebra api adds/dels interfaces using the same call @@ -78,6 +79,7 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, if (!ifp) return 0; + pim = pim_get_pim_instance(vrf_id); if (PIM_DEBUG_ZEBRA) { zlog_debug( "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", @@ -86,8 +88,19 @@ static int pim_zebra_if_add(int command, struct zclient *zclient, if_is_operative(ifp)); } - if (if_is_operative(ifp)) + if (if_is_operative(ifp)) { + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + /* + * If we have a pim_ifp already and this is an if_add + * that means that we probably have a vrf move event + * If that is the case, set the proper vrfness. + */ + if (pim_ifp) + pim_ifp->pim = pim; pim_if_addr_add_all(ifp); + } /* * If we are a vrf device that is up, open up the pim_socket for @@ -145,6 +158,7 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, static int pim_zebra_if_state_up(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { + struct pim_instance *pim; struct interface *ifp; uint32_t table_id; @@ -164,7 +178,19 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, if_is_operative(ifp)); } + pim = pim_get_pim_instance(vrf_id); if (if_is_operative(ifp)) { + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + /* + * If we have a pim_ifp already and this is an if_add + * that means that we probably have a vrf move event + * If that is the case, set the proper vrfness. + */ + if (pim_ifp) + pim_ifp->pim = pim; + /* pim_if_addr_add_all() suffices for bringing up both IGMP and PIM @@ -274,6 +300,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, struct connected *c; struct prefix *p; struct pim_interface *pim_ifp; + struct pim_instance *pim; /* zebra api notifies address adds/dels events by using the same call @@ -327,8 +354,12 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, } pim_if_addr_add(c); - if (pim_ifp) + if (pim_ifp) { + pim = pim_get_pim_instance(vrf_id); + pim_ifp->pim = pim; + pim_rp_check_on_if_add(pim_ifp); + } if (if_is_loopback(c->ifp)) { struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); diff --git a/pimd/subdir.am b/pimd/subdir.am index 2254362221..0696d9b1e8 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -20,6 +20,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_ifchannel.c \ pimd/pim_igmp.c \ pimd/pim_igmp_mtrace.c \ + pimd/pim_igmp_stats.c \ pimd/pim_igmpv2.c \ pimd/pim_igmpv3.c \ pimd/pim_instance.c \ @@ -69,6 +70,7 @@ noinst_HEADERS += \ pimd/pim_igmp.h \ pimd/pim_igmp_join.h \ pimd/pim_igmp_mtrace.h \ + pimd/pim_igmp_stats.h \ pimd/pim_igmpv2.h \ pimd/pim_igmpv3.h \ pimd/pim_instance.h \ diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index f83c08e611..362be27a5f 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -534,7 +534,7 @@ rm -rf %{buildroot} %defattr(-,root,root) %doc */*.sample* AUTHORS COPYING %doc doc/mpls -%doc ChangeLog INSTALL NEWS README REPORTING-BUGS SERVICES +%doc ChangeLog NEWS README %if 0%{?frr_user:1} %dir %attr(751,%frr_user,%frr_user) %{_sysconfdir} %dir %attr(750,%frr_user,%frr_user) /var/log/frr diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index 1c80cf055a..a478b416bf 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -149,6 +149,8 @@ int main(int argc, char **argv, char **envp) vrf_init(NULL, NULL, NULL, NULL); + route_map_init(); + sharp_zebra_init(); /* Get configuration file. */ diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 4d19484a64..956da9d4ed 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -81,14 +81,16 @@ DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, DEFPY (install_routes, install_routes_cmd, - "sharp install routes A.B.C.D$start nexthop A.B.C.D$nexthop (1-1000000)$routes", + "sharp install routes A.B.C.D$start nexthop A.B.C.D$nexthop (1-1000000)$routes [instance (0-255)$instance]", "Sharp routing Protocol\n" "install some routes\n" "Routes to install\n" "Address to start /32 generation at\n" "Nexthop to use\n" "Nexthop address\n" - "How many to create\n") + "How many to create\n" + "Instance to use\n" + "Instance\n") { int i; struct prefix p; @@ -112,7 +114,7 @@ DEFPY (install_routes, temp = ntohl(p.u.prefix4.s_addr); for (i = 0; i < routes; i++) { - route_add(&p, &nhop); + route_add(&p, (uint8_t)instance, &nhop); p.u.prefix4.s_addr = htonl(++temp); } @@ -151,17 +153,18 @@ DEFPY(vrf_label, vrf_label_cmd, DEFPY (remove_routes, remove_routes_cmd, - "sharp remove routes A.B.C.D$start (1-1000000)$routes", + "sharp remove routes A.B.C.D$start (1-1000000)$routes [instance (0-255)$instance]", "Sharp Routing Protocol\n" "Remove some routes\n" "Routes to remove\n" "Starting spot\n" - "Routes to uniinstall\n") + "Routes to uniinstall\n" + "instance to use\n" + "Value of instance\n") { int i; struct prefix p; uint32_t temp; - total_routes = routes; removed_routes = 0; @@ -175,7 +178,7 @@ DEFPY (remove_routes, temp = ntohl(p.u.prefix4.s_addr); for (i = 0; i < routes; i++) { - route_delete(&p); + route_delete(&p, (uint8_t)instance); p.u.prefix4.s_addr = htonl(++temp); } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 999255e925..fcb555170b 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -176,7 +176,7 @@ void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP); } -void route_add(struct prefix *p, struct nexthop *nh) +void route_add(struct prefix *p, uint8_t instance, struct nexthop *nh) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -184,6 +184,7 @@ void route_add(struct prefix *p, struct nexthop *nh) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_SHARP; + api.instance = instance; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); @@ -200,7 +201,7 @@ void route_add(struct prefix *p, struct nexthop *nh) zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } -void route_delete(struct prefix *p) +void route_delete(struct prefix *p, uint8_t instance) { struct zapi_route api; @@ -208,6 +209,7 @@ void route_delete(struct prefix *p) api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_SHARP; api.safi = SAFI_UNICAST; + api.instance = instance; memcpy(&api.prefix, p, sizeof(*p)); zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 0c906fc4ff..58438ed01d 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -25,7 +25,7 @@ extern void sharp_zebra_init(void); extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); -extern void route_add(struct prefix *p, struct nexthop *nh); -extern void route_delete(struct prefix *p); +extern void route_add(struct prefix *p, uint8_t instance, struct nexthop *nh); +extern void route_delete(struct prefix *p, uint8_t instance); extern void sharp_zebra_nexthop_watch(struct prefix *p, bool watch); #endif diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 9e0cb1f5ca..8db1cb2ca1 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -945,6 +945,24 @@ static struct test_segment mp_unreach_segments[] = { }, {NULL, NULL, {0}, 0, 0}}; +static struct test_segment mp_prefix_sid[] = { + { + "PREFIX-SID", + "PREFIX-SID Test 1", + { + 0x01, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, + 0x03, 0x00, 0x08, 0x00, + 0x00, 0x0a, 0x1b, 0xfe, + 0x00, 0x00, 0x0a + }, + .len = 21, + .parses = SHOULD_PARSE, + }, + {NULL, NULL, { 0 }, 0, 0}, +}; + /* nlri_parse indicates 0 on successful parse, and -1 otherwise. * attr_parse indicates BGP_ATTR_PARSE_PROCEED/0 on success, * and BGP_ATTR_PARSE_ERROR/-1 or lower negative ret on err. @@ -1000,10 +1018,20 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) printf("%s: %s\n", t->name, t->desc); - if (type == BGP_ATTR_MP_REACH_NLRI) + switch (type) { + case BGP_ATTR_MP_REACH_NLRI: parse_ret = bgp_mp_reach_parse(&attr_args, &nlri); - else + break; + case BGP_ATTR_MP_UNREACH_NLRI: parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri); + break; + case BGP_ATTR_PREFIX_SID: + parse_ret = bgp_attr_prefix_sid(t->len, &attr_args, &nlri); + break; + default: + printf("unknown type"); + return; + } if (!parse_ret) { iana_afi_t pkt_afi; iana_safi_t pkt_safi; @@ -1022,7 +1050,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) if (!parse_ret) { if (type == BGP_ATTR_MP_REACH_NLRI) nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 0); - else + else if (type == BGP_ATTR_MP_UNREACH_NLRI) nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 1); } handle_result(peer, t, parse_ret, nlri_ret); @@ -1085,6 +1113,10 @@ int main(void) parse_test(peer, &mp_unreach_segments[i++], BGP_ATTR_MP_UNREACH_NLRI); + i = 0; + while (mp_prefix_sid[i].name) + parse_test(peer, &mp_prefix_sid[i++], + BGP_ATTR_PREFIX_SID); printf("failures: %d\n", failed); return failed; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 867dc9cd15..30de9d7711 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -341,6 +341,7 @@ static int vtysh_execute_func(const char *line, int pager) if (user_mode) { if (strncmp("en", vector_slot(vline, 0), 2) == 0) { + cmd_free_strvec(vline); fprintf(stdout, "%% Command not allowed: enable\n"); return CMD_WARNING; } @@ -845,27 +846,29 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) return (retcode); } -/* We don't care about the point of the cursor when '?' is typed. */ -static int vtysh_rl_describe(void) +/* + * Function processes cli commands terminated with '?' character when entered + * through either 'vtysh' or 'vtysh -c' interfaces. + */ +static int vtysh_process_questionmark(const char *input, int input_len) { - int ret; + int ret, width = 0; unsigned int i; - vector vline; - vector describe; - int width; + vector vline, describe; struct cmd_token *token; - vline = cmd_make_strvec(rl_line_buffer); + if (!input) + return 1; + + vline = cmd_make_strvec(input); /* In case of '> ?'. */ if (vline == NULL) { vline = vector_init(1); vector_set(vline, NULL); - } else if (rl_end && isspace((int)rl_line_buffer[rl_end - 1])) + } else if (input_len && isspace((int)input[input_len - 1])) vector_set(vline, NULL); - fprintf(stdout, "\n"); - describe = cmd_describe_command(vline, vty, &ret); /* Ambiguous and no match error. */ @@ -874,7 +877,6 @@ static int vtysh_rl_describe(void) cmd_free_strvec(vline); vector_free(describe); fprintf(stdout, "%% Ambiguous command.\n"); - rl_on_new_line(); return 0; break; case CMD_ERR_NO_MATCH: @@ -882,7 +884,6 @@ static int vtysh_rl_describe(void) if (describe) vector_free(describe); fprintf(stdout, "%% There is no matched command.\n"); - rl_on_new_line(); return 0; break; } @@ -932,9 +933,61 @@ static int vtysh_rl_describe(void) cmd_free_strvec(vline); vector_free(describe); + return 0; +} + +/* + * Entry point for user commands terminated with '?' character and typed through + * the usual vtysh's stdin interface. This is the function being registered with + * readline() api's. + */ +static int vtysh_rl_describe(void) +{ + int ret; + + fprintf(stdout, "\n"); + + ret = vtysh_process_questionmark(rl_line_buffer, rl_end); rl_on_new_line(); - return 0; + return ret; +} + +/* + * Function in charged of processing vtysh instructions terminating with '?' + * character and received through the 'vtysh -c' interface. If user's + * instruction is well-formatted, we will call the same processing routine + * utilized by the traditional vtysh's stdin interface. + */ +int vtysh_execute_command_questionmark(char *input) +{ + int input_len, qmark_count = 0; + const char *str; + + if (!(input && *input)) + return 1; + + /* Finding out question_mark count and strlen */ + for (str = input; *str; ++str) { + if (*str == '?') + qmark_count++; + } + input_len = str - input; + + /* + * Verify that user's input terminates in '?' and that patterns such as + * 'cmd ? subcmd ?' are prevented. + */ + if (qmark_count != 1 || input[input_len - 1] != '?') + return 1; + + /* + * Questionmark-processing function is not expecting to receive '?' + * character in input string. + */ + input[input_len - 1] = '\0'; + + return vtysh_process_questionmark(input, input_len - 1); } /* Result of cmd_complete_command() call will be stored here @@ -1537,7 +1590,7 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, } DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, - "pbr-map NAME seq (1-1000)", + "pbr-map NAME seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert to/delete from existing pbr-map entry\n" @@ -1547,7 +1600,7 @@ DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, return CMD_SUCCESS; } -DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]", +DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" @@ -2361,13 +2414,22 @@ DEFUNSH(VTYSH_ALL, no_vtysh_service_password_encrypt, DEFUNSH(VTYSH_ALL, vtysh_config_password, vtysh_password_cmd, "password [(8-8)] LINE", - "Assign the terminal connection password\n" + "Modify the terminal connection password\n" "Specifies a HIDDEN password will follow\n" "The password string\n") { return CMD_SUCCESS; } +DEFUNSH(VTYSH_ALL, no_vtysh_config_password, no_vtysh_password_cmd, + "no password", NO_STR + "Modify the terminal connection password\n") +{ + vty_out(vty, NO_PASSWD_CMD_WARNING); + + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ALL, vtysh_config_enable_password, vtysh_enable_password_cmd, "enable password [(8-8)] LINE", "Modify enable password parameters\n" @@ -2383,6 +2445,8 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password, "Modify enable password parameters\n" "Assign the privileged level password\n") { + vty_out(vty, NO_PASSWD_CMD_WARNING); + return CMD_SUCCESS; } @@ -3605,6 +3669,7 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd); install_element(CONFIG_NODE, &vtysh_password_cmd); + install_element(CONFIG_NODE, &no_vtysh_password_cmd); install_element(CONFIG_NODE, &vtysh_enable_password_cmd); install_element(CONFIG_NODE, &no_vtysh_enable_password_cmd); } diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index f3e58f309e..ccfdd6557b 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -71,6 +71,7 @@ void vtysh_user_init(void); int vtysh_execute(const char *); int vtysh_execute_no_pager(const char *); +int vtysh_execute_command_questionmark(char *input); char *vtysh_prompt(void); diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index cd59d8094b..3dd70983bc 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -611,7 +611,16 @@ int main(int argc, char **argv, char **env) if (logfile) log_it(cmd->line); - ret = vtysh_execute_no_pager(cmd->line); + /* + * Parsing logic for regular commands will be different than + * for those commands requiring further processing, such as + * cli instructions terminating with question-mark character. + */ + if (!vtysh_execute_command_questionmark(cmd->line)) + ret = CMD_SUCCESS; + else + ret = vtysh_execute_no_pager(cmd->line); + if (!no_error && !(ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING)) diff --git a/zebra/connected.c b/zebra/connected.c index 23f2f666a0..a9a4dfe08f 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -403,10 +403,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) * head. */ rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, false, NULL); + &p, NULL, &nh, 0, 0, false); rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, false, NULL); + &p, NULL, &nh, 0, 0, false); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { char buf[PREFIX_STRLEN]; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e28c189f86..7423e9237f 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1025,7 +1025,7 @@ static void if_netlink_check_ifp_instance_consistency(uint16_t cmd, struct interface *ifp, ns_id_t ns_id) { - struct interface *old_ifp; + struct interface *other_ifp; /* * look if interface name is also found on other netns @@ -1037,29 +1037,42 @@ static void if_netlink_check_ifp_instance_consistency(uint16_t cmd, if (!vrf_is_backend_netns() || !strcmp(ifp->name, "lo")) return; - old_ifp = if_lookup_by_name_not_ns(ns_id, ifp->name); - if (!old_ifp) + other_ifp = if_lookup_by_name_not_ns(ns_id, ifp->name); + if (!other_ifp) + return; + /* because previous interface may be inactive, + * interface is moved back to default vrf + * then one may find the same pointer; ignore + */ + if (other_ifp == ifp) return; if ((cmd == RTM_NEWLINK) - && (CHECK_FLAG(old_ifp->status, ZEBRA_INTERFACE_ACTIVE))) + && (CHECK_FLAG(other_ifp->status, ZEBRA_INTERFACE_ACTIVE))) return; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s %s(%u) %s VRF %u", - cmd == RTM_DELLINK ? - "RTM_DELLINK replaced by" : - "RTM_NEWLINK replaces", + if (IS_ZEBRA_DEBUG_KERNEL && cmd == RTM_NEWLINK) { + zlog_debug("RTM_NEWLINK %s(%u, VRF %u) replaces %s(%u, VRF %u)\n", ifp->name, - old_ifp->ifindex, - cmd == RTM_DELLINK ? - "in" : "from", - old_ifp->vrf_id); + ifp->ifindex, + ifp->vrf_id, + other_ifp->name, + other_ifp->ifindex, + other_ifp->vrf_id); + } else if (IS_ZEBRA_DEBUG_KERNEL && cmd == RTM_DELLINK) { + zlog_debug("RTM_DELLINK %s(%u, VRF %u) is replaced by %s(%u, VRF %u)\n", + ifp->name, + ifp->ifindex, + ifp->vrf_id, + other_ifp->name, + other_ifp->ifindex, + other_ifp->vrf_id); + } /* the found interface replaces the current one * remove it */ if (cmd == RTM_DELLINK) if_delete(ifp); else - if_delete(old_ifp); + if_delete(other_ifp); /* the found interface is replaced by the current one * suppress it */ diff --git a/zebra/interface.c b/zebra/interface.c index 6f59a2d399..7de18d683c 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -202,6 +202,7 @@ struct interface *if_link_per_ns(struct zebra_ns *ns, struct interface *ifp) if (rn->info) { ifp = (struct interface *)rn->info; route_unlock_node(rn); /* get */ + ifp->node = rn; return ifp; } @@ -725,6 +726,9 @@ void if_delete_update(struct interface *ifp) return; } + if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) + return; + /* Mark interface as inactive */ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE); @@ -1224,8 +1228,13 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) br_slave->bridge_ifindex); } - if (zebra_if->link_ifindex != IFINDEX_INTERNAL) - vty_out(vty, " Link ifindex %u\n", zebra_if->link_ifindex); + if (zebra_if->link_ifindex != IFINDEX_INTERNAL) { + vty_out(vty, " Link ifindex %u", zebra_if->link_ifindex); + if (zebra_if->link) + vty_out(vty, "(%s)\n", zebra_if->link->name); + else + vty_out(vty, "(Unknown)\n"); + } if (HAS_LINK_PARAMS(ifp)) { int i; diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index db4f19460a..6b587dab38 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -463,7 +463,7 @@ int netlink_parse_info(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *, int read_in = 0; while (1) { - char buf[NL_PKT_BUF_SIZE]; + char buf[NL_RCV_PKT_BUF_SIZE]; struct iovec iov = {.iov_base = buf, .iov_len = sizeof buf}; struct sockaddr_nl snl; struct msghdr msg = {.msg_name = (void *)&snl, diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index 8441eeac76..dc075b9aff 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -23,6 +23,7 @@ #ifdef HAVE_NETLINK +#define NL_RCV_PKT_BUF_SIZE 32768 #define NL_PKT_BUF_SIZE 8192 extern void netlink_parse_rtattr(struct rtattr **tb, int max, diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index e60e05bcdf..b85c4748c4 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1043,7 +1043,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true, NULL); + NULL, 0, 0, true); if (!nh.type) { nh.type = NEXTHOP_TYPE_IPV4; @@ -1058,7 +1058,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true, NULL); + &nh, 0, 0, true); } if (dest.sa.sa_family == AF_INET6) { /* One day we might have a debug section here like one in the @@ -1089,7 +1089,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - NULL, 0, 0, true, NULL); + NULL, 0, 0, true); if (!nh.type) { nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX @@ -1106,7 +1106,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &nh, 0, 0, true, NULL); + &nh, 0, 0, true); } } diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 190ac1e57f..f3fa3ba94e 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -50,7 +50,6 @@ DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk"); * it will be a proxy to relay messages to external label manager * This zclient thus is to connect to it */ -static struct stream *ibuf; static struct stream *obuf; static struct zclient *zclient; bool lm_is_external; @@ -60,7 +59,7 @@ static void delete_label_chunk(void *val) XFREE(MTYPE_LM_CHUNK, val); } -static int relay_response_back(struct zserv *zserv) +static int relay_response_back(void) { int ret = 0; struct stream *src, *dst; @@ -69,49 +68,72 @@ static int relay_response_back(struct zserv *zserv) uint8_t version; vrf_id_t vrf_id; uint16_t resp_cmd; + uint8_t proto; + const char *proto_str; + unsigned short instance; + struct zserv *zserv; + /* input buffer with msg from label manager */ src = zclient->ibuf; - dst = obuf; stream_reset(src); + /* parse header */ ret = zclient_read_header(src, zclient->sock, &size, &marker, &version, &vrf_id, &resp_cmd); if (ret < 0 && errno != EAGAIN) { - zlog_err("%s: Error reading Label Manager response: %s", - __func__, strerror(errno)); + zlog_err("Error reading Label Manager response: %s", + strerror(errno)); return -1; } - zlog_debug("%s: Label Manager response received, %d bytes", __func__, - size); + zlog_debug("Label Manager response received, %d bytes", size); if (size == 0) return -1; - /* send response back */ + /* Get the 'proto' field of the message */ + proto = stream_getc(src); + + /* Get the 'instance' field of the message */ + instance = stream_getw(src); + + proto_str = zebra_route_string(proto); + + /* lookup the client to relay the msg to */ + zserv = zebra_find_client(proto, instance); + if (!zserv) { + zlog_err( + "Error relaying LM response: can't find client %s, instance %u", + proto_str, instance); + return -1; + } + zlog_debug("Found client to relay LM response to client %s instance %u", + proto_str, instance); + + /* copy msg into output buffer */ + dst = obuf; stream_copy(dst, src); - ret = writen(zserv->sock, src->data, stream_get_endp(src)); + + /* send response back */ + ret = writen(zserv->sock, dst->data, stream_get_endp(dst)); if (ret <= 0) { - zlog_err("%s: Error sending Label Manager response back: %s", - __func__, strerror(errno)); + zlog_err("Error relaying LM response to %s instance %u: %s", + proto_str, instance, strerror(errno)); return -1; } - zlog_debug("%s: Label Manager response (%d bytes) sent back", __func__, - ret); + zlog_debug("Relayed LM response (%d bytes) to %s instance %u", ret, + proto_str, instance); return 0; } static int lm_zclient_read(struct thread *t) { - struct zserv *zserv; int ret; - /* Get socket to zebra. */ - zserv = THREAD_ARG(t); zclient->t_read = NULL; /* read response and send it back */ - ret = relay_response_back(zserv); + ret = relay_response_back(); return ret; } @@ -125,6 +147,10 @@ static int reply_error(int cmd, struct zserv *zserv, vrf_id_t vrf_id) zclient_create_header(s, cmd, vrf_id); + /* proto */ + stream_putc(s, zserv->proto); + /* instance */ + stream_putw(s, zserv->instance); /* result */ stream_putc(s, 1); @@ -148,38 +174,73 @@ static int reply_error(int cmd, struct zserv *zserv, vrf_id_t vrf_id) * @return 0 on success, -1 otherwise */ int zread_relay_label_manager_request(int cmd, struct zserv *zserv, - vrf_id_t vrf_id) + struct stream *msg, vrf_id_t vrf_id) { - struct stream *src, *dst; + struct stream *dst; int ret = 0; + uint8_t proto; + const char *proto_str; + unsigned short instance; if (zclient->sock < 0) { - zlog_err( - "%s: Error relaying label chunk request: no zclient socket", - __func__); + zlog_err("Unable to relay LM request: no socket"); reply_error(cmd, zserv, vrf_id); return -1; } + /* peek msg to get proto and instance id. This zebra, which acts as + * a proxy needs to have such values for each client in order to + * relay responses back to it. + */ + + /* Get the 'proto' field of incoming msg */ + proto = stream_getc(msg); + + /* Get the 'instance' field of incoming msg */ + instance = stream_getw(msg); + + /* stringify proto */ + proto_str = zebra_route_string(proto); + + /* check & set client proto if unset */ + if (zserv->proto && zserv->proto != proto) { + zlog_warn("Client proto(%u) != msg proto(%u)", zserv->proto, + proto); + return -1; + } + + /* check & set client instance if unset */ + if (zserv->instance && zserv->instance != instance) { + zlog_err("Client instance(%u) != msg instance(%u)", + zserv->instance, instance); + return -1; + } + + /* recall proto and instance */ + zserv->instance = instance; + zserv->proto = proto; + /* in case there's any incoming message enqueued, read and forward it */ while (ret == 0) - ret = relay_response_back(zserv); + ret = relay_response_back(); - /* Send request to external label manager */ - src = ibuf; + /* get the msg buffer used toward the 'master' Label Manager */ dst = zclient->obuf; - stream_copy(dst, src); + /* copy the message */ + stream_copy(dst, msg); + /* Send request to external label manager */ ret = writen(zclient->sock, dst->data, stream_get_endp(dst)); if (ret <= 0) { - zlog_err("%s: Error relaying label chunk request: %s", __func__, - strerror(errno)); + zlog_err("Error relaying LM request from %s instance %u: %s", + proto_str, instance, strerror(errno)); reply_error(cmd, zserv, vrf_id); return -1; } - zlog_debug("%s: Label chunk request relayed. %d bytes sent", __func__, - ret); + zlog_debug("Relayed LM request (%d bytes) from %s instance %u", ret, + proto_str, instance); + /* Release label chunk has no response */ if (cmd == ZEBRA_RELEASE_LABEL_CHUNK) @@ -187,7 +248,7 @@ int zread_relay_label_manager_request(int cmd, struct zserv *zserv, /* make sure we listen to the response */ if (!zclient->t_read) - thread_add_read(zclient->master, lm_zclient_read, zserv, + thread_add_read(zclient->master, lm_zclient_read, NULL, zclient->sock, &zclient->t_read); return 0; @@ -276,7 +337,7 @@ void label_manager_init(char *lm_zserv_path) { /* this is an actual label manager */ if (!lm_zserv_path) { - zlog_debug("Initializing own label manager"); + zlog_debug("Initializing internal label manager"); lm_is_external = false; lbl_mgr.lc_list = list_new(); lbl_mgr.lc_list->del = delete_label_chunk; @@ -287,7 +348,6 @@ void label_manager_init(char *lm_zserv_path) lm_zclient_init(lm_zserv_path); } - ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ); obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); hook_register(zapi_client_close, release_daemon_label_chunks); @@ -393,6 +453,5 @@ int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, void label_manager_close() { list_delete_and_null(&lbl_mgr.lc_list); - stream_free(ibuf); stream_free(obuf); } diff --git a/zebra/label_manager.h b/zebra/label_manager.h index b998372224..3e3def5f98 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -64,7 +64,7 @@ struct label_manager { bool lm_is_external; int zread_relay_label_manager_request(int cmd, struct zserv *zserv, - vrf_id_t vrf_id); + struct stream *msg, vrf_id_t vrf_id); void label_manager_init(char *lm_zserv_path); struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 810ee33839..b1387815ba 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -539,8 +539,8 @@ 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->ng.nexthop, re->vrf_id, - re->tag, rmap_name); + afi, re->type, re->instance, &rn->p, re->ng.nexthop, + re->vrf_id, re->tag, rmap_name); if (ret != RMAP_MATCH) { UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); @@ -592,7 +592,7 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, re->flags, &p, NULL, re->ng.nexthop, - zebrad.rtm_table_default, re->metric, false, NULL); + zebrad.rtm_table_default, re->metric, false); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index d68bf787c0..7b9e6d56a7 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -313,8 +313,7 @@ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - uint32_t table_id, uint32_t metric, bool fromkernel, - struct ethaddr *rmac); + uint32_t table_id, uint32_t metric, bool fromkernel); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a35dc9a177..2fd7bee056 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -613,12 +613,12 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, if (gate) memcpy(&nh.gate, gate, sz); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, - &p, NULL, &nh, table, metric, true, NULL); + &p, NULL, &nh, table, metric, true); } else { /* XXX: need to compare the entire list of nexthops * here for NLM_F_APPEND stupidity */ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, - &p, NULL, NULL, table, metric, true, NULL); + &p, NULL, NULL, table, metric, true); } } @@ -1233,14 +1233,6 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, "netlink_route_multipath() (%s): " "nexthop via if %u", routedesc, nexthop->ifindex); - } else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via if %u", - routedesc, nexthop->ifindex); - } else { - rtnh->rtnh_ifindex = 0; } } @@ -1316,8 +1308,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, struct zebra_ns *zns = zebra_ns_lookup(ns_id); - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; @@ -1674,8 +1665,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) struct zebra_ns *zns; zns = zvrf->zns; - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -1767,8 +1757,7 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp, struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); zns = zvrf->zns; - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -2083,8 +2072,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, return -1; } - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -2400,8 +2388,7 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); zns = zvrf->zns; - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 2ff660b3f9..9a18cc22f0 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -887,6 +887,10 @@ static int zsend_assign_label_chunk_response(struct zserv *client, zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id); if (lmc) { + /* proto */ + stream_putc(s, lmc->proto); + /* instance */ + stream_putw(s, lmc->instance); /* keep */ stream_putc(s, lmc->keep); /* start and end labels */ @@ -912,6 +916,12 @@ static int zsend_label_manager_connect_response(struct zserv *client, zclient_create_header(s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id); + /* proto */ + stream_putc(s, client->proto); + + /* instance */ + stream_putw(s, client->instance); + /* result */ stream_putc(s, result); @@ -1382,7 +1392,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) &(api_nh->gate.ipv4), sizeof(struct in_addr)); zebra_vxlan_evpn_vrf_route_add( - vrf_id, &api.rmac, &vtep_ip, + vrf_id, &api_nh->rmac, &vtep_ip, &api.prefix); } break; @@ -1415,7 +1425,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) &(api_nh->gate.ipv6), sizeof(struct in6_addr)); zebra_vxlan_evpn_vrf_route_add( - vrf_id, &api.rmac, &vtep_ip, + vrf_id, &api_nh->rmac, &vtep_ip, &api.prefix); } break; @@ -1522,7 +1532,7 @@ static void zread_route_del(ZAPI_HANDLER_ARGS) rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, api.flags, &api.prefix, src_p, NULL, table_id, api.metric, - false, &api.rmac); + false); /* Stats */ switch (api.prefix.family) { @@ -1724,7 +1734,7 @@ static void zread_ipv4_delete(ZAPI_HANDLER_ARGS) table_id = zvrf->table_id; rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, NULL, NULL, table_id, 0, false, NULL); + api.flags, &p, NULL, NULL, table_id, 0, false); client->v4_route_del_cnt++; stream_failure: @@ -2148,8 +2158,7 @@ static void zread_ipv6_delete(ZAPI_HANDLER_ARGS) src_pp = NULL; rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, src_pp, NULL, client->rtm_table, 0, false, - NULL); + api.flags, &p, src_pp, NULL, client->rtm_table, 0, false); client->v6_route_del_cnt++; @@ -2398,6 +2407,26 @@ static void zread_label_manager_connect(struct zserv *client, stream_failure: return; } +static int msg_client_id_mismatch(const char *op, struct zserv *client, + uint8_t proto, unsigned int instance) +{ + if (proto != client->proto) { + zlog_err("%s: msg vs client proto mismatch, client=%u msg=%u", + op, client->proto, proto); + /* TODO: fail when BGP sets proto and instance */ + /* return 1; */ + } + + if (instance != client->instance) { + zlog_err( + "%s: msg vs client instance mismatch, client=%u msg=%u", + op, client->instance, instance); + /* TODO: fail when BGP sets proto and instance */ + /* return 1; */ + } + + return 0; +} static void zread_get_label_chunk(struct zserv *client, struct stream *msg, vrf_id_t vrf_id) @@ -2406,21 +2435,32 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, uint8_t keep; uint32_t size; struct label_manager_chunk *lmc; + uint8_t proto; + unsigned short instance; /* Get input stream. */ s = msg; /* Get data. */ + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); STREAM_GETC(s, keep); STREAM_GETL(s, size); + /* detect client vs message (proto,instance) mismatch */ + if (msg_client_id_mismatch("Get-label-chunk", client, proto, instance)) + return; + lmc = assign_label_chunk(client->proto, client->instance, keep, size); if (!lmc) - zlog_err("%s: Unable to assign Label Chunk of size %u", - __func__, size); + zlog_err( + "Unable to assign Label Chunk of size %u to %s instance %u", + size, zebra_route_string(client->proto), + client->instance); else - zlog_debug("Assigned Label Chunk %u - %u to %u", lmc->start, - lmc->end, keep); + zlog_debug("Assigned Label Chunk %u - %u to %s instance %u", + lmc->start, lmc->end, + zebra_route_string(client->proto), client->instance); /* send response back */ zsend_assign_label_chunk_response(client, vrf_id, lmc); @@ -2432,14 +2472,23 @@ static void zread_release_label_chunk(struct zserv *client, struct stream *msg) { struct stream *s; uint32_t start, end; + uint8_t proto; + unsigned short instance; /* Get input stream. */ s = msg; /* Get data. */ + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); STREAM_GETL(s, start); STREAM_GETL(s, end); + /* detect client vs message (proto,instance) mismatch */ + if (msg_client_id_mismatch("Release-label-chunk", client, proto, + instance)) + return; + release_label_chunk(client->proto, client->instance, start, end); stream_failure: @@ -2453,7 +2502,7 @@ static void zread_label_manager_request(ZAPI_HANDLER_ARGS) /* external label manager */ if (lm_is_external) - zread_relay_label_manager_request(hdr->command, client, + zread_relay_label_manager_request(hdr->command, client, msg, zvrf_id(zvrf)); /* this is a label manager */ else { diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 07e81aa020..d20f93f521 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -298,6 +298,9 @@ DEFUN (zebra_ptm_enable_if, int old_ptm_enable; int send_linkdown = 0; + if_data = ifp->info; + if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC; + if (ifp->ifindex == IFINDEX_INTERNAL) { return CMD_SUCCESS; } @@ -317,9 +320,6 @@ DEFUN (zebra_ptm_enable_if, } } - if_data = ifp->info; - if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC; - return CMD_SUCCESS; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 67832f2d3f..dec4ed06a9 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -924,7 +924,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, + ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, nexthop->vrf_id, re->tag); if (ret == RMAP_DENYMATCH) { if (IS_ZEBRA_DEBUG_RIB) { @@ -2394,8 +2394,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - uint32_t table_id, uint32_t metric, bool fromkernel, - struct ethaddr *rmac) + uint32_t table_id, uint32_t metric, bool fromkernel) { struct route_table *table; struct route_node *rn; @@ -2569,7 +2568,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, &(tmp_nh->gate.ipv6), sizeof(struct in6_addr)); } - zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac, + zebra_vxlan_evpn_vrf_route_del(re->vrf_id, &vtep_ip, p); } } diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 013e841a5c..10ba88880a 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -49,6 +49,7 @@ struct nh_rmap_obj { struct nexthop *nexthop; vrf_id_t vrf_id; uint32_t source_protocol; + uint8_t instance; int metric; route_tag_t tag; }; @@ -287,7 +288,7 @@ DEFUN (no_match_ip_nexthop_prefix_len, DEFUN (match_source_protocol, match_source_protocol_cmd, - "match source-protocol <bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static>", + "match source-protocol <bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static|sharp>", MATCH_STR "Match protocol via which the route was learnt\n" "BGP protocol\n" @@ -303,7 +304,8 @@ DEFUN (match_source_protocol, "Routes from directly connected peer\n" "Routes from system configuration\n" "Routes from kernel\n" - "Statically configured routes\n") + "Statically configured routes\n" + "SHARP process\n") { char *proto = argv[2]->text; int i; @@ -319,7 +321,7 @@ DEFUN (match_source_protocol, DEFUN (no_match_source_protocol, no_match_source_protocol_cmd, - "no match source-protocol [<bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static>]", + "no match source-protocol [<bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static|sharp>]", NO_STR MATCH_STR "No match protocol via which the route was learnt\n" @@ -336,13 +338,40 @@ DEFUN (no_match_source_protocol, "Routes from directly connected peer\n" "Routes from system configuration\n" "Routes from kernel\n" - "Statically configured routes\n") + "Statically configured routes\n" + "SHARP process\n") { char *proto = (argc == 4) ? argv[3]->text : NULL; return zebra_route_match_delete(vty, "source-protocol", proto, RMAP_EVENT_MATCH_DELETED); } +DEFUN (match_source_instance, + match_source_instance_cmd, + "match source-instance (0-255)", + MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + char *instance = argv[2]->arg; + + return zebra_route_match_add(vty, "source-instance", instance, + RMAP_EVENT_MATCH_ADDED); +} + +DEFUN (no_match_source_instance, + no_match_source_instance_cmd, + "no match source-instance [(0-255)]", + NO_STR MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + char *instance = (argc == 4) ? argv[3]->arg : NULL; + + return zebra_route_match_delete(vty, "source-instance", instance, + RMAP_EVENT_MATCH_ADDED); +} + /* set functions */ DEFUN (set_src, @@ -1172,6 +1201,47 @@ static struct route_map_rule_cmd route_match_source_protocol_cmd = { "source-protocol", route_match_source_protocol, route_match_source_protocol_compile, route_match_source_protocol_free}; +/* `source-instance` */ +static route_map_result_t route_match_source_instance(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) +{ + uint8_t *instance = (uint8_t *)rule; + struct nh_rmap_obj *nh_data; + + if (type != RMAP_ZEBRA) + return RMAP_NOMATCH; + + nh_data = (struct nh_rmap_obj *)object; + if (!nh_data) + return RMAP_DENYMATCH; + + return (nh_data->instance == *instance) ? RMAP_MATCH : RMAP_NOMATCH; +} + +static void *route_match_source_instance_compile(const char *arg) +{ + uint8_t *instance; + int i; + + i = atoi(arg); + instance = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t)); + + *instance = i; + + return instance; +} + +static void route_match_source_instance_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_source_instance_cmd = { + "source-instance", route_match_source_instance, + route_match_source_instance_compile, route_match_source_instance_free}; + /* `set src A.B.C.D' */ /* Set src. */ @@ -1252,7 +1322,7 @@ void zebra_route_map_write_delay_timer(struct vty *vty) } route_map_result_t zebra_route_map_check(int family, int rib_type, - struct prefix *p, + uint8_t instance, struct prefix *p, struct nexthop *nexthop, vrf_id_t vrf_id, route_tag_t tag) { @@ -1263,6 +1333,7 @@ route_map_result_t zebra_route_map_check(int family, int rib_type, nh_obj.nexthop = nexthop; nh_obj.vrf_id = vrf_id; nh_obj.source_protocol = rib_type; + nh_obj.instance = instance; nh_obj.metric = 0; nh_obj.tag = tag; @@ -1296,9 +1367,10 @@ void zebra_del_import_table_route_map(afi_t afi, uint32_t table) } route_map_result_t -zebra_import_table_route_map_check(int family, int re_type, struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, - route_tag_t tag, const char *rmap_name) +zebra_import_table_route_map_check(int family, int re_type, uint8_t instance, + struct prefix *p, struct nexthop *nexthop, + vrf_id_t vrf_id, route_tag_t tag, + const char *rmap_name) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_DENYMATCH; @@ -1307,6 +1379,7 @@ zebra_import_table_route_map_check(int family, int re_type, struct prefix *p, nh_obj.nexthop = nexthop; nh_obj.vrf_id = vrf_id; nh_obj.source_protocol = re_type; + nh_obj.instance = instance; nh_obj.metric = 0; nh_obj.tag = tag; @@ -1331,6 +1404,7 @@ route_map_result_t zebra_nht_route_map_check(int family, int client_proto, nh_obj.nexthop = nexthop; nh_obj.vrf_id = nexthop->vrf_id; nh_obj.source_protocol = re->type; + nh_obj.instance = re->instance; nh_obj.metric = re->metric; nh_obj.tag = re->tag; @@ -1471,6 +1545,8 @@ void zebra_route_map_init() route_map_install_match(&route_match_ipv6_address_prefix_len_cmd); route_map_install_match(&route_match_ip_nexthop_prefix_len_cmd); route_map_install_match(&route_match_source_protocol_cmd); + route_map_install_match(&route_match_source_instance_cmd); + /* */ route_map_install_set(&route_set_src_cmd); /* */ @@ -1482,6 +1558,9 @@ void zebra_route_map_init() install_element(RMAP_NODE, &no_match_ip_address_prefix_len_cmd); install_element(RMAP_NODE, &match_source_protocol_cmd); install_element(RMAP_NODE, &no_match_source_protocol_cmd); + install_element(RMAP_NODE, &match_source_instance_cmd); + install_element(RMAP_NODE, &no_match_source_instance_cmd); + /* */ install_element(RMAP_NODE, &set_src_cmd); install_element(RMAP_NODE, &no_set_src_cmd); diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index 14c7c58848..20d425a2bc 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -34,14 +34,14 @@ extern void zebra_del_import_table_route_map(afi_t afi, uint32_t table); extern void zebra_route_map_write_delay_timer(struct vty *); extern route_map_result_t -zebra_import_table_route_map_check(int family, int rib_type, struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, - route_tag_t tag, const char *rmap_name); -extern route_map_result_t zebra_route_map_check(int family, int rib_type, - struct prefix *p, - struct nexthop *nexthop, - vrf_id_t vrf_id, - route_tag_t tag); +zebra_import_table_route_map_check(int family, int rib_type, uint8_t instance, + struct prefix *p, struct nexthop *nexthop, + vrf_id_t vrf_id, route_tag_t tag, + const char *rmap_name); +extern route_map_result_t +zebra_route_map_check(int family, int rib_type, uint8_t instance, + struct prefix *p, struct nexthop *nexthop, + vrf_id_t vrf_id, route_tag_t tag); extern route_map_result_t zebra_nht_route_map_check(int family, int client_proto, struct prefix *p, struct route_entry *, struct nexthop *nexthop); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 2ae9ac5082..e6f80f92a7 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2798,12 +2798,14 @@ DEFUN (vrf_vni_mapping, DEFUN (no_vrf_vni_mapping, no_vrf_vni_mapping_cmd, - "no vni " CMD_VNI_RANGE, + "no vni " CMD_VNI_RANGE "[prefix-routes-only]", NO_STR "VNI corresponding to tenant VRF\n" - "VNI-ID") + "VNI-ID\n" + "prefix-routes-only\n") { int ret = 0; + int filter = 0; char err[ERR_STR_SZ]; vni_t vni = strtoul(argv[2]->arg, NULL, 10); @@ -2812,7 +2814,11 @@ DEFUN (no_vrf_vni_mapping, assert(vrf); assert(zvrf); - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0); + if (argc == 4) + filter = 1; + + ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, + ERR_STR_SZ, filter, 0); if (ret != 0) { vty_out(vty, "%s\n", err); return CMD_WARNING; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 6e901a0457..3278c86b99 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -3238,15 +3238,9 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, /* handle rmac delete */ -static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, +static void zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac, struct prefix *host_prefix) { - zebra_mac_t *zrmac = NULL; - - zrmac = zl3vni_rmac_lookup(zl3vni, rmac); - if (!zrmac) - return -1; - host_list_delete_host(zrmac->host_list, host_prefix); if (list_isempty(zrmac->host_list)) { @@ -3256,7 +3250,6 @@ static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, /* del the rmac entry */ zl3vni_rmac_del(zl3vni, zrmac); } - return 0; } /* @@ -3394,15 +3387,9 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, } /* handle nh neigh delete */ -static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, - struct prefix *host_prefix) +static void zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *nh, + struct prefix *host_prefix) { - zebra_neigh_t *nh = NULL; - - nh = zl3vni_nh_lookup(zl3vni, vtep_ip); - if (!nh) - return -1; - host_list_delete_host(nh->host_list, host_prefix); if (list_isempty(nh->host_list)) { @@ -3412,8 +3399,6 @@ static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, /* delete the nh entry */ zl3vni_nh_del(zl3vni, nh); } - - return 0; } /* handle neigh update from kernel - the only thing of interest is to @@ -3958,34 +3943,65 @@ void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac, struct prefix *host_prefix) { zebra_l3vni_t *zl3vni = NULL; + struct ipaddr ipv4_vtep; zl3vni = zl3vni_from_vrf(vrf_id); if (!zl3vni || !is_l3vni_oper_up(zl3vni)) return; - /* add the next hop neighbor */ + /* + * add the next hop neighbor - + * neigh to be installed is the ipv6 nexthop neigh + */ zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix); - /* add the rmac */ - zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix); + /* + * if the remote vtep is a ipv4 mapped ipv6 address convert it to ipv4 + * address. Rmac is programmed against the ipv4 vtep because we only + * support ipv4 tunnels in the h/w right now + */ + memset(&ipv4_vtep, 0, sizeof(struct ipaddr)); + ipv4_vtep.ipa_type = IPADDR_V4; + if (vtep_ip->ipa_type == IPADDR_V6) + ipv4_mapped_ipv6_to_ipv4(&vtep_ip->ipaddr_v6, + &(ipv4_vtep.ipaddr_v4)); + else + memcpy(&(ipv4_vtep.ipaddr_v4), &vtep_ip->ipaddr_v4, + sizeof(struct in_addr)); + + /* + * add the rmac - remote rmac to be installed is against the ipv4 + * nexthop address + */ + zl3vni_remote_rmac_add(zl3vni, rmac, &ipv4_vtep, host_prefix); } /* handle evpn vrf route delete */ -void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, struct ethaddr *rmac, +void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, struct ipaddr *vtep_ip, struct prefix *host_prefix) { zebra_l3vni_t *zl3vni = NULL; + zebra_neigh_t *nh = NULL; + zebra_mac_t *zrmac = NULL; zl3vni = zl3vni_from_vrf(vrf_id); if (!zl3vni) return; + /* find the next hop entry and rmac entry */ + nh = zl3vni_nh_lookup(zl3vni, vtep_ip); + if (!nh) + return; + zrmac = zl3vni_rmac_lookup(zl3vni, &nh->emac); + /* delete the next hop entry */ - zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix); + zl3vni_remote_nh_del(zl3vni, nh, host_prefix); /* delete the rmac entry */ - zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix); + if (zrmac) + zl3vni_remote_rmac_del(zl3vni, zrmac, host_prefix); + } void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, @@ -5143,7 +5159,7 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) l += IPV4_MAX_BYTELEN; /* Get flags - sticky mac and/or gateway mac */ - flags = stream_getc(s); + STREAM_GETC(s, flags); sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); l++; @@ -6516,6 +6532,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, return -1; } + if (filter && !CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY)) { + snprintf(err, ERR_STR_SZ, + "prefix-routes-only is not set for the vni"); + return -1; + } + zebra_vxlan_process_l3vni_oper_down(zl3vni); /* delete and uninstall all rmacs */ @@ -6602,7 +6624,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) } s = msg; - advertise = stream_getc(s); + STREAM_GETC(s, advertise); vni = stream_get3(s); zvni = zvni_lookup(vni); @@ -6641,6 +6663,9 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) zvni_advertise_subnet(zvni, vlan_if, 1); else zvni_advertise_subnet(zvni, vlan_if, 0); + +stream_failure: + return; } /* @@ -6663,7 +6688,7 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) s = msg; STREAM_GETC(s, advertise); - STREAM_GET(&vni, s, 3); + STREAM_GETL(s, vni); if (!vni) { if (IS_ZEBRA_DEBUG_VXLAN) diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 6153c7d7e3..34d1152751 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -160,7 +160,6 @@ extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ipaddr *ip, struct prefix *host_prefix); extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, - struct ethaddr *rmac, struct ipaddr *vtep_ip, struct prefix *host_prefix); |
