diff options
122 files changed, 4954 insertions, 956 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c index 48f6994d82..9ea123c8f9 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -73,7 +73,6 @@ int protocol_port; /* babel's port */ int protocol_socket = -1; /* socket: communicate with others babeld */ static char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG; -static char *babel_config_file = NULL; static char *babel_vty_addr = NULL; static int babel_vty_port = BABEL_VTY_PORT; @@ -198,7 +197,7 @@ main(int argc, char **argv) babelz_zebra_init (); /* Get zebra configuration file. */ - vty_read_config (babel_config_file, babel_config_default); + vty_read_config (babeld_di.config_file, babel_config_default); /* init buffer */ rc = resize_receive_buffer(1500); @@ -389,7 +388,7 @@ show_babel_main_configuration (struct vty *vty) "id = %s\n" "kernel_metric = %d\n", state_file, - babel_config_file ? babel_config_file : babel_config_default, + babeld_di.config_file ? babeld_di.config_file : babel_config_default, format_address(protocol_group), protocol_port, babel_vty_addr ? babel_vty_addr : "None", diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index a2880b7b94..8a410adca1 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -87,7 +87,7 @@ libbgp_a_SOURCES = \ bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \ bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c \ bgp_keepalives.c bgp_io.c bgp_flowspec.c bgp_flowspec_util.c \ - bgp_flowspec_vty.c bgp_labelpool.c + bgp_flowspec_vty.c bgp_labelpool.c bgp_pbr.c noinst_HEADERS = \ bgp_memory.h \ @@ -101,7 +101,7 @@ noinst_HEADERS = \ $(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \ bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h bgp_keepalives.h \ bgp_io.h bgp_flowspec.h bgp_flowspec_private.h bgp_flowspec_util.h \ - bgp_labelpool.h + bgp_labelpool.h bgp_pbr.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@ diff --git a/bgpd/bgp_attr.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_debug.c b/bgpd/bgp_debug.c index 29ac5f520d..3e3fbcbfe8 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -59,6 +59,7 @@ unsigned long conf_bgp_debug_update_groups; unsigned long conf_bgp_debug_vpn; unsigned long conf_bgp_debug_flowspec; unsigned long conf_bgp_debug_labelpool; +unsigned long conf_bgp_debug_pbr; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -75,6 +76,7 @@ unsigned long term_bgp_debug_update_groups; unsigned long term_bgp_debug_vpn; unsigned long term_bgp_debug_flowspec; unsigned long term_bgp_debug_labelpool; +unsigned long term_bgp_debug_pbr; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -1653,7 +1655,40 @@ DEFUN (no_debug_bgp_vpn, if (vty->node != CONFIG_NODE) vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text); + return CMD_SUCCESS; +} +/* debug bgp pbr */ +DEFUN (debug_bgp_pbr, + debug_bgp_pbr_cmd, + "debug bgp pbr", + DEBUG_STR + BGP_STR + "BGP policy based routing\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON(pbr, PBR); + else { + TERM_DEBUG_ON(pbr, PBR); + vty_out(vty, "BGP policy based routing is on\n"); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_pbr, + no_debug_bgp_pbr_cmd, + "no debug bgp pbr", + NO_STR + DEBUG_STR + BGP_STR + "BGP policy based routing\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF(pbr, PBR); + else { + TERM_DEBUG_OFF(pbr, PBR); + vty_out(vty, "BGP policy based routing is off\n"); + } return CMD_SUCCESS; } @@ -1733,6 +1768,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL); TERM_DEBUG_OFF(flowspec, FLOWSPEC); TERM_DEBUG_OFF(labelpool, LABELPOOL); + TERM_DEBUG_OFF(pbr, PBR); vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -1808,6 +1844,9 @@ DEFUN_NOSH (show_debugging_bgp, if (BGP_DEBUG(labelpool, LABELPOOL)) vty_out(vty, " BGP labelpool debugging is on\n"); + if (BGP_DEBUG(pbr, PBR)) + vty_out(vty, " BGP policy based routing debugging is on\n"); + vty_out(vty, "\n"); return CMD_SUCCESS; } @@ -1865,6 +1904,9 @@ int bgp_debug_count(void) if (BGP_DEBUG(labelpool, LABELPOOL)) ret++; + if (BGP_DEBUG(pbr, PBR)) + ret++; + return ret; } @@ -1966,6 +2008,10 @@ static int bgp_config_write_debug(struct vty *vty) write++; } + if (CONF_BGP_DEBUG(pbr, PBR)) { + vty_out(vty, "debug bgp pbr\n"); + write++; + } return write; } @@ -2069,6 +2115,13 @@ void bgp_debug_init(void) install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd); install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd); install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd); + + /* debug bgp pbr */ + install_element(ENABLE_NODE, &debug_bgp_pbr_cmd); + install_element(CONFIG_NODE, &debug_bgp_pbr_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_pbr_cmd); + } /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index ad476ee918..d5d8fbb505 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -75,6 +75,7 @@ extern unsigned long conf_bgp_debug_update_groups; extern unsigned long conf_bgp_debug_vpn; extern unsigned long conf_bgp_debug_flowspec; extern unsigned long conf_bgp_debug_labelpool; +extern unsigned long conf_bgp_debug_pbr; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -89,6 +90,7 @@ extern unsigned long term_bgp_debug_update_groups; extern unsigned long term_bgp_debug_vpn; extern unsigned long term_bgp_debug_flowspec; extern unsigned long term_bgp_debug_labelpool; +extern unsigned long term_bgp_debug_pbr; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -123,6 +125,8 @@ struct bgp_debug_filter { #define BGP_DEBUG_VPN_LEAK_LABEL 0x08 #define BGP_DEBUG_FLOWSPEC 0x01 #define BGP_DEBUG_LABELPOOL 0x01 +#define BGP_DEBUG_PBR 0x01 +#define BGP_DEBUG_PBR_ERROR 0x02 #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 8eb0222a1b..85b9ffd8ca 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -34,6 +34,7 @@ #include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_flowspec_private.h" +#include "bgpd/bgp_pbr.h" /* struct used to dump the rate contained in FS set traffic-rate EC */ union traffic_rate { @@ -931,3 +932,52 @@ int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) ecom->val = p; return 1; } + +int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, + struct bgp_pbr_entry_action *api) +{ + if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) { + api->action = ACTION_TRAFFICRATE; + api->u.r.rate_info[3] = ecom_eval->val[4]; + api->u.r.rate_info[2] = ecom_eval->val[5]; + api->u.r.rate_info[1] = ecom_eval->val[6]; + api->u.r.rate_info[0] = ecom_eval->val[7]; + } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) { + api->action = ACTION_TRAFFIC_ACTION; + /* else distribute code is set by default */ + if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)) + api->u.za.filter |= TRAFFIC_ACTION_TERMINATE; + else + api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE; + if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) + api->u.za.filter |= TRAFFIC_ACTION_SAMPLE; + + } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) { + api->action = ACTION_MARKING; + api->u.marking_dscp = ecom_eval->val[7]; + } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) { + /* must use external function */ + return 0; + } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) { + /* see draft-ietf-idr-flowspec-redirect-ip-02 + * Q1: how come a ext. community can host ipv6 address + * Q2 : from cisco documentation: + * Announces the reachability of one or more flowspec NLRI. + * When a BGP speaker receives an UPDATE message with the + * redirect-to-IP extended community, it is expected to + * create a traffic filtering rule for every flow-spec + * NLRI in the message that has this path as its best + * path. The filter entry matches the IP packets + * described in the NLRI field and redirects them or + * copies them towards the IPv4 or IPv6 address specified + * in the 'Network Address of Next- Hop' + * field of the associated MP_REACH_NLRI. + */ + struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *) + ecom_eval + 2; + + api->u.zr.redirect_ip_v4 = ip_ecom->ip; + } else + return -1; + return 0; +} diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 3aeb458dc6..88bdb5e2ae 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -172,4 +172,8 @@ extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type, extern struct ecommunity *ecommunity_new(void); extern int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval); +struct bgp_pbr_entry_action; +extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, + struct bgp_pbr_entry_action *api); + #endif /* _QUAGGA_BGP_ECOMMUNITY_H */ diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 49808e7cdd..a8676ab37c 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -510,15 +510,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 +532,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 +565,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 +579,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 +1295,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 +1541,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 +1794,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 +1806,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); @@ -1970,10 +1969,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 +1981,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 +2228,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 +2419,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 +2846,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 +2854,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 +2876,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 +2961,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 +3035,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 +3046,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 +3105,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 +3116,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); @@ -3583,44 +3579,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 +3640,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 +3706,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 +3720,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 +3737,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: @@ -4421,8 +4425,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; } diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 1eecb9ecf7..1efde3a719 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, diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index c74a1bfb7c..fd3c229472 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; } @@ -2873,7 +2884,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); } } @@ -4382,12 +4393,22 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, 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)) diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 007b27f17e..956cf28c21 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -25,6 +25,7 @@ #include "bgp_table.h" #include "bgp_flowspec_util.h" #include "bgp_flowspec_private.h" +#include "bgp_pbr.h" static void hex2bin(uint8_t *hex, int *bin) { @@ -50,6 +51,109 @@ static int hexstr2num(uint8_t *hexstr, int len) return num; } +/* call bgp_flowspec_op_decode + * returns offset + */ +static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len, + struct bgp_pbr_match_val *mval, + uint8_t *match_num, int *error) +{ + int ret; + + ret = bgp_flowspec_op_decode( + BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, + nlri_content, + len, + mval, error); + if (*error < 0) + zlog_err("%s: flowspec_op_decode error %d", + __func__, *error); + else + *match_num = *error; + return ret; +} + +static bool bgp_flowspec_contains_prefix(struct prefix *pfs, + struct prefix *input, + int prefix_check) +{ + uint32_t offset = 0; + int type; + int ret = 0, error = 0; + uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; + size_t len = pfs->u.prefix_flowspec.prefixlen; + struct prefix compare; + + error = 0; + while (offset < len-1 && error >= 0) { + type = nlri_content[offset]; + offset++; + switch (type) { + case FLOWSPEC_DEST_PREFIX: + case FLOWSPEC_SRC_PREFIX: + memset(&compare, 0, sizeof(struct prefix)); + ret = bgp_flowspec_ip_address( + BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, + nlri_content+offset, + len - offset, + &compare, &error); + if (ret <= 0) + break; + if (prefix_check && + compare.prefixlen != input->prefixlen) + break; + if (compare.family != input->family) + break; + if ((input->family == AF_INET) && + IPV4_ADDR_SAME(&input->u.prefix4, + &compare.u.prefix4)) + return true; + if ((input->family == AF_INET6) && + IPV6_ADDR_SAME(&input->u.prefix6.s6_addr, + &compare.u.prefix6.s6_addr)) + return true; + break; + case FLOWSPEC_IP_PROTOCOL: + case FLOWSPEC_PORT: + case FLOWSPEC_DEST_PORT: + case FLOWSPEC_SRC_PORT: + case FLOWSPEC_ICMP_TYPE: + case FLOWSPEC_ICMP_CODE: + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; + case FLOWSPEC_TCP_FLAGS: + ret = bgp_flowspec_tcpflags_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; + case FLOWSPEC_PKT_LEN: + case FLOWSPEC_DSCP: + ret = bgp_flowspec_op_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, + &error); + break; + case FLOWSPEC_FRAGMENT: + ret = bgp_flowspec_fragment_type_decode( + BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, + &error); + break; + default: + error = -1; + break; + } + offset += ret; + } + return false; +} /* * handle the flowspec address src/dst or generic address NLRI @@ -122,9 +226,12 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, uint32_t offset = 0; int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; int len_written; + struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result; *error = 0; do { + if (loop > BGP_PBR_MATCH_VAL_MAX) + *error = -2; hex2bin(&nlri_ptr[offset], op); offset++; len = 2*op[2]+op[3]; @@ -168,7 +275,24 @@ int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, ptr += len_written; break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - /* TODO : FS OPAQUE */ + /* limitation: stop converting */ + if (*error == -2) + break; + mval->value = value; + if (op[5] == 1) + mval->compare_operator |= + OPERATOR_COMPARE_LESS_THAN; + if (op[6] == 1) + mval->compare_operator |= + OPERATOR_COMPARE_GREATER_THAN; + if (op[7] == 1) + mval->compare_operator |= + OPERATOR_COMPARE_EQUAL_TO; + if (op[1] == 1) + mval->unary_operator = OPERATOR_UNARY_AND; + else + mval->unary_operator = OPERATOR_UNARY_OR; + mval++; break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: @@ -203,12 +327,15 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, int op[8]; int len, value_size, loop = 0, value; char *ptr = (char *)result; /* for return_string */ + struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result; uint32_t offset = 0; int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; int len_written; *error = 0; do { + if (loop > BGP_PBR_MATCH_VAL_MAX) + *error = -2; hex2bin(&nlri_ptr[offset], op); /* if first element, AND bit can not be set */ if (op[1] == 1 && loop == 0) @@ -252,7 +379,29 @@ int bgp_flowspec_tcpflags_decode(enum bgp_flowspec_util_nlri_t type, ptr += len_written; break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - /* TODO : FS OPAQUE */ + /* limitation: stop converting */ + if (*error == -2) + break; + mval->value = value; + if (op[6] == 1) { + /* different from */ + mval->compare_operator |= + OPERATOR_COMPARE_LESS_THAN; + mval->compare_operator |= + OPERATOR_COMPARE_GREATER_THAN; + } else + mval->compare_operator |= + OPERATOR_COMPARE_EQUAL_TO; + if (op[7] == 1) + mval->compare_operator |= + OPERATOR_COMPARE_EXACT_MATCH; + if (op[1] == 1) + mval->unary_operator = + OPERATOR_UNARY_AND; + else + mval->unary_operator = + OPERATOR_UNARY_OR; + mval++; break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: @@ -284,6 +433,8 @@ int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, int op[8]; int len, value, value_size, loop = 0; char *ptr = (char *)result; /* for return_string */ + struct bgp_pbr_fragment_val *mval = + (struct bgp_pbr_fragment_val *)result; uint32_t offset = 0; int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX; int len_written; @@ -340,7 +491,7 @@ int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, } break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - /* TODO : FS OPAQUE */ + mval->bitmask = (uint8_t)value; break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: @@ -354,89 +505,158 @@ int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, return offset; } - -static bool bgp_flowspec_contains_prefix(struct prefix *pfs, - struct prefix *input, - int prefix_check) +int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, + struct bgp_pbr_entry_main *bpem) { - uint32_t offset = 0; - int type; - int ret = 0, error = 0; - uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; - size_t len = pfs->u.prefix_flowspec.prefixlen; - struct prefix compare; + int offset = 0, error = 0; + struct prefix *prefix; + struct bgp_pbr_match_val *mval; + uint8_t *match_num; + uint8_t bitmask = 0; + int ret = 0, type; - error = 0; - while (offset < len-1 && error >= 0) { + while (offset < len - 1 && error >= 0) { type = nlri_content[offset]; offset++; switch (type) { case FLOWSPEC_DEST_PREFIX: case FLOWSPEC_SRC_PREFIX: - memset(&compare, 0, sizeof(struct prefix)); + bitmask = 0; + if (type == FLOWSPEC_DEST_PREFIX) { + bitmask |= PREFIX_DST_PRESENT; + prefix = &bpem->dst_prefix; + } else { + bitmask |= PREFIX_SRC_PRESENT; + prefix = &bpem->src_prefix; + } ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, - nlri_content+offset, + nlri_content + offset, len - offset, - &compare, &error); - if (ret <= 0) - break; - if (prefix_check && - compare.prefixlen != input->prefixlen) - break; - if (compare.family != input->family) - break; - if ((input->family == AF_INET) && - IPV4_ADDR_SAME(&input->u.prefix4, - &compare.u.prefix4)) - return true; - if ((input->family == AF_INET6) && - IPV6_ADDR_SAME(&input->u.prefix6.s6_addr, - &compare.u.prefix6.s6_addr)) - return true; + prefix, &error); + if (error < 0) + zlog_err("%s: flowspec_ip_address error %d", + __func__, error); + else + bpem->match_bitmask |= bitmask; + offset += ret; break; case FLOWSPEC_IP_PROTOCOL: + match_num = &(bpem->match_protocol_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->protocol); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_PORT: + match_num = &(bpem->match_port_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->port); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_DEST_PORT: + match_num = &(bpem->match_dst_port_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->dst_port); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_SRC_PORT: + match_num = &(bpem->match_src_port_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->src_port); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_ICMP_TYPE: - case FLOWSPEC_ICMP_CODE: - ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, - nlri_content+offset, - len - offset, - NULL, &error); + match_num = &(bpem->match_icmp_type_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->icmp_type); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); break; - case FLOWSPEC_TCP_FLAGS: - ret = bgp_flowspec_tcpflags_decode( - BGP_FLOWSPEC_VALIDATE_ONLY, - nlri_content+offset, - len - offset, - NULL, &error); + case FLOWSPEC_ICMP_CODE: + match_num = &(bpem->match_icmp_code_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->icmp_code); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); break; case FLOWSPEC_PKT_LEN: + match_num = + &(bpem->match_packet_length_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->packet_length); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_DSCP: - ret = bgp_flowspec_op_decode( - BGP_FLOWSPEC_VALIDATE_ONLY, - nlri_content + offset, - len - offset, NULL, - &error); + match_num = &(bpem->match_dscp_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->dscp); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; + case FLOWSPEC_TCP_FLAGS: + ret = bgp_flowspec_tcpflags_decode( + BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, + nlri_content + offset, + len - offset, + &bpem->tcpflags, &error); + if (error < 0) + zlog_err("%s: flowspec_tcpflags_decode error %d", + __func__, error); + else + bpem->match_tcpflags_num = error; + /* contains the number of slots used */ + offset += ret; break; case FLOWSPEC_FRAGMENT: ret = bgp_flowspec_fragment_type_decode( - BGP_FLOWSPEC_VALIDATE_ONLY, - nlri_content + offset, - len - offset, NULL, - &error); + BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, + nlri_content + offset, + len - offset, &bpem->fragment, + &error); + if (error < 0) + zlog_err("%s: flowspec_fragment_type_decode error %d", + __func__, error); + else + bpem->match_bitmask |= FRAGMENT_PRESENT; + offset += ret; break; default: - error = -1; - break; + zlog_err("%s: unknown type %d\n", __func__, type); } - offset += ret; } - return false; + return error; } + struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi, struct bgp_table *rib, struct prefix *match, diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index aa21461102..e4454ab4db 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -50,6 +50,9 @@ extern int bgp_flowspec_fragment_type_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, void *result, int *error); +struct bgp_pbr_entry_main; +extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, + struct bgp_pbr_entry_main *bpem); extern struct bgp_node *bgp_flowspec_get_match_per_ip(afi_t afi, struct bgp_table *rib, diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c new file mode 100644 index 0000000000..04d6314fd7 --- /dev/null +++ b/bgpd/bgp_pbr.c @@ -0,0 +1,1140 @@ +/* + * BGP pbr + * Copyright (C) 6WIND + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" +#include "prefix.h" +#include "zclient.h" +#include "jhash.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_pbr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_flowspec_util.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_mplsvpn.h" + +DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry") +DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match") +DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action") + +static int bgp_pbr_match_counter_unique; +static int bgp_pbr_match_entry_counter_unique; +static int bgp_pbr_action_counter_unique; +static int bgp_pbr_match_iptable_counter_unique; + +struct bgp_pbr_match_iptable_unique { + uint32_t unique; + struct bgp_pbr_match *bpm_found; +}; + +struct bgp_pbr_match_entry_unique { + uint32_t unique; + struct bgp_pbr_match_entry *bpme_found; +}; + +struct bgp_pbr_action_unique { + uint32_t unique; + struct bgp_pbr_action *bpa_found; +}; + +static int bgp_pbr_action_walkcb(struct hash_backet *backet, void *arg) +{ + struct bgp_pbr_action *bpa = (struct bgp_pbr_action *)backet->data; + struct bgp_pbr_action_unique *bpau = (struct bgp_pbr_action_unique *) + arg; + uint32_t unique = bpau->unique; + + if (bpa->unique == unique) { + bpau->bpa_found = bpa; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +static int bgp_pbr_match_entry_walkcb(struct hash_backet *backet, void *arg) +{ + struct bgp_pbr_match_entry *bpme = + (struct bgp_pbr_match_entry *)backet->data; + struct bgp_pbr_match_entry_unique *bpmeu = + (struct bgp_pbr_match_entry_unique *)arg; + uint32_t unique = bpmeu->unique; + + if (bpme->unique == unique) { + bpmeu->bpme_found = bpme; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +struct bgp_pbr_match_ipsetname { + char *ipsetname; + struct bgp_pbr_match *bpm_found; +}; + +static int bgp_pbr_match_pername_walkcb(struct hash_backet *backet, void *arg) +{ + struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data; + struct bgp_pbr_match_ipsetname *bpmi = + (struct bgp_pbr_match_ipsetname *)arg; + char *ipset_name = bpmi->ipsetname; + + if (!strncmp(ipset_name, bpm->ipset_name, + ZEBRA_IPSET_NAME_SIZE)) { + bpmi->bpm_found = bpm; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +static int bgp_pbr_match_iptable_walkcb(struct hash_backet *backet, void *arg) +{ + struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data; + struct bgp_pbr_match_iptable_unique *bpmiu = + (struct bgp_pbr_match_iptable_unique *)arg; + uint32_t unique = bpmiu->unique; + + if (bpm->unique2 == unique) { + bpmiu->bpm_found = bpm; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +struct bgp_pbr_match_unique { + uint32_t unique; + struct bgp_pbr_match *bpm_found; +}; + +static int bgp_pbr_match_walkcb(struct hash_backet *backet, void *arg) +{ + struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data; + struct bgp_pbr_match_unique *bpmu = (struct bgp_pbr_match_unique *) + arg; + uint32_t unique = bpmu->unique; + + if (bpm->unique == unique) { + bpmu->bpm_found = bpm; + return HASHWALK_ABORT; + } + return HASHWALK_CONTINUE; +} + +static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, + const char *prepend) +{ + char *ptr = str; + + if (prepend) + ptr += sprintf(ptr, "%s", prepend); + else { + if (mval->unary_operator & OPERATOR_UNARY_OR) + ptr += sprintf(ptr, ", or "); + if (mval->unary_operator & OPERATOR_UNARY_AND) + ptr += sprintf(ptr, ", and "); + } + if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) + ptr += sprintf(ptr, "<"); + if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) + ptr += sprintf(ptr, ">"); + if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) + ptr += sprintf(ptr, "="); + if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) + ptr += sprintf(ptr, "match"); + ptr += sprintf(ptr, " %u", mval->value); + return (int)(ptr - str); +} + +#define INCREMENT_DISPLAY(_ptr, _cnt) do { \ + if (_cnt) \ + (_ptr) += sprintf((_ptr), "; "); \ + _cnt++; \ + } while (0) + +/* return 1 if OK, 0 if validation should stop) */ +static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) +{ + /* because bgp pbr entry may contain unsupported + * combinations, a message will be displayed here if + * not supported. + * for now, only match/set supported is + * - combination src/dst => redirect nexthop [ + rate] + * - combination src/dst => redirect VRF [ + rate] + * - combination src/dst => drop + */ + if (api->match_src_port_num || api->match_dst_port_num + || api->match_port_num || api->match_protocol_num + || api->match_icmp_type_num || api->match_icmp_type_num + || api->match_packet_length_num || api->match_dscp_num + || api->match_tcpflags_num) { + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_debug("BGP: some SET actions not supported by Zebra. ignoring."); + } + return 0; + } + if (!(api->match_bitmask & PREFIX_SRC_PRESENT) && + !(api->match_bitmask & PREFIX_DST_PRESENT)) { + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_debug("BGP: match actions without src" + " or dst address can not operate." + " ignoring."); + } + return 0; + } + return 1; +} + +/* return -1 if build or validation failed */ +static int bgp_pbr_build_and_validate_entry(struct prefix *p, + struct bgp_info *info, + struct bgp_pbr_entry_main *api) +{ + int ret; + int i, action_count = 0; + struct ecommunity *ecom; + struct ecommunity_val *ecom_eval; + struct bgp_pbr_entry_action *api_action; + struct prefix *src = NULL, *dst = NULL; + int valid_prefix = 0; + afi_t afi = AFI_IP; + + /* extract match from flowspec entries */ + ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen, api); + if (ret < 0) + return -1; + /* extract actiosn from flowspec ecom list */ + if (info && info->attr && info->attr->ecommunity) { + ecom = info->attr->ecommunity; + for (i = 0; i < ecom->size; i++) { + ecom_eval = (struct ecommunity_val *) + ecom->val + (i * ECOMMUNITY_SIZE); + + if (action_count > ACTIONS_MAX_NUM) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + zlog_err("%s: flowspec actions exceeds limit (max %u)", + __func__, action_count); + break; + } + api_action = &api->actions[action_count]; + + if ((ecom_eval->val[1] == + (char)ECOMMUNITY_REDIRECT_VRF) && + (ecom_eval->val[0] == + (char)ECOMMUNITY_ENCODE_TRANS_EXP || + ecom_eval->val[0] == + (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || + ecom_eval->val[0] == + (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) { + struct ecommunity *eckey = ecommunity_new(); + struct ecommunity_val ecom_copy; + + memcpy(&ecom_copy, ecom_eval, + sizeof(struct ecommunity_val)); + ecom_copy.val[0] &= + ~ECOMMUNITY_ENCODE_TRANS_EXP; + ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET; + ecommunity_add_val(eckey, &ecom_copy); + + api_action->action = ACTION_REDIRECT; + api_action->u.redirect_vrf = + get_first_vrf_for_redirect_with_rt( + eckey); + ecommunity_free(&eckey); + } else if ((ecom_eval->val[0] == + (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) && + (ecom_eval->val[1] == + (char)ECOMMUNITY_REDIRECT_IP_NH)) { + api_action->action = ACTION_REDIRECT_IP; + api_action->u.zr.redirect_ip_v4.s_addr = + info->attr->nexthop.s_addr; + api_action->u.zr.duplicate = ecom_eval->val[7]; + } else { + if (ecom_eval->val[0] != + (char)ECOMMUNITY_ENCODE_TRANS_EXP) + continue; + ret = ecommunity_fill_pbr_action(ecom_eval, + api_action); + if (ret != 0) + continue; + } + api->action_num++; + } + } + + /* validate if incoming matc/action is compatible + * with our policy routing engine + */ + if (!bgp_pbr_validate_policy_route(api)) + return -1; + + /* check inconsistency in the match rule */ + if (api->match_bitmask & PREFIX_SRC_PRESENT) { + src = &api->src_prefix; + afi = family2afi(src->family); + valid_prefix = 1; + } + if (api->match_bitmask & PREFIX_DST_PRESENT) { + dst = &api->dst_prefix; + if (valid_prefix && afi != family2afi(dst->family)) { + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_debug("%s: inconsistency:" + " no match for afi src and dst (%u/%u)", + __func__, afi, family2afi(dst->family)); + } + return -1; + } + } + return 0; +} + +static void bgp_pbr_match_entry_free(void *arg) +{ + struct bgp_pbr_match_entry *bpme; + + bpme = (struct bgp_pbr_match_entry *)arg; + + if (bpme->installed) { + bgp_send_pbr_ipset_entry_match(bpme, false); + bpme->installed = false; + bpme->backpointer = NULL; + } + XFREE(MTYPE_PBR_MATCH_ENTRY, bpme); +} + +static void bgp_pbr_match_free(void *arg) +{ + struct bgp_pbr_match *bpm; + + bpm = (struct bgp_pbr_match *)arg; + + hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free); + + if (hashcount(bpm->entry_hash) == 0) { + /* delete iptable entry first */ + /* then delete ipset match */ + if (bpm->installed) { + if (bpm->installed_in_iptable) { + bgp_send_pbr_iptable(bpm->action, + bpm, false); + bpm->installed_in_iptable = false; + bpm->action->refcnt--; + } + bgp_send_pbr_ipset_match(bpm, false); + bpm->installed = false; + bpm->action = NULL; + } + } + hash_free(bpm->entry_hash); + + XFREE(MTYPE_PBR_MATCH, bpm); +} + +static void *bgp_pbr_match_alloc_intern(void *arg) +{ + struct bgp_pbr_match *bpm, *new; + + bpm = (struct bgp_pbr_match *)arg; + + new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new)); + memcpy(new, bpm, sizeof(*bpm)); + + return new; +} + +static void bgp_pbr_action_free(void *arg) +{ + struct bgp_pbr_action *bpa; + + bpa = (struct bgp_pbr_action *)arg; + + if (bpa->refcnt == 0) { + if (bpa->installed && bpa->table_id != 0) { + bgp_send_pbr_rule_action(bpa, false); + bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), + AFI_IP, + bpa->table_id, + false); + } + } + XFREE(MTYPE_PBR_ACTION, bpa); +} + +static void *bgp_pbr_action_alloc_intern(void *arg) +{ + struct bgp_pbr_action *bpa, *new; + + bpa = (struct bgp_pbr_action *)arg; + + new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new)); + + memcpy(new, bpa, sizeof(*bpa)); + + return new; +} + +static void *bgp_pbr_match_entry_alloc_intern(void *arg) +{ + struct bgp_pbr_match_entry *bpme, *new; + + bpme = (struct bgp_pbr_match_entry *)arg; + + new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new)); + + memcpy(new, bpme, sizeof(*bpme)); + + return new; +} + +uint32_t bgp_pbr_match_hash_key(void *arg) +{ + struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg; + uint32_t key; + + key = jhash_1word(pbm->vrf_id, 0x4312abde); + key = jhash_1word(pbm->flags, key); + return jhash_1word(pbm->type, key); +} + +int bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) +{ + const struct bgp_pbr_match *r1, *r2; + + r1 = (const struct bgp_pbr_match *)arg1; + r2 = (const struct bgp_pbr_match *)arg2; + + if (r1->vrf_id != r2->vrf_id) + return 0; + + if (r1->type != r2->type) + return 0; + + if (r1->flags != r2->flags) + return 0; + + if (r1->action != r2->action) + return 0; + + return 1; +} + +uint32_t bgp_pbr_match_entry_hash_key(void *arg) +{ + struct bgp_pbr_match_entry *pbme; + uint32_t key; + + pbme = (struct bgp_pbr_match_entry *)arg; + key = prefix_hash_key(&pbme->src); + key = jhash_1word(prefix_hash_key(&pbme->dst), key); + + return key; +} + +int bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2) +{ + const struct bgp_pbr_match_entry *r1, *r2; + + r1 = (const struct bgp_pbr_match_entry *)arg1; + r2 = (const struct bgp_pbr_match_entry *)arg2; + + /* on updates, comparing + * backpointer is not necessary + */ + + /* unique value is self calculated + */ + + /* rate is ignored for now + */ + + if (!prefix_same(&r1->src, &r2->src)) + return 0; + + if (!prefix_same(&r1->dst, &r2->dst)) + return 0; + + return 1; +} + +uint32_t bgp_pbr_action_hash_key(void *arg) +{ + struct bgp_pbr_action *pbra; + uint32_t key; + + pbra = (struct bgp_pbr_action *)arg; + key = jhash_1word(pbra->table_id, 0x4312abde); + key = jhash_1word(pbra->fwmark, key); + return key; +} + +int bgp_pbr_action_hash_equal(const void *arg1, const void *arg2) +{ + const struct bgp_pbr_action *r1, *r2; + + r1 = (const struct bgp_pbr_action *)arg1; + r2 = (const struct bgp_pbr_action *)arg2; + + /* unique value is self calculated + * table and fwmark is self calculated + */ + if (r1->rate != r2->rate) + return 0; + + if (r1->vrf_id != r2->vrf_id) + return 0; + + if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop))) + return 0; + return 1; +} + +struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id, + uint32_t unique) +{ + struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id); + struct bgp_pbr_action_unique bpau; + + if (!bgp || unique == 0) + return NULL; + bpau.unique = unique; + bpau.bpa_found = NULL; + hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau); + return bpau.bpa_found; +} + +struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id, + uint32_t unique) +{ + struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id); + struct bgp_pbr_match_unique bpmu; + + if (!bgp || unique == 0) + return NULL; + bpmu.unique = unique; + bpmu.bpm_found = NULL; + hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu); + return bpmu.bpm_found; +} + +struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id, + char *ipset_name, + uint32_t unique) +{ + struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id); + struct bgp_pbr_match_entry_unique bpmeu; + struct bgp_pbr_match_ipsetname bpmi; + + if (!bgp || unique == 0) + return NULL; + bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE); + snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name); + bpmi.bpm_found = NULL; + hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi); + XFREE(MTYPE_TMP, bpmi.ipsetname); + if (!bpmi.bpm_found) + return NULL; + bpmeu.bpme_found = NULL; + bpmeu.unique = unique; + hash_walk(bpmi.bpm_found->entry_hash, + bgp_pbr_match_entry_walkcb, &bpmeu); + return bpmeu.bpme_found; +} + +struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, + uint32_t unique) +{ + struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id); + struct bgp_pbr_match_iptable_unique bpmiu; + + if (!bgp || unique == 0) + return NULL; + bpmiu.unique = unique; + bpmiu.bpm_found = NULL; + hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu); + return bpmiu.bpm_found; +} + +void bgp_pbr_cleanup(struct bgp *bgp) +{ + if (bgp->pbr_match_hash) { + hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free); + hash_free(bgp->pbr_match_hash); + bgp->pbr_match_hash = NULL; + } + if (bgp->pbr_action_hash) { + hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free); + hash_free(bgp->pbr_action_hash); + bgp->pbr_action_hash = NULL; + } +} + +void bgp_pbr_init(struct bgp *bgp) +{ + bgp->pbr_match_hash = + hash_create_size(8, bgp_pbr_match_hash_key, + bgp_pbr_match_hash_equal, + "Match Hash"); + bgp->pbr_action_hash = + hash_create_size(8, bgp_pbr_action_hash_key, + bgp_pbr_action_hash_equal, + "Match Hash Entry"); +} + +void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) +{ + int i = 0; + char return_string[512]; + char *ptr = return_string; + char buff[64]; + int nb_items = 0; + + ptr += sprintf(ptr, "MATCH : "); + if (api->match_bitmask & PREFIX_SRC_PRESENT) { + struct prefix *p = &(api->src_prefix); + + ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64)); + INCREMENT_DISPLAY(ptr, nb_items); + } + if (api->match_bitmask & PREFIX_DST_PRESENT) { + struct prefix *p = &(api->dst_prefix); + + INCREMENT_DISPLAY(ptr, nb_items); + ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64)); + } + + if (api->match_protocol_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_protocol_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i], + i > 0 ? NULL : "@proto "); + + if (api->match_src_port_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_src_port_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i], + i > 0 ? NULL : "@srcport "); + + if (api->match_dst_port_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_dst_port_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i], + i > 0 ? NULL : "@dstport "); + + if (api->match_port_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_port_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i], + i > 0 ? NULL : "@port "); + + if (api->match_icmp_type_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_icmp_type_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i], + i > 0 ? NULL : "@icmptype "); + + if (api->match_icmp_code_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_icmp_code_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i], + i > 0 ? NULL : "@icmpcode "); + + if (api->match_packet_length_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_packet_length_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i], + i > 0 ? NULL : "@plen "); + + if (api->match_dscp_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_dscp_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i], + i > 0 ? NULL : "@dscp "); + + if (api->match_tcpflags_num) + INCREMENT_DISPLAY(ptr, nb_items); + for (i = 0; i < api->match_tcpflags_num; i++) + ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i], + i > 0 ? NULL : "@tcpflags "); + + if (api->match_bitmask & FRAGMENT_PRESENT) { + INCREMENT_DISPLAY(ptr, nb_items); + ptr += sprintf(ptr, "@fragment %u", api->fragment.bitmask); + } + if (!nb_items) + ptr = return_string; + else + ptr += sprintf(ptr, "; "); + if (api->action_num) + ptr += sprintf(ptr, "SET : "); + nb_items = 0; + for (i = 0; i < api->action_num; i++) { + switch (api->actions[i].action) { + case ACTION_TRAFFICRATE: + INCREMENT_DISPLAY(ptr, nb_items); + ptr += sprintf(ptr, "@set rate %f", + api->actions[i].u.r.rate); + break; + case ACTION_TRAFFIC_ACTION: + INCREMENT_DISPLAY(ptr, nb_items); + ptr += sprintf(ptr, "@action "); + if (api->actions[i].u.za.filter + & TRAFFIC_ACTION_TERMINATE) + ptr += sprintf(ptr, + " terminate (apply filter(s))"); + if (api->actions[i].u.za.filter + & TRAFFIC_ACTION_DISTRIBUTE) + ptr += sprintf(ptr, " distribute"); + if (api->actions[i].u.za.filter + & TRAFFIC_ACTION_SAMPLE) + ptr += sprintf(ptr, " sample"); + break; + case ACTION_REDIRECT_IP: + INCREMENT_DISPLAY(ptr, nb_items); + char local_buff[INET_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, + &api->actions[i].u.zr.redirect_ip_v4, + local_buff, INET_ADDRSTRLEN) != NULL) + ptr += sprintf(ptr, + "@redirect ip nh %s", local_buff); + break; + case ACTION_REDIRECT: + INCREMENT_DISPLAY(ptr, nb_items); + ptr += sprintf(ptr, "@redirect vrf %u", + api->actions[i].u.redirect_vrf); + break; + case ACTION_MARKING: + INCREMENT_DISPLAY(ptr, nb_items); + ptr += sprintf(ptr, "@set dscp %u", + api->actions[i].u.marking_dscp); + break; + default: + break; + } + } + zlog_info("%s", return_string); +} + +static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, + struct bgp_pbr_match *bpm, + struct bgp_pbr_match_entry *bpme) +{ + /* if bpme is null, bpm is also null + */ + if (bpme == NULL) + return; + /* ipset del entry */ + if (bpme->installed) { + bgp_send_pbr_ipset_entry_match(bpme, false); + bpme->installed = false; + bpme->backpointer = NULL; + } + hash_release(bpm->entry_hash, bpme); + if (hashcount(bpm->entry_hash) == 0) { + /* delete iptable entry first */ + /* then delete ipset match */ + if (bpm->installed) { + if (bpm->installed_in_iptable) { + bgp_send_pbr_iptable(bpm->action, + bpm, false); + bpm->installed_in_iptable = false; + bpm->action->refcnt--; + } + bgp_send_pbr_ipset_match(bpm, false); + bpm->installed = false; + bpm->action = NULL; + } + hash_release(bgp->pbr_match_hash, bpm); + /* XXX release pbr_match_action if not used + * note that drop does not need to call send_pbr_action + */ + } + if (bpa->refcnt == 0) { + if (bpa->installed && bpa->table_id != 0) { + bgp_send_pbr_rule_action(bpa, false); + bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), + AFI_IP, + bpa->table_id, + false); + } + } +} + +struct bgp_pbr_match_entry_remain { + struct bgp_pbr_match_entry *bpme_to_match; + struct bgp_pbr_match_entry *bpme_found; +}; + +static int bgp_pbr_get_remaining_entry(struct hash_backet *backet, void *arg) +{ + struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)backet->data; + struct bgp_pbr_match_entry_remain *bpmer = + (struct bgp_pbr_match_entry_remain *)arg; + struct bgp_pbr_match *bpm_temp; + struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match; + + if (!bpme->backpointer || + bpm == bpme->backpointer || + bpme->backpointer->action == bpm->action) + return HASHWALK_CONTINUE; + /* ensure bpm other characteristics are equal */ + bpm_temp = bpme->backpointer; + if (bpm_temp->vrf_id != bpm->vrf_id || + bpm_temp->type != bpm->type || + bpm_temp->flags != bpm->flags) + return HASHWALK_CONTINUE; + + /* look for remaining bpme */ + bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme); + if (!bpmer->bpme_found) + return HASHWALK_CONTINUE; + return HASHWALK_ABORT; +} + +static void bgp_pbr_policyroute_remove_from_zebra(struct bgp *bgp, + struct bgp_info *binfo, + vrf_id_t vrf_id, + struct prefix *src, + struct prefix *dst) +{ + struct bgp_pbr_match temp; + struct bgp_pbr_match_entry temp2; + struct bgp_pbr_match *bpm; + struct bgp_pbr_match_entry *bpme; + struct bgp_pbr_match_entry_remain bpmer; + + /* as we don't know information from EC + * look for bpm that have the bpm + * with vrf_id characteristics + */ + memset(&temp2, 0, sizeof(temp2)); + memset(&temp, 0, sizeof(temp)); + if (src) { + temp.flags |= MATCH_IP_SRC_SET; + prefix_copy(&temp2.src, src); + } else + temp2.src.family = AF_INET; + if (dst) { + temp.flags |= MATCH_IP_DST_SET; + prefix_copy(&temp2.dst, dst); + } else + temp2.dst.family = AF_INET; + + if (src == NULL || dst == NULL) + temp.type = IPSET_NET; + else + temp.type = IPSET_NET_NET; + if (vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ + temp.vrf_id = 0; + else + temp.vrf_id = vrf_id; + bpme = &temp2; + bpm = &temp; + bpme->backpointer = bpm; + /* right now, a previous entry may already exist + * flush previous entry if necessary + */ + bpmer.bpme_to_match = bpme; + bpmer.bpme_found = NULL; + hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer); + if (bpmer.bpme_found) { + static struct bgp_pbr_match *local_bpm; + static struct bgp_pbr_action *local_bpa; + + local_bpm = bpmer.bpme_found->backpointer; + local_bpa = local_bpm->action; + bgp_pbr_flush_entry(bgp, local_bpa, + local_bpm, bpmer.bpme_found); + } +} + +static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, + struct bgp_info *binfo, + vrf_id_t vrf_id, + struct prefix *src, + struct prefix *dst, + struct nexthop *nh, + float *rate) +{ + struct bgp_pbr_match temp; + struct bgp_pbr_match_entry temp2; + struct bgp_pbr_match *bpm; + struct bgp_pbr_match_entry *bpme = NULL; + struct bgp_pbr_action temp3; + struct bgp_pbr_action *bpa = NULL; + struct bgp_pbr_match_entry_remain bpmer; + + /* look for bpa first */ + memset(&temp3, 0, sizeof(temp3)); + if (rate) + temp3.rate = *rate; + if (nh) + memcpy(&temp3.nh, nh, sizeof(struct nexthop)); + temp3.vrf_id = vrf_id; + bpa = hash_get(bgp->pbr_action_hash, &temp3, + bgp_pbr_action_alloc_intern); + + if (bpa->fwmark == 0) { + /* drop is handled by iptable */ + if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) { + bpa->table_id = 0; + bpa->installed = true; + } else { + bpa->fwmark = bgp_zebra_tm_get_id(); + bpa->table_id = bpa->fwmark; + bpa->installed = false; + } + bpa->bgp = bgp; + bpa->unique = ++bgp_pbr_action_counter_unique; + /* 0 value is forbidden */ + bpa->install_in_progress = false; + } + + /* then look for bpm */ + memset(&temp, 0, sizeof(temp)); + if (src == NULL || dst == NULL) + temp.type = IPSET_NET; + else + temp.type = IPSET_NET_NET; + temp.vrf_id = vrf_id; + if (src) + temp.flags |= MATCH_IP_SRC_SET; + if (dst) + temp.flags |= MATCH_IP_DST_SET; + temp.action = bpa; + bpm = hash_get(bgp->pbr_match_hash, &temp, + bgp_pbr_match_alloc_intern); + + /* new, then self allocate ipset_name and unique */ + if (bpm && bpm->unique == 0) { + bpm->unique = ++bgp_pbr_match_counter_unique; + /* 0 value is forbidden */ + sprintf(bpm->ipset_name, "match%p", bpm); + bpm->entry_hash = hash_create_size(8, + bgp_pbr_match_entry_hash_key, + bgp_pbr_match_entry_hash_equal, + "Match Entry Hash"); + bpm->installed = false; + + /* unique2 should be updated too */ + bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique; + bpm->installed_in_iptable = false; + bpm->install_in_progress = false; + bpm->install_iptable_in_progress = false; + } + + memset(&temp2, 0, sizeof(temp2)); + if (src) + prefix_copy(&temp2.src, src); + else + temp2.src.family = AF_INET; + if (dst) + prefix_copy(&temp2.dst, dst); + else + temp2.dst.family = AF_INET; + if (bpm) + bpme = hash_get(bpm->entry_hash, &temp2, + bgp_pbr_match_entry_alloc_intern); + if (bpme && bpme->unique == 0) { + bpme->unique = ++bgp_pbr_match_entry_counter_unique; + /* 0 value is forbidden */ + bpme->backpointer = bpm; + bpme->installed = false; + bpme->install_in_progress = false; + } + + /* BGP FS: append entry to zebra + * - policies are not routing entries and as such + * route replace semantics don't necessarily follow + * through to policy entries + * - because of that, not all policing information will be stored + * into zebra. and non selected policies will be suppressed from zebra + * - as consequence, in order to bring consistency + * a policy will be added, then ifan ecmp policy exists, + * it will be suppressed subsequently + */ + /* ip rule add */ + if (!bpa->installed) { + bgp_send_pbr_rule_action(bpa, true); + bgp_zebra_announce_default(bgp, nh, + AFI_IP, bpa->table_id, true); + } + + /* ipset create */ + if (bpm && !bpm->installed) + bgp_send_pbr_ipset_match(bpm, true); + /* ipset add */ + if (bpme && !bpme->installed) + bgp_send_pbr_ipset_entry_match(bpme, true); + + /* iptables */ + if (bpm && !bpm->installed_in_iptable) + bgp_send_pbr_iptable(bpa, bpm, true); + + /* A previous entry may already exist + * flush previous entry if necessary + */ + bpmer.bpme_to_match = bpme; + bpmer.bpme_found = NULL; + hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer); + if (bpmer.bpme_found) { + static struct bgp_pbr_match *local_bpm; + static struct bgp_pbr_action *local_bpa; + + local_bpm = bpmer.bpme_found->backpointer; + local_bpa = local_bpm->action; + bgp_pbr_flush_entry(bgp, local_bpa, + local_bpm, bpmer.bpme_found); + } + + +} + +static void bgp_pbr_handle_entry(struct bgp *bgp, + struct bgp_info *binfo, + struct bgp_pbr_entry_main *api, + bool add) +{ + struct nexthop nh; + int i = 0; + int continue_loop = 1; + float rate = 0; + struct prefix *src = NULL, *dst = NULL; + + if (api->match_bitmask & PREFIX_SRC_PRESENT) + src = &api->src_prefix; + if (api->match_bitmask & PREFIX_DST_PRESENT) + dst = &api->dst_prefix; + memset(&nh, 0, sizeof(struct nexthop)); + nh.vrf_id = VRF_UNKNOWN; + + if (!add) + return bgp_pbr_policyroute_remove_from_zebra(bgp, binfo, + api->vrf_id, src, dst); + /* no action for add = true */ + for (i = 0; i < api->action_num; i++) { + switch (api->actions[i].action) { + case ACTION_TRAFFICRATE: + /* drop packet */ + if (api->actions[i].u.r.rate == 0) { + nh.vrf_id = api->vrf_id; + nh.type = NEXTHOP_TYPE_BLACKHOLE; + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + api->vrf_id, src, dst, + &nh, &rate); + } else { + /* update rate. can be reentrant */ + rate = api->actions[i].u.r.rate; + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_warn("PBR: ignoring Set action rate %f", + api->actions[i].u.r.rate); + } + } + break; + case ACTION_TRAFFIC_ACTION: + if (api->actions[i].u.za.filter + & TRAFFIC_ACTION_SAMPLE) { + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_warn("PBR: Sample action Ignored"); + } + } +#if 0 + if (api->actions[i].u.za.filter + & TRAFFIC_ACTION_DISTRIBUTE) { + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_warn("PBR: Distribute action Applies"); + } + continue_loop = 0; + /* continue forwarding entry as before + * no action + */ + } +#endif /* XXX to confirm behaviour of traffic action. for now , ignore */ + /* terminate action: run other filters + */ + break; + case ACTION_REDIRECT_IP: + nh.type = NEXTHOP_TYPE_IPV4; + nh.gate.ipv4.s_addr = + api->actions[i].u.zr.redirect_ip_v4.s_addr; + nh.vrf_id = api->vrf_id; + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + api->vrf_id, + src, dst, + &nh, &rate); + /* XXX combination with REDIRECT_VRF + * + REDIRECT_NH_IP not done + */ + continue_loop = 0; + break; + case ACTION_REDIRECT: + nh.vrf_id = api->actions[i].u.redirect_vrf; + nh.type = NEXTHOP_TYPE_IPV4; + bgp_pbr_policyroute_add_to_zebra(bgp, binfo, + api->vrf_id, + src, dst, + &nh, &rate); + continue_loop = 0; + break; + case ACTION_MARKING: + if (BGP_DEBUG(pbr, PBR)) { + bgp_pbr_print_policy_route(api); + zlog_warn("PBR: Set DSCP %u Ignored", + api->actions[i].u.marking_dscp); + } + break; + default: + break; + } + if (continue_loop == 0) + break; + } +} + +void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, + struct bgp_info *info, afi_t afi, safi_t safi, + bool nlri_update) +{ + struct bgp_pbr_entry_main api; + + if (afi == AFI_IP6) + return; /* IPv6 not supported */ + if (safi != SAFI_FLOWSPEC) + return; /* not supported */ + /* Make Zebra API structure. */ + memset(&api, 0, sizeof(api)); + api.vrf_id = bgp->vrf_id; + api.afi = afi; + + if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + zlog_err("%s: cancel updating entry in bgp pbr", + __func__); + return; + } + bgp_pbr_handle_entry(bgp, info, &api, nlri_update); +} diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h new file mode 100644 index 0000000000..5129ada37b --- /dev/null +++ b/bgpd/bgp_pbr.h @@ -0,0 +1,256 @@ +/* + * BGP pbr + * Copyright (C) 6WIND + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 __BGP_PBR_H__ +#define __BGP_PBR_H__ + +#include "nexthop.h" +#include "zclient.h" + +/* flowspec case: 0 to 3 actions maximum: + * 1 redirect + * 1 set dscp + * 1 set traffic rate + */ +#define ACTIONS_MAX_NUM 4 +enum bgp_pbr_action_enum { + ACTION_TRAFFICRATE = 1, + ACTION_TRAFFIC_ACTION = 2, + ACTION_REDIRECT = 3, + ACTION_MARKING = 4, + ACTION_REDIRECT_IP = 5 +}; + +#define TRAFFIC_ACTION_SAMPLE (1 << 0) +#define TRAFFIC_ACTION_TERMINATE (1 << 1) +#define TRAFFIC_ACTION_DISTRIBUTE (1 << 2) + +#define OPERATOR_COMPARE_LESS_THAN (1<<1) +#define OPERATOR_COMPARE_GREATER_THAN (1<<2) +#define OPERATOR_COMPARE_EQUAL_TO (1<<3) +#define OPERATOR_COMPARE_EXACT_MATCH (1<<4) + +#define OPERATOR_UNARY_OR (1<<1) +#define OPERATOR_UNARY_AND (1<<2) + +/* struct used to store values [0;65535] + * this can be used for port number of protocol + */ +#define BGP_PBR_MATCH_VAL_MAX 5 + +struct bgp_pbr_match_val { + uint16_t value; + uint8_t compare_operator; + uint8_t unary_operator; +} bgp_pbr_value_t; + +#define FRAGMENT_DONT 1 +#define FRAGMENT_IS 2 +#define FRAGMENT_FIRST 4 +#define FRAGMENT_LAST 8 + +struct bgp_pbr_fragment_val { + uint8_t bitmask; +}; + +struct bgp_pbr_entry_action { + /* used to store enum bgp_pbr_action_enum enumerate */ + uint8_t action; + union { + union { + uint8_t rate_info[4]; /* IEEE.754.1985 */ + float rate; + } r __attribute__((aligned(8))); + struct _pbr_action { + uint8_t do_sample; + uint8_t filter; + } za; + vrf_id_t redirect_vrf; + struct _pbr_redirect_ip { + struct in_addr redirect_ip_v4; + uint8_t duplicate; + } zr; + uint8_t marking_dscp; + } u __attribute__((aligned(8))); +}; + +/* BGP Policy Route structure */ +struct bgp_pbr_entry_main { + uint8_t type; + uint16_t instance; + + uint32_t flags; + + uint8_t message; + + /* + * This is an enum but we are going to treat it as a uint8_t + * for purpose of encoding/decoding + */ + afi_t afi; + safi_t safi; + +#define PREFIX_SRC_PRESENT (1 << 0) +#define PREFIX_DST_PRESENT (1 << 1) +#define FRAGMENT_PRESENT (1 << 2) + uint8_t match_bitmask; + + uint8_t match_src_port_num; + uint8_t match_dst_port_num; + uint8_t match_port_num; + uint8_t match_protocol_num; + uint8_t match_icmp_type_num; + uint8_t match_icmp_code_num; + uint8_t match_packet_length_num; + uint8_t match_dscp_num; + uint8_t match_tcpflags_num; + + struct prefix src_prefix; + struct prefix dst_prefix; + + struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val port[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val icmp_type[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_fragment_val fragment; + + uint16_t action_num; + struct bgp_pbr_entry_action actions[ACTIONS_MAX_NUM]; + + uint8_t distance; + + uint32_t metric; + + route_tag_t tag; + + uint32_t mtu; + + vrf_id_t vrf_id; +}; + +struct bgp_pbr_match { + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; + + /* mapped on enum ipset_type + */ + uint32_t type; + +#define MATCH_IP_SRC_SET (1 << 0) +#define MATCH_IP_DST_SET (1 << 1) + uint32_t flags; + + vrf_id_t vrf_id; + + /* unique identifier for ipset create transaction + */ + uint32_t unique; + + /* unique identifier for iptable add transaction + */ + uint32_t unique2; + + bool installed; + bool install_in_progress; + + bool installed_in_iptable; + bool install_iptable_in_progress; + + struct hash *entry_hash; + + struct bgp_pbr_action *action; + +}; + +struct bgp_pbr_match_entry { + struct bgp_pbr_match *backpointer; + + uint32_t unique; + + struct prefix src; + struct prefix dst; + + bool installed; + bool install_in_progress; +}; + +struct bgp_pbr_action { + + /* + * The Unique identifier of this specific pbrms + */ + uint32_t unique; + + uint32_t fwmark; + + uint32_t table_id; + + float rate; + + /* + * nexthop information, or drop information + * contains src vrf_id and nh contains dest vrf_id + */ + vrf_id_t vrf_id; + struct nexthop nh; + + bool installed; + bool install_in_progress; + uint32_t refcnt; + struct bgp *bgp; +}; + +extern struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id, + uint32_t unique); + +extern struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id, + uint32_t unique); + +extern struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup( + vrf_id_t vrf_id, char *name, + uint32_t unique); +extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, + uint32_t unique); + +extern void bgp_pbr_cleanup(struct bgp *bgp); +extern void bgp_pbr_init(struct bgp *bgp); + +extern uint32_t bgp_pbr_action_hash_key(void *arg); +extern int bgp_pbr_action_hash_equal(const void *arg1, + const void *arg2); +extern uint32_t bgp_pbr_match_entry_hash_key(void *arg); +extern int bgp_pbr_match_entry_hash_equal(const void *arg1, + const void *arg2); +extern uint32_t bgp_pbr_match_hash_key(void *arg); +extern int bgp_pbr_match_hash_equal(const void *arg1, + const void *arg2); + +void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api); + +struct bgp_node; +struct bgp_info; +extern void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, + struct bgp_info *new_select, + afi_t afi, safi_t safi, + bool nlri_update); + +#endif /* __BGP_PBR_H__ */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 90fa39b445..fd5648ac39 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -75,6 +75,7 @@ #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_flowspec.h" #include "bgpd/bgp_flowspec_util.h" +#include "bgpd/bgp_pbr.h" #ifndef VTYSH_EXTRACT_PL #include "bgpd/bgp_route_clippy.c" @@ -99,6 +100,8 @@ static const struct message bgp_pmsi_tnltype_str[] = { {0} }; +#define VRFID_NONE_STR "-" + struct bgp_node *bgp_afi_node_get(struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p, struct prefix_rd *prd) @@ -1333,6 +1336,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, @@ -2229,7 +2234,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* If best route remains the same and this is not due to user-initiated * clear, see exactly what needs to be done. */ - if (old_select && old_select == new_select && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) && !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED) @@ -2334,10 +2338,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 @@ -2351,12 +2363,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); } @@ -4624,7 +4652,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) { @@ -5099,10 +5127,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"); @@ -6471,6 +6499,13 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, json_object *json_nexthops = NULL; json_object *json_nexthop_global = NULL; json_object *json_nexthop_ll = NULL; + char vrf_id_str[VRF_NAMSIZ] = {0}; + bool nexthop_self = CHECK_FLAG(binfo->flags, BGP_INFO_ANNC_NH_SELF) + ? true + : false; + bool nexthop_othervrf = false; + vrf_id_t nexthop_vrfid; + const char *nexthop_vrfname = "Default"; if (json_paths) json_path = json_object_new_object(); @@ -6500,6 +6535,39 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, } /* + * If vrf id of nexthop is different from that of prefix, + * set up printable string to append + */ + if (binfo->extra && binfo->extra->bgp_orig) { + const char *self = ""; + + if (nexthop_self) + self = "<"; + + nexthop_othervrf = true; + nexthop_vrfid = binfo->extra->bgp_orig->vrf_id; + + if (binfo->extra->bgp_orig->vrf_id == VRF_UNKNOWN) + snprintf(vrf_id_str, sizeof(vrf_id_str), + "@%s%s", VRFID_NONE_STR, self); + else + snprintf(vrf_id_str, sizeof(vrf_id_str), "@%u%s", + binfo->extra->bgp_orig->vrf_id, self); + + if (binfo->extra->bgp_orig->inst_type != + BGP_INSTANCE_TYPE_DEFAULT) + + nexthop_vrfname = binfo->extra->bgp_orig->name; + } else { + const char *self = ""; + + if (nexthop_self) + self = "<"; + + snprintf(vrf_id_str, sizeof(vrf_id_str), "%s", self); + } + + /* * For ENCAP and EVPN routes, nexthop address family is not * neccessarily the same as the prefix address family. * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field @@ -6541,7 +6609,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, json_object_boolean_true_add(json_nexthop_global, "used"); } else - vty_out(vty, "%s", nexthop); + vty_out(vty, "%s%s", nexthop, vrf_id_str); } else if (safi == SAFI_EVPN) { if (json_paths) { json_nexthop_global = json_object_new_object(); @@ -6553,7 +6621,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, json_object_boolean_true_add(json_nexthop_global, "used"); } else - vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); + vty_out(vty, "%-16s%s", inet_ntoa(attr->nexthop), + vrf_id_str); } else if (safi == SAFI_FLOWSPEC) { if (attr->nexthop.s_addr != 0) { if (json_paths) { @@ -6587,11 +6656,17 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, json_object_boolean_true_add(json_nexthop_global, "used"); } else { + char buf[BUFSIZ]; + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) - vty_out(vty, "%-16s", - inet_ntoa(attr->mp_nexthop_global_in)); + snprintf(buf, sizeof(buf), "%s%s", + inet_ntoa(attr->mp_nexthop_global_in), + vrf_id_str); else - vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); + snprintf(buf, sizeof(buf), "%s%s", + inet_ntoa(attr->nexthop), + vrf_id_str); + vty_out(vty, "%-16s", buf); } } @@ -6658,11 +6733,12 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, vty_out(vty, "%*s", len, " "); } else { len = vty_out( - vty, "%s", + vty, "%s%s", inet_ntop( AF_INET6, &attr->mp_nexthop_local, - buf, BUFSIZ)); + buf, BUFSIZ), + vrf_id_str); len = 16 - len; if (len < 1) @@ -6672,10 +6748,11 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, } } else { len = vty_out( - vty, "%s", + vty, "%s%s", inet_ntop(AF_INET6, &attr->mp_nexthop_global, buf, - BUFSIZ)); + BUFSIZ), + vrf_id_str); len = 16 - len; if (len < 1) @@ -6734,6 +6811,21 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, vty_out(vty, "%s", bgp_origin_str[attr->origin]); if (json_paths) { + if (nexthop_self) + json_object_boolean_true_add(json_path, + "announceNexthopSelf"); + if (nexthop_othervrf) { + json_object_string_add(json_path, "nhVrfName", + nexthop_vrfname); + + json_object_int_add(json_path, "nhVrfId", + ((nexthop_vrfid == VRF_UNKNOWN) + ? -1 + : (int)nexthop_vrfid)); + } + } + + if (json_paths) { if (json_nexthop_global || json_nexthop_ll) { json_nexthops = json_object_new_array(); @@ -7063,10 +7155,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, @@ -7333,6 +7425,9 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, int addpath_capable; int has_adj; unsigned int first_as; + bool nexthop_self = CHECK_FLAG(binfo->flags, BGP_INFO_ANNC_NH_SELF) + ? true + : false; if (json_paths) { json_path = json_object_new_object(); @@ -7636,6 +7731,49 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, } } + /* + * Note when vrfid of nexthop is different from that of prefix + */ + if (binfo->extra && binfo->extra->bgp_orig) { + vrf_id_t nexthop_vrfid = binfo->extra->bgp_orig->vrf_id; + + if (json_paths) { + const char *vn; + + if (binfo->extra->bgp_orig->inst_type == + BGP_INSTANCE_TYPE_DEFAULT) + + vn = "Default"; + else + vn = binfo->extra->bgp_orig->name; + + json_object_string_add(json_path, "nhVrfName", + vn); + + if (nexthop_vrfid == VRF_UNKNOWN) { + json_object_int_add(json_path, + "nhVrfId", -1); + } else { + json_object_int_add(json_path, + "nhVrfId", (int)nexthop_vrfid); + } + } else { + if (nexthop_vrfid == VRF_UNKNOWN) + vty_out(vty, " vrf ?"); + else + vty_out(vty, " vrf %u", nexthop_vrfid); + } + } + + if (nexthop_self) { + if (json_paths) { + json_object_boolean_true_add(json_path, + "announceNexthopSelf"); + } else { + vty_out(vty, " announce-nh-self"); + } + } + if (!json_paths) vty_out(vty, "\n"); @@ -8333,10 +8471,16 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (!use_json && header) { vty_out(vty, "BGP table version is %" PRIu64 - ", local router ID is %s\n", + ", local router ID is %s, vrf id ", table->version, inet_ntoa(bgp->router_id)); + if (bgp->vrf_id == VRF_UNKNOWN) + vty_out(vty, "%s", VRFID_NONE_STR); + else + vty_out(vty, "%u", bgp->vrf_id); + vty_out(vty, "\n"); vty_out(vty, BGP_SHOW_SCODE_HEADER); + vty_out(vty, BGP_SHOW_NCODE_HEADER); vty_out(vty, BGP_SHOW_OCODE_HEADER); if (type == bgp_show_type_dampend_paths || type == bgp_show_type_damp_neighbor) @@ -10146,9 +10290,15 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, "0.0.0.0"); } else { vty_out(vty, "BGP table version is %" PRIu64 - ", local router ID is %s\n", + ", local router ID is %s, vrf id ", table->version, inet_ntoa(bgp->router_id)); + if (bgp->vrf_id == VRF_UNKNOWN) + vty_out(vty, "%s", VRFID_NONE_STR); + else + vty_out(vty, "%u", bgp->vrf_id); + vty_out(vty, "\n"); vty_out(vty, BGP_SHOW_SCODE_HEADER); + vty_out(vty, BGP_SHOW_NCODE_HEADER); vty_out(vty, BGP_SHOW_OCODE_HEADER); vty_out(vty, "Originating default network 0.0.0.0\n\n"); @@ -10179,12 +10329,21 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, json_ocode); } else { vty_out(vty, - "BGP table version is 0, local router ID is %s\n", + "BGP table version is 0, local router ID is %s, vrf id ", inet_ntoa( - bgp->router_id)); + bgp->router_id)); + if (bgp->vrf_id == VRF_UNKNOWN) + vty_out(vty, "%s", + VRFID_NONE_STR); + else + vty_out(vty, "%u", + bgp->vrf_id); + vty_out(vty, "\n"); vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, + BGP_SHOW_NCODE_HEADER); + vty_out(vty, BGP_SHOW_OCODE_HEADER); } header1 = 0; @@ -10237,13 +10396,25 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, } else { vty_out(vty, "BGP table version is %" PRIu64 - ", local router ID is %s\n", + ", local router ID is %s, vrf id ", table->version, inet_ntoa( bgp->router_id)); + if (bgp->vrf_id == + VRF_UNKNOWN) + vty_out(vty, + "%s", + VRFID_NONE_STR); + else + vty_out(vty, + "%u", + bgp->vrf_id); + vty_out(vty, "\n"); vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, + BGP_SHOW_NCODE_HEADER); + vty_out(vty, BGP_SHOW_OCODE_HEADER); } header1 = 0; @@ -11253,14 +11424,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)); } @@ -11272,7 +11444,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_route.h b/bgpd/bgp_route.h index 1e788b00f1..00e5677fe0 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -54,10 +54,11 @@ enum bgp_show_type { #define BGP_SHOW_SCODE_HEADER \ - "Status codes: s suppressed, d damped, " \ + "Status codes: s suppressed, d damped, " \ "h history, * valid, > best, = multipath,\n" \ - " i internal, r RIB-failure, S Stale, R Removed\n" -#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n" + " i internal, r RIB-failure, S Stale, R Removed\n" +#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n" +#define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n" #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n" /* Maximum number of labels we can process or send with a prefix. We @@ -319,7 +320,8 @@ static inline void bgp_bump_version(struct bgp_node *node) static inline int bgp_fibupd_safi(safi_t safi) { if (safi == SAFI_UNICAST || safi == SAFI_MULTICAST - || safi == SAFI_LABELED_UNICAST) + || safi == SAFI_LABELED_UNICAST + || safi == SAFI_FLOWSPEC) return 1; return 0; } diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 4cc889286e..63400f7d31 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -635,7 +635,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 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..b8c81232bb 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -12153,7 +12153,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 +12176,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 +12185,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 +12587,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); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 581b4e6f5b..52a246387e 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -56,6 +56,7 @@ #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_labelpool.h" +#include "bgpd/bgp_pbr.h" /* All information about zebra. */ struct zclient *zclient = NULL; @@ -997,6 +998,9 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p, static struct thread *bgp_tm_thread_connect; static bool bgp_tm_status_connected; +static bool bgp_tm_chunk_obtained; +#define BGP_FLOWSPEC_TABLE_CHUNK 100000 +static uint32_t bgp_tm_min, bgp_tm_max, bgp_tm_chunk_size; static int bgp_zebra_tm_connect(struct thread *t) { @@ -1017,12 +1021,27 @@ static int bgp_zebra_tm_connect(struct thread *t) if (!bgp_tm_status_connected) zlog_debug("Connecting to table manager. Success"); bgp_tm_status_connected = true; + if (!bgp_tm_chunk_obtained) { + if (bgp_zebra_get_table_range(bgp_tm_chunk_size, + &bgp_tm_min, + &bgp_tm_max) >= 0) + bgp_tm_chunk_obtained = true; + } } thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, &bgp_tm_thread_connect); return 0; } +uint32_t bgp_zebra_tm_get_id(void) +{ + static int table_id; + + if (!bgp_tm_chunk_obtained) + return ++table_id; + return bgp_tm_min++; +} + void bgp_zebra_init_tm_connect(void) { int delay = 1; @@ -1032,6 +1051,9 @@ void bgp_zebra_init_tm_connect(void) if (bgp_tm_thread_connect != NULL) return; bgp_tm_status_connected = false; + bgp_tm_chunk_obtained = false; + bgp_tm_min = bgp_tm_max = 0; + bgp_tm_chunk_size = BGP_FLOWSPEC_TABLE_CHUNK; thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, &bgp_tm_thread_connect); } @@ -1173,6 +1195,10 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (bgp_debug_zebra(p)) prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + if (safi == SAFI_FLOWSPEC) + return bgp_pbr_update_entry(bgp, &rn->p, + info, afi, safi, true); + /* * vrf leaking support (will have only one nexthop) */ @@ -1181,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; @@ -1262,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) { @@ -1297,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) { @@ -1350,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++; } @@ -1459,6 +1472,7 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, struct bgp *bgp, safi_t safi) { struct zapi_route api; + struct peer *peer; /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. @@ -1466,8 +1480,13 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, if (!bgp_install_info_to_zebra(bgp)) return; + if (safi == SAFI_FLOWSPEC) { + peer = info->peer; + return bgp_pbr_update_entry(peer->bgp, p, + info, AFI_IP, safi, false); + } + 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; @@ -1880,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); @@ -1908,6 +1927,271 @@ int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise) return zclient_send_message(zclient); } +static int rule_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + uint32_t seqno, priority, unique; + enum zapi_rule_notify_owner note; + struct bgp_pbr_action *bgp_pbra; + ifindex_t ifi; + + if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, + &ifi, ¬e)) + return -1; + + bgp_pbra = bgp_pbr_action_rule_lookup(vrf_id, unique); + if (!bgp_pbra) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Fail to look BGP rule (%u)", + __PRETTY_FUNCTION__, unique); + return 0; + } + + switch (note) { + case ZAPI_RULE_FAIL_INSTALL: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received RULE_FAIL_INSTALL", + __PRETTY_FUNCTION__); + bgp_pbra->installed = false; + bgp_pbra->install_in_progress = false; + break; + case ZAPI_RULE_INSTALLED: + bgp_pbra->installed = true; + bgp_pbra->install_in_progress = false; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received RULE_INSTALLED", + __PRETTY_FUNCTION__); + break; + case ZAPI_RULE_REMOVED: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received RULE REMOVED", + __PRETTY_FUNCTION__); + break; + } + + return 0; +} + +static int ipset_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + uint32_t unique; + enum zapi_ipset_notify_owner note; + struct bgp_pbr_match *bgp_pbim; + + if (!zapi_ipset_notify_decode(zclient->ibuf, + &unique, + ¬e)) + return -1; + + bgp_pbim = bgp_pbr_match_ipset_lookup(vrf_id, unique); + if (!bgp_pbim) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Fail to look BGP match (%u)", + __PRETTY_FUNCTION__, unique); + return 0; + } + + switch (note) { + case ZAPI_IPSET_FAIL_INSTALL: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET_FAIL_INSTALL", + __PRETTY_FUNCTION__); + bgp_pbim->installed = false; + bgp_pbim->install_in_progress = false; + break; + case ZAPI_IPSET_INSTALLED: + bgp_pbim->installed = true; + bgp_pbim->install_in_progress = false; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET_INSTALLED", + __PRETTY_FUNCTION__); + break; + case ZAPI_IPSET_REMOVED: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET REMOVED", + __PRETTY_FUNCTION__); + break; + } + + return 0; +} + +static int ipset_entry_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + uint32_t unique; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; + enum zapi_ipset_entry_notify_owner note; + struct bgp_pbr_match_entry *bgp_pbime; + + if (!zapi_ipset_entry_notify_decode( + zclient->ibuf, + &unique, + ipset_name, + ¬e)) + return -1; + bgp_pbime = bgp_pbr_match_ipset_entry_lookup(vrf_id, + ipset_name, + unique); + if (!bgp_pbime) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Fail to look BGP match entry (%u)", + __PRETTY_FUNCTION__, unique); + return 0; + } + + switch (note) { + case ZAPI_IPSET_ENTRY_FAIL_INSTALL: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET_ENTRY_FAIL_INSTALL", + __PRETTY_FUNCTION__); + bgp_pbime->installed = false; + bgp_pbime->install_in_progress = false; + break; + case ZAPI_IPSET_ENTRY_INSTALLED: + bgp_pbime->installed = true; + bgp_pbime->install_in_progress = false; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET_ENTRY_INSTALLED", + __PRETTY_FUNCTION__); + break; + case ZAPI_IPSET_ENTRY_REMOVED: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPSET_ENTRY_REMOVED", + __PRETTY_FUNCTION__); + break; + } + return 0; +} + +static int iptable_notify_owner(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + uint32_t unique; + enum zapi_iptable_notify_owner note; + struct bgp_pbr_match *bgpm; + + if (!zapi_iptable_notify_decode( + zclient->ibuf, + &unique, + ¬e)) + return -1; + bgpm = bgp_pbr_match_iptable_lookup(vrf_id, unique); + if (!bgpm) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Fail to look BGP iptable (%u)", + __PRETTY_FUNCTION__, unique); + return 0; + } + switch (note) { + case ZAPI_IPTABLE_FAIL_INSTALL: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPTABLE_FAIL_INSTALL", + __PRETTY_FUNCTION__); + bgpm->installed_in_iptable = false; + bgpm->install_iptable_in_progress = false; + break; + case ZAPI_IPTABLE_INSTALLED: + bgpm->installed_in_iptable = true; + bgpm->install_iptable_in_progress = false; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPTABLE_INSTALLED", + __PRETTY_FUNCTION__); + bgpm->action->refcnt++; + break; + case ZAPI_IPTABLE_REMOVED: + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Received IPTABLE REMOVED", + __PRETTY_FUNCTION__); + break; + } + return 0; +} + +static void bgp_encode_pbr_rule_action(struct stream *s, + struct bgp_pbr_action *pbra) +{ + struct prefix any; + + stream_putl(s, 0); /* seqno unused */ + stream_putl(s, 0); /* ruleno unused */ + + stream_putl(s, pbra->unique); + + memset(&any, 0, sizeof(any)); + any.family = AF_INET; + stream_putc(s, any.family); + stream_putc(s, any.prefixlen); + stream_put(s, &any.u.prefix, prefix_blen(&any)); + + stream_putw(s, 0); /* src port */ + + stream_putc(s, any.family); + stream_putc(s, any.prefixlen); + stream_put(s, &any.u.prefix, prefix_blen(&any)); + + stream_putw(s, 0); /* dst port */ + + stream_putl(s, pbra->fwmark); /* fwmark */ + + stream_putl(s, pbra->table_id); + + stream_putl(s, 0); /* ifindex unused */ +} + +static void bgp_encode_pbr_ipset_match(struct stream *s, + struct bgp_pbr_match *pbim) +{ + stream_putl(s, pbim->unique); + stream_putl(s, pbim->type); + + stream_put(s, pbim->ipset_name, + ZEBRA_IPSET_NAME_SIZE); + + +} + +static void bgp_encode_pbr_ipset_entry_match(struct stream *s, + struct bgp_pbr_match_entry *pbime) +{ + stream_putl(s, pbime->unique); + /* check that back pointer is not null */ + stream_put(s, pbime->backpointer->ipset_name, + ZEBRA_IPSET_NAME_SIZE); + + stream_putc(s, pbime->src.family); + stream_putc(s, pbime->src.prefixlen); + stream_put(s, &pbime->src.u.prefix, prefix_blen(&pbime->src)); + + stream_putc(s, pbime->dst.family); + stream_putc(s, pbime->dst.prefixlen); + stream_put(s, &pbime->dst.u.prefix, prefix_blen(&pbime->dst)); +} + +static void bgp_encode_pbr_iptable_match(struct stream *s, + struct bgp_pbr_action *bpa, + struct bgp_pbr_match *pbm) +{ + stream_putl(s, pbm->unique2); + + stream_putl(s, pbm->type); + + stream_putl(s, pbm->flags); + + /* TODO: correlate with what is contained + * into bgp_pbr_action. + * currently only forward supported + */ + if (bpa->nh.type == NEXTHOP_TYPE_BLACKHOLE) + stream_putl(s, ZEBRA_IPTABLES_DROP); + else + stream_putl(s, ZEBRA_IPTABLES_FORWARD); + stream_putl(s, bpa->fwmark); + stream_put(s, pbm->ipset_name, + ZEBRA_IPSET_NAME_SIZE); +} + /* BGP has established connection with Zebra. */ static void bgp_zebra_connected(struct zclient *zclient) { @@ -2167,6 +2451,10 @@ void bgp_zebra_init(struct thread_master *master) zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix; zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix; zclient->label_chunk = bgp_zebra_process_label_chunk; + zclient->rule_notify_owner = rule_notify_owner; + zclient->ipset_notify_owner = ipset_notify_owner; + zclient->ipset_entry_notify_owner = ipset_entry_notify_owner; + zclient->iptable_notify_owner = iptable_notify_owner; } void bgp_zebra_destroy(void) @@ -2182,3 +2470,176 @@ int bgp_zebra_num_connects(void) { return zclient_num_connects; } + +void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, bool install) +{ + struct stream *s; + + if (pbra->install_in_progress) + return; + zlog_debug("%s: table %d fwmark %d %d", __PRETTY_FUNCTION__, + pbra->table_id, pbra->fwmark, install); + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE, + VRF_DEFAULT); + stream_putl(s, 1); /* send one pbr action */ + + bgp_encode_pbr_rule_action(s, pbra); + + stream_putw_at(s, 0, stream_get_endp(s)); + if (!zclient_send_message(zclient) && install) + pbra->install_in_progress = true; +} + +void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, bool install) +{ + struct stream *s; + + if (pbrim->install_in_progress) + return; + zlog_debug("%s: name %s type %d %d", __PRETTY_FUNCTION__, + pbrim->ipset_name, pbrim->type, install); + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + install ? ZEBRA_IPSET_CREATE : + ZEBRA_IPSET_DESTROY, + VRF_DEFAULT); + + stream_putl(s, 1); /* send one pbr action */ + + bgp_encode_pbr_ipset_match(s, pbrim); + + stream_putw_at(s, 0, stream_get_endp(s)); + if (!zclient_send_message(zclient) && install) + pbrim->install_in_progress = true; +} + +void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, + bool install) +{ + struct stream *s; + + if (pbrime->install_in_progress) + return; + zlog_debug("%s: name %s %d %d", __PRETTY_FUNCTION__, + pbrime->backpointer->ipset_name, + pbrime->unique, install); + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + install ? ZEBRA_IPSET_ENTRY_ADD : + ZEBRA_IPSET_ENTRY_DELETE, + VRF_DEFAULT); + + stream_putl(s, 1); /* send one pbr action */ + + bgp_encode_pbr_ipset_entry_match(s, pbrime); + + stream_putw_at(s, 0, stream_get_endp(s)); + if (!zclient_send_message(zclient) && install) + pbrime->install_in_progress = true; +} + +void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, + struct bgp_pbr_match *pbm, + bool install) +{ + struct stream *s; + + if (pbm->install_iptable_in_progress) + return; + zlog_debug("%s: name %s type %d mark %d %d", __PRETTY_FUNCTION__, + pbm->ipset_name, pbm->type, pba->fwmark, install); + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + install ? ZEBRA_IPTABLE_ADD : + ZEBRA_IPTABLE_DELETE, + VRF_DEFAULT); + + bgp_encode_pbr_iptable_match(s, pba, pbm); + + stream_putw_at(s, 0, stream_get_endp(s)); + if (!zclient_send_message(zclient) && install) { + pbm->install_iptable_in_progress = true; + pba->refcnt++; + } +} + +/* inject in table <table_id> a default route to: + * - if nexthop IP is present : to this nexthop + * - if vrf is different from local : to the matching VRF + */ +void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, + afi_t afi, uint32_t table_id, bool announce) +{ + struct zapi_nexthop *api_nh; + struct zapi_route api; + struct prefix p; + + if (!nh || nh->type != NEXTHOP_TYPE_IPV4 + || nh->vrf_id == VRF_UNKNOWN) + return; + memset(&p, 0, sizeof(struct prefix)); + /* default route */ + if (afi != AFI_IP) + return; + p.family = AF_INET; + memset(&api, 0, sizeof(api)); + api.vrf_id = bgp->vrf_id; + api.type = ZEBRA_ROUTE_BGP; + api.safi = SAFI_UNICAST; + api.prefix = p; + api.tableid = table_id; + api.nexthop_num = 1; + SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + api_nh = &api.nexthops[0]; + + /* redirect IP */ + if (nh->gate.ipv4.s_addr) { + char buff[PREFIX_STRLEN]; + + api_nh->vrf_id = nh->vrf_id; + api_nh->gate.ipv4 = nh->gate.ipv4; + api_nh->type = NEXTHOP_TYPE_IPV4; + + inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_info("BGP: sending default route to %s table %d (redirect IP)", + buff, table_id); + zclient_route_send(announce ? ZEBRA_ROUTE_ADD + : ZEBRA_ROUTE_DELETE, + zclient, &api); + } else if (nh->vrf_id != bgp->vrf_id) { + struct vrf *vrf; + struct interface *ifp; + + vrf = vrf_lookup_by_id(nh->vrf_id); + if (!vrf) + return; + /* create default route with interface <VRF> + * with nexthop-vrf <VRF> + */ + ifp = if_lookup_by_name_all_vrf(vrf->name); + if (!ifp) + return; + api_nh->vrf_id = nh->vrf_id; + api_nh->type = NEXTHOP_TYPE_IFINDEX; + api_nh->ifindex = ifp->ifindex; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_info("BGP: sending default route to %s table %d (redirect VRF)", + vrf->name, table_id); + zclient_route_send(announce ? ZEBRA_ROUTE_ADD + : ZEBRA_ROUTE_DELETE, + zclient, &api); + return; + } +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 7263317b6f..7ac40fecff 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -25,6 +25,7 @@ extern void bgp_zebra_init(struct thread_master *master); extern void bgp_zebra_init_tm_connect(void); +extern uint32_t bgp_zebra_tm_get_id(void); extern void bgp_zebra_destroy(void); extern int bgp_zebra_get_table_range(uint32_t chunk_size, uint32_t *start, uint32_t *end); @@ -70,4 +71,20 @@ extern int bgp_zebra_advertise_all_vni(struct bgp *, int); extern int bgp_zebra_num_connects(void); +struct bgp_pbr_action; +struct bgp_pbr_match; +struct bgp_pbr_match_entry; +extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, + bool install); +extern void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, + bool install); +extern void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, + bool install); +extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, + struct bgp_pbr_match *pbm, + bool install); + +extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, + afi_t afi, uint32_t table_id, bool announce); + #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index ccfa90419b..b8ac11a47b 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -83,6 +83,7 @@ #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_flowspec.h" #include "bgpd/bgp_labelpool.h" +#include "bgpd/bgp_pbr.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_QOBJ_TYPE(bgp_master) @@ -1173,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. */ @@ -3006,6 +3012,7 @@ static struct bgp *bgp_create(as_t *as, const char *name, bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id); bgp_evpn_init(bgp); + bgp_pbr_init(bgp); return bgp; } @@ -3401,7 +3408,7 @@ void bgp_free(struct bgp *bgp) bf_release_index(bm->rd_idspace, bgp->vrf_rd_id); bgp_evpn_cleanup(bgp); - + bgp_pbr_cleanup(bgp); if (bgp->name) XFREE(MTYPE_BGP, bgp->name); if (bgp->name_pretty) @@ -4070,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 @@ -5945,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; @@ -5988,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; } @@ -7107,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) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 576c89f25e..d9ce77a55a 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -400,6 +400,25 @@ struct bgp { /* Allocate MPLS labels */ uint8_t allocate_mpls_labels[AFI_MAX][SAFI_MAX]; + /* Allocate hash entries to store policy routing information + * The hash are used to host pbr rules somewhere. + * Actually, pbr will only be used by flowspec + * those hash elements will have relationship together as + * illustrated in below diagram: + * + * pbr_action a <----- pbr_match i <--- pbr_match_entry 1..n + * <----- pbr_match j <--- pbr_match_entry 1..m + * + * - here in BGP structure, the list of match and actions will + * stand for the list of ipset sets, and table_ids in the kernel + * - the arrow above between pbr_match and pbr_action indicate + * that a backpointer permits match to find the action + * - the arrow betwen match_entry and match is a hash list + * contained in match, that lists the whole set of entries + */ + struct hash *pbr_match_hash; + struct hash *pbr_action_hash; + /* timer to re-evaluate neighbor default-originate route-maps */ struct thread *t_rmap_def_originate_eval; #define RMAP_DEFAULT_ORIGINATE_EVAL_TIMER 5 @@ -817,7 +836,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 6fca1cd7ec..8b49295444 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.60) -AC_INIT(frr, 4.1-dev, [https://github.com/frrouting/frr/issues]) +AC_INIT(frr, 5.1-dev, [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST(PACKAGE_URL) PACKAGE_FULLNAME="FRRouting" @@ -232,7 +232,6 @@ else fi fi AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"]) -AM_CONDITIONAL([SHARPD], [test "x$enable_dev_build" = "xyes"]) dnl always want these CFLAGS AC_C_FLAG([-fno-omit-frame-pointer]) @@ -354,6 +353,10 @@ AC_ARG_ENABLE(isisd, AS_HELP_STRING([--disable-isisd], [do not build isisd])) AC_ARG_ENABLE(pimd, AS_HELP_STRING([--disable-pimd], [do not build pimd])) +AC_ARG_ENABLE(pbrd, + AS_HELP_STRING([--disable-pbrd], [do not build pbrd])) +AC_ARG_ENABLE(sharpd, + AS_HELP_STRING([--enable-sharpd], [do not build sharpd])) AC_ARG_ENABLE(bgp-announce, AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(bgp-vnc, @@ -1378,6 +1381,7 @@ AM_CONDITIONAL(OSPF6D, test "${enable_ospf6d}" != "no") AM_CONDITIONAL(ISISD, test "${enable_isisd}" != "no") AM_CONDITIONAL(PIMD, test "${enable_pimd}" != "no") AM_CONDITIONAL(PBRD, test "${enable_pbrd}" != "no") +AM_CONDITIONAL(SHARPD, test "${enable_sharpd}" = "yes") if test "${enable_bgp_announce}" = "no";then AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra) 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.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 3f5264469d..8fa057424a 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -82,6 +82,10 @@ if EIGRPD man_MANS += $(MANPAGE_BUILDDIR)/eigrpd.8 endif +if SHARPD +man_MANS += $(MANPAGE_BUILDDIR)/sharpd.8 +endif + # Automake is particular about manpages. It is aware of them and has some # special facilities for handling them, but it assumes that manpages are always # given in groff source and so these facilities are limited to simply @@ -146,6 +150,7 @@ EXTRA_DIST = frr-sphinx.mk \ manpages/ripd.rst \ manpages/pbrd.rst \ manpages/ripngd.rst \ + manpages/sharpd.rst \ manpages/vtysh.rst \ manpages/watchfrr.rst \ manpages/zebra.rst \ @@ -212,6 +217,7 @@ EXTRA_DIST = frr-sphinx.mk \ user/routemap.rst \ user/routeserver.rst \ user/rpki.rst \ + user/sharp.rst \ user/snmp.rst \ user/snmptrap.rst \ user/Useful_Sysctl_Settings.md \ diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 17a7e1c0ba..2c855ee880 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 + process-architecture library bgpd ospf 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/process-architecture.rst b/doc/developer/process-architecture.rst new file mode 100644 index 0000000000..806afa644c --- /dev/null +++ b/doc/developer/process-architecture.rst @@ -0,0 +1,320 @@ +.. _process-architecture: + +Process Architecture +==================== + +FRR inherited its overall design architecture from Quagga. The chosen model for +Quagga is that of a suite of independent daemons that do IPC via Unix domain +sockets. Within each daemon, the architecture follows the event-driven model. +FRR has inherited this model as well. As FRR is deployed at larger scales and +gains ever more features, each adding to the overall processing workload, we +are approaching the saturation point for a single thread per daemon. In light +of this, there are ongoing efforts to introduce multithreading to various +components of FRR. This document aims to describe the current design choices +and overall model for integrating the event-driven and multithreaded +architectures into a cohesive whole. + +Terminology +----------- +Because this document describes the architecture for true kernel threads as +well as the event system, a digression on terminology is in order here. + +Historically Quagga's event system was viewed as an implementation of userspace +threading. Because of this design choice, the names for various datastructures +within the event system are variations on the term "thread". The primary +context datastructure in this system is called a "threadmaster". What would +today be called an 'event' or 'task' in systems such as libevent are called +"threads" and the datastructure for them is ``struct thread``. To add to the +confusion, these "threads" have various types, one of which is "event". To +hopefully avoid some of this confusion, this document refers to these "threads" +as a 'task' except where the datastructures are explicitly named. When they are +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 +------------------ +This section presents a brief overview of the event model as currently +implemented in FRR. This doc should be expanded and broken off into its own +section. For now it provides basic information necessary to understand the +interplay between the event system and kernel threads. + +The core event system is implemented in :file:`lib/thread.[ch]`. The primary +structure is ``struct thread_master``, hereafter referred to as a +``threadmaster``. A ``threadmaster`` is a global state object, or context, that +holds all the tasks currently pending execution as well as statistics on tasks +that have already executed. The event system is driven by adding tasks to this +data structure and then calling a function to retrieve the next task to +execute. At initialization, a daemon will typically create one +``threadmaster``, add a small set of initial tasks, and then run a loop to +fetch each task and execute it. + +These tasks have various types corresponding to their general action. The types +are given by integer macros in :file:`thread.h` and are: + +``THREAD_READ`` + Task which waits for a file descriptor to become ready for reading and then + executes. + +``THREAD_WRITE`` + Task which waits for a file descriptor to become ready for writing and then + executes. + +``THREAD_TIMER`` + Task which executes after a certain amount of time has passed since it was + scheduled. + +``THREAD_EVENT`` + Generic task that executes with high priority and carries an arbitrary + 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. + +``THREAD_UNUSED`` + Type used internally for ``struct thread`` objects that aren't being used. + The event system pools ``struct thread`` to avoid heap allocations; this is + the type they have when they're in the pool. + +``THREAD_EXECUTE`` + Just before a task is run its type is changed to this. This is used to show + ``X`` as the type in the output of :clicmd:`show thread cpu`. + +The programmer never has to work with these types explicitly. Each type of task +is created and queued via special-purpose functions (actually macros, but +irrelevant for the time being) for the specific type. For example, to add a +``THREAD_READ`` task, you would call + +:: + + thread_add_read(struct thread_master *master, int (*handler)(struct thread *), void *arg, int fd, struct thread **ref); + +The ``struct thread`` is then created and added to the appropriate internal +datastructure within the ``threadmaster``. + +The Event Loop +^^^^^^^^^^^^^^ +To use the event system, after creating a ``threadmaster`` the program adds an +initial set of tasks. As these tasks execute, they add more tasks that execute +at some point in the future. This sequence of tasks drives the lifecycle of the +program. When no more tasks are available, the program dies. Typically at +startup the first task added is an I/O task for VTYSH as well as any network +sockets needed for peerings or IPC. + +To retrieve the next task to run the program calls ``thread_fetch()``. +``thread_fetch()`` internally computes which task to execute next based on +rudimentary priority logic. Events (type ``THREAD_EVENT``) execute with the +highest priority, followed by expired timers and finally I/O tasks (type +``THREAD_READ`` and ``THREAD_WRITE``). When scheduling a task a function and an +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 +scheduled from, that can be accessed at runtime for debugging, logging and +informational purposes. Each task type has its own specific scheduling function +that follow the naming convention ``thread_add_<type>``; see :file:`thread.h` +for details. + +There are some gotchas to keep in mind: + +- I/O tasks are keyed off the file descriptor associated with the I/O + operation. This means that for any given file descriptor, only one of each + type of I/O task (``THREAD_READ`` and ``THREAD_WRITE``) can be scheduled. For + example, scheduling two write tasks one after the other will overwrite the + first task with the second, resulting in total loss of the first task and + difficult bugs. + +- Timer tasks are only as accurate as the monotonic clock provided by the + underlying operating system. + +- Memory management of the arbitrary handler argument passed in the schedule + call is the responsibility of the caller. + + +Kernel Thread Architecture +-------------------------- +Efforts have begun to introduce kernel threads into FRR to improve performance +and stability. Naturally a kernel thread architecture has long been seen as +orthogonal to an event-driven architecture, and the two do have significant +overlap in terms of design choices. Since the event model is tightly integrated +into FRR, careful thought has been put into how pthreads are introduced, what +role they fill, and how they will interoperate with the event model. + +Design Overview +^^^^^^^^^^^^^^^ +Each kernel thread behaves as a lightweight process within FRR, sharing the +same process memory space. On the other hand, the event system is designed to +run in a single process and drive serial execution of a set of tasks. With this +consideration, a natural choice is to implement the event system within each +kernel thread. This allows us to leverage the event-driven execution model with +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 +wrapper for both systems implemented in :file:`lib/frr_pthread.[ch]`. The +header provides a core datastructure, ``struct frr_pthread``, that encapsulates +structures from both POSIX threads and :file:`thread.[ch]`. In particular, this +datastructure has a pointer to a ``threadmaster`` that runs within the pthread. +It also has fields for a name as well as start and stop functions that have +signatures similar to the POSIX arguments for ``pthread_create()``. + +Calling ``frr_pthread_new()`` creates and registers a new ``frr_pthread``. The +returned structure has a pre-initialized ``threadmaster``, and its ``start`` +and ``stop`` functions are initialized to defaults that will run a basic event +loop with the given threadmaster. Calling ``frr_pthread_run`` starts the thread +with the ``start`` function. From there, the model is the same as the regular +event model. To schedule tasks on a particular pthread, simply use the regular +:file:`thread.c` functions as usual and provide the ``threadmaster`` pointed to +from the ``frr_pthread``. As part of implementing the wrapper, the +:file:`thread.c` functions were made thread-safe. Consequently, it is safe to +schedule events on a ``threadmaster`` belonging both to the calling thread as +well as *any other pthread*. This serves as the basis for inter-thread +communication and boils down to a slightly more complicated method of message +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 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 +pthreads; running :clicmd:`show thread cpu` will display the usual event +breakdown, but it will do so for each pthread running in the program. For +example, :ref:`bgpd` runs a dedicated I/O pthread and shows the following +output for :clicmd:`show thread cpu`: + +:: + + frr# show thread cpu + + Thread statistics for bgpd: + + Showing statistics for pthread main + ------------------------------------ + CPU (user+system): Real (wall-clock): + Active Runtime(ms) Invoked Avg uSec Max uSecs Avg uSec Max uSecs Type Thread + 0 1389.000 10 138900 248000 135549 255349 T subgroup_coalesce_timer + 0 0.000 1 0 0 18 18 T bgp_startup_timer_expire + 0 850.000 18 47222 222000 47795 233814 T work_queue_run + 0 0.000 10 0 0 6 14 T update_subgroup_merge_check_thread_cb + 0 0.000 8 0 0 117 160 W zclient_flush_data + 2 2.000 1 2000 2000 831 831 R bgp_accept + 0 1.000 1 1000 1000 2832 2832 E zclient_connect + 1 42082.000 240574 174 37000 178 72810 R vtysh_read + 1 152.000 1885 80 2000 96 6292 R zclient_read + 0 549346.000 2997298 183 7000 153 20242 E bgp_event + 0 2120.000 300 7066 14000 6813 22046 T (bgp_holdtime_timer) + 0 0.000 2 0 0 57 59 T update_group_refresh_default_originate_route_map + 0 90.000 1 90000 90000 73729 73729 T bgp_route_map_update_timer + 0 1417.000 9147 154 48000 132 61998 T bgp_process_packet + 300 71807.000 2995200 23 3000 24 11066 T (bgp_connect_timer) + 0 1894.000 12713 148 45000 112 33606 T (bgp_generate_updgrp_packets) + 0 0.000 1 0 0 105 105 W vtysh_write + 0 52.000 599 86 2000 138 6992 T (bgp_start_timer) + 1 1.000 8 125 1000 164 593 R vtysh_accept + 0 15.000 600 25 2000 15 153 T (bgp_routeadv_timer) + 0 11.000 299 36 3000 53 3128 RW bgp_connect_check + + + Showing statistics for pthread BGP I/O thread + ---------------------------------------------- + CPU (user+system): Real (wall-clock): + Active Runtime(ms) Invoked Avg uSec Max uSecs Avg uSec Max uSecs Type Thread + 0 1611.000 9296 173 13000 188 13685 R bgp_process_reads + 0 2995.000 11753 254 26000 182 29355 W bgp_process_writes + + + Showing statistics for pthread BGP Keepalives thread + ----------------------------------------------------- + CPU (user+system): Real (wall-clock): + Active Runtime(ms) Invoked Avg uSec Max uSecs Avg uSec Max uSecs Type Thread + No data to display yet. + +Attentive readers will notice that there is a third thread, the Keepalives +thread. This thread is responsible for -- surprise -- generating keepalives for +peers. However, there are no statistics showing for that thread. Although the +pthread uses the ``frr_pthread`` wrapper, it opts not to use the embedded +``threadmaster`` facilities. Instead it replaces the ``start`` and ``stop`` +functions with custom functions. This was done because the ``threadmaster`` +facilities introduce a small but significant amount of overhead relative to the +pthread's task. In this case since the pthread does not need the event-driven +model and does not need to receive tasks from other pthreads, it is simpler and +more efficient to implement it outside of the provided event facilities. The +point to take away from this example is that while the facilities to make using +pthreads within FRR easy are already implemented, the wrapper is flexible and +allows usage of other models while still integrating with the rest of the FRR +core infrastructure. Starting and stopping this pthread works the same as it +does for any other ``frr_pthread``; the only difference is that event +statistics are not collected for it, because there are no events. + +Notes on Design and Documentation +--------------------------------- +Because of the choice to embed the existing event system into each pthread +within FRR, at this time there is not integrated support for other models of +pthread use such as divide and conquer. Similarly, there is no explicit support +for thread pooling or similar higher level constructs. The currently existing +infrastructure is designed around the concept of long-running worker threads +responsible for specific jobs within each daemon. This is not to say that +divide and conquer, thread pooling, etc. could not be implemented in the +future. However, designs in this direction must be very careful to take into +account the existing codebase. Introducing kernel threads into programs that +have been written under the assumption of a single thread of execution must be +done very carefully to avoid insidious errors and to ensure the program remains +understandable and maintainable. + +In keeping with these goals, future work on kernel threading should be +extensively documented here and FRR developers should be very careful with +their design choices, as poor choices tightly integrated can prove to be +catastrophic for development efforts in the future. 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/figures/threadmaster.svg b/doc/figures/threadmaster.svg new file mode 100644 index 0000000000..a8d2c6adfe --- /dev/null +++ b/doc/figures/threadmaster.svg @@ -0,0 +1,42 @@ +<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> + <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ --> + <g> + <title>Layer 1</title> + <rect stroke="#000000" id="svg_14" height="209.999998" width="78.999998" y="42.000002" x="44" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/> + <rect stroke="#000000" id="svg_7" height="391.000012" width="278.000011" y="20" x="24.999992" fill="#e5e5e5"/> + <rect stroke="#000000" id="svg_8" height="371.999978" width="259.000006" y="27.5" x="34.5" fill="#aad4ff"/> + <text stroke="#000000" transform="matrix(0.6990958452224731,0,0,0.7454545497894287,7.038336245343089,56.89090812206268) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_19" y="259.756097" x="80.760476" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">I/O</text> + <text id="svg_35" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="139.834812" x="60.305235" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task3</text> + <text id="svg_36" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="143.062739" x="62.867657" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task3</text> + <rect stroke="#000000" id="svg_9" height="196.000002" width="53" y="35" x="44" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/> + <text id="svg_40" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="144.681173" x="161.373439" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <rect id="svg_15" stroke="#000000" height="196.000002" width="53" y="35" x="104" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/> + <rect id="svg_16" stroke="#000000" height="196.000002" width="53" y="35" x="164" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#ffaaaa"/> + <rect id="svg_17" stroke="#000000" height="196.000002" width="53" y="35" x="225" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#a2f9a2"/> + <text id="svg_20" stroke="#000000" transform="matrix(0.5625633135745076,0,0,0.5774844344081629,24.86726517334988,96.24168390657653) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="264.951043" x="181.542341" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">Timer</text> + <text id="svg_21" stroke="#000000" transform="matrix(0.5749753656942643,0,0,0.6334746061662456,38.254388934566045,86.97425370192815) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="256.836039" x="263.041264" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">Event</text> + <text id="svg_22" stroke="#000000" transform="matrix(0.5985540174236015,0,0,0.6031454293478589,42.9067138136931,90.27170546109113) " opacity="0.95" xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="263.072047" x="346.818415" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">Ready</text> + <line id="svg_24" y2="62" x2="278.034186" y1="62" x1="44" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/> + <line id="svg_25" y2="90" x2="278.034186" y1="90" x1="44" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/> + <line id="svg_26" y2="118.5" x2="278.034186" y1="118.5" x1="44" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/> + <rect id="svg_27" height="45" width="182" y="287" x="103" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="#cccccc"/> + <rect id="svg_28" height="45" width="182" y="342" x="103" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="#cccccc"/> + <text stroke="#000000" transform="matrix(0.791700541973114,0,0,0.7857142686843872,21.471117571927607,58.28571891784668) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_31" y="325.818183" x="222.3407" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">thread_fetch()</text> + <text stroke="#000000" transform="matrix(0.8357433386864528,0,0,0.7633649135312126,6.654719490831912,79.40761438745744) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_32" y="380.181209" x="228.949154" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">thread_call()</text> + <text stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" id="svg_33" y="47" x="56" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task1</text> + <text style="cursor: move;" id="svg_34" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="94.51867" x="58.592092" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task2</text> + <text style="cursor: move;" id="svg_37" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="141.444306" x="58.584825" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task9</text> + <text style="cursor: move;" id="svg_38" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="50.812022" x="163.086583" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task4</text> + <text style="cursor: move;" id="svg_39" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="92.891296" x="164.799726" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">task7</text> + <text id="svg_41" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="138.207438" x="158.803698" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <text id="svg_42" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="54.046518" x="265.881445" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <text id="svg_43" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="96.125793" x="264.168301" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <text id="svg_44" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="143.060369" x="259.02887" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <text id="svg_45" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="50.809651" x="370.383203" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <text id="svg_46" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="99.36266" x="372.096346" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <text id="svg_47" stroke="#000000" transform="matrix(0.5837222373092742,0,0,0.6178813716933291,36.307626092448714,21.493443100305285) " xml:space="preserve" text-anchor="middle" font-family="Monospace" font-size="24" y="141.441935" x="372.096346" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">...</text> + <line stroke="#000000" id="svg_54" y2="286.009435" x2="275" y1="230.500001" x1="275" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="none"/> + <line stroke="#000000" id="svg_57" y2="278.49643" x2="267.246429" y1="286" x1="275.5" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="none"/> + <line id="svg_58" y2="285.754309" x2="274.995691" y1="278.5" x1="282.25" opacity="0.95" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" stroke="#000000" fill="none"/> + </g> +</svg> diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index 41683ed678..2b0f7e3893 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -323,6 +323,7 @@ man_pages = [ ('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8), ('pimd', 'pimd', fwfrr.format("a PIM "), [], 8), ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8), + ('sharpd', 'sharpd', fwfrr.format("a SHARP "), [], 8), ('mtracebis', 'mtracebis', "a multicast trace client", [], 8), ('ripd', 'ripd', fwfrr.format("a RIP "), [], 8), ('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8), diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index 964cc07d71..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 @@ -22,6 +19,7 @@ Welcome to FRR's documentation! mtracebis ripd ripngd + sharpd watchfrr zebra vtysh diff --git a/doc/manpages/sharpd.rst b/doc/manpages/sharpd.rst new file mode 100644 index 0000000000..016f3f9254 --- /dev/null +++ b/doc/manpages/sharpd.rst @@ -0,0 +1,38 @@ +****** +SHARPD +****** + +.. include:: defines.rst +.. |DAEMON| replace:: sharpd + +SYNOPSIS +======== +|DAEMON| |synopsis-options-hv| + +|DAEMON| |synopsis-options| + +DESCRIPTION +=========== +|DAEMON| is a routing component that works with the FRRouting engine. + +OPTIONS +======= +OPTIONS available for the |DAEMON| command: + +.. include:: common-options.rst + +FILES +===== + +|INSTALL_PREFIX_SBIN|/|DAEMON| + The default location of the |DAEMON| binary. + +|INSTALL_PREFIX_ETC|/|DAEMON|.conf + The default location of the |DAEMON| config file. + +$(PWD)/|DAEMON|.log + If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|. + +.. include:: epilogue.rst + + 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/index.rst b/doc/user/index.rst index c264b4c83e..9b9189dc30 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -1,5 +1,5 @@ -Welcome to FRR's documentation! -=============================== +FRRouting User Guide +==================== .. toctree:: :maxdepth: 2 @@ -25,6 +25,7 @@ Welcome to FRR's documentation! pbr ripd ripngd + sharp vnc glossary appendix diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 8501054fdb..700a727d8f 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -117,11 +117,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 +171,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/` 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/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/sharp.rst b/doc/user/sharp.rst new file mode 100644 index 0000000000..090628044c --- /dev/null +++ b/doc/user/sharp.rst @@ -0,0 +1,67 @@ +.. _sharp: + +*** +SHARP +*** + +:abbr:`SHARP` Super Happy Advanced Routing Process. This daemon is useful +for the testing of FRR itself as well as useful for creation of Proof of +Concept labs. + +.. _starting-sharp: + +Starting SHARP +============== + +Default configuration file for *sharpd* is :file:`sharpd.conf`. The typical +location of :file:`sharpd.conf` is |INSTALL_PREFIX_ETC|/sharpd.conf. + +If the user is using integrated config, then :file:`sharpd.conf` need not be +present and the :file:`frr.conf` is read instead. + +.. program:: sharpd + +:abbr:`SHARP` supports all the common FRR daemon start options which are +documented elsewhere. + +.. _using-sharp: + +USING SHARP +=========== + +All sharp commands are under the enable node and proceeded by the +:abbr:`sharp` keyword. There are currently no permenent sharp +commands for configuration. + +..index:: sharp install +..clicmd:: sharp install routes A.B.C.D nexthop E.F.G.H (1-1000000) + +Install up to a million /32 routes starting at A.B.C.D with specified nexthop +E.F.G.H. The nexthop is a NEXTHOP_TYPE_IPV4 and must be reachable to be +installed into the kernel. The routes are installed into zebra as +ZEBRA_ROUTE_SHARP and can be used as part of a normal route redistribution. +Route installation time is noted in the debug log and upon zebra successful +installation into the kernel and sharp receiving the notification of all +route installs the success will be noted in the debug log as well. + +..index:: sharp remove +..clicmd:: sharp remove routes A.B.C.D (1-1000000) + +Remove up 1000000 million /32 routes starting at A.B.C.D. The routes are +removed from zebra. Route deletion start is noted in the debug log +and when all routes have been successfully deleted the debug log will +be updated with this information as well. + +..index:: sharp label +..clicmd:: sharp label <ipv4|ipv6> vrf NAME label (0-1000000) + +Install a label into the kernel that causes the specified vrf NAME table to be +used for pop and forward operations when the specified label is seen. + +..index:: sharp watch +..clicmd: sharp watch nexthop <A.B.C.D|X:X::X:X> + +Instruct zebra to monitor and notify sharp when the specified nexthop is +changed. The notification from zebra is written into the debug log. + + 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..78e1959d36 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -31,6 +31,21 @@ Besides the common invocation options (:ref:`common-invocation-options`), the When program terminates, retain routes added by zebra. +.. 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. More information in the :ref:`VRF Commands` + chapter. + .. program:: configure .. _interface-commands: @@ -47,6 +62,10 @@ Standard Commands .. clicmd:: interface IFNAME +.. index:: interface IFNAME vrf VRF + +.. clicmd:: interface IFNAME vrf VRF + .. index:: shutdown .. clicmd:: shutdown @@ -351,6 +370,139 @@ default) should the specified gateways not be reachable. E.g.: .. _multicast-rib-commands: +VRF (Virtual Routing and Forwarding) +==================================== + +Currently, the user has the possibility to configure VRFs. 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, the user will however configure the +*Default VRF*. +On the *Zebra* context, this can be done when being in configuration mode, when +configuring a static route 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 by following command: :clicmd:`vrf VRF`. the user can enter +the same route command as before, but this time, the route command will apply to vrf +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 +.. clicmd:: ip route NETWORK NETMASK GATEWAY + + This command is based on VRF configuration mode or in configuration mode. If + applied on configuration mode, this applies to default VRF. Otherwise, this + command applies to the VRF of the vrf configuration mode. By default, above + command permits configuring a network in the table identifier of the mentioned + VRF. If :option:`-n` option is given, the network will be configured in the + default routing table of the *Linux network namespace*. + +.. 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 VRF configuration mode. There, this command is only + available with :option:`-n` command. This commands permits configuring a network + route in the given ``TABLENO`` of the *Linux network namespace*. + +.. 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``. + +.. 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 ====================== diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c index 6d74670514..d9e89357ca 100644 --- a/eigrpd/eigrp_hello.c +++ b/eigrpd/eigrp_hello.c @@ -630,7 +630,7 @@ static struct eigrp_packet *eigrp_hello_encode(struct eigrp_interface *ei, uint16_t length = EIGRP_HEADER_LEN; // allocate a new packet to be sent - ep = eigrp_packet_new(ei->ifp->mtu, NULL); + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), NULL); if (ep) { // encode common header feilds diff --git a/eigrpd/eigrp_macros.h b/eigrpd/eigrp_macros.h index eea7a26425..b30e19a867 100644 --- a/eigrpd/eigrp_macros.h +++ b/eigrpd/eigrp_macros.h @@ -35,6 +35,8 @@ //-------------------------------------------------------------------------- +#define EIGRP_PACKET_MTU(mtu) ((mtu) - (sizeof(struct ip))) + /* Topology Macros */ diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index 990d1dc08e..59864532cf 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -51,6 +51,7 @@ #include "eigrpd/eigrp_zebra.h" #include "eigrpd/eigrp_vty.h" #include "eigrpd/eigrp_dump.h" +#include "eigrpd/eigrp_macros.h" #include "eigrpd/eigrp_network.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" @@ -1088,7 +1089,7 @@ struct eigrp_packet *eigrp_packet_duplicate(struct eigrp_packet *old, { struct eigrp_packet *new; - new = eigrp_packet_new(nbr->ei->ifp->mtu, nbr); + new = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); new->length = old->length; new->retrans_counter = old->retrans_counter; new->dst = old->dst; diff --git a/eigrpd/eigrp_query.c b/eigrpd/eigrp_query.c index 00234bb35c..dd4231fa00 100644 --- a/eigrpd/eigrp_query.c +++ b/eigrpd/eigrp_query.c @@ -167,6 +167,7 @@ void eigrp_send_query(struct eigrp_interface *ei) struct eigrp_prefix_entry *pe; bool has_tlv = false; bool new_packet = true; + uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu); for (ALL_LIST_ELEMENTS(ei->eigrp->topology_changes_internalIPV4, node, nnode, pe)) { @@ -174,7 +175,7 @@ void eigrp_send_query(struct eigrp_interface *ei) continue; if (new_packet) { - ep = eigrp_packet_new(ei->ifp->mtu, NULL); + ep = eigrp_packet_new(eigrp_mtu, NULL); /* Prepare EIGRP INIT UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_QUERY, ei->eigrp, @@ -197,7 +198,7 @@ void eigrp_send_query(struct eigrp_interface *ei) listnode_add(pe->rij, nbr); } - if (length + EIGRP_TLV_MAX_IPV4_BYTE > (uint16_t)ei->ifp->mtu) { + if (length + EIGRP_TLV_MAX_IPV4_BYTE > eigrp_mtu) { if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) && ei->params.auth_keychain != NULL) { eigrp_make_md5_digest(ei, ep->s, diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c index a702c1fbd1..b7490cd492 100644 --- a/eigrpd/eigrp_reply.c +++ b/eigrpd/eigrp_reply.c @@ -85,7 +85,7 @@ void eigrp_send_reply(struct eigrp_neighbor *nbr, struct eigrp_prefix_entry *pe) * End of filtering */ - ep = eigrp_packet_new(ei->ifp->mtu, nbr); + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr); /* Prepare EIGRP INIT UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_REPLY, eigrp, ep->s, 0, diff --git a/eigrpd/eigrp_siaquery.c b/eigrpd/eigrp_siaquery.c index d398d75724..ff38325465 100644 --- a/eigrpd/eigrp_siaquery.c +++ b/eigrpd/eigrp_siaquery.c @@ -119,7 +119,7 @@ void eigrp_send_siaquery(struct eigrp_neighbor *nbr, struct eigrp_packet *ep; uint16_t length = EIGRP_HEADER_LEN; - ep = eigrp_packet_new(nbr->ei->ifp->mtu, nbr); + ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); /* Prepare EIGRP INIT UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_SIAQUERY, nbr->ei->eigrp, ep->s, 0, diff --git a/eigrpd/eigrp_siareply.c b/eigrpd/eigrp_siareply.c index 3b7a82b665..d3dd123f90 100644 --- a/eigrpd/eigrp_siareply.c +++ b/eigrpd/eigrp_siareply.c @@ -118,7 +118,7 @@ void eigrp_send_siareply(struct eigrp_neighbor *nbr, struct eigrp_packet *ep; uint16_t length = EIGRP_HEADER_LEN; - ep = eigrp_packet_new(nbr->ei->ifp->mtu, nbr); + ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); /* Prepare EIGRP INIT UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_SIAREPLY, nbr->ei->eigrp, ep->s, 0, diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c index 2d1bc46e6b..becb29a95f 100644 --- a/eigrpd/eigrp_topology.c +++ b/eigrpd/eigrp_topology.c @@ -443,17 +443,24 @@ void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest) struct eigrp *eigrp = eigrp_lookup(); for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) { - if (((uint64_t)entry->distance - <= (uint64_t)dest->distance * (uint64_t)eigrp->variance) - && entry->distance != EIGRP_MAX_METRIC) // is successor - { - entry->flags |= EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG; - entry->flags &= ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG; - } else if (entry->reported_distance - < dest->fdistance) // is feasible successor - { - entry->flags |= EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG; - entry->flags &= ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG; + if (entry->reported_distance < dest->fdistance) { + // is feasible successor, can be successor + if (((uint64_t)entry->distance + <= (uint64_t)dest->distance + * (uint64_t)eigrp->variance) + && entry->distance != EIGRP_MAX_METRIC) { + // is successor + entry->flags |= + EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG; + entry->flags &= + ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG; + } else { + // is feasible successor only + entry->flags |= + EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG; + entry->flags &= + ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG; + } } else { entry->flags &= ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG; entry->flags &= ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG; diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c index bd80ea366f..a3080136b5 100644 --- a/eigrpd/eigrp_update.c +++ b/eigrpd/eigrp_update.c @@ -420,7 +420,7 @@ void eigrp_update_send_init(struct eigrp_neighbor *nbr) struct eigrp_packet *ep; uint16_t length = EIGRP_HEADER_LEN; - ep = eigrp_packet_new(nbr->ei->ifp->mtu, nbr); + ep = eigrp_packet_new(EIGRP_PACKET_MTU(nbr->ei->ifp->mtu), nbr); /* Prepare EIGRP INIT UPDATE header */ if (IS_DEBUG_EIGRP_PACKET(0, RECV)) @@ -533,10 +533,10 @@ void eigrp_update_send_EOT(struct eigrp_neighbor *nbr) struct eigrp *eigrp = ei->eigrp; struct prefix *dest_addr; uint32_t seq_no = eigrp->sequence_number; - uint16_t mtu = ei->ifp->mtu; + uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu); struct route_node *rn; - ep = eigrp_packet_new(mtu, nbr); + ep = eigrp_packet_new(eigrp_mtu, nbr); /* Prepare EIGRP EOT UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, EIGRP_EOT_FLAG, @@ -557,13 +557,13 @@ void eigrp_update_send_EOT(struct eigrp_neighbor *nbr) if (eigrp_nbr_split_horizon_check(te, ei)) continue; - if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > mtu) { + if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) { eigrp_update_place_on_nbr_queue(nbr, ep, seq_no, length); seq_no++; length = EIGRP_HEADER_LEN; - ep = eigrp_packet_new(mtu, nbr); + ep = eigrp_packet_new(eigrp_mtu, nbr); eigrp_packet_header_init( EIGRP_OPC_UPDATE, nbr->ei->eigrp, ep->s, EIGRP_EOT_FLAG, seq_no, @@ -604,13 +604,14 @@ void eigrp_update_send(struct eigrp_interface *ei) struct eigrp *eigrp = ei->eigrp; struct prefix *dest_addr; uint32_t seq_no = eigrp->sequence_number; + uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu); if (ei->nbrs->count == 0) return; uint16_t length = EIGRP_HEADER_LEN; - ep = eigrp_packet_new(ei->ifp->mtu, NULL); + ep = eigrp_packet_new(eigrp_mtu, NULL); /* Prepare EIGRP INIT UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, 0, seq_no, 0); @@ -633,8 +634,7 @@ void eigrp_update_send(struct eigrp_interface *ei) if (eigrp_nbr_split_horizon_check(ne, ei)) continue; - if ((length + EIGRP_TLV_MAX_IPV4_BYTE) - > (uint16_t)ei->ifp->mtu) { + if ((length + EIGRP_TLV_MAX_IPV4_BYTE) > eigrp_mtu) { if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) && (ei->params.auth_keychain != NULL)) { eigrp_make_md5_digest(ei, ep->s, @@ -651,7 +651,7 @@ void eigrp_update_send(struct eigrp_interface *ei) eigrp_update_send_to_all_nbrs(ei, ep); length = EIGRP_HEADER_LEN; - ep = eigrp_packet_new(ei->ifp->mtu, NULL); + ep = eigrp_packet_new(eigrp_mtu, NULL); eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, 0, seq_no, 0); if ((ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) @@ -790,7 +790,7 @@ static void eigrp_update_send_GR_part(struct eigrp_neighbor *nbr) } } - ep = eigrp_packet_new(ei->ifp->mtu, nbr); + ep = eigrp_packet_new(EIGRP_PACKET_MTU(ei->ifp->mtu), nbr); /* Prepare EIGRP Graceful restart UPDATE header */ eigrp_packet_header_init(EIGRP_OPC_UPDATE, eigrp, ep->s, flags, 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 0720762da0..69e301fcfa 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"); @@ -1873,7 +1889,7 @@ DEFUN (config_hostname, { struct cmd_token *word = argv[1]; - if (!isalpha((int)word->arg[0])) { + if (!isalnum((int)word->arg[0])) { vty_out(vty, "Please specify string starting with alphabet\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -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,36 @@ 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) { + vty_out(vty, + "Please be aware that removing the password is a security risk and " + "you should think twice about this command\n"); + warned = true; + XFREE(MTYPE_HOST, host.password); + } + host.password = NULL; + + if (host.password_encrypt) { + if (!warned) + vty_out(vty, + "Please be aware that removing the password is a security risk " + "and you should think twice about this command\n"); + 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 +2041,24 @@ 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) { + vty_out(vty, + "Please be aware that removing the password is a security risk and " + "you should think twice about this command\n"); + warned = true; XFREE(MTYPE_HOST, host.enable); + } host.enable = NULL; - if (host.enable_encrypt) + if (host.enable_encrypt) { + if (!warned) + vty_out(vty, + "Please be aware that removing the password is a security risk " + "and you should think twice about this command\n"); XFREE(MTYPE_HOST, host.enable_encrypt); + } host.enable_encrypt = NULL; return CMD_SUCCESS; @@ -2304,7 +2362,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 +2388,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 +2407,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 +2419,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 +2775,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..9ba53e0907 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. */ 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/plist.c b/lib/plist.c index 01b55f9f1d..e1dac46a90 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -73,7 +73,7 @@ struct prefix_master { struct prefix_list_list str; /* Whether sequential number is used. */ - int seqnum; + bool seqnum; /* The latest update. */ struct prefix_list *recent; @@ -348,7 +348,7 @@ static void prefix_list_delete(struct prefix_list *plist) static struct prefix_list_entry * prefix_list_entry_make(struct prefix *prefix, enum prefix_list_type type, - int seq, int le, int ge, int any) + int64_t seq, int le, int ge, int any) { struct prefix_list_entry *pentry; @@ -381,10 +381,10 @@ void prefix_list_delete_hook(void (*func)(struct prefix_list *plist)) } /* Calculate new sequential number. */ -static int prefix_new_seq_get(struct prefix_list *plist) +static int64_t prefix_new_seq_get(struct prefix_list *plist) { - int maxseq; - int newseq; + int64_t maxseq; + int64_t newseq; struct prefix_list_entry *pentry; maxseq = newseq = 0; @@ -401,7 +401,7 @@ static int prefix_new_seq_get(struct prefix_list *plist) /* Return prefix list entry which has same seq number. */ static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist, - int seq) + int64_t seq) { struct prefix_list_entry *pentry; @@ -413,7 +413,8 @@ static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist, static struct prefix_list_entry * prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix, - enum prefix_list_type type, int seq, int le, int ge) + enum prefix_list_type type, int64_t seq, + int le, int ge) { struct prefix_list_entry *pentry; @@ -771,7 +772,7 @@ static void __attribute__((unused)) prefix_list_print(struct prefix_list *plist) p = &pentry->prefix; - printf(" seq %u %s %s/%d", pentry->seq, + printf(" seq %" PRId64 " %s %s/%d", pentry->seq, prefix_list_type_str(pentry), inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); @@ -793,7 +794,7 @@ prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new) size_t validbits = new->prefix.prefixlen; struct pltrie_table *table; struct prefix_list_entry *pentry; - int seq = 0; + int64_t seq = 0; if (new->seq == -1) seq = prefix_new_seq_get(plist); @@ -845,13 +846,13 @@ static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, struct prefix_list_entry *dup; struct prefix p, p_tmp; int any = 0; - int seqnum = -1; + int64_t seqnum = -1; int lenum = 0; int genum = 0; /* Sequential number. */ if (seq) - seqnum = atoi(seq); + seqnum = (int64_t)atol(seq); /* ge and le number */ if (ge) @@ -972,7 +973,7 @@ static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi, struct prefix_list *plist; struct prefix_list_entry *pentry; struct prefix p; - int seqnum = -1; + int64_t seqnum = -1; int lenum = 0; int genum = 0; @@ -998,7 +999,7 @@ static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi, /* Check sequence number. */ if (seq) - seqnum = atoi(seq); + seqnum = (int64_t)atol(seq); /* ge and le number */ if (ge) @@ -1113,7 +1114,7 @@ static void vty_show_prefix_entry(struct vty *vty, afi_t afi, vty_out(vty, " Description: %s\n", plist->desc); vty_out(vty, - " count: %d, range entries: %d, sequences: %u - %u\n", + " count: %d, range entries: %d, sequences: %" PRId64 " - %" PRId64 "\n", plist->count, plist->rangecount, plist->head ? plist->head->seq : 0, plist->tail ? plist->tail->seq : 0); @@ -1128,7 +1129,7 @@ static void vty_show_prefix_entry(struct vty *vty, afi_t afi, vty_out(vty, " "); if (master->seqnum) - vty_out(vty, "seq %u ", pentry->seq); + vty_out(vty, "seq %" PRId64 " ", pentry->seq); vty_out(vty, "%s ", prefix_list_type_str(pentry)); @@ -1164,14 +1165,14 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, { struct prefix_list *plist; struct prefix_master *master; - int seqnum = 0; + int64_t seqnum = 0; master = prefix_master_get(afi, 0); if (master == NULL) return CMD_WARNING; if (seq) - seqnum = atoi(seq); + seqnum = (int64_t)atol(seq); if (name) { plist = prefix_list_lookup(afi, name); @@ -1236,7 +1237,7 @@ static int vty_show_prefix_list_prefix(struct vty *vty, afi_t afi, } if (match) { - vty_out(vty, " seq %u %s ", pentry->seq, + vty_out(vty, " seq %" PRId64 " %s ", pentry->seq, prefix_list_type_str(pentry)); if (pentry->any) @@ -1387,7 +1388,7 @@ DEFPY (ip_prefix_list_sequence_number, PREFIX_LIST_STR "Include/exclude sequence numbers in NVGEN\n") { - prefix_master_ipv4.seqnum = no ? 0 : 1; + prefix_master_ipv4.seqnum = no ? false : true; return CMD_SUCCESS; } @@ -1581,7 +1582,7 @@ DEFPY (ipv6_prefix_list_sequence_number, PREFIX_LIST_STR "Include/exclude sequence numbers in NVGEN\n") { - prefix_master_ipv6.seqnum = no ? 0 : 1; + prefix_master_ipv6.seqnum = no ? false : true; return CMD_SUCCESS; } @@ -1744,7 +1745,7 @@ static int config_write_prefix_afi(afi_t afi, struct vty *vty) afi == AFI_IP ? "" : "v6", plist->name); if (master->seqnum) - vty_out(vty, "seq %u ", pentry->seq); + vty_out(vty, "seq %" PRId64 " ", pentry->seq); vty_out(vty, "%s ", prefix_list_type_str(pentry)); @@ -1783,7 +1784,7 @@ static int config_write_prefix_afi(afi_t afi, struct vty *vty) afi == AFI_IP ? "" : "v6", plist->name); if (master->seqnum) - vty_out(vty, "seq %u ", pentry->seq); + vty_out(vty, "seq %" PRId64 " ", pentry->seq); vty_out(vty, "%s", prefix_list_type_str(pentry)); @@ -1959,7 +1960,8 @@ int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name, struct prefix *p = &pentry->prefix; char buf[BUFSIZ]; - vty_out(vty, " seq %u %s %s/%d", pentry->seq, + vty_out(vty, " seq %" PRId64 " %s %s/%d", + pentry->seq, prefix_list_type_str(pentry), inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); diff --git a/lib/plist_int.h b/lib/plist_int.h index aa81a3bce2..6bc2d034d6 100644 --- a/lib/plist_int.h +++ b/lib/plist_int.h @@ -48,7 +48,7 @@ struct prefix_list { /* Each prefix-list's entry. */ struct prefix_list_entry { - int seq; + int64_t seq; int le; int ge; 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/routemap.c b/lib/routemap.c index ea61043a8d..892b19dac5 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -722,7 +722,7 @@ static void route_map_delete(struct route_map *map) /* Clear all dependencies */ route_map_clear_all_references(name); - map->deleted = 1; + map->deleted = true; /* Execute deletion hook. */ if (route_map_master.delete_hook) { (*route_map_master.delete_hook)(name); @@ -762,19 +762,19 @@ int route_map_mark_updated(const char *name, int del_later) map = route_map_lookup_by_name(name); - /* If we did not find the routemap with deleted=0 try again - * with deleted=1 + /* If we did not find the routemap with deleted=false try again + * with deleted=true */ if (!map) { memset(&tmp_map, 0, sizeof(struct route_map)); tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name); - tmp_map.deleted = 1; + tmp_map.deleted = true; map = hash_lookup(route_map_master_hash, &tmp_map); XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name); } if (map) { - map->to_be_processed = 1; + map->to_be_processed = true; ret = 0; } @@ -786,7 +786,7 @@ int route_map_clear_updated(struct route_map *map) int ret = -1; if (map) { - map->to_be_processed = 0; + map->to_be_processed = false; if (map->deleted) route_map_free_map(map); } @@ -2743,7 +2743,7 @@ void route_map_finish(void) /* cleanup route_map */ while (route_map_master.head) { struct route_map *map = route_map_master.head; - map->to_be_processed = 0; + map->to_be_processed = false; route_map_delete(map); } diff --git a/lib/routemap.h b/lib/routemap.h index 0046b77c46..990c7fa72f 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -158,8 +158,8 @@ struct route_map { struct route_map *prev; /* Maintain update info */ - int to_be_processed; /* True if modification isn't acted on yet */ - int deleted; /* If 1, then this node will be deleted */ + bool to_be_processed; /* True if modification isn't acted on yet */ + bool deleted; /* If 1, then this node will be deleted */ QOBJ_FIELDS }; diff --git a/lib/zclient.c b/lib/zclient.c index dc27cbef70..05bd907589 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)); } } @@ -1374,6 +1380,26 @@ stream_failure: return false; } +bool zapi_iptable_notify_decode(struct stream *s, + uint32_t *unique, + enum zapi_iptable_notify_owner *note) +{ + uint32_t uni; + + STREAM_GET(note, s, sizeof(*note)); + + STREAM_GETL(s, uni); + + if (zclient_debug) + zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); + *unique = uni; + + return true; + +stream_failure: + return false; +} + struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) { struct nexthop *n = nexthop_new(); @@ -2765,6 +2791,22 @@ static int zclient_read(struct thread *thread) (*zclient->label_chunk)(command, zclient, length, vrf_id); break; + case ZEBRA_IPSET_NOTIFY_OWNER: + if (zclient->ipset_notify_owner) + (*zclient->ipset_notify_owner)(command, zclient, length, + vrf_id); + break; + case ZEBRA_IPSET_ENTRY_NOTIFY_OWNER: + if (zclient->ipset_entry_notify_owner) + (*zclient->ipset_entry_notify_owner)(command, + zclient, length, + vrf_id); + break; + case ZEBRA_IPTABLE_NOTIFY_OWNER: + if (zclient->iptable_notify_owner) + (*zclient->iptable_notify_owner)(command, + zclient, length, + vrf_id); default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index 71f5b38384..c5eaf9c0fd 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -258,6 +258,10 @@ struct zclient { struct zclient *zclient, uint16_t length, vrf_id_t vrf_id); + int (*iptable_notify_owner)(int command, + struct zclient *zclient, + uint16_t length, + vrf_id_t vrf_id); }; /* Zebra API message flag. */ @@ -298,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; }; /* @@ -338,8 +344,6 @@ struct zapi_route { vrf_id_t vrf_id; uint32_t tableid; - - struct ethaddr rmac; }; /* Zebra IPv4 route message API. */ @@ -680,6 +684,9 @@ bool zapi_ipset_entry_notify_decode(struct stream *s, uint32_t *unique, char *ipset_name, enum zapi_ipset_entry_notify_owner *note); +bool zapi_iptable_notify_decode(struct stream *s, + uint32_t *unique, + enum zapi_iptable_notify_owner *note); extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); extern bool zapi_nexthop_update_decode(struct stream *s, 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_intra.c b/ospf6d/ospf6_intra.c index de4ee2e1ac..26e6deadae 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1314,17 +1314,60 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread) return 0; } +static void ospf6_intra_prefix_update_route_origin(struct ospf6_route *oa_route) +{ + struct ospf6_path *h_path; + struct ospf6_route *g_route, *nroute; + + /* Update Global ospf6 route path */ + g_route = ospf6_route_lookup(&oa_route->prefix, + ospf6->route_table); + + for (ospf6_route_lock(g_route); g_route && + ospf6_route_is_prefix(&oa_route->prefix, g_route); + g_route = nroute) { + nroute = ospf6_route_next(g_route); + if (g_route->type != oa_route->type) + continue; + if (g_route->path.area_id != oa_route->path.area_id) + continue; + if (g_route->path.type != OSPF6_PATH_TYPE_INTRA) + continue; + if (g_route->path.cost != oa_route->path.cost) + continue; + + if (ospf6_route_is_same_origin(g_route, oa_route)) { + h_path = (struct ospf6_path *)listgetdata( + listhead(g_route->paths)); + g_route->path.origin.type = h_path->origin.type; + g_route->path.origin.id = h_path->origin.id; + g_route->path.origin.adv_router = + h_path->origin.adv_router; + break; + } + } + + h_path = (struct ospf6_path *)listgetdata( + listhead(oa_route->paths)); + oa_route->path.origin.type = h_path->origin.type; + oa_route->path.origin.id = h_path->origin.id; + oa_route->path.origin.adv_router = h_path->origin.adv_router; +} + void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, struct ospf6_route *old, struct ospf6_route *route) { - struct ospf6_route *old_route; + struct ospf6_route *old_route, *ls_entry; struct ospf6_path *ecmp_path, *o_path = NULL; struct listnode *anode, *anext; struct listnode *nnode, *rnode, *rnext; struct ospf6_nexthop *nh, *rnh; char buf[PREFIX2STR_BUFFER]; bool route_found = false; + struct interface *ifp; + struct ospf6_lsa *lsa; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; /* check for old entry match with new route origin, * delete old entry. @@ -1361,7 +1404,7 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, o_path->cost, route->path.cost); } - /* Remove selected current rout path's nh from + /* Remove selected current path's nh from * effective nh list. */ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) { @@ -1385,22 +1428,6 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, * Update FIB with effective NHs. */ if (listcount(old_route->paths)) { - if (old_route->path.origin.id == - route->path.origin.id && - old_route->path.origin.adv_router == - route->path.origin.adv_router) { - struct ospf6_path *h_path; - - h_path = (struct ospf6_path *) - listgetdata(listhead(old_route->paths)); - old_route->path.origin.type = - h_path->origin.type; - old_route->path.origin.id = - h_path->origin.id; - 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)) { @@ -1415,6 +1442,14 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, if (oa->route_table->hook_add) (*oa->route_table->hook_add) (old_route); + + if (old_route->path.origin.id == + route->path.origin.id && + old_route->path.origin.adv_router == + route->path.origin.adv_router) { + ospf6_intra_prefix_update_route_origin( + old_route); + } break; } } else { @@ -1426,8 +1461,12 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, old_route->path.cost, route->path.cost); } - ospf6_route_remove(old_route, + if (oa->route_table->hook_remove) + ospf6_route_remove(old_route, oa->route_table); + else + SET_FLAG(old_route->flag, + OSPF6_ROUTE_REMOVE); break; } } @@ -1467,72 +1506,101 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, /* 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); /* Add the new path to route's path list */ listnode_add_sort(old_route->paths, ecmp_path); - UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE); - SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE); - /* Update RIB/FIB */ - if (oa->route_table->hook_add) - (*oa->route_table->hook_add) - (old_route); if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s: route %s %p another path added with nh %u, effective paths %u nh %u", + zlog_debug( + "%s: route %s %p another path added with nh %u, effective paths %u nh %u", __PRETTY_FUNCTION__, buf, (void *)old_route, listcount(ecmp_path->nh_list), old_route->paths ? - listcount(old_route->paths) - : 0, + listcount(old_route->paths) : 0, 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); - } } + } else { 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); + } + + list_delete_all_node(old_route->nh_list); - if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { - prefix2str(&route->prefix, - buf, sizeof(buf)); - zlog_debug("%s: existing route %s %p with effective paths %u nh count %u", - __PRETTY_FUNCTION__, buf, - (void *)old_route, - listcount(old_route->paths), - old_route->nh_list ? - listcount(old_route->nh_list) - : 0); + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + ls_entry = ospf6_route_lookup( + &o_path->ls_prefix, + oa->spf_table); + if (ls_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("%s: ls_prfix %s ls_entry not found.", + __PRETTY_FUNCTION__, + buf); + continue; } + lsa = ospf6_lsdb_lookup(o_path->origin.type, + o_path->origin.id, + o_path->origin.adv_router, + oa->lsdb); + if (lsa == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN( + INTRA_PREFIX)) { + struct prefix adv_prefix; - UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE); - SET_FLAG(old_route->flag, OSPF6_ROUTE_CHANGE); - /* Update ospf6 route table and RIB/FIB */ - if (oa->route_table->hook_add) - (*oa->route_table->hook_add) - (old_route); + ospf6_linkstate_prefix( + o_path->origin.adv_router, + o_path->origin.id, &adv_prefix); + prefix2str(&adv_prefix, buf, + sizeof(buf)); + zlog_debug("%s: adv_router %s lsa not found", + __PRETTY_FUNCTION__, + buf); + } + continue; + } + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa *) + OSPF6_LSA_HEADER_END(lsa->header); + + if (intra_prefix_lsa->ref_adv_router + == oa->ospf6->router_id) { + ifp = if_lookup_prefix( + &old_route->prefix, + VRF_DEFAULT); + if (ifp) + ospf6_route_add_nexthop( + old_route, + ifp->ifindex, + NULL); + } else { + ospf6_route_merge_nexthops(old_route, + ls_entry); + } + } + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: route %s %p with final effective paths %u nh%u", + __PRETTY_FUNCTION__, buf, + (void *)old_route, + old_route->paths ? + listcount(old_route->paths) : 0, + listcount(old_route->nh_list)); } + + /* used in intra_route_calculation() to add to + * global ospf6 route table. + */ + UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE); + SET_FLAG(old_route->flag, OSPF6_ROUTE_ADD); + /* Update ospf6 route table and RIB/FIB */ + if (oa->route_table->hook_add) + (*oa->route_table->hook_add)(old_route); /* Delete the new route its info added to existing * route. */ @@ -1642,7 +1710,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) route->path.metric_type = 1; route->path.cost = ls_entry->path.cost + ntohs(op->prefix_metric); - + memcpy(&route->path.ls_prefix, &ls_prefix, + sizeof(struct prefix)); if (direct_connect) { ifp = if_lookup_prefix(&route->prefix, VRF_DEFAULT); if (ifp) @@ -1660,20 +1729,21 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) if (old && (ospf6_route_cmp(route, old) == 0)) { if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug(" Update route: %s old cost %u new cost %u nh count %u paths %u", - buf, + zlog_debug("%s Update route: %s old cost %u new cost %u paths %u nh %u", + __PRETTY_FUNCTION__, buf, old->path.cost, route->path.cost, - listcount(route->nh_list), - listcount(route->paths)); + listcount(route->paths), + listcount(route->nh_list)); } ospf6_intra_prefix_route_ecmp_path(oa, old, route); } else { if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug(" route %s add with cost %u nh %u paths %u", - buf, route->path.cost, - listcount(route->nh_list), - listcount(route->paths)); + zlog_debug("%s route %s add with cost %u paths %u nh %u", + __PRETTY_FUNCTION__, buf, + route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); } ospf6_route_add(route, oa->route_table); } @@ -1684,12 +1754,102 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) zlog_debug("Trailing garbage ignored"); } +static void ospf6_intra_prefix_lsa_remove_update_route(struct ospf6_lsa *lsa, + struct ospf6_area *oa, + struct ospf6_route *route) +{ + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + struct ospf6_path *o_path; + bool nh_updated = false; + char buf[PREFIX2STR_BUFFER]; + + /* Iterate all paths of route to find maching + * with LSA remove info. + * If route->path is same, replace + * from paths list. + */ + for (ALL_LIST_ELEMENTS(route->paths, anode, anext, o_path)) { + if ((o_path->origin.type != lsa->header->type) || + (o_path->origin.adv_router != lsa->header->adv_router) || + (o_path->origin.id != lsa->header->id)) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug( + "%s: route %s path found with cost %u nh %u to remove.", + __PRETTY_FUNCTION__, buf, o_path->cost, + listcount(o_path->nh_list)); + } + + /* Remove found path's nh_list from + * the route's nh_list. + */ + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) { + for (ALL_LIST_ELEMENTS(route->nh_list, rnode, + rnext, rnh)) { + if (!ospf6_nexthop_is_same(rnh, nh)) + continue; + listnode_delete(route->nh_list, rnh); + ospf6_nexthop_delete(rnh); + } + } + /* Delete the path from route's + * path list + */ + listnode_delete(route->paths, o_path); + ospf6_path_free(o_path); + nh_updated = true; + break; + } + + if (nh_updated) { + /* Iterate all paths and merge nexthop, + * unlesss any of the nexthop similar to + * ones deleted as part of path deletion. + */ + for (ALL_LIST_ELEMENTS(route->paths, anode, anext, o_path)) + ospf6_merge_nexthops(route->nh_list, o_path->nh_list); + + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: route %s update paths %u nh %u", + __PRETTY_FUNCTION__, buf, + route->paths ? listcount(route->paths) : 0, + route->nh_list ? listcount(route->nh_list) + : 0); + } + + /* Update Global Route table and + * RIB/FIB with effective + * nh_list + */ + if (oa->route_table->hook_add) + (*oa->route_table->hook_add)(route); + + /* route's primary path is similar + * to LSA, replace route's primary + * path with route's paths list + * head. + */ + if ((route->path.origin.id == lsa->header->id) && + (route->path.origin.adv_router == + lsa->header->adv_router)) { + ospf6_intra_prefix_update_route_origin(route); + } + } + +} + void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) { struct ospf6_area *oa; struct ospf6_intra_prefix_lsa *intra_prefix_lsa; struct prefix prefix; - struct ospf6_route *route, *nroute, *route_to_del; + struct ospf6_route *route, *nroute; int prefix_num; struct ospf6_prefix *op; char *start, *current, *end; @@ -1717,22 +1877,6 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) break; prefix_num--; - route_to_del = ospf6_route_create(); - - memset(&route_to_del->prefix, 0, sizeof(struct prefix)); - route_to_del->prefix.family = AF_INET6; - route_to_del->prefix.prefixlen = op->prefix_length; - ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, op); - - route_to_del->type = OSPF6_DEST_TYPE_NETWORK; - route_to_del->path.origin.type = lsa->header->type; - route_to_del->path.origin.id = lsa->header->id; - route_to_del->path.origin.adv_router = lsa->header->adv_router; - route_to_del->path.prefix_options = op->prefix_options; - route_to_del->path.area_id = oa->area_id; - route_to_del->path.type = OSPF6_PATH_TYPE_INTRA; - route_to_del->path.metric_type = 1; - memset(&prefix, 0, sizeof(struct prefix)); prefix.family = AF_INET6; prefix.prefixlen = op->prefix_length; @@ -1757,134 +1901,8 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) * after removal of one of the path. */ if (listcount(route->paths) > 1) { - struct listnode *anode, *anext; - struct listnode *nnode, *rnode, *rnext; - struct ospf6_nexthop *nh, *rnh; - struct ospf6_path *o_path; - bool nh_updated = false; - - /* Iterate all paths of route to find maching - * with LSA remove info. - * If route->path is same, replace - * from paths list. - */ - for (ALL_LIST_ELEMENTS(route->paths, anode, - anext, o_path)) { - if ((o_path->origin.type != - lsa->header->type) || - (o_path->origin.adv_router != - lsa->header->adv_router) || - (o_path->origin.id != - lsa->header->id)) - continue; - - if (IS_OSPF6_DEBUG_EXAMIN - (INTRA_PREFIX)) { - prefix2str(&prefix, buf, - sizeof(buf)); - zlog_debug( - "%s: route %s path found with cost %u nh %u to remove.", - __PRETTY_FUNCTION__, - buf, o_path->cost, - listcount( - o_path->nh_list)); - } - /* Remove old route from global - * ospf6 route table. - * nh_update section will add - * back with effective nh. - */ - if (oa->route_table->hook_remove) - (*oa->route_table->hook_remove) - (route); - /* Remove found path's nh_list from - * the route's nh_list. - */ - for (ALL_LIST_ELEMENTS_RO( - o_path->nh_list, - nnode, nh)) { - for (ALL_LIST_ELEMENTS( - route->nh_list, - rnode, rnext, rnh)) { - if ( - !ospf6_nexthop_is_same( - rnh, nh)) - continue; - listnode_delete( - route->nh_list, - rnh); - ospf6_nexthop_delete( - rnh); - } - } - /* Delete the path from route's - * path list - */ - listnode_delete(route->paths, o_path); - ospf6_path_free(o_path); - nh_updated = true; - break; - } - - if (nh_updated) { - - /* Iterate all paths and merge nexthop, - * unlesss any of the nexthop similar to - * ones deleted as part of path - * deletion. - */ - for (ALL_LIST_ELEMENTS(route->paths, - anode, anext, o_path)) { - ospf6_merge_nexthops( - route->nh_list, - o_path->nh_list); - } - - if (IS_OSPF6_DEBUG_EXAMIN( - INTRA_PREFIX)) { - prefix2str(&route->prefix, buf, - sizeof(buf)); - assert(route->nh_list); - zlog_debug("%s: route %s update paths %u nh %u" - , __PRETTY_FUNCTION__, - buf, - listcount(route->paths), - listcount( - route->nh_list)); - } - - /* route's primary path is similar - * to LSA, replace route's primary - * path with route's paths list - * head. - */ - if ((route->path.origin.id == - lsa->header->id) && - (route->path.origin.adv_router - == lsa->header->adv_router)) { - struct ospf6_path *h_path; - - h_path = (struct ospf6_path *) - listgetdata(listhead( - route->paths)); - route->path.origin.type = - h_path->origin.type; - route->path.origin.id = - h_path->origin.id; - route->path.origin.adv_router = - h_path->origin.adv_router; - } - - /* Update Global Route table and - * RIB/FIB with effective - * nh_list - */ - if (oa->route_table->hook_add) - (*oa->route_table->hook_add) - (route); - } - continue; - + ospf6_intra_prefix_lsa_remove_update_route( + lsa, oa, route); } else { if (route->path.origin.type != lsa->header->type @@ -1896,9 +1914,11 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("route remove %s with path %u cost %u nh %u", - buf, route->path.type, + zlog_debug("%s: route remove %s with path type %u cost %u paths %u nh %u", + __PRETTY_FUNCTION__, buf, + route->path.type, route->path.cost, + listcount(route->paths), listcount(route->nh_list)); } ospf6_route_remove(route, oa->route_table); @@ -1906,8 +1926,6 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) } if (route) ospf6_route_unlock(route); - - ospf6_route_delete(route_to_del); } if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) @@ -2029,8 +2047,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; @@ -2096,6 +2116,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 @@ -2104,6 +2125,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 { @@ -2155,8 +2178,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) @@ -2166,8 +2195,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) @@ -2187,11 +2217,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 8be00d9b41..15d8eb6cf2 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -611,9 +611,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route, prefix2str(&route->prefix, buf, sizeof(buf)); if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) - zlog_debug("%s %p: route add %p: %s", + zlog_debug("%s %p: route add %p: %s paths %u nh %u", ospf6_route_table_name(table), (void *)table, - (void *)route, buf); + (void *)route, buf, listcount(route->paths), + listcount(route->nh_list)); else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) zlog_debug("%s: route add: %s", ospf6_route_table_name(table), buf); @@ -664,11 +665,13 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route, if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) zlog_debug( - "%s %p: route add %p cost %u nh %u: update of %p old cost %u nh %u", + "%s %p: route add %p cost %u paths %u nh %u: update of %p cost %u paths %u nh %u", ospf6_route_table_name(table), (void *)table, (void *)route, route->path.cost, + listcount(route->paths), listcount(route->nh_list), (void *)old, - old->path.cost, listcount(old->nh_list)); + old->path.cost, listcount(old->paths), + listcount(old->nh_list)); else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) zlog_debug("%s: route add: update", ospf6_route_table_name(table)); @@ -922,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_route.h b/ospf6d/ospf6_route.h index a69e9a920f..02002533e6 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -91,6 +91,9 @@ struct ospf6_path { /* Cost */ uint8_t metric_type; uint32_t cost; + + struct prefix ls_prefix; + union { uint32_t cost_e2; uint32_t cost_config; 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/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 1ccf3ebffa..5be96e86d0 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -470,6 +470,18 @@ 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) { @@ -485,7 +497,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, + pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_MAP_NAMELEN - 10, pbrms->seqno, find.name); if (!pbrms->internal_nhg_name) pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 03f2104835..b7d2b1a928 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[100]; + char buf[PBR_MAP_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, + PBR_MAP_NAMELEN - 10, 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 ff7238ae97..5996a3ac96 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -1547,27 +1547,20 @@ int pim_if_connected_to_source(struct interface *ifp, struct in_addr src) return 0; } -int pim_if_is_loopback(struct pim_instance *pim, struct interface *ifp) +bool pim_if_is_loopback(struct interface *ifp) { - if (if_is_loopback(ifp)) - return 1; - - if (strcmp(ifp->name, pim->vrf->name) == 0) - return 1; + if (if_is_loopback(ifp) || if_is_vrf(ifp)) + return true; - return 0; + return false; } -int pim_if_is_vrf_device(struct interface *ifp) +bool pim_if_is_vrf_device(struct interface *ifp) { - struct vrf *vrf; + if (if_is_vrf(ifp)) + return true; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (strncmp(ifp->name, vrf->name, strlen(ifp->name)) == 0) - return 1; - } - - return 0; + return false; } int pim_if_ifchannel_count(struct pim_interface *pim_ifp) diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 5ecd07d227..cf025cbd4a 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -207,9 +207,9 @@ void pim_if_create_pimreg(struct pim_instance *pim); int pim_if_connected_to_source(struct interface *ifp, struct in_addr src); int pim_update_source_set(struct interface *ifp, struct in_addr source); -int pim_if_is_loopback(struct pim_instance *pim, struct interface *ifp); +bool pim_if_is_loopback(struct interface *ifp); -int pim_if_is_vrf_device(struct interface *ifp); +bool pim_if_is_vrf_device(struct interface *ifp); int pim_if_ifchannel_count(struct pim_interface *pim_ifp); #endif /* PIM_IFACE_H */ 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_msdp.c b/pimd/pim_msdp.c index 53a3382987..829c917b0f 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -228,6 +228,8 @@ static void pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa, /* release all mem associated with a sa */ static void pim_msdp_sa_free(struct pim_msdp_sa *sa) { + pim_msdp_sa_state_timer_setup(sa, false); + XFREE(MTYPE_PIM_MSDP_SA, sa); } @@ -1170,6 +1172,13 @@ enum pim_msdp_err pim_msdp_peer_add(struct pim_instance *pim, /* release all mem associated with a peer */ 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); + if (mp->ibuf) { stream_free(mp->ibuf); } @@ -1611,6 +1620,8 @@ void pim_msdp_init(struct pim_instance *pim, struct thread_master *master) /* counterpart to MSDP init; XXX: unused currently */ void pim_msdp_exit(struct pim_instance *pim) { + pim_msdp_sa_adv_timer_setup(pim, false); + /* XXX: stop listener and delete all peer sessions */ if (pim->msdp.peer_hash) { diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index ffe5d52a15..de09b070f4 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -653,7 +653,7 @@ static int pim_hello_send(struct interface *ifp, uint16_t holdtime) { struct pim_interface *pim_ifp = ifp->info; - if (pim_if_is_loopback(pim_ifp->pim, ifp)) + if (pim_if_is_loopback(ifp)) return 0; if (hello_send(ifp, holdtime)) { @@ -755,7 +755,7 @@ void pim_hello_restart_triggered(struct interface *ifp) /* * No need to ever start loopback or vrf device hello's */ - if (pim_if_is_loopback(pim_ifp->pim, ifp)) + if (pim_if_is_loopback(ifp)) return; /* 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.service b/redhat/frr.service index cc6ec429a3..3ae0aabfe2 100644 --- a/redhat/frr.service +++ b/redhat/frr.service @@ -9,7 +9,7 @@ Type=forking NotifyAccess=all StartLimitInterval=3m StartLimitBurst=3 -TimeoutSec=1m +TimeoutSec=2m WatchdogSec=60s RestartSec=5 Restart=on-abnormal diff --git a/snapcraft/defaults/babeld.conf.default b/snapcraft/defaults/babeld.conf.default new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/snapcraft/defaults/babeld.conf.default diff --git a/snapcraft/defaults/eigrpd.conf.default b/snapcraft/defaults/eigrpd.conf.default new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/snapcraft/defaults/eigrpd.conf.default diff --git a/snapcraft/defaults/pbrd.conf.default b/snapcraft/defaults/pbrd.conf.default new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/snapcraft/defaults/pbrd.conf.default diff --git a/snapcraft/scripts/Makefile b/snapcraft/scripts/Makefile index 966623d11d..110f4b2878 100644 --- a/snapcraft/scripts/Makefile +++ b/snapcraft/scripts/Makefile @@ -12,6 +12,9 @@ install: install -D -m 0755 pimd-service $(DESTDIR)/bin/ install -D -m 0755 ldpd-service $(DESTDIR)/bin/ install -D -m 0755 nhrpd-service $(DESTDIR)/bin/ + install -D -m 0755 babeld-service $(DESTDIR)/bin/ + install -D -m 0755 eigrpd-service $(DESTDIR)/bin/ + install -D -m 0755 pbrd-service $(DESTDIR)/bin/ install -D -m 0755 set-options $(DESTDIR)/bin/ install -D -m 0755 show_version $(DESTDIR)/bin/ diff --git a/snapcraft/scripts/babeld-service b/snapcraft/scripts/babeld-service new file mode 100644 index 0000000000..9e022f8569 --- /dev/null +++ b/snapcraft/scripts/babeld-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/babeld.conf ]; then + cp $SNAP/etc/frr/babeld.conf.default $SNAP_DATA/babeld.conf +fi +exec $SNAP/sbin/babeld \ + -f $SNAP_DATA/babeld.conf \ + --pid_file $SNAP_DATA/babeld.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/scripts/eigrpd-service b/snapcraft/scripts/eigrpd-service new file mode 100644 index 0000000000..fe945e5f7d --- /dev/null +++ b/snapcraft/scripts/eigrpd-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/eigrpd.conf ]; then + cp $SNAP/etc/frr/eigrpd.conf.default $SNAP_DATA/eigrpd.conf +fi +exec $SNAP/sbin/eigrpd \ + -f $SNAP_DATA/eigrpd.conf \ + --pid_file $SNAP_DATA/eigrpd.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/scripts/pbrd-service b/snapcraft/scripts/pbrd-service new file mode 100644 index 0000000000..a9265a1ae6 --- /dev/null +++ b/snapcraft/scripts/pbrd-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/pbrd.conf ]; then + cp $SNAP/etc/frr/pbrd.conf.default $SNAP_DATA/pbrd.conf +fi +exec $SNAP/sbin/pbrd \ + -f $SNAP_DATA/pbrd.conf \ + --pid_file $SNAP_DATA/pbrd.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index 17fabb16f8..b4e1812c86 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -4,7 +4,8 @@ summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP routing daemon description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM routing daemon FRRouting (FRR) is free software which manages TCP/IP based routing protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2, - RIPng, PIM and LDP as well as the IPv6 versions of these. + RIPng, PIM, LDP, Babel, EIGRP and PBR (Policy-based routing) as well as + the IPv6 versions of these. FRRouting (frr) is a fork of Quagga. confinement: strict grade: devel @@ -91,6 +92,27 @@ apps: - network - network-bind - network-control + babeld: + command: bin/babeld-service + daemon: simple + plugs: + - network + - network-bind + - network-control + eigrpd: + command: bin/eigrpd-service + daemon: simple + plugs: + - network + - network-bind + - network-control + pbrd: + command: bin/pbrd-service + daemon: simple + plugs: + - network + - network-bind + - network-control set: command: bin/set-options zebra-debug: @@ -153,7 +175,25 @@ apps: - network - network-bind - network-control - + babeld-debug: + command: sbin/babeld -f $SNAP_DATA/babeld.conf --pid_file $SNAP_DATA/babeld.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control + eigrpd-debug: + command: sbin/eigrpd -f $SNAP_DATA/eigrpd.conf --pid_file $SNAP_DATA/eigrpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control + pbrd-debug: + command: sbin/pbrd -f $SNAP_DATA/pbrd.conf --pid_file $SNAP_DATA/pbrd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control + parts: frr: build-packages: @@ -190,6 +230,9 @@ parts: - libtinfo5 - libreadline6 - libjson-c2 + - libc-ares2 + - libatm1 + - libprotobuf-c1 plugin: autotools source: ../frr-@PACKAGE_VERSION@.tar.gz configflags: @@ -228,6 +271,9 @@ parts: ripngd.conf.default: etc/frr/ripngd.conf.default ldpd.conf.default: etc/frr/ldpd.conf.default nhrpd.conf.default: etc/frr/nhrpd.conf.default + babeld.conf.default: etc/frr/babeld.conf.default + eigrpd.conf.default: etc/frr/eigrpd.conf.default + pbrd.conf.default: etc/frr/pbrd.conf.default vtysh.conf.default: etc/frr/vtysh.conf.default frr-scripts: plugin: make 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/tools/etc/iproute2/rt_protos.d/frr.conf b/tools/etc/iproute2/rt_protos.d/frr.conf index b8d4c1c03b..cac75bdfba 100644 --- a/tools/etc/iproute2/rt_protos.d/frr.conf +++ b/tools/etc/iproute2/rt_protos.d/frr.conf @@ -8,4 +8,5 @@ 191 nhrp 192 eigrp 193 ldp -194 sharp
\ No newline at end of file +194 sharp +195 pbr @@ -552,16 +552,19 @@ case "$1" in # Additionally if a new protocol is added # we need to add it here as well as # in rt_netlink.h( follow the directions! ) + ip route flush proto 4 + ip route flush proto 11 + ip route flush proto 42 ip route flush proto 186 + ip route flush proto 187 ip route flush proto 188 - ip route flush proto 4 ip route flush proto 189 ip route flush proto 190 - ip route flush proto 11 - ip route flush proto 187 - ip route flush proto 192 - ip route flush proto 42 ip route flush proto 191 + ip route flush proto 192 + ip route flush proto 193 + ip route flush proto 194 + ip route flush proto 195 else [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr diff --git a/tools/frr.service b/tools/frr.service index 8800bf6b0f..5f44274ec3 100644 --- a/tools/frr.service +++ b/tools/frr.service @@ -10,7 +10,7 @@ Type=forking NotifyAccess=all StartLimitInterval=3m StartLimitBurst=3 -TimeoutSec=1m +TimeoutSec=2m WatchdogSec=60s RestartSec=5 Restart=on-abnormal diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 52641de72c..d82f9fd1b8 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -54,6 +54,7 @@ vtysh_scan += $(top_srcdir)/bgpd/bgp_nexthop.c vtysh_scan += $(top_srcdir)/bgpd/bgp_route.c vtysh_scan += $(top_srcdir)/bgpd/bgp_routemap.c vtysh_scan += $(top_srcdir)/bgpd/bgp_vty.c +vtysh_scan += $(top_srcdir)/bgpd/bgp_flowspec_vty.c endif if RPKI diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index cdc047e5ae..01ba007767 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1537,7 +1537,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 +1547,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 +2361,20 @@ 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") +{ + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ALL, vtysh_config_enable_password, vtysh_enable_password_cmd, "enable password [(8-8)] LINE", "Modify enable password parameters\n" @@ -2611,20 +2618,25 @@ DEFUN (vtysh_write_memory, /* If integrated frr.conf explicitely set. */ if (want_config_integrated()) { ret = CMD_WARNING_CONFIG_FAILED; + + /* first attempt to use watchfrr if it's available */ + bool used_watchfrr = false; + for (i = 0; i < array_size(vtysh_client); i++) if (vtysh_client[i].flag == VTYSH_WATCHFRR) break; - if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1) + if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1) { + used_watchfrr = true; ret = vtysh_client_execute(&vtysh_client[i], "do write integrated", outputfile); + } /* - * If watchfrr returns CMD_WARNING_CONFIG_FAILED this means - * that it could not write the config, but additionally - * indicates that we should not try either + * If we didn't use watchfrr, fallback to writing the config + * ourselves */ - if (ret != CMD_SUCCESS && ret != CMD_WARNING_CONFIG_FAILED) { + if (!used_watchfrr) { printf("\nWarning: attempting direct configuration write without " "watchfrr.\nFile permissions and ownership may be " "incorrect, or write may fail.\n\n"); @@ -3600,6 +3612,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/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/interface.c b/zebra/interface.c index 6f59a2d399..8d430be3e3 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1224,8 +1224,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 1a94807317..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); } } @@ -1386,7 +1386,7 @@ static void routing_socket(struct zebra_ns *zns) zlog_err("routing_socket: Can't raise privileges"); routing_sock = - ns_socket(AF_ROUTE, SOCK_RAW, 0, (ns_id_t)zns->ns->ns_id); + ns_socket(AF_ROUTE, SOCK_RAW, 0, zns->ns_id); if (routing_sock < 0) { if (zserv_privs.change(ZPRIVS_LOWER)) diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 810ee33839..5a6565aec9 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -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 cdc52211cc..9510a0e12c 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -98,7 +98,8 @@ static inline int is_selfroute(int proto) || (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) - || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)) { + || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) + || (proto == RTPROT_PBR)) { return 1; } @@ -142,7 +143,18 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_SHARP: proto = RTPROT_SHARP; break; + case ZEBRA_ROUTE_PBR: + proto = RTPROT_PBR; + break; default: + /* + * When a user adds a new protocol this will show up + * to let them know to do something about it. This + * is intentionally a warn because we should see + * this as part of development of a new protocol + */ + zlog_warn("%s: Please add this protocol(%d) to proper rt_netlink.c handling", + __PRETTY_FUNCTION__, proto); proto = RTPROT_ZEBRA; break; } @@ -184,7 +196,22 @@ static inline int proto2zebra(int proto, int family) case RTPROT_STATIC: proto = ZEBRA_ROUTE_STATIC; break; + case RTPROT_SHARP: + proto = ZEBRA_ROUTE_SHARP; + break; + case RTPROT_PBR: + proto = ZEBRA_ROUTE_PBR; + break; default: + /* + * When a user adds a new protocol this will show up + * to let them know to do something about it. This + * is intentionally a warn because we should see + * this as part of development of a new protocol + */ + zlog_warn("%s: Please add this protocol(%d) to proper rt_netlink.c handling", + __PRETTY_FUNCTION__, + proto); proto = ZEBRA_ROUTE_KERNEL; break; } @@ -586,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); } } @@ -1206,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; } } diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 51350fd6fb..78888f48ca 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -52,6 +52,7 @@ #define RTPROT_EIGRP 192 #define RTPROT_LDP 193 #define RTPROT_SHARP 194 +#define RTPROT_PBR 195 void rt_netlink_init(void); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 2ff660b3f9..6bd12391db 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1382,7 +1382,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 +1415,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 +1522,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 +1724,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 +2148,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++; 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..7ec640164a 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -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_vty.c b/zebra/zebra_vty.c index 16aece9747..e6f80f92a7 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1215,6 +1215,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_int_add(json_route, "metric", re->metric); } + json_object_int_add(json_route, "internalStatus", + re->status); + json_object_int_add(json_route, "internalFlags", + re->flags); if (uptime < ONE_DAY_SECOND) sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -1231,6 +1235,9 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, for (ALL_NEXTHOPS(re->ng, nexthop)) { json_nexthop = json_object_new_object(); + json_object_int_add(json_nexthop, "flags", + nexthop->flags); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) json_object_boolean_true_add(json_nexthop, "duplicate"); @@ -2791,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); @@ -2805,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); |
