diff options
113 files changed, 4915 insertions, 2659 deletions
diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index 9fa32ee6fa..2895ac1e69 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -1321,7 +1321,7 @@ interface_config_write (struct vty *vty) int write = 0; for (ALL_LIST_ELEMENTS_RO (vrf_iflist(VRF_DEFAULT), node, ifp)) { - vty_out (vty, "interface %s\n",ifp->name); + vty_frame (vty, "interface %s\n",ifp->name); if (ifp->desc) vty_out (vty, " description %s\n",ifp->desc); babel_interface_nfo *babel_ifp = babel_get_if_nfo (ifp); @@ -1377,7 +1377,7 @@ interface_config_write (struct vty *vty) write++; } } - vty_out (vty, "!\n"); + vty_endframe (vty, "!\n"); write++; } return write; diff --git a/babeld/babeld.c b/babeld/babeld.c index f995745e41..207c37d9b1 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -331,8 +331,8 @@ babel_main_loop(struct thread *thread) /* if there is no timeout, we must wait. */ if(timeval_compare(&tv, &babel_now) > 0) { timeval_minus(&tv, &tv, &babel_now); - debugf(BABEL_DEBUG_TIMEOUT, "babel main loop : timeout: %ld msecs", - tv.tv_sec * 1000 + tv.tv_usec / 1000); + debugf(BABEL_DEBUG_TIMEOUT, "babel main loop : timeout: %lld msecs", + (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000); /* it happens often to have less than 1 ms, it's bad. */ timeval_add_msec(&tv, &tv, 300); babel_set_timer(&tv); diff --git a/babeld/kernel.c b/babeld/kernel.c index 394d7b1e81..3343ca2e95 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -169,7 +169,7 @@ zebra_route(int add, int family, const unsigned char *pref, unsigned short plen, api.prefix = quagga_prefix; if(metric >= KERNEL_INFINITY) { - api.flags = ZEBRA_FLAG_REJECT; + zapi_route_set_blackhole(&api, BLACKHOLE_REJECT); } else { SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = 1; diff --git a/babeld/message.c b/babeld/message.c index 5990373b69..e31d5de5df 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -391,7 +391,7 @@ parse_packet(const unsigned char *from, struct interface *ifp, } else if(type == MESSAGE_HELLO) { unsigned short seqno, interval; int changed; - unsigned int timestamp; + unsigned int timestamp = 0; DO_NTOHS(seqno, message + 4); DO_NTOHS(interval, message + 6); debugf(BABEL_DEBUG_COMMON,"Received hello %d (%d) from %s on %s.", diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 324813c085..4dd38459f8 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -72,7 +72,7 @@ struct bgp_attr_encap_subtlv { unsigned long refcnt; uint16_t type; uint16_t length; - uint8_t value[1]; /* will be extended */ + uint8_t value[0]; /* will be extended */ }; #if ENABLE_BGP_VNC diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 75c1237bca..d3107552ba 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -45,6 +45,8 @@ #include "bgpd/bgp_encap_types.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nexthop.h" /* * Definitions and external declarations. @@ -1199,6 +1201,13 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return 0; } + /* Update the tunnel-ip hash */ + bgp_tip_del(bgp, &vpn->originator_ip); + bgp_tip_add(bgp, &originator_ip); + + /* filter routes as martian nexthop db has changed */ + bgp_filter_evpn_routes_upon_martian_nh_change(bgp); + /* Need to withdraw type-3 route as the originator IP is part * of the key. */ @@ -1431,7 +1440,7 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, if (is_route_matching_for_vni(bgp, vpn, ri)) { if (install) ret = install_evpn_route_entry( - bgp, vpn, evp, ri); + bgp, vpn, evp, ri); else ret = uninstall_evpn_route_entry( bgp, vpn, evp, ri); @@ -2179,6 +2188,71 @@ char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len) } /* + * Function to convert evpn route to json format. + * NOTE: We don't use prefix2str as the output here is a bit different. + */ +void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX2STR_BUFFER]; + + if (!json) + return; + + 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", 0); + json_object_int_add(json, "ipLen", + 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)); + } else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + if (IS_EVPN_PREFIX_IPADDR_NONE(p)) { + json_object_int_add(json, "routeType", + p->prefix.route_type); + json_object_int_add( + json, "esi", + 0); /* TODO: we don't support esi yet */ + json_object_int_add(json, "ethTag", 0); + json_object_int_add(json, "macLen", 8 * ETH_ALEN); + json_object_string_add(json, "mac", + prefix_mac2str(&p->prefix.mac, + buf1, + sizeof(buf1))); + } else { + u_char family; + + 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, "esi", + 0); /* TODO: we don't support esi yet */ + json_object_int_add(json, "ethTag", 0); + json_object_int_add(json, "macLen", 8 * ETH_ALEN); + json_object_string_add(json, "mac", + prefix_mac2str(&p->prefix.mac, + buf1, + sizeof(buf1))); + json_object_int_add(json, "ipLen", + 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, + PREFIX2STR_BUFFER)); + } + } else { + /* Currently, this is to cater to other AF_ETHERNET code. */ + } +} + +/* * Function to convert evpn route to string. * NOTE: We don't use prefix2str as the output here is a bit different. */ @@ -2562,6 +2636,76 @@ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, return install_uninstall_evpn_route(bgp, afi, safi, p, ri, 0); } +/* filter routes which have martian next hops */ +int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + struct bgp_node *rd_rn, *rn; + struct bgp_table *table; + struct bgp_info *ri; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Walk entire global routing table and evaluate routes which could be + * imported into this VPN. Note that we cannot just look at the routes + * for the VNI's RD - + * remote routes applicable for this VNI could have any RD. + */ + /* EVPN routes are a 2-level table. */ + for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; + rd_rn = bgp_route_next(rd_rn)) { + table = (struct bgp_table *)(rd_rn->info); + if (!table) + continue; + + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + + for (ri = rn->info; ri; ri = ri->next) { + + /* Consider "valid" remote routes applicable for + * this VNI. */ + if (!(ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (bgp_nexthop_self(bgp, + ri->attr->nexthop)) { + + char attr_str[BUFSIZ]; + char pbuf[PREFIX_STRLEN]; + + bgp_dump_attr(ri->attr, attr_str, + BUFSIZ); + + if (bgp_debug_update(ri->peer, &rn->p, + NULL, 1)) + zlog_debug( + "%u: prefix %s with attr %s - DENIED due to martian or self nexthop", + bgp->vrf_id, + prefix2str( + &rn->p, + pbuf, + sizeof(pbuf)), + attr_str); + + bgp_evpn_unimport_route(bgp, afi, safi, + &rn->p, ri); + + bgp_rib_remove(rn, ri, ri->peer, + afi, safi); + + + } + + } + } + } + + return 0; +} + /* * Handle del of a local MACIP. */ @@ -2657,6 +2801,11 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) */ delete_routes_for_vni(bgp, vpn); + /* + * tunnel is no longer active, del tunnel ip address from tip_hash + */ + bgp_tip_del(bgp, &vpn->originator_ip); + /* Clear "live" flag and see if hash needs to be freed. */ UNSET_FLAG(vpn->flags, VNI_FLAG_LIVE); if (!is_vni_configured(vpn)) @@ -2702,15 +2851,22 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, bgp->vrf_id, vni); return -1; } + } - /* if the VNI is live already, there is nothibng more to do */ + /* if the VNI is live already, there is nothing more to do */ if (is_vni_live(vpn)) return 0; /* Mark as "live" */ SET_FLAG(vpn->flags, VNI_FLAG_LIVE); + /* tunnel is now active, add tunnel-ip to db */ + bgp_tip_add(bgp, &originator_ip); + + /* filter routes as nexthop database has changed */ + bgp_filter_evpn_routes_upon_martian_nh_change(bgp); + /* Create EVPN type-3 route and schedule for processing. */ build_evpn_type3_prefix(&p, vpn->originator_ip); if (update_evpn_route(bgp, vpn, &p, 0)) { @@ -2725,6 +2881,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, */ install_routes_for_vni(bgp, vpn); + /* If we are advertising gateway mac-ip + It needs to be conveyed again to zebra */ + bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, vpn->vni); + return 0; } diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index e9b7857212..985f41f586 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -28,6 +28,7 @@ extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw); extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len); extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len); +extern void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json); extern void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, struct prefix_rd *prd, mpls_label_t *label, struct attr *attr, int addpath_encode, @@ -38,6 +39,7 @@ extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri); extern int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, struct prefix *p, struct bgp_info *ri); +extern int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp); extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip); extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 9dc459cd4e..7102038f17 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -28,6 +28,8 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" +#define RT_ADDRSTRLEN 28 + /* EVPN prefix lengths. */ #define EVPN_TYPE_2_ROUTE_PREFIXLEN 224 #define EVPN_TYPE_3_ROUTE_PREFIXLEN 224 diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 948c7f50f2..e414ad1e67 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -38,6 +38,7 @@ #define SHOW_DISPLAY_STANDARD 0 #define SHOW_DISPLAY_TAGS 1 #define SHOW_DISPLAY_OVERLAY 2 +#define VNI_STR_LEN 32 /* * Context for VNI hash walk - used by callbacks. @@ -46,15 +47,12 @@ struct vni_walk_ctx { struct bgp *bgp; struct vty *vty; struct in_addr vtep_ip; -}; - -struct evpn_config_write { - int write; - struct vty *vty; + json_object *json; }; #if defined(HAVE_CUMULUS) -static void display_import_rt(struct vty *vty, struct irt_node *irt) +static void display_import_rt(struct vty *vty, struct irt_node *irt, + json_object *json) { u_char *pnt; u_char type, sub_type; @@ -68,7 +66,14 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) } eip; struct listnode *node, *nnode; struct bgpevpn *tmp_vpn; + json_object *json_rt = NULL; + json_object *json_vnis = NULL; + char rt_buf[RT_ADDRSTRLEN]; + if (json) { + json_rt = json_object_new_object(); + json_vnis = json_object_new_array(); + } /* TODO: This needs to go into a function */ @@ -88,7 +93,13 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) eas.val |= (*pnt++ << 8); eas.val |= (*pnt++); - vty_out(vty, "Route-target: %u:%u", eas.as, eas.val); + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + break; case ECOMMUNITY_ENCODE_IP: @@ -97,7 +108,14 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - vty_out(vty, "Route-target: %s:%u", inet_ntoa(eip.ip), eip.val); + snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip), + eip.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + break; case ECOMMUNITY_ENCODE_AS4: @@ -109,65 +127,103 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt) eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - vty_out(vty, "Route-target: %u:%u", eas.as, eas.val); + snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + + if (json) + json_object_string_add(json_rt, "rt", rt_buf); + else + vty_out(vty, "Route-target: %s", rt_buf); + break; default: return; } - vty_out(vty, "\n"); - vty_out(vty, "List of VNIs importing routes with this route-target:\n"); + if (!json) { + vty_out(vty, + "\nList of VNIs importing routes with this route-target:\n"); + } + + for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) { + if (json) + json_object_array_add( + json_vnis, json_object_new_int64(tmp_vpn->vni)); + else + vty_out(vty, " %u\n", tmp_vpn->vni); + } + + if (json) { + json_object_object_add(json_rt, "vnis", json_vnis); + json_object_object_add(json, rt_buf, json_rt); + } - for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) - vty_out(vty, " %u\n", tmp_vpn->vni); } -static void show_import_rt_entry(struct hash_backet *backet, struct vty *vty) +static void show_import_rt_entry(struct hash_backet *backet, void *args[]) { + json_object *json = NULL; + struct vty *vty = NULL; struct irt_node *irt = (struct irt_node *)backet->data; - display_import_rt(vty, irt); + + vty = args[0]; + json = args[1]; + + display_import_rt(vty, irt, json); + + return; } static void bgp_evpn_show_route_rd_header(struct vty *vty, - struct bgp_node *rd_rn) + struct bgp_node *rd_rn, + json_object *json) { u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip; u_char *pnt; + char rd_str[RD_ADDRSTRLEN]; pnt = rd_rn->p.u.val; /* Decode RD type. */ type = decode_rd_type(pnt); + if (json) + return; + vty_out(vty, "Route Distinguisher: "); switch (type) { case RD_TYPE_AS: decode_rd_as(pnt + 2, &rd_as); - vty_out(vty, "%u:%d", rd_as.as, rd_as.val); + snprintf(rd_str, RD_ADDRSTRLEN, "%u:%d", rd_as.as, rd_as.val); break; case RD_TYPE_IP: decode_rd_ip(pnt + 2, &rd_ip); - vty_out(vty, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val); + snprintf(rd_str, RD_ADDRSTRLEN, "%s:%d", inet_ntoa(rd_ip.ip), + rd_ip.val); break; default: - vty_out(vty, "Unknown RD type"); + snprintf(rd_str, RD_ADDRSTRLEN, "Unknown RD type"); break; } - vty_out(vty, "\n"); + vty_out(vty, "%s\n", rd_str); } -static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp) +static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, + json_object *json) { char ri_header[] = " Network Next Hop Metric LocPrf Weight Path\n"; + 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, @@ -180,43 +236,83 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp) vty_out(vty, "%s", ri_header); } -static void display_vni(struct vty *vty, struct bgpevpn *vpn) +static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) { char buf1[INET6_ADDRSTRLEN]; char *ecom_str; struct listnode *node, *nnode; struct ecommunity *ecom; + json_object *json_import_rtl; + json_object *json_export_rtl; + + if (json) { + json_import_rtl = json_object_new_array(); + json_export_rtl = json_object_new_array(); + json_object_int_add(json, "vni", vpn->vni); + json_object_string_add(json, "kernelFlag", + is_vni_live(vpn) ? "Yes" : "No"); + json_object_string_add( + json, "rd", + prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + json_object_string_add(json, "originatorIp", + inet_ntoa(vpn->originator_ip)); + json_object_string_add(json, "advertiseGatewayMacip", + vpn->advertise_gw_macip ? "Yes" : "No"); + } else { + vty_out(vty, "VNI: %d", vpn->vni); + if (is_vni_live(vpn)) + vty_out(vty, " (known to the kernel)"); + vty_out(vty, "\n"); - vty_out(vty, "VNI: %d", vpn->vni); - if (is_vni_live(vpn)) - vty_out(vty, " (known to the kernel)"); - vty_out(vty, "\n"); + vty_out(vty, " RD: %s\n", + prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); + vty_out(vty, " Originator IP: %s\n", + inet_ntoa(vpn->originator_ip)); + vty_out(vty, " Advertise-gw-macip : %s\n", + vpn->advertise_gw_macip ? "Yes" : "No"); + } - vty_out(vty, " RD: %s\n", - prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN)); - vty_out(vty, " Originator IP: %s\n", inet_ntoa(vpn->originator_ip)); - vty_out(vty, " Advertise-gw-macip : %s\n", - vpn->advertise_gw_macip ? "Yes" : "No"); + if (!json) + vty_out(vty, " Import Route Target:\n"); - vty_out(vty, " Import Route Target:\n"); for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " %s\n", ecom_str); + + if (json) + json_object_array_add(json_import_rtl, + json_object_new_string(ecom_str)); + else + vty_out(vty, " %s\n", ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } - vty_out(vty, " Export Route Target:\n"); + if (json) + json_object_object_add(json, "importRts", json_import_rtl); + else + vty_out(vty, " Export Route Target:\n"); + for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, " %s\n", ecom_str); + + if (json) + json_object_array_add(json_export_rtl, + json_object_new_string(ecom_str)); + else + vty_out(vty, " %s\n", ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); } + + if (json) + json_object_object_add(json, "exportRts", json_export_rtl); } static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, - struct vty *vty, struct in_addr vtep_ip) + struct vty *vty, struct in_addr vtep_ip, + json_object *json) { struct bgp_node *rn; struct bgp_info *ri; @@ -228,41 +324,79 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, for (rn = bgp_table_top(vpn->route_table); rn; rn = bgp_route_next(rn)) { struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + int add_prefix_to_json = 0; + char prefix_str[BUFSIZ]; + json_object *json_paths = NULL; + json_object *json_prefix = NULL; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; + if (json) + json_prefix = json_object_new_object(); + if (rn->info) { /* Overall header/legend displayed once. */ if (header) { - bgp_evpn_show_route_header(vty, bgp); + bgp_evpn_show_route_header(vty, bgp, json); header = 0; } prefix_cnt++; } + if (json) + json_paths = json_object_new_array(); + /* For EVPN, the prefix is displayed for each path (to fit in * with code that already exists). */ for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; + if (vtep_ip.s_addr && !IPV4_ADDR_SAME(&(vtep_ip), &(ri->attr->nexthop))) continue; + if (json) + json_path = json_object_new_array(); + + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; - route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL); + add_prefix_to_json = 1; + } + + if (json && add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, json_prefix); } } - if (prefix_cnt == 0) - vty_out(vty, "No EVPN prefixes %sexist for this VNI\n", - type ? "(of requested type) " : ""); - else - vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", - prefix_cnt, path_cnt, - type ? " (of requested type)" : ""); + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) + vty_out(vty, "No EVPN prefixes %sexist for this VNI", + type ? "(of requested type) " : ""); + else + vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); + } } static void show_vni_routes_hash(struct hash_backet *backet, void *arg) @@ -270,13 +404,31 @@ static void show_vni_routes_hash(struct hash_backet *backet, void *arg) struct bgpevpn *vpn = (struct bgpevpn *)backet->data; struct vni_walk_ctx *wctx = arg; struct vty *vty = wctx->vty; + json_object *json = wctx->json; + json_object *json_vni = NULL; + char vni_str[VNI_STR_LEN]; + + snprintf(vni_str, VNI_STR_LEN, "%d", vpn->vni); + if (json) { + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "vni", vpn->vni); + } else { + vty_out(vty, "\nVNI: %d\n\n", vpn->vni); + } + + show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip, json_vni); - vty_out(vty, "\nVNI: %d\n\n", vpn->vni); - show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip); + if (json) + json_object_object_add(json, vni_str, json_vni); } -static void show_vni_entry(struct hash_backet *backet, struct vty *vty) +static void show_vni_entry(struct hash_backet *backet, void *args[]) { + struct vty *vty; + json_object *json; + json_object *json_vni; + json_object *json_import_rtl; + json_object *json_export_rtl; struct bgpevpn *vpn = (struct bgpevpn *)backet->data; char buf1[10]; char buf2[INET6_ADDRSTRLEN]; @@ -285,42 +437,92 @@ static void show_vni_entry(struct hash_backet *backet, struct vty *vty) struct listnode *node, *nnode; struct ecommunity *ecom; + vty = args[0]; + json = args[1]; + + if (json) { + json_vni = json_object_new_object(); + json_import_rtl = json_object_new_array(); + json_export_rtl = json_object_new_array(); + } + buf1[0] = '\0'; if (is_vni_live(vpn)) sprintf(buf1, "*"); - vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni, - inet_ntoa(vpn->originator_ip), - prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + if (json) { + json_object_int_add(json_vni, "vni", vpn->vni); + json_object_string_add(json_vni, "inKernel", + is_vni_live(vpn) ? "True" : "False"); + json_object_string_add(json_vni, "originatorIp", + inet_ntoa(vpn->originator_ip)); + json_object_string_add( + json_vni, "rd", + prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + } else { + vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni, + inet_ntoa(vpn->originator_ip), + prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); + } for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (listcount(vpn->import_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); - else - sprintf(rt_buf, "%s", ecom_str); - vty_out(vty, " %-25s", rt_buf); + if (json) { + json_object_array_add(json_import_rtl, + json_object_new_string(ecom_str)); + } else { + if (listcount(vpn->import_rtl) > 1) + sprintf(rt_buf, "%s, ...", ecom_str); + else + sprintf(rt_buf, "%s", ecom_str); + vty_out(vty, " %-25s", rt_buf); + } XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); - break; + + /* If there are multiple import RTs we break here and show only + * one */ + if (!json) + break; } + if (json) + json_object_object_add(json_vni, "importRTs", json_import_rtl); + for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) { ecom_str = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (listcount(vpn->export_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); - else - sprintf(rt_buf, "%s", ecom_str); - vty_out(vty, " %-25s", rt_buf); + if (json) { + json_object_array_add(json_export_rtl, + json_object_new_string(ecom_str)); + } else { + if (listcount(vpn->export_rtl) > 1) + sprintf(rt_buf, "%s, ...", ecom_str); + else + sprintf(rt_buf, "%s", ecom_str); + vty_out(vty, " %-25s", rt_buf); + } XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); - break; + + /* If there are multiple export RTs we break here and show only + * one */ + if (!json) + break; + } + + if (json) { + char vni_str[VNI_STR_LEN]; + + json_object_object_add(json_vni, "exportRTs", json_export_rtl); + snprintf(vni_str, VNI_STR_LEN, "%u", vpn->vni); + json_object_object_add(json, vni_str, json_vni); + } else { + vty_out(vty, "\n"); } - vty_out(vty, "\n"); } #endif /* HAVE_CUMULUS */ @@ -380,155 +582,150 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) continue; - if ((table = rn->info) != NULL) { - rd_header = 1; - - for (rm = bgp_table_top(table); rm; - rm = bgp_route_next(rm)) - for (ri = rm->info; ri; ri = ri->next) { - total_count++; - if (type == bgp_show_type_neighbor) { - union sockunion *su = - output_arg; - - if (ri->peer->su_remote == NULL - || !sockunion_same( - ri->peer->su_remote, - su)) - continue; - } - if (header == 0) { - if (use_json) { - if (option - == SHOW_DISPLAY_TAGS) { - json_object_int_add( - json, - "bgpTableVersion", - 0); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_object_add( - json, - "bgpStatusCodes", - json_scode); - json_object_object_add( - json, - "bgpOriginCodes", - json_ocode); - } - } else { - if (option - == SHOW_DISPLAY_TAGS) - vty_out(vty, - V4_HEADER_TAG); - else if ( - option - == SHOW_DISPLAY_OVERLAY) - vty_out(vty, - V4_HEADER_OVERLAY); - else { - vty_out(vty, - "BGP table version is 0, local router ID is %s\n", - inet_ntoa( - bgp->router_id)); - vty_out(vty, - "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); - vty_out(vty, - "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"); - vty_out(vty, - V4_HEADER); - } - } - header = 0; - } - if (rd_header) { - u_int16_t type; - struct rd_as rd_as; - struct rd_ip rd_ip; - u_char *pnt; + if ((table = rn->info) == NULL) + continue; - pnt = rn->p.u.val; + rd_header = 1; - /* Decode RD type. */ - type = decode_rd_type(pnt); - /* Decode RD value. */ - if (type == RD_TYPE_AS) - decode_rd_as(pnt + 2, - &rd_as); - else if (type == RD_TYPE_AS4) - decode_rd_as4(pnt + 2, - &rd_as); - else if (type == RD_TYPE_IP) - decode_rd_ip(pnt + 2, - &rd_ip); - if (use_json) { - char buffer[BUFSIZ]; - if (type == RD_TYPE_AS - || type == RD_TYPE_AS4) - sprintf(buffer, - "%u:%d", - rd_as.as, - rd_as.val); - else if (type - == RD_TYPE_IP) - sprintf(buffer, - "%s:%d", - inet_ntoa( - rd_ip.ip), - rd_ip.val); + for (rm = bgp_table_top(table); rm; + rm = bgp_route_next(rm)) + for (ri = rm->info; ri; ri = ri->next) { + total_count++; + if (type == bgp_show_type_neighbor) { + union sockunion *su = + output_arg; + + if (ri->peer->su_remote == NULL + || !sockunion_same( + ri->peer->su_remote, + su)) + continue; + } + if (header == 0) { + if (use_json) { + if (option + == SHOW_DISPLAY_TAGS) { + json_object_int_add( + json, + "bgpTableVersion", + 0); json_object_string_add( - json_nroute, - "routeDistinguisher", - buffer); - } else { + json, + "bgpLocalRouterId", + inet_ntoa( + bgp->router_id)); + json_object_object_add( + json, + "bgpStatusCodes", + json_scode); + json_object_object_add( + json, + "bgpOriginCodes", + json_ocode); + } + } else { + if (option + == SHOW_DISPLAY_TAGS) + vty_out(vty, + V4_HEADER_TAG); + else if ( + option + == SHOW_DISPLAY_OVERLAY) + vty_out(vty, + V4_HEADER_OVERLAY); + else { + vty_out(vty, + "BGP table version is 0, local router ID is %s\n", + inet_ntoa( + bgp->router_id)); vty_out(vty, - "Route Distinguisher: "); - if (type == RD_TYPE_AS) - vty_out(vty, - "as2 %u:%d", - rd_as.as, - rd_as.val); - else if (type - == RD_TYPE_AS4) - vty_out(vty, - "as4 %u:%d", - rd_as.as, - rd_as.val); - else if (type - == RD_TYPE_IP) - vty_out(vty, - "ip %s:%d", - inet_ntoa( - rd_ip.ip), - rd_ip.val); - vty_out(vty, "\n\n"); + "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); + vty_out(vty, + "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"); + vty_out(vty, + V4_HEADER); } - rd_header = 0; } - if (use_json) - json_array = - json_object_new_array(); - else - json_array = NULL; - if (option == SHOW_DISPLAY_TAGS) - route_vty_out_tag( - vty, &rm->p, ri, 0, - SAFI_EVPN, json_array); - else if (option == SHOW_DISPLAY_OVERLAY) - route_vty_out_overlay( - vty, &rm->p, ri, 0, - json_array); - else - route_vty_out(vty, &rm->p, ri, - 0, SAFI_EVPN, - json_array); - output_count++; + header = 0; } - /* XXX json */ - } + if (rd_header) { + u_int16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + u_char *pnt; + + pnt = rn->p.u.val; + + /* Decode RD type. */ + type = decode_rd_type(pnt); + /* Decode RD value. */ + if (type == RD_TYPE_AS) + decode_rd_as(pnt + 2, &rd_as); + else if (type == RD_TYPE_AS4) + decode_rd_as4(pnt + 2, &rd_as); + else if (type == RD_TYPE_IP) + decode_rd_ip(pnt + 2, &rd_ip); + if (use_json) { + char buffer[BUFSIZ]; + if (type == RD_TYPE_AS + || type == RD_TYPE_AS4) + sprintf(buffer, + "%u:%d", + rd_as.as, + rd_as.val); + else if (type + == RD_TYPE_IP) + sprintf(buffer, + "%s:%d", + inet_ntoa( + rd_ip.ip), + rd_ip.val); + json_object_string_add( + json_nroute, + "routeDistinguisher", + buffer); + } else { + vty_out(vty, + "Route Distinguisher: "); + if (type == RD_TYPE_AS) + vty_out(vty, + "as2 %u:%d", + rd_as.as, + rd_as.val); + else if (type + == RD_TYPE_AS4) + vty_out(vty, + "as4 %u:%d", + rd_as.as, + rd_as.val); + else if (type + == RD_TYPE_IP) + vty_out(vty, + "ip %s:%d", + inet_ntoa( + rd_ip.ip), + rd_ip.val); + vty_out(vty, "\n\n"); + } + rd_header = 0; + } + if (use_json) + json_array = json_object_new_array(); + else + json_array = NULL; + if (option == SHOW_DISPLAY_TAGS) + route_vty_out_tag(vty, &rm->p, ri, 0, + SAFI_EVPN, + json_array); + else if (option == SHOW_DISPLAY_OVERLAY) + route_vty_out_overlay(vty, &rm->p, ri, + 0, json_array); + else + route_vty_out(vty, &rm->p, ri, 0, + SAFI_EVPN, json_array); + output_count++; + } + /* XXX json */ } if (output_count == 0) vty_out(vty, "No prefixes displayed, %ld exist\n", total_count); @@ -1281,19 +1478,26 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) /* * Display import RT mapping to VNIs (vty handler) */ -static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp) +static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp, + json_object *json) { + void *args[2]; + + args[0] = vty; + args[1] = json; + hash_iterate( bgp->import_rt_hash, (void (*)(struct hash_backet *, void *))show_import_rt_entry, - vty); + args); } /* * Display EVPN routes for all VNIs - vty handler. */ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, - struct in_addr vtep_ip) + struct in_addr vtep_ip, + json_object *json) { u_int32_t num_vnis; struct vni_walk_ctx wctx; @@ -1305,6 +1509,7 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, wctx.bgp = bgp; wctx.vty = vty; wctx.vtep_ip = vtep_ip; + wctx.json = json; hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))show_vni_routes_hash, &wctx); @@ -1314,7 +1519,8 @@ static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp, * Display EVPN routes for a VNI -- for specific type-3 route (vty handler). */ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, - vni_t vni, struct in_addr orig_ip) + vni_t vni, struct in_addr orig_ip, + json_object *json) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -1323,6 +1529,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, u_int32_t path_cnt = 0; afi_t afi; safi_t safi; + json_object *json_paths = NULL; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -1338,20 +1545,42 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, build_evpn_type3_prefix(&p, orig_ip); rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); if (!rn || !rn->info) { - vty_out(vty, "%% Network not in table\n"); + if (!json) + vty_out(vty, "%% Network not in table\n"); return; } + if (json) + json_paths = json_object_new_array(); + /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, NULL); + route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, json); /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { - route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, NULL); + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; } - vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); + if (json) { + if (path_cnt) + json_object_object_add(json, "paths", json_paths); + + json_object_int_add(json, "numPaths", path_cnt); + } else { + vty_out(vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); + } } /* @@ -1360,7 +1589,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, */ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, vni_t vni, struct ethaddr *mac, - struct ipaddr *ip) + struct ipaddr *ip, json_object *json) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -1369,6 +1598,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, u_int32_t path_cnt = 0; afi_t afi; safi_t safi; + json_object *json_paths = NULL; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -1376,7 +1606,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, /* Locate VNI. */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vty_out(vty, "VNI not found\n"); + if (!json) + vty_out(vty, "VNI not found\n"); return; } @@ -1384,20 +1615,42 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, build_evpn_type2_prefix(&p, mac, ip); rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); if (!rn || !rn->info) { - vty_out(vty, "%% Network not in table\n"); + if (!json) + vty_out(vty, "%% Network not in table\n"); return; } + if (json) + json_paths = json_object_new_array(); + /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, NULL); + route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, json); /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { - route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, NULL); + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; } - vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); + if (json) { + if (path_cnt) + json_object_object_add(json, "paths", json_paths); + + json_object_int_add(json, "numPaths", path_cnt); + } else { + vty_out(vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); + } } /* @@ -1406,19 +1659,21 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, * If the vtep_ip is non zero, only routes behind that vtep are shown */ static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni, - int type, struct in_addr vtep_ip) + int type, struct in_addr vtep_ip, + json_object *json) { struct bgpevpn *vpn; /* Locate VNI. */ vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vty_out(vty, "VNI not found\n"); + if (!json) + vty_out(vty, "VNI not found\n"); return; } /* Walk this VNI's route table and display appropriate routes. */ - show_vni_routes(bgp, vpn, type, vty, vtep_ip); + show_vni_routes(bgp, vpn, type, vty, vtep_ip, json); } /* @@ -1428,7 +1683,7 @@ static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni, */ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, struct prefix_rd *prd, struct ethaddr *mac, - struct ipaddr *ip) + struct ipaddr *ip, json_object *json) { struct prefix_evpn p; struct bgp_node *rn; @@ -1436,6 +1691,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, afi_t afi; safi_t safi; u_int32_t path_cnt = 0; + json_object *json_paths = NULL; + char prefix_str[BUFSIZ]; afi = AFI_L2VPN; safi = SAFI_EVPN; @@ -1445,20 +1702,44 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, (struct prefix *)&p, prd); if (!rn || !rn->info) { - vty_out(vty, "%% Network not in table\n"); + if (!json) + vty_out(vty, "%% Network not in table\n"); return; } + bgp_evpn_route2str((struct prefix_evpn *)&p, prefix_str, + sizeof(prefix_str)); + /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, prd, afi, safi, NULL); + route_vty_out_detail_header(vty, bgp, rn, prd, afi, safi, json); + + if (json) + json_paths = json_object_new_array(); /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { - route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, NULL); + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; } - vty_out(vty, "\nDisplayed %u paths for requested prefix\n", path_cnt); + if (json && path_cnt) { + if (path_cnt) + json_object_object_add(json, prefix_str, json_paths); + json_object_int_add(json, "numPaths", path_cnt); + } else { + vty_out(vty, "\nDisplayed %u paths for requested prefix\n", + path_cnt); + } } /* @@ -1466,7 +1747,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, * If 'type' is non-zero, only routes matching that type are shown. */ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, - struct prefix_rd *prd, int type) + struct prefix_rd *prd, int type, + json_object *json) { struct bgp_node *rd_rn; struct bgp_table *table; @@ -1476,28 +1758,49 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, afi_t afi; safi_t safi; u_int32_t prefix_cnt, path_cnt; + char rd_str[RD_ADDRSTRLEN]; + json_object *json_rd = NULL; + int add_rd_to_json = 0; afi = AFI_L2VPN; safi = SAFI_EVPN; prefix_cnt = path_cnt = 0; + prefix_rd2str((struct prefix_rd *)prd, rd_str, sizeof(rd_str)); + rd_rn = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)prd); if (!rd_rn) return; + table = (struct bgp_table *)rd_rn->info; if (table == NULL) return; + if (json) { + json_rd = json_object_new_object(); + json_object_string_add(json_rd, "rd", rd_str); + } + /* Display all prefixes with this RD. */ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + json_object *json_prefix = NULL; + json_object *json_paths = NULL; + char prefix_str[BUFSIZ]; + int add_prefix_to_json = 0; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; + if (json) + json_prefix = json_object_new_object(); + if (rn->info) { /* RD header and legend - once overall. */ - if (rd_header) { + if (rd_header && !json) { vty_out(vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:" "[MAC]\n"); @@ -1509,34 +1812,64 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, /* Prefix and num paths displayed once per prefix. */ route_vty_out_detail_header(vty, bgp, rn, prd, afi, - safi, NULL); + safi, json_prefix); prefix_cnt++; } + if (json) + json_paths = json_object_new_array(); + /* Display each path for this prefix. */ for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; + + if (json) + json_path = json_object_new_array(); + route_vty_out_detail(vty, bgp, &rn->p, ri, afi, safi, - NULL); + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + path_cnt++; + add_prefix_to_json = 1; + add_rd_to_json = 1; + } + + if (json && add_prefix_to_json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); } } - if (prefix_cnt == 0) - vty_out(vty, "No prefixes exist with this RD%s\n", - type ? " (of requested type)" : ""); - else - vty_out(vty, - "\nDisplayed %u prefixes (%u paths) with this RD%s\n", - prefix_cnt, path_cnt, - type ? " (of requested type)" : ""); + if (json && add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) + vty_out(vty, "No prefixes exist with this RD%s\n", + type ? " (of requested type)" : ""); + else + vty_out(vty, + "\nDisplayed %u prefixes (%u paths) with this RD%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); + } } /* * Display BGP EVPN routing table - all routes (vty handler). * If 'type' is non-zero, only routes matching that type are shown. */ -static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type) +static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, + json_object *json) { struct bgp_node *rd_rn; struct bgp_table *table; @@ -1557,15 +1890,36 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type) */ for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; rd_rn = bgp_route_next(rd_rn)) { + char rd_str[RD_ADDRSTRLEN]; + json_object *json_rd = NULL; /* contains routes for an RD */ + int add_rd_to_json = 0; + table = (struct bgp_table *)rd_rn->info; if (table == NULL) continue; + prefix_rd2str((struct prefix_rd *)&rd_rn->p, rd_str, + sizeof(rd_str)); + + if (json) { + json_rd = json_object_new_object(); + json_object_string_add(json_rd, "rd", rd_str); + } + rd_header = 1; /* Display all prefixes for an RD */ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + json_object *json_prefix = + NULL; /* contains prefix under a RD */ + json_object *json_paths = + NULL; /* array of paths under a prefix*/ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + char prefix_str[BUFSIZ]; + int add_prefix_to_json = 0; + + bgp_evpn_route2str((struct prefix_evpn *)&rn->p, + prefix_str, sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; @@ -1573,74 +1927,126 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type) if (rn->info) { /* Overall header/legend displayed once. */ if (header) { - bgp_evpn_show_route_header(vty, bgp); + bgp_evpn_show_route_header(vty, bgp, + json); header = 0; } /* RD header - per RD. */ if (rd_header) { - bgp_evpn_show_route_rd_header(vty, - rd_rn); + bgp_evpn_show_route_rd_header( + vty, rd_rn, json); rd_header = 0; } prefix_cnt++; } + if (json) { + json_prefix = json_object_new_object(); + json_paths = json_object_new_array(); + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + rn->p.prefixlen); + } + /* For EVPN, the prefix is displayed for each path (to * fit in * with code that already exists). */ for (ri = rn->info; ri; ri = ri->next) { + json_object *json_path = NULL; path_cnt++; + add_prefix_to_json = 1; + add_rd_to_json = 1; + + if (json) + json_path = json_object_new_array(); + route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, - NULL); + json_path); + + if (json) + json_object_array_add(json_paths, + json_path); + } + + if (json && add_prefix_to_json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); } } + + if (json && add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); } - if (prefix_cnt == 0) - vty_out(vty, "No EVPN prefixes %sexist\n", - type ? "(of requested type) " : ""); - else - vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", - prefix_cnt, path_cnt, - type ? " (of requested type)" : ""); + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) { + vty_out(vty, "No EVPN prefixes %sexist\n", + type ? "(of requested type) " : ""); + } else { + vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n", + prefix_cnt, path_cnt, + type ? " (of requested type)" : ""); + } + } } /* * Display specified VNI (vty handler) */ -static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni) +static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni, + json_object *json) { struct bgpevpn *vpn; vpn = bgp_evpn_lookup_vni(bgp, vni); if (!vpn) { - vty_out(vty, "VNI not found\n"); - return; + if (json) { + vty_out(vty, "{}\n"); + } else { + vty_out(vty, "VNI not found\n"); + return; + } } - display_vni(vty, vpn); + display_vni(vty, vpn, json); } /* * Display a VNI (upon user query). */ -static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp) +static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp, + json_object *json) { u_int32_t num_vnis; + void *args[2]; num_vnis = hashcount(bgp->vnihash); if (!num_vnis) return; - vty_out(vty, "Number of VNIs: %u\n", num_vnis); - vty_out(vty, "Flags: * - Kernel \n"); - vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI", "Orig IP", - "RD", "Import RT", "Export RT"); + + if (json) { + json_object_int_add(json, "numVnis", num_vnis); + } else { + vty_out(vty, "Number of VNIs: %u\n", num_vnis); + vty_out(vty, "Flags: * - Kernel\n"); + vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI", + "Orig IP", "RD", "Import RT", "Export RT"); + } + + args[0] = vty; + args[1] = json; hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))show_vni_entry, - vty); + args); } /* @@ -1709,17 +2115,14 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp) } #endif /* HAVE_CUMULUS */ -static void write_vni_config(struct vty *vty, struct bgpevpn *vpn, int *write) +static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) { char buf1[INET6_ADDRSTRLEN]; - afi_t afi = AFI_L2VPN; - safi_t safi = SAFI_EVPN; char *ecom_str; struct listnode *node, *nnode; struct ecommunity *ecom; if (is_vni_configured(vpn)) { - bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " vni %d\n", vpn->vni); if (is_rd_configured(vpn)) vty_out(vty, " rd %s\n", @@ -1755,10 +2158,10 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn, int *write) } static void write_vni_config_for_entry(struct hash_backet *backet, - struct evpn_config_write *cfg) + struct vty *vty) { struct bgpevpn *vpn = (struct bgpevpn *)backet->data; - write_vni_config(cfg->vty, vpn, &cfg->write); + write_vni_config(vty, vpn); } #if defined(HAVE_CUMULUS) @@ -1863,19 +2266,24 @@ DEFUN (no_bgp_evpn_advertise_all_vni, /* * Display VNI information - for all or a specific VNI */ -DEFUN (show_bgp_l2vpn_evpn_vni, - show_bgp_l2vpn_evpn_vni_cmd, - "show bgp l2vpn evpn vni [(1-16777215)]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "Show VNI\n" - "VNI number\n") +DEFUN(show_bgp_l2vpn_evpn_vni, + show_bgp_l2vpn_evpn_vni_cmd, + "show bgp l2vpn evpn vni [(1-16777215)] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show VNI\n" + "VNI number\n" + JSON_STR) { struct bgp *bgp; vni_t vni; int idx = 0; + u_char uj = 0; + json_object *json = NULL; + + uj = use_json(argc, argv); bgp = bgp_get_default(); if (!bgp) @@ -1884,18 +2292,46 @@ DEFUN (show_bgp_l2vpn_evpn_vni, if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; - if (argc == ((idx + 1) + 1)) { - vty_out(vty, "Advertise gateway macip flag: %s\n", - bgp->advertise_gw_macip ? "Enabled" : "Disabled"); + if (uj) + json = json_object_new_object(); - /* Display all VNIs */ - vty_out(vty, "Advertise All VNI flag: %s\n", - bgp->advertise_all_vni ? "Enabled" : "Disabled"); - evpn_show_all_vnis(vty, bgp); + if ((uj && argc == ((idx + 1) + 2)) || (!uj && argc == (idx + 1) + 1)) { + if (uj) { + json_object_string_add(json, "advertiseGatewayMacip", + bgp->advertise_gw_macip + ? "Enabled" + : "Disabled"); + json_object_string_add(json, "advertiseAllVnis", + bgp->advertise_all_vni + ? "Enabled" + : "Disabled"); + } else { + vty_out(vty, "Advertise Gateway Macip: %s\n", + bgp->advertise_gw_macip ? "Enabled" + : "Disabled"); + + /* Display all VNIs */ + vty_out(vty, "Advertise All VNI flag: %s\n", + bgp->advertise_all_vni ? "Enabled" + : "Disabled"); + } + + evpn_show_all_vnis(vty, bgp, json); } else { + int vni_idx = 0; + + if (!argv_find(argv, argc, "vni", &vni_idx)) + return CMD_WARNING; + /* Display specific VNI */ - vni = strtoul(argv[argc - 1]->arg, NULL, 10); - evpn_show_vni(vty, bgp, vni); + vni = strtoul(argv[vni_idx + 1]->arg, NULL, 10); + evpn_show_vni(vty, bgp, vni, json); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } return CMD_SUCCESS; @@ -1904,15 +2340,15 @@ DEFUN (show_bgp_l2vpn_evpn_vni, /* * Display EVPN neighbor summary. */ -DEFUN (show_bgp_l2vpn_evpn_summary, - show_bgp_l2vpn_evpn_summary_cmd, - "show bgp l2vpn evpn summary [json]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "Summary of BGP neighbor status\n" - JSON_STR) +DEFUN(show_bgp_l2vpn_evpn_summary, + show_bgp_l2vpn_evpn_summary_cmd, + "show bgp l2vpn evpn summary [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Summary of BGP neighbor status\n" + JSON_STR) { u_char uj = use_json(argc, argv); return bgp_show_summary_vty(vty, NULL, AFI_L2VPN, SAFI_EVPN, uj); @@ -1921,176 +2357,236 @@ DEFUN (show_bgp_l2vpn_evpn_summary, /* * Display global EVPN routing table. */ -DEFUN (show_bgp_l2vpn_evpn_route, - show_bgp_l2vpn_evpn_route_cmd, - "show bgp l2vpn evpn route [type <macip|multicast>]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "Specify Route type\n" - "MAC-IP (Type-2) route\n" - "Multicast (Type-3) route\n") +DEFUN(show_bgp_l2vpn_evpn_route, show_bgp_l2vpn_evpn_route_cmd, + "show bgp l2vpn evpn route [type <macip|multicast>] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + JSON_STR) { struct bgp *bgp; - int idx = 0; + int type_idx; int type = 0; + u_char uj = 0; + json_object *json = NULL; + + uj = use_json(argc, argv); bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; + if (uj) + json = json_object_new_object(); - if (argc == ((idx + 1) + 3)) { + /* get the type */ + if (argv_find(argv, argc, "type", &type_idx)) { /* Specific type is requested */ - if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0) + if (strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0) + else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; } - evpn_show_all_routes(vty, bgp, type); + evpn_show_all_routes(vty, bgp, type, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display global EVPN routing table for specific RD. */ -DEFUN (show_bgp_l2vpn_evpn_route_rd, - show_bgp_l2vpn_evpn_route_rd_cmd, - "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn [type <macip|multicast>]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "Route Distinguisher\n" - "ASN:XX or A.B.C.D:XX\n" - "Specify Route type\n" - "MAC-IP (Type-2) route\n" - "Multicast (Type-3) route\n") +DEFUN(show_bgp_l2vpn_evpn_route_rd, show_bgp_l2vpn_evpn_route_rd_cmd, + "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn [type <macip|multicast>] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + JSON_STR) { struct bgp *bgp; int ret; struct prefix_rd prd; - int idx = 0; int type = 0; + int rd_idx = 0; + int type_idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); - ret = str2prefix_rd(argv[idx + 3]->arg, &prd); - if (!ret) { - vty_out(vty, "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + /* get the RD */ + if (argv_find(argv, argc, "rd", &rd_idx)) { + ret = str2prefix_rd(argv[rd_idx + 1]->arg, &prd); + + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } - if (argc == ((idx + 1) + 5)) { + /* get the type */ + if (argv_find(argv, argc, "type", &type_idx)) { /* Specific type is requested */ - if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0) + if (strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0) + else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; else return CMD_WARNING; } - evpn_show_route_rd(vty, bgp, &prd, type); + evpn_show_route_rd(vty, bgp, &prd, type, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display global EVPN routing table for specific RD and MACIP. */ -DEFUN (show_bgp_l2vpn_evpn_route_rd_macip, - show_bgp_l2vpn_evpn_route_rd_macip_cmd, - "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "Route Distinguisher\n" - "ASN:XX or A.B.C.D:XX\n" - "MAC\n" - "MAC address (e.g., 00:e0:ec:20:12:62)\n" - "IP\n" - "IP address (IPv4 or IPv6)\n") +DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, + show_bgp_l2vpn_evpn_route_rd_macip_cmd, + "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IP address (IPv4 or IPv6)\n" + JSON_STR) { struct bgp *bgp; int ret; struct prefix_rd prd; struct ethaddr mac; struct ipaddr ip; - int idx = 0; + int rd_idx = 0; + int mac_idx = 0; + int ip_idx = 0; + int uj = 0; + json_object *json = NULL; + + memset(&mac, 0, sizeof(struct ethaddr)); + memset(&ip, 0, sizeof(struct ipaddr)); bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); - ret = str2prefix_rd(argv[idx + 3]->arg, &prd); - if (!ret) { - vty_out(vty, "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + /* get the prd */ + if (argv_find(argv, argc, "rd", &rd_idx)) { + ret = str2prefix_rd(argv[rd_idx + 1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } - if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) { - vty_out(vty, "%% Malformed MAC address\n"); - return CMD_WARNING; + + /* get the mac */ + if (argv_find(argv, argc, "mac", &mac_idx)) { + if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) { + vty_out(vty, "%% Malformed MAC address\n"); + return CMD_WARNING; + } } - memset(&ip, 0, sizeof(ip)); - if (argc == (idx + 1 + 7) && argv[argc - 1]->arg != NULL) { - /* Specific MAC+IP requested */ - if (str2ipaddr(argv[argc - 1]->arg, &ip) != 0) { + + /* get the ip if specified */ + if (argv_find(argv, argc, "ip", &ip_idx)) { + if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) { vty_out(vty, "%% Malformed IP address\n"); return CMD_WARNING; } } - evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip); + evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table. */ -DEFUN (show_bgp_l2vpn_evpn_route_vni, - show_bgp_l2vpn_evpn_route_vni_cmd, - "show bgp l2vpn evpn route vni (1-16777215) [<type <macip|multicast> | vtep A.B.C.D>]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "VNI number\n" - "Specify Route type\n" - "MAC-IP (Type-2) route\n" - "Multicast (Type-3) route\n" - "Remote VTEP\n" - "Remote VTEP IP address\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, + "show bgp l2vpn evpn route vni (1-16777215) [<type <macip|multicast> | vtep A.B.C.D>] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Specify Route type\n" + "MAC-IP (Type-2) route\n" + "Multicast (Type-3) route\n" + "Remote VTEP\n" + "Remote VTEP IP address\n" + JSON_STR) { vni_t vni; struct bgp *bgp; struct in_addr vtep_ip; int type = 0; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; @@ -2098,7 +2594,8 @@ DEFUN (show_bgp_l2vpn_evpn_route_vni, vni = strtoul(argv[idx + 3]->arg, NULL, 10); - if (argc == (idx + 1 + 5) && argv[idx + 4]->arg) { + if ((!uj && ((argc == (idx + 1 + 5)) && argv[idx + 4]->arg)) + || (uj && ((argc == (idx + 1 + 6)) && argv[idx + 4]->arg))) { if (strncmp(argv[idx + 4]->arg, "type", 4) == 0) { if (strncmp(argv[idx + 5]->arg, "ma", 2) == 0) type = BGP_EVPN_MAC_IP_ROUTE; @@ -2115,156 +2612,231 @@ DEFUN (show_bgp_l2vpn_evpn_route_vni, return CMD_WARNING; } - evpn_show_routes_vni(vty, bgp, vni, type, vtep_ip); + evpn_show_routes_vni(vty, bgp, vni, type, vtep_ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table for specific MACIP. */ -DEFUN (show_bgp_l2vpn_evpn_route_vni_macip, - show_bgp_l2vpn_evpn_route_vni_macip_cmd, - "show bgp l2vpn evpn route vni (1-16777215) mac WORD [ip WORD]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "VNI number\n" - "MAC\n" - "MAC address (e.g., 00:e0:ec:20:12:62)\n" - "IP\n" - "IP address (IPv4 or IPv6)\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni_macip, + show_bgp_l2vpn_evpn_route_vni_macip_cmd, + "show bgp l2vpn evpn route vni (1-16777215) mac WORD [ip WORD] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IP address (IPv4 or IPv6)\n" + JSON_STR) { vni_t vni; struct bgp *bgp; struct ethaddr mac; struct ipaddr ip; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; + /* get the VNI */ vni = strtoul(argv[idx + 3]->arg, NULL, 10); + + /* get the mac */ if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) { vty_out(vty, "%% Malformed MAC address\n"); return CMD_WARNING; } + + /* get the ip */ memset(&ip, 0, sizeof(ip)); - if (argc == (idx + 1 + 7) && argv[idx + 7]->arg != NULL) { + if ((!uj && ((argc == (idx + 1 + 7)) && argv[idx + 7]->arg != NULL)) + || (uj + && ((argc == (idx + 1 + 8)) && argv[idx + 7]->arg != NULL))) { if (str2ipaddr(argv[idx + 7]->arg, &ip) != 0) { vty_out(vty, "%% Malformed IP address\n"); return CMD_WARNING; } } - evpn_show_route_vni_macip(vty, bgp, vni, &mac, &ip); + evpn_show_route_vni_macip(vty, bgp, vni, &mac, &ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table for specific multicast IP (remote VTEP). */ -DEFUN (show_bgp_l2vpn_evpn_route_vni_multicast, - show_bgp_l2vpn_evpn_route_vni_multicast_cmd, - "show bgp l2vpn evpn route vni (1-16777215) multicast A.B.C.D", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "VNI number\n" - "Multicast (Type-3) route\n" - "Originating Router IP address\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni_multicast, + show_bgp_l2vpn_evpn_route_vni_multicast_cmd, + "show bgp l2vpn evpn route vni (1-16777215) multicast A.B.C.D [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Multicast (Type-3) route\n" + "Originating Router IP address\n" + JSON_STR) { vni_t vni; struct bgp *bgp; int ret; struct in_addr orig_ip; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; + /* get the VNI */ vni = strtoul(argv[idx + 3]->arg, NULL, 10); + + /* get the ip */ ret = inet_aton(argv[idx + 5]->arg, &orig_ip); if (!ret) { vty_out(vty, "%% Malformed Originating Router IP address\n"); return CMD_WARNING; } - evpn_show_route_vni_multicast(vty, bgp, vni, orig_ip); + evpn_show_route_vni_multicast(vty, bgp, vni, orig_ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display per-VNI EVPN routing table - for all VNIs. */ -DEFUN (show_bgp_l2vpn_evpn_route_vni_all, - show_bgp_l2vpn_evpn_route_vni_all_cmd, - "show bgp l2vpn evpn route vni all [vtep A.B.C.D]", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "EVPN route information\n" - "VXLAN Network Identifier\n" - "All VNIs\n" - "Remote VTEP\n" - "Remote VTEP IP address\n") +DEFUN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_l2vpn_evpn_route_vni_all_cmd, + "show bgp l2vpn evpn route vni all [vtep A.B.C.D] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "EVPN route information\n" + "VXLAN Network Identifier\n" + "All VNIs\n" + "Remote VTEP\n" + "Remote VTEP IP address\n" + JSON_STR) { struct bgp *bgp; struct in_addr vtep_ip; int idx = 0; + int uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; + /* check if we need json output */ + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + if (!argv_find(argv, argc, "evpn", &idx)) return CMD_WARNING; vtep_ip.s_addr = 0; - if (argc == (idx + 1 + 5) && argv[idx + 5]->arg) { + if ((!uj && (argc == (idx + 1 + 5) && argv[idx + 5]->arg)) + || (uj && (argc == (idx + 1 + 6) && argv[idx + 5]->arg))) { if (!inet_aton(argv[idx + 5]->arg, &vtep_ip)) { vty_out(vty, "%% Malformed VTEP IP address\n"); return CMD_WARNING; } } - evpn_show_routes_vni_all(vty, bgp, vtep_ip); + evpn_show_routes_vni_all(vty, bgp, vtep_ip, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } /* * Display EVPN import route-target hash table */ -DEFUN (show_bgp_l2vpn_evpn_import_rt, - show_bgp_l2vpn_evpn_import_rt_cmd, - "show bgp l2vpn evpn import-rt", - SHOW_STR - BGP_STR - L2VPN_HELP_STR - EVPN_HELP_STR - "Show import route target\n") +DEFUN(show_bgp_l2vpn_evpn_import_rt, show_bgp_l2vpn_evpn_import_rt_cmd, + "show bgp l2vpn evpn import-rt [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Show import route target\n" + JSON_STR) { struct bgp *bgp; + u_char uj = 0; + json_object *json = NULL; bgp = bgp_get_default(); if (!bgp) return CMD_WARNING; - evpn_show_import_rts(vty, bgp); + uj = use_json(argc, argv); + if (uj) + json = json_object_new_object(); + + evpn_show_import_rts(vty, bgp, json); + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } @@ -2741,29 +3313,19 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, * Output EVPN configuration information. */ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi, int *write) + safi_t safi) { - struct evpn_config_write cfg; - - if (bgp->vnihash) { - cfg.write = *write; - cfg.vty = vty; + if (bgp->vnihash) hash_iterate(bgp->vnihash, (void (*)(struct hash_backet *, void *))write_vni_config_for_entry, - &cfg); - *write = cfg.write; - } + vty); - if (bgp->advertise_all_vni) { - bgp_config_write_family_header(vty, afi, safi, write); + if (bgp->advertise_all_vni) vty_out(vty, " advertise-all-vni\n"); - } - if (bgp->advertise_gw_macip) { - bgp_config_write_family_header(vty, afi, safi, write); + if (bgp->advertise_gw_macip) vty_out(vty, " advertise-default-gw\n"); - } } void bgp_ethernetvpn_init(void) diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h index 3bc24593f4..4d07f7d038 100644 --- a/bgpd/bgp_evpn_vty.h +++ b/bgpd/bgp_evpn_vty.h @@ -22,7 +22,7 @@ #define _FRR_BGP_EVPN_VTY_H extern void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, - afi_t afi, safi_t safi, int *write); + afi_t afi, safi_t safi); extern void bgp_ethernetvpn_init(void); #define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n" diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 3df40fa87a..37054ce425 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -101,6 +101,7 @@ DEFINE_MTYPE(BGPD, BGP_DAMP_ARRAY, "BGP Dampening array") DEFINE_MTYPE(BGPD, BGP_REGEXP, "BGP regexp") DEFINE_MTYPE(BGPD, BGP_AGGREGATE, "BGP aggregate") DEFINE_MTYPE(BGPD, BGP_ADDR, "BGP own address") +DEFINE_MTYPE(BGPD, TIP_ADDR, "BGP own tunnel-ip address") DEFINE_MTYPE(BGPD, BGP_REDIST, "BGP redistribution") DEFINE_MTYPE(BGPD, BGP_FILTER_NAME, "BGP Filter Information") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 152cfaeaf2..35b83a0401 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -97,6 +97,7 @@ DECLARE_MTYPE(BGP_DAMP_ARRAY) DECLARE_MTYPE(BGP_REGEXP) DECLARE_MTYPE(BGP_AGGREGATE) DECLARE_MTYPE(BGP_ADDR) +DECLARE_MTYPE(TIP_ADDR) DECLARE_MTYPE(BGP_REDIST) DECLARE_MTYPE(BGP_FILTER_NAME) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 63a84684bb..69c4ee1b67 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -88,11 +88,86 @@ static void bgp_nexthop_cache_reset(struct bgp_table *table) } } -/* BGP own address structure */ -struct bgp_addr { - struct in_addr addr; - int refcnt; -}; +static void *bgp_tip_hash_alloc(void *p) +{ + const struct in_addr *val = (const struct in_addr *)p; + struct tip_addr *addr; + + addr = XMALLOC(MTYPE_TIP_ADDR, sizeof(struct tip_addr)); + addr->refcnt = 0; + addr->addr.s_addr = val->s_addr; + + return addr; +} + +static void bgp_tip_hash_free(void *addr) +{ + XFREE(MTYPE_TIP_ADDR, addr); +} + +static unsigned int bgp_tip_hash_key_make(void *p) +{ + const struct tip_addr *addr = p; + + return jhash_1word(addr->addr.s_addr, 0); +} + +static int bgp_tip_hash_cmp(const void *p1, const void *p2) +{ + const struct tip_addr *addr1 = p1; + const struct tip_addr *addr2 = p2; + + return addr1->addr.s_addr == addr2->addr.s_addr; +} + +void bgp_tip_hash_init(struct bgp *bgp) +{ + bgp->tip_hash = hash_create(bgp_tip_hash_key_make, + bgp_tip_hash_cmp, NULL); +} + +void bgp_tip_hash_destroy(struct bgp *bgp) +{ + if (bgp->tip_hash == NULL) + return; + hash_clean(bgp->tip_hash, bgp_tip_hash_free); + hash_free(bgp->tip_hash); + bgp->tip_hash = NULL; +} + +void bgp_tip_add(struct bgp *bgp, struct in_addr *tip) +{ + struct tip_addr tmp; + struct tip_addr *addr; + + tmp.addr = *tip; + + addr = hash_get(bgp->tip_hash, &tmp, bgp_tip_hash_alloc); + if (!addr) + return; + + addr->refcnt++; +} + +void bgp_tip_del(struct bgp *bgp, struct in_addr *tip) +{ + struct tip_addr tmp; + struct tip_addr *addr; + + tmp.addr = *tip; + + addr = hash_lookup(bgp->tip_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + + addr->refcnt--; + + if (addr->refcnt == 0) { + hash_release(bgp->tip_hash, addr); + XFREE(MTYPE_TIP_ADDR, addr); + } +} static void *bgp_address_hash_alloc(void *p) { @@ -304,6 +379,7 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) int bgp_nexthop_self(struct bgp *bgp, struct in_addr nh_addr) { struct bgp_addr tmp, *addr; + struct tip_addr tmp_tip, *tip; tmp.addr = nh_addr; @@ -311,6 +387,11 @@ int bgp_nexthop_self(struct bgp *bgp, struct in_addr nh_addr) if (addr) return 1; + tmp_tip.addr = nh_addr; + tip = hash_lookup(bgp->tip_hash, &tmp_tip); + if (tip) + return 1; + return 0; } diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 37dad577c2..b482778fdf 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -66,6 +66,18 @@ struct bgp_nexthop_cache { struct bgp *bgp; }; +/* BGP own address structure */ +struct bgp_addr { + struct in_addr addr; + int refcnt; +}; + +/* Own tunnel-ip address structure */ +struct tip_addr { + struct in_addr addr; + int refcnt; +}; + extern int bgp_nexthop_lookup(afi_t, struct peer *peer, struct bgp_info *, int *, int *); extern void bgp_connected_add(struct bgp *bgp, struct connected *c); @@ -82,5 +94,9 @@ extern void bgp_scan_finish(struct bgp *bgp); extern void bgp_scan_vty_init(void); extern void bgp_address_init(struct bgp *bgp); extern void bgp_address_destroy(struct bgp *bgp); +extern void bgp_tip_add(struct bgp *bgp, struct in_addr *tip); +extern void bgp_tip_del(struct bgp *bgp, struct in_addr *tip); +extern void bgp_tip_hash_init(struct bgp *bgp); +extern void bgp_tip_hash_destroy(struct bgp *bgp); #endif /* _QUAGGA_BGP_NEXTHOP_H */ diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 3a7a60b14d..a0fc10c745 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -482,6 +482,34 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) evaluate_paths(bnc); } +/* + * Cleanup nexthop registration and status information for BGP nexthops + * pertaining to this VRF. This is invoked upon VRF deletion. + */ +void bgp_cleanup_nexthops(struct bgp *bgp) +{ + afi_t afi; + struct bgp_node *rn; + struct bgp_nexthop_cache *bnc; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (!bgp->nexthop_cache_table[afi]) + continue; + + for (rn = bgp_table_top(bgp->nexthop_cache_table[afi]); rn; + rn = bgp_route_next(rn)) { + bnc = rn->info; + if (!bnc) + continue; + + /* Clear relevant flags. */ + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + } + } +} + /** * make_prefix - make a prefix structure from the path (essentially * path's node. diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index f649bb2259..4b297f410c 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -66,4 +66,10 @@ void bgp_unlink_nexthop_by_peer(struct peer *); */ extern void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer); +/* + * Cleanup nexthop registration and status information for BGP nexthops + * pertaining to this VRF. This is invoked upon VRF deletion. + */ +extern void bgp_cleanup_nexthops(struct bgp *bgp); + #endif /* _BGP_NHT_H */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 3ee865e3ba..5c9ba89a57 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -1271,7 +1271,6 @@ void bgp_open_capability(struct stream *s, struct peer *peer) as_t local_as; u_int32_t restart_time; u_char afi_safi_count = 0; - struct utsname names; int adv_addpath_tx = 0; /* Remember current pointer for Opt Parm Len. */ @@ -1441,8 +1440,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) } /* Hostname capability */ - uname(&names); - if (names.nodename[0] != '\0') { + if (cmd_hostname_get()) { SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV); stream_putc(s, BGP_OPEN_OPT_CAP); rcapp = stream_get_endp(s); /* Ptr to length placeholder */ @@ -1450,26 +1448,21 @@ void bgp_open_capability(struct stream *s, struct peer *peer) stream_putc(s, CAPABILITY_CODE_FQDN); capp = stream_get_endp(s); stream_putc(s, 0); /* dummy len for now */ - len = strlen(names.nodename); + len = strlen(cmd_hostname_get()); if (len > BGP_MAX_HOSTNAME) len = BGP_MAX_HOSTNAME; stream_putc(s, len); - stream_put(s, names.nodename, len); -#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME - if ((names.domainname[0] != '\0') - && (strcmp(names.domainname, "(none)") != 0)) { - len = strlen(names.domainname); + stream_put(s, cmd_hostname_get(), len); + if (cmd_domainname_get()) { + len = strlen(cmd_domainname_get()); if (len > BGP_MAX_HOSTNAME) len = BGP_MAX_HOSTNAME; stream_putc(s, len); - stream_put(s, names.domainname, len); + stream_put(s, cmd_domainname_get(), len); } else -#endif - { stream_putc(s, 0); /* 0 length */ - } /* Set the lengths straight */ len = stream_get_endp(s) - rcapp - 1; @@ -1478,14 +1471,10 @@ void bgp_open_capability(struct stream *s, struct peer *peer) stream_putc_at(s, capp, len); if (bgp_debug_neighbor_events(peer)) -#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME zlog_debug( "%s Sending hostname cap with hn = %s, dn = %s", - peer->host, names.nodename, names.domainname); -#else - zlog_debug("%s Sending hostname cap with hn = %s", - peer->host, names.nodename); -#endif + peer->host, cmd_hostname_get(), + cmd_domainname_get()); } /* Sending base graceful-restart capability irrespective of the config diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 768b2b8fb6..aafd173a7b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2042,11 +2042,10 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, /* Do we need to allocate or free labels? * Right now, since we only deal with per-prefix labels, it is not - * necessary - * to do this upon changes to best path except of the label index - * changes. + * necessary to do this upon changes to best path except if the label + * index changes */ - if (safi == SAFI_UNICAST) { + if (bgp->allocate_mpls_labels[afi][safi]) { if (new_select) { if (!old_select || bgp_label_index_differs(new_select, old_select) @@ -2067,8 +2066,11 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } else bgp_register_for_label(rn, new_select); } - } else if (CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) + } else if (CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { bgp_unregister_for_label(rn); + } + } else if (CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { + bgp_unregister_for_label(rn); } /* If best route remains the same and this is not due to user-initiated @@ -2198,18 +2200,21 @@ static wq_item_status bgp_process_wq(struct work_queue *wq, void *data) struct bgp_process_queue *pqnode = data; struct bgp *bgp = pqnode->bgp; struct bgp_table *table; - struct bgp_node *rn, *nrn; + struct bgp_node *rn; /* eoiu marker */ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER)) { bgp_process_main_one(bgp, NULL, 0, 0); - + assert(STAILQ_FIRST(&pqnode->pqueue) == NULL); /* should always have dedicated wq call */ return WQ_SUCCESS; } - STAILQ_FOREACH_SAFE(rn, &pqnode->pqueue, pq, nrn) { + while (!STAILQ_EMPTY(&pqnode->pqueue)) { + rn = STAILQ_FIRST(&pqnode->pqueue); + STAILQ_REMOVE_HEAD(&pqnode->pqueue, pq); + STAILQ_NEXT(rn, pq) = NULL; /* complete unlink */ table = bgp_node_table(rn); - + /* note, new RNs may be added as part of processing */ bgp_process_main_one(bgp, rn, table->afi, table->safi); bgp_unlock_node(rn); @@ -2248,8 +2253,7 @@ void bgp_process_queue_init(void) bm->process_main_queue->spec.yield = 50 * 1000L; } -static struct bgp_process_queue *bgp_process_queue_work(struct work_queue *wq, - struct bgp *bgp) +static struct bgp_process_queue *bgp_processq_alloc(struct bgp *bgp) { struct bgp_process_queue *pqnode; @@ -2259,8 +2263,6 @@ static struct bgp_process_queue *bgp_process_queue_work(struct work_queue *wq, pqnode->bgp = bgp_lock(bgp); STAILQ_INIT(&pqnode->pqueue); - work_queue_add(wq, pqnode); - return pqnode; } @@ -2269,6 +2271,7 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) #define ARBITRARY_PROCESS_QLEN 10000 struct work_queue *wq = bm->process_main_queue; struct bgp_process_queue *pqnode; + int pqnode_reuse = 0; /* already scheduled for processing? */ if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) @@ -2285,19 +2288,24 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER) || pqnode->bgp != bgp || pqnode->queued >= ARBITRARY_PROCESS_QLEN) - pqnode = bgp_process_queue_work(wq, bgp); + pqnode = bgp_processq_alloc(bgp); + else + pqnode_reuse = 1; } else - pqnode = bgp_process_queue_work(wq, bgp); - + pqnode = bgp_processq_alloc(bgp); /* all unlocked in bgp_process_wq */ bgp_table_lock(bgp_node_table(rn)); SET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED); bgp_lock_node(rn); + assert(STAILQ_NEXT(rn, pq) == NULL); /* can't be enqueued twice */ STAILQ_INSERT_TAIL(&pqnode->pqueue, rn, pq); pqnode->queued++; + if (!pqnode_reuse) + work_queue_add(wq, pqnode); + return; } @@ -2308,9 +2316,10 @@ void bgp_add_eoiu_mark(struct bgp *bgp) if (bm->process_main_queue == NULL) return; - pqnode = bgp_process_queue_work(bm->process_main_queue, bgp); + pqnode = bgp_processq_alloc(bgp); SET_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER); + work_queue_add(bm->process_main_queue, pqnode); } static int bgp_maximum_prefix_restart_timer(struct thread *thread) @@ -2422,7 +2431,7 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, /* Unconditionally remove the route from the RIB, without taking * damping into consideration (eg, because the session went down) */ -static void bgp_rib_remove(struct bgp_node *rn, struct bgp_info *ri, +void bgp_rib_remove(struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, afi_t afi, safi_t safi) { bgp_aggregate_decrement(peer->bgp, &rn->p, ri, afi, safi); @@ -3388,21 +3397,22 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) for (ain = rn->adj_in; ain; ain = ain->next) { - if (ain->peer == peer) { - struct bgp_info *ri = rn->info; - mpls_label_t label = - (ri && ri->extra) ? ri->extra->label - : MPLS_INVALID_LABEL; + if (ain->peer != peer) + continue; - ret = bgp_update( - peer, &rn->p, ain->addpath_rx_id, - ain->attr, afi, safi, ZEBRA_ROUTE_BGP, - BGP_ROUTE_NORMAL, prd, &label, 1, NULL); + struct bgp_info *ri = rn->info; + mpls_label_t label = + (ri && ri->extra) ? ri->extra->label + : MPLS_INVALID_LABEL; - if (ret < 0) { - bgp_unlock_node(rn); - return; - } + ret = bgp_update(peer, &rn->p, ain->addpath_rx_id, + ain->attr, afi, safi, ZEBRA_ROUTE_BGP, + BGP_ROUTE_NORMAL, prd, &label, 1, + NULL); + + if (ret < 0) { + bgp_unlock_node(rn); + return; } } } @@ -3451,24 +3461,25 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data) /* It is possible that we have multiple paths for a prefix from a peer * if that peer is using AddPath. */ - for (ri = rn->info; ri; ri = ri->next) - if (ri->peer == peer) { - /* graceful restart STALE flag set. */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT) - && peer->nsf[afi][safi] - && !CHECK_FLAG(ri->flags, BGP_INFO_STALE) - && !CHECK_FLAG(ri->flags, BGP_INFO_UNUSEABLE)) - bgp_info_set_flag(rn, ri, BGP_INFO_STALE); - else { - /* If this is an EVPN route, process for - * un-import. */ - if (safi == SAFI_EVPN) - bgp_evpn_unimport_route(peer->bgp, afi, - safi, &rn->p, - ri); - bgp_rib_remove(rn, ri, peer, afi, safi); - } + for (ri = rn->info; ri; ri = ri->next) { + if (ri->peer != peer) + continue; + + /* graceful restart STALE flag set. */ + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT) + && peer->nsf[afi][safi] + && !CHECK_FLAG(ri->flags, BGP_INFO_STALE) + && !CHECK_FLAG(ri->flags, BGP_INFO_UNUSEABLE)) + bgp_info_set_flag(rn, ri, BGP_INFO_STALE); + else { + /* If this is an EVPN route, process for + * un-import. */ + if (safi == SAFI_EVPN) + bgp_evpn_unimport_route(peer->bgp, afi, safi, + &rn->p, ri); + bgp_rib_remove(rn, ri, peer, afi, safi); } + } return WQ_SUCCESS; } @@ -3702,34 +3713,33 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) struct bgp_info *ri; /* look for neighbor in tables */ - if ((table = rn->info) != NULL) { - for (rm = bgp_table_top(table); rm; - rm = bgp_route_next(rm)) - for (ri = rm->info; ri; ri = ri->next) - if (ri->peer == peer) { - if (CHECK_FLAG( - ri->flags, - BGP_INFO_STALE)) - bgp_rib_remove( - rm, ri, - peer, - afi, - safi); - break; - } - } + if ((table = rn->info) == NULL) + continue; + + for (rm = bgp_table_top(table); rm; + rm = bgp_route_next(rm)) + for (ri = rm->info; ri; ri = ri->next) { + if (ri->peer != peer) + continue; + if (!CHECK_FLAG(ri->flags, + BGP_INFO_STALE)) + break; + + bgp_rib_remove(rm, ri, peer, afi, safi); + break; + } } } else { for (rn = bgp_table_top(peer->bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) - for (ri = rn->info; ri; ri = ri->next) - if (ri->peer == peer) { - if (CHECK_FLAG(ri->flags, - BGP_INFO_STALE)) - bgp_rib_remove(rn, ri, peer, - afi, safi); + for (ri = rn->info; ri; ri = ri->next) { + if (ri->peer != peer) + continue; + if (!CHECK_FLAG(ri->flags, BGP_INFO_STALE)) break; - } + bgp_rib_remove(rn, ri, peer, afi, safi); + break; + } } } @@ -4553,28 +4563,30 @@ void bgp_static_add(struct bgp *bgp) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) - if (rn->info != NULL) { - if ((safi == SAFI_MPLS_VPN) - || (safi == SAFI_ENCAP) - || (safi == SAFI_EVPN)) { - table = rn->info; - - for (rm = bgp_table_top(table); - rm; - rm = bgp_route_next(rm)) { - bgp_static = rm->info; - bgp_static_update_safi( - bgp, &rm->p, - bgp_static, afi, - safi); - } - } else { - bgp_static_update(bgp, &rn->p, - rn->info, afi, - safi); + rn = bgp_route_next(rn)) { + if (rn->info == NULL) + continue; + + if ((safi == SAFI_MPLS_VPN) + || (safi == SAFI_ENCAP) + || (safi == SAFI_EVPN)) { + table = rn->info; + + for (rm = bgp_table_top(table); + rm; + rm = bgp_route_next(rm)) { + bgp_static = rm->info; + bgp_static_update_safi( + bgp, &rm->p, + bgp_static, afi, + safi); } + } else { + bgp_static_update(bgp, &rn->p, + rn->info, afi, + safi); } + } } /* Called from bgp_delete(). Delete all static routes from the BGP @@ -4591,37 +4603,38 @@ void bgp_static_delete(struct bgp *bgp) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) - if (rn->info != NULL) { - if ((safi == SAFI_MPLS_VPN) - || (safi == SAFI_ENCAP) - || (safi == SAFI_EVPN)) { - table = rn->info; - - for (rm = bgp_table_top(table); - rm; - rm = bgp_route_next(rm)) { - bgp_static = rm->info; - bgp_static_withdraw_safi( - bgp, &rm->p, - AFI_IP, safi, - (struct - prefix_rd *)&rn - ->p); - bgp_static_free( - bgp_static); - rn->info = NULL; - bgp_unlock_node(rn); - } - } else { - bgp_static = rn->info; - bgp_static_withdraw(bgp, &rn->p, - afi, safi); + rn = bgp_route_next(rn)) { + if (rn->info == NULL) + continue; + + if ((safi == SAFI_MPLS_VPN) + || (safi == SAFI_ENCAP) + || (safi == SAFI_EVPN)) { + table = rn->info; + + for (rm = bgp_table_top(table); + rm; + rm = bgp_route_next(rm)) { + bgp_static = rm->info; + bgp_static_withdraw_safi( + bgp, &rm->p, + AFI_IP, safi, + (struct + prefix_rd *)&rn + ->p); bgp_static_free(bgp_static); rn->info = NULL; bgp_unlock_node(rn); } + } else { + bgp_static = rn->info; + bgp_static_withdraw(bgp, &rn->p, + afi, safi); + bgp_static_free(bgp_static); + rn->info = NULL; + bgp_unlock_node(rn); } + } } void bgp_static_redo_import_check(struct bgp *bgp) @@ -4638,29 +4651,31 @@ void bgp_static_redo_import_check(struct bgp *bgp) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) - if (rn->info != NULL) { - if ((safi == SAFI_MPLS_VPN) - || (safi == SAFI_ENCAP) - || (safi == SAFI_EVPN)) { - table = rn->info; - - for (rm = bgp_table_top(table); - rm; - rm = bgp_route_next(rm)) { - bgp_static = rm->info; - bgp_static_update_safi( - bgp, &rm->p, - bgp_static, afi, - safi); - } - } else { - bgp_static = rn->info; - bgp_static_update(bgp, &rn->p, - bgp_static, - afi, safi); + rn = bgp_route_next(rn)) { + if (rn->info == NULL) + continue; + + if ((safi == SAFI_MPLS_VPN) + || (safi == SAFI_ENCAP) + || (safi == SAFI_EVPN)) { + table = rn->info; + + for (rm = bgp_table_top(table); + rm; + rm = bgp_route_next(rm)) { + bgp_static = rm->info; + bgp_static_update_safi( + bgp, &rm->p, + bgp_static, afi, + safi); } + } else { + bgp_static = rn->info; + bgp_static_update(bgp, &rn->p, + bgp_static, + afi, safi); } + } bgp_flag_unset(bgp, BGP_FLAG_FORCE_STATIC_PROCESS); } @@ -4944,16 +4959,13 @@ static int bgp_table_map_unset(struct vty *vty, afi_t afi, safi_t safi, return CMD_SUCCESS; } -int bgp_config_write_table_map(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi, int *write) +void bgp_config_write_table_map(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { if (bgp->table_map[afi][safi].name) { - bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " table-map %s\n", bgp->table_map[afi][safi].name); } - - return 0; } DEFUN (bgp_table_map, @@ -5684,83 +5696,76 @@ static void bgp_aggregate_add(struct bgp *bgp, struct prefix *p, afi_t afi, /* If routes exists below this node, generate aggregate routes. */ top = bgp_node_get(table, p); for (rn = bgp_node_get(table, p); rn; - rn = bgp_route_next_until(rn, top)) - if (rn->p.prefixlen > p->prefixlen) { - match = 0; + rn = bgp_route_next_until(rn, top)) { + if (rn->p.prefixlen <= p->prefixlen) + continue; - for (ri = rn->info; ri; ri = ri->next) { - if (BGP_INFO_HOLDDOWN(ri)) - continue; + match = 0; - if (ri->attr->flag - & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) - atomic_aggregate = 1; + for (ri = rn->info; ri; ri = ri->next) { + if (BGP_INFO_HOLDDOWN(ri)) + continue; - if (ri->sub_type != BGP_ROUTE_AGGREGATE) { - /* summary-only aggregate route suppress - aggregated - route announcement. */ - if (aggregate->summary_only) { - (bgp_info_extra_get(ri)) - ->suppress++; - bgp_info_set_flag( - rn, ri, - BGP_INFO_ATTR_CHANGED); - match++; - } + if (ri->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) + atomic_aggregate = 1; - /* If at least one route among routes - * that are aggregated has - * ORIGIN with the value INCOMPLETE, - * then the aggregated route - * MUST have the ORIGIN attribute with - * the value INCOMPLETE. - * Otherwise, if at least one route - * among routes that are - * aggregated has ORIGIN with the value - * EGP, then the aggregated - * route MUST have the ORIGIN attribute - * with the value EGP. - */ - if (origin < ri->attr->origin) - origin = ri->attr->origin; + if (ri->sub_type == BGP_ROUTE_AGGREGATE) + continue; - /* as-set aggregate route generate - origin, as path, - community aggregation. */ - if (aggregate->as_set) { - if (aspath) { - asmerge = aspath_aggregate( - aspath, - ri->attr->aspath); - aspath_free(aspath); - aspath = asmerge; - } else - aspath = aspath_dup( - ri->attr->aspath); + /* summary-only aggregate route suppress + * aggregated route announcement. */ + if (aggregate->summary_only) { + (bgp_info_extra_get(ri))->suppress++; + bgp_info_set_flag(rn, ri, + BGP_INFO_ATTR_CHANGED); + match++; + } - if (ri->attr->community) { - if (community) { - commerge = community_merge( - community, - ri->attr->community); - community = community_uniq_sort( - commerge); - community_free( - commerge); - } else - community = community_dup( - ri->attr->community); - } - } - aggregate->count++; + /* If at least one route among routes that are + * aggregated has ORIGIN with the value INCOMPLETE, + * then the aggregated route MUST have the ORIGIN + * attribute with the value INCOMPLETE. Otherwise, if + * at least one route among routes that are aggregated + * has ORIGIN with the value EGP, then the aggregated + * route MUST have the ORIGIN attribute with the value + * EGP. + */ + if (origin < ri->attr->origin) + origin = ri->attr->origin; + + /* as-set aggregate route generate origin, as path, + * community aggregation. */ + if (aggregate->as_set) { + if (aspath) { + asmerge = aspath_aggregate(aspath, + ri->attr->aspath); + aspath_free(aspath); + aspath = asmerge; + } else + aspath = aspath_dup(ri->attr->aspath); + + if (ri->attr->community) { + if (community) { + commerge = community_merge( + community, + ri->attr->community); + community = community_uniq_sort( + commerge); + community_free( + commerge); + } else + community = community_dup( + ri->attr->community); } } - - /* If this node is suppressed, process the change. */ - if (match) - bgp_process(bgp, rn, afi, safi); + aggregate->count++; } + + /* If this node is suppressed, process the change. */ + if (match) + bgp_process(bgp, rn, afi, safi); + } bgp_unlock_node(top); /* Add aggregate route to BGP table. */ @@ -5806,34 +5811,34 @@ void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, /* If routes exists below this node, generate aggregate routes. */ top = bgp_node_get(table, p); for (rn = bgp_node_get(table, p); rn; - rn = bgp_route_next_until(rn, top)) - if (rn->p.prefixlen > p->prefixlen) { - match = 0; + rn = bgp_route_next_until(rn, top)) { + if (rn->p.prefixlen <= p->prefixlen) + continue; + match = 0; - for (ri = rn->info; ri; ri = ri->next) { - if (BGP_INFO_HOLDDOWN(ri)) - continue; + for (ri = rn->info; ri; ri = ri->next) { + if (BGP_INFO_HOLDDOWN(ri)) + continue; - if (ri->sub_type != BGP_ROUTE_AGGREGATE) { - if (aggregate->summary_only - && ri->extra) { - ri->extra->suppress--; - - if (ri->extra->suppress == 0) { - bgp_info_set_flag( - rn, ri, - BGP_INFO_ATTR_CHANGED); - match++; - } - } - aggregate->count--; + if (ri->sub_type == BGP_ROUTE_AGGREGATE) + continue; + + if (aggregate->summary_only && ri->extra) { + ri->extra->suppress--; + + if (ri->extra->suppress == 0) { + bgp_info_set_flag(rn, ri, + BGP_INFO_ATTR_CHANGED); + match++; } } - - /* If this node was suppressed, process the change. */ - if (match) - bgp_process(bgp, rn, afi, safi); + aggregate->count--; } + + /* If this node was suppressed, process the change. */ + if (match) + bgp_process(bgp, rn, afi, safi); + } bgp_unlock_node(top); /* Delete aggregate route from BGP table. */ @@ -6267,46 +6272,65 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type, } /* Static function to display route. */ -static void route_vty_out_route(struct prefix *p, struct vty *vty) +static void route_vty_out_route(struct prefix *p, struct vty *vty, + json_object *json) { int len; u_int32_t destination; char buf[BUFSIZ]; if (p->family == AF_INET) { - len = vty_out(vty, "%s", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ)); - destination = ntohl(p->u.prefix4.s_addr); - - if ((IN_CLASSC(destination) && p->prefixlen == 24) - || (IN_CLASSB(destination) && p->prefixlen == 16) - || (IN_CLASSA(destination) && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* When mask is natural, mask is not displayed. */ - } else - len += vty_out(vty, "/%d", p->prefixlen); + if (!json) { + len = vty_out(vty, "%s", + inet_ntop(p->family, &p->u.prefix, buf, + BUFSIZ)); + destination = ntohl(p->u.prefix4.s_addr); + + if ((IN_CLASSC(destination) && p->prefixlen == 24) + || (IN_CLASSB(destination) && p->prefixlen == 16) + || (IN_CLASSA(destination) && p->prefixlen == 8) + || p->u.prefix4.s_addr == 0) { + /* When mask is natural, + mask is not displayed. */ + } else + len += vty_out(vty, "/%d", p->prefixlen); + } else { + json_object_string_add(json, "prefix", + inet_ntop(p->family, + &p->u.prefix, buf, + BUFSIZ)); + json_object_int_add(json, "prefixLen", p->prefixlen); + } } else if (p->family == AF_ETHERNET) { prefix2str(p, buf, PREFIX_STRLEN); len = vty_out(vty, "%s", buf); } else if (p->family == AF_EVPN) { #if defined(HAVE_CUMULUS) - len = vty_out(vty, "%s", - bgp_evpn_route2str((struct prefix_evpn *)p, buf, - BUFSIZ)); + if (!json) + len = vty_out(vty, "%s", + bgp_evpn_route2str((struct prefix_evpn *)p, + buf, BUFSIZ)); + else + bgp_evpn_route2json((struct prefix_evpn *) p, json); #else prefix2str(p, buf, PREFIX_STRLEN); len = vty_out(vty, "%s", buf); #endif - } else - len = vty_out(vty, "%s/%d", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), - p->prefixlen); + } else { + if (!json) + len = vty_out(vty, "%s/%d", + inet_ntop(p->family, &p->u.prefix, buf, + BUFSIZ), + p->prefixlen); + } - len = 17 - len; - if (len < 1) - vty_out(vty, "\n%*s", 20, " "); - else - vty_out(vty, "%*s", len, " "); + if (!json) { + len = 17 - len; + if (len < 1) + vty_out(vty, "\n%*s", 20, " "); + else + vty_out(vty, "%*s", len, " "); + } } enum bgp_display_type { @@ -6410,9 +6434,11 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo, if (!json_paths) { /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, json_path); else vty_out(vty, "%*s", 17, " "); + } else { + route_vty_out_route(p, vty, json_path); } /* Print attribute */ @@ -6708,7 +6734,7 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, json_net, "addrPrefix", inet_ntop(p->family, &p->u.prefix, buff, BUFSIZ)); else - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); /* Print attribute */ if (attr) { @@ -6841,7 +6867,7 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (json == NULL) { if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); } @@ -6956,7 +6982,7 @@ void route_vty_out_overlay(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); @@ -7025,7 +7051,7 @@ static void damp_route_vty_out(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); } @@ -7094,7 +7120,7 @@ static void flap_route_vty_out(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty); + route_vty_out_route(p, vty, NULL); else vty_out(vty, "%*s", 17, " "); } @@ -8020,7 +8046,8 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp, static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type); -static int bgp_show_regexp(struct vty *vty, const char *regstr, afi_t afi, +static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, + const char *regstr, afi_t afi, safi_t safi, enum bgp_show_type type); static int bgp_show_community(struct vty *vty, struct bgp *bgp, int argc, struct cmd_token **argv, int exact, afi_t afi, @@ -8376,6 +8403,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, #if defined(HAVE_CUMULUS) char buf3[EVPN_ROUTE_STRLEN]; #endif + char prefix_str[BUFSIZ]; int count = 0; int best = 0; int suppress = 0; @@ -8398,9 +8426,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, json_object_int_add(json, "localLabel", label); json_object_string_add(json, "prefix", - inet_ntop(p->family, &p->u.prefix, buf2, - INET6_ADDRSTRLEN)); - json_object_int_add(json, "prefixlen", p->prefixlen); + prefix2str(p, prefix_str, + sizeof(prefix_str))); } else { #if defined(HAVE_CUMULUS) if (safi == SAFI_EVPN) @@ -8560,55 +8587,44 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) continue; - if ((table = rn->info) != NULL) { - header = 1; - - if ((rm = bgp_node_match(table, &match)) - != NULL) { - if (prefix_check - && rm->p.prefixlen - != match.prefixlen) { - bgp_unlock_node(rm); - continue; - } + if ((table = rn->info) == NULL) + continue; - for (ri = rm->info; ri; ri = ri->next) { - if (header) { - route_vty_out_detail_header( - vty, bgp, rm, - (struct - prefix_rd *)&rn - ->p, - AFI_IP, safi, - json); - header = 0; - } - display++; - - if (pathtype == BGP_PATH_ALL - || (pathtype - == BGP_PATH_BESTPATH - && CHECK_FLAG( - ri->flags, - BGP_INFO_SELECTED)) - || (pathtype - == BGP_PATH_MULTIPATH - && (CHECK_FLAG( - ri->flags, - BGP_INFO_MULTIPATH) - || CHECK_FLAG( - ri->flags, - BGP_INFO_SELECTED)))) - route_vty_out_detail( - vty, bgp, - &rm->p, ri, - AFI_IP, safi, - json_paths); - } + header = 1; + + if ((rm = bgp_node_match(table, &match)) == NULL) + continue; + + if (prefix_check + && rm->p.prefixlen != match.prefixlen) { + bgp_unlock_node(rm); + continue; + } - bgp_unlock_node(rm); + for (ri = rm->info; ri; ri = ri->next) { + if (header) { + route_vty_out_detail_header(vty, bgp, + rm, (struct prefix_rd *)&rn->p, + AFI_IP, safi, json); + header = 0; } + display++; + + if (pathtype == BGP_PATH_ALL + || (pathtype == BGP_PATH_BESTPATH + && CHECK_FLAG(ri->flags, + BGP_INFO_SELECTED)) + || (pathtype == BGP_PATH_MULTIPATH + && (CHECK_FLAG(ri->flags, + BGP_INFO_MULTIPATH) + || CHECK_FLAG(ri->flags, + BGP_INFO_SELECTED)))) + route_vty_out_detail(vty, bgp, &rm->p, + ri, AFI_IP, safi, + json_paths); } + + bgp_unlock_node(rm); } } else { header = 1; @@ -8838,32 +8854,28 @@ DEFUN (show_ip_bgp_large_community, static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi); -/* BGP route print out function. */ + +/* BGP route print out function without JSON */ DEFUN (show_ip_bgp, show_ip_bgp_cmd, "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\ - [<\ - cidr-only\ - |dampening <flap-statistics|dampened-paths|parameters>\ - |route-map WORD\ - |prefix-list WORD\ - |filter-list WORD\ - |statistics\ - |community [<AA:NN|local-AS|no-advertise|no-export> [exact-match]]\ - |community-list <(1-500)|WORD> [exact-match]\ - |A.B.C.D/M longer-prefixes\ - |X:X::X:X/M longer-prefixes>\ - ] [json]", + <dampening <parameters>\ + |route-map WORD\ + |prefix-list WORD\ + |filter-list WORD\ + |statistics\ + |community <AA:NN|local-AS|no-advertise|no-export> [exact-match]\ + |community-list <(1-500)|WORD> [exact-match]\ + |A.B.C.D/M longer-prefixes\ + |X:X::X:X/M longer-prefixes\ + >", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR - "Display only routes with non-natural netmasks\n" "Display detailed information about dampening\n" - "Display flap statistics of routes\n" - "Display paths suppressed due to dampening\n" "Display detail of configured dampening parameters\n" "Display routes matching the route-map\n" "A route-map to match on\n" @@ -8885,13 +8897,11 @@ DEFUN (show_ip_bgp, "IPv4 prefix\n" "Display route and more specific routes\n" "IPv6 prefix\n" - "Display route and more specific routes\n" - JSON_STR) + "Display route and more specific routes\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; int exact_match = 0; - enum bgp_show_type sh_type = bgp_show_type_normal; struct bgp *bgp = NULL; int idx = 0; @@ -8900,23 +8910,8 @@ DEFUN (show_ip_bgp, if (!idx) return CMD_WARNING; - int uj = use_json(argc, argv); - if (uj) - argc--; - - if (argv_find(argv, argc, "cidr-only", &idx)) - return bgp_show(vty, bgp, afi, safi, bgp_show_type_cidr_only, - NULL, uj); - if (argv_find(argv, argc, "dampening", &idx)) { - if (argv_find(argv, argc, "dampened-paths", &idx)) - return bgp_show(vty, bgp, afi, safi, - bgp_show_type_dampend_paths, NULL, uj); - else if (argv_find(argv, argc, "flap-statistics", &idx)) - return bgp_show(vty, bgp, afi, safi, - bgp_show_type_flap_statistics, NULL, - uj); - else if (argv_find(argv, argc, "parameters", &idx)) + if (argv_find(argv, argc, "parameters", &idx)) return bgp_show_dampening_parameters(vty, afi, safi); } @@ -8945,10 +8940,6 @@ DEFUN (show_ip_bgp, return bgp_show_community(vty, bgp, argc, argv, exact_match, afi, safi); } - /* show all communities */ - else - return bgp_show(vty, bgp, afi, safi, - bgp_show_type_community_all, NULL, uj); } if (argv_find(argv, argc, "community-list", &idx)) { @@ -8965,6 +8956,66 @@ DEFUN (show_ip_bgp, safi, bgp_show_type_prefix_longer); + return CMD_WARNING; +} + +/* BGP route print out function with JSON */ +DEFUN (show_ip_bgp_json, + show_ip_bgp_json_cmd, + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\ + [<\ + cidr-only\ + |dampening <flap-statistics|dampened-paths>\ + |community \ + >] [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Display only routes with non-natural netmasks\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display paths suppressed due to dampening\n" + "Display routes matching the communities\n" + JSON_STR) +{ + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + enum bgp_show_type sh_type = bgp_show_type_normal; + struct bgp *bgp = NULL; + int idx = 0; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp); + if (!idx) + return CMD_WARNING; + + int uj = use_json(argc, argv); + if (uj) + argc--; + + if (argv_find(argv, argc, "cidr-only", &idx)) + return bgp_show(vty, bgp, afi, safi, bgp_show_type_cidr_only, + NULL, uj); + + if (argv_find(argv, argc, "dampening", &idx)) { + if (argv_find(argv, argc, "dampened-paths", &idx)) + return bgp_show(vty, bgp, afi, safi, + bgp_show_type_dampend_paths, NULL, uj); + else if (argv_find(argv, argc, "flap-statistics", &idx)) + return bgp_show(vty, bgp, afi, safi, + bgp_show_type_flap_statistics, NULL, + uj); + } + + if (argv_find(argv, argc, "community", &idx)) { + /* show all communities */ + return bgp_show(vty, bgp, afi, safi, + bgp_show_type_community_all, NULL, uj); + } + if (safi == SAFI_MPLS_VPN) return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_normal, NULL, 0, uj); @@ -9074,7 +9125,7 @@ DEFUN (show_ip_bgp_regexp, idx++; char *regstr = argv_concat(argv, argc, idx); - int rc = bgp_show_regexp(vty, (const char *)regstr, afi, safi, + int rc = bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi, bgp_show_type_regexp); XFREE(MTYPE_TMP, regstr); return rc; @@ -9109,7 +9160,8 @@ DEFUN (show_ip_bgp_instance_all, return CMD_SUCCESS; } -static int bgp_show_regexp(struct vty *vty, const char *regstr, afi_t afi, +static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, + const char *regstr, afi_t afi, safi_t safi, enum bgp_show_type type) { regex_t *regex; @@ -9121,7 +9173,7 @@ static int bgp_show_regexp(struct vty *vty, const char *regstr, afi_t afi, return CMD_WARNING; } - rc = bgp_show(vty, NULL, afi, safi, type, regex, 0); + rc = bgp_show(vty, bgp, afi, safi, type, regex, 0); bgp_regex_free(regex); return rc; } @@ -9958,60 +10010,55 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { if (in) { for (ain = rn->adj_in; ain; ain = ain->next) { - if (ain->peer == peer) { - if (header1) { - if (use_json) { - json_object_int_add( - json, - "bgpTableVersion", - 0); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_object_add( - json, - "bgpStatusCodes", - json_scode); - json_object_object_add( - json, - "bgpOriginCodes", - json_ocode); - } else { - vty_out(vty, - "BGP table version is 0, local router ID is %s\n", - inet_ntoa( - bgp->router_id)); - vty_out(vty, - BGP_SHOW_SCODE_HEADER); - vty_out(vty, - BGP_SHOW_OCODE_HEADER); - } - header1 = 0; - } - if (header2) { - if (!use_json) - vty_out(vty, - BGP_SHOW_HEADER); - header2 = 0; - } - if (ain->attr) { - bgp_attr_dup(&attr, ain->attr); - if (bgp_input_modifier( - peer, &rn->p, &attr, - afi, safi, - rmap_name) - != RMAP_DENY) { - route_vty_out_tmp( - vty, &rn->p, - &attr, safi, - use_json, - json_ar); - output_count++; - } else - filtered_count++; + if (ain->peer != peer) + continue; + if (header1) { + if (use_json) { + json_object_int_add( + json, + "bgpTableVersion", + 0); + json_object_string_add( + json, + "bgpLocalRouterId", + inet_ntoa( + bgp->router_id)); + json_object_object_add( + json, + "bgpStatusCodes", + json_scode); + json_object_object_add( + json, + "bgpOriginCodes", + json_ocode); + } else { + vty_out(vty, + "BGP table version is 0, local router ID is %s\n", + inet_ntoa( + bgp->router_id)); + vty_out(vty, + BGP_SHOW_SCODE_HEADER); + vty_out(vty, + BGP_SHOW_OCODE_HEADER); } + header1 = 0; + } + if (header2) { + if (!use_json) + vty_out(vty, BGP_SHOW_HEADER); + header2 = 0; + } + if (ain->attr) { + bgp_attr_dup(&attr, ain->attr); + if (bgp_input_modifier(peer, &rn->p, + &attr, afi, safi, rmap_name) + != RMAP_DENY) { + route_vty_out_tmp(vty, &rn->p, + &attr, safi, use_json, + json_ar); + output_count++; + } else + filtered_count++; } } } else { @@ -10815,32 +10862,27 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, rn = bgp_route_next(rn)) { if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) continue; + if ((table = rn->info) == NULL) + continue; + if ((rm = bgp_node_match(table, &match)) == NULL) + continue; - if ((table = rn->info) != NULL) - if ((rm = bgp_node_match(table, &match)) - != NULL) { - if (!prefix_check - || rm->p.prefixlen - == match.prefixlen) { - ri = rm->info; - while (ri) { - if (ri->extra - && ri->extra - ->damp_info) { - ri_temp = - ri->next; - bgp_damp_info_free( - ri->extra - ->damp_info, - 1); - ri = ri_temp; - } else - ri = ri->next; - } - } - - bgp_unlock_node(rm); + if (!prefix_check + || rm->p.prefixlen == match.prefixlen) { + ri = rm->info; + while (ri) { + if (ri->extra + && ri->extra->damp_info) { + ri_temp = ri->next; + bgp_damp_info_free( + ri->extra->damp_info, 1); + ri = ri_temp; + } else + ri = ri->next; } + } + + bgp_unlock_node(rm); } } else { if ((rn = bgp_node_match(bgp->rib[afi][safi], &match)) @@ -10934,8 +10976,8 @@ DEFUN (clear_ip_bgp_dampening_address_mask, } /* also used for encap safi */ -static int bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, - afi_t afi, safi_t safi, int *write) +static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { struct bgp_node *prn; struct bgp_node *rn; @@ -10949,48 +10991,43 @@ static int bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, /* Network configuration. */ for (prn = bgp_table_top(bgp->route[afi][safi]); prn; - prn = bgp_route_next(prn)) - if ((table = prn->info) != NULL) - for (rn = bgp_table_top(table); rn; - rn = bgp_route_next(rn)) - if ((bgp_static = rn->info) != NULL) { - p = &rn->p; - prd = (struct prefix_rd *)&prn->p; + prn = bgp_route_next(prn)) { + if ((table = prn->info) == NULL) + continue; - /* "address-family" display. */ - bgp_config_write_family_header( - vty, afi, safi, write); + for (rn = bgp_table_top(table); rn; + rn = bgp_route_next(rn)) { + if ((bgp_static = rn->info) == NULL) + continue; - /* "network" configuration display. */ - prefix_rd2str(prd, rdbuf, - RD_ADDRSTRLEN); - label = decode_label( - &bgp_static->label); + p = &rn->p; + prd = (struct prefix_rd *)&prn->p; - vty_out(vty, " network %s/%d rd %s", - inet_ntop(p->family, - &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen, rdbuf); - if (safi == SAFI_MPLS_VPN) - vty_out(vty, " label %u", - label); - - if (bgp_static->rmap.name) - vty_out(vty, " route-map %s", - bgp_static->rmap.name); - else { - if (bgp_static->backdoor) - vty_out(vty, - " backdoor"); - } - vty_out(vty, "\n"); - } - return 0; + /* "network" configuration display. */ + prefix_rd2str(prd, rdbuf, RD_ADDRSTRLEN); + label = decode_label(&bgp_static->label); + + vty_out(vty, " network %s/%d rd %s", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN), + p->prefixlen, rdbuf); + if (safi == SAFI_MPLS_VPN) + vty_out(vty, " label %u", label); + + if (bgp_static->rmap.name) + vty_out(vty, " route-map %s", + bgp_static->rmap.name); + else { + if (bgp_static->backdoor) + vty_out(vty, " backdoor"); + } + vty_out(vty, "\n"); + } + } } -static int bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, - afi_t afi, safi_t safi, int *write) +static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { struct bgp_node *prn; struct bgp_node *rn; @@ -11004,59 +11041,51 @@ static int bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, /* Network configuration. */ for (prn = bgp_table_top(bgp->route[afi][safi]); prn; - prn = bgp_route_next(prn)) - if ((table = prn->info) != NULL) - for (rn = bgp_table_top(table); rn; - rn = bgp_route_next(rn)) - if ((bgp_static = rn->info) != NULL) { - char *macrouter = NULL; - char *esi = NULL; - - if (bgp_static->router_mac) - macrouter = prefix_mac2str( - bgp_static->router_mac, - NULL, 0); - if (bgp_static->eth_s_id) - esi = esi2str( - bgp_static->eth_s_id); - p = &rn->p; - prd = (struct prefix_rd *)&prn->p; + prn = bgp_route_next(prn)) { + if ((table = prn->info) == NULL) + continue; - /* "address-family" display. */ - bgp_config_write_family_header( - vty, afi, safi, write); + for (rn = bgp_table_top(table); rn; + rn = bgp_route_next(rn)) { + if ((bgp_static = rn->info) == NULL) + continue; - /* "network" configuration display. */ - prefix_rd2str(prd, rdbuf, - RD_ADDRSTRLEN); + char *macrouter = NULL; + char *esi = NULL; - inet_ntop(AF_INET, - &bgp_static->igpnexthop, buf2, - SU_ADDRSTRLEN); + if (bgp_static->router_mac) + macrouter = prefix_mac2str( + bgp_static->router_mac, NULL, 0); + if (bgp_static->eth_s_id) + esi = esi2str(bgp_static->eth_s_id); + p = &rn->p; + prd = (struct prefix_rd *)&prn->p; - prefix2str(p, buf, sizeof(buf)), - vty_out(vty, - " network %s rd %s ethtag %u tag %u esi %s gwip %s routermac %s", - buf, rdbuf, - p->u.prefix_evpn - .eth_tag, - decode_label( - &bgp_static - ->label), - esi, buf2, macrouter); - vty_out(vty, "\n"); - if (macrouter) - XFREE(MTYPE_TMP, macrouter); - if (esi) - XFREE(MTYPE_TMP, esi); - } - return 0; + /* "network" configuration display. */ + prefix_rd2str(prd, rdbuf, RD_ADDRSTRLEN); + + inet_ntop(AF_INET, &bgp_static->igpnexthop, buf2, + SU_ADDRSTRLEN); + + prefix2str(p, buf, sizeof(buf)); + vty_out(vty, + " network %s rd %s ethtag %u tag %u esi %s gwip %s routermac %s\n", + buf, rdbuf, p->u.prefix_evpn .eth_tag, + decode_label(&bgp_static ->label), esi, buf2, + macrouter); + + if (macrouter) + XFREE(MTYPE_TMP, macrouter); + if (esi) + XFREE(MTYPE_TMP, esi); + } + } } /* Configuration of static route announcement and aggregate information. */ -int bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi, int *write) +void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { struct bgp_node *rn; struct prefix *p; @@ -11064,106 +11093,99 @@ int bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, struct bgp_aggregate *bgp_aggregate; char buf[SU_ADDRSTRLEN]; - if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) - return bgp_config_write_network_vpn(vty, bgp, afi, safi, write); + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) { + bgp_config_write_network_vpn(vty, bgp, afi, safi); + return; + } - if (afi == AFI_L2VPN && safi == SAFI_EVPN) - return bgp_config_write_network_evpn(vty, bgp, afi, safi, - write); + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + bgp_config_write_network_evpn(vty, bgp, afi, safi); + return; + } /* Network configuration. */ for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) - if ((bgp_static = rn->info) != NULL) { - p = &rn->p; - - /* "address-family" display. */ - bgp_config_write_family_header(vty, afi, safi, write); + rn = bgp_route_next(rn)) { + if ((bgp_static = rn->info) == NULL) + continue; - /* "network" configuration display. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) - && afi == AFI_IP) { - u_int32_t destination; - struct in_addr netmask; - - destination = ntohl(p->u.prefix4.s_addr); - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " network %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN)); - - if ((IN_CLASSC(destination) - && p->prefixlen == 24) - || (IN_CLASSB(destination) - && p->prefixlen == 16) - || (IN_CLASSA(destination) - && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* Natural mask is not display. */ - } else - vty_out(vty, " mask %s", - inet_ntoa(netmask)); - } else { - vty_out(vty, " network %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + p = &rn->p; + + /* "network" configuration display. */ + if (bgp_option_check(BGP_OPT_CONFIG_CISCO) + && afi == AFI_IP) { + u_int32_t destination; + struct in_addr netmask; + + destination = ntohl(p->u.prefix4.s_addr); + masklen2ip(p->prefixlen, &netmask); + vty_out(vty, " network %s", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN)); + + if ((IN_CLASSC(destination) && p->prefixlen == 24) + || (IN_CLASSB(destination) && p->prefixlen == 16) + || (IN_CLASSA(destination) && p->prefixlen == 8) + || p->u.prefix4.s_addr == 0) { + /* Natural mask is not display. */ + } else + vty_out(vty, " mask %s", inet_ntoa(netmask)); + } else { + vty_out(vty, " network %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN), + p->prefixlen); + } - if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) - vty_out(vty, " label-index %u", - bgp_static->label_index); + if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) + vty_out(vty, " label-index %u", + bgp_static->label_index); - if (bgp_static->rmap.name) - vty_out(vty, " route-map %s", - bgp_static->rmap.name); - else { - if (bgp_static->backdoor) - vty_out(vty, " backdoor"); - } - - vty_out(vty, "\n"); + if (bgp_static->rmap.name) + vty_out(vty, " route-map %s", bgp_static->rmap.name); + else { + if (bgp_static->backdoor) + vty_out(vty, " backdoor"); } + vty_out(vty, "\n"); + } + /* Aggregate-address configuration. */ for (rn = bgp_table_top(bgp->aggregate[afi][safi]); rn; - rn = bgp_route_next(rn)) - if ((bgp_aggregate = rn->info) != NULL) { - p = &rn->p; + rn = bgp_route_next(rn)) { + if ((bgp_aggregate = rn->info) == NULL) + continue; - /* "address-family" display. */ - bgp_config_write_family_header(vty, afi, safi, write); + p = &rn->p; - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) - && afi == AFI_IP) { - struct in_addr netmask; + if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { + struct in_addr netmask; - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " aggregate-address %s %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - inet_ntoa(netmask)); - } else { - vty_out(vty, " aggregate-address %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } - - if (bgp_aggregate->as_set) - vty_out(vty, " as-set"); + masklen2ip(p->prefixlen, &netmask); + vty_out(vty, " aggregate-address %s %s", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN), + inet_ntoa(netmask)); + } else { + vty_out(vty, " aggregate-address %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN), + p->prefixlen); + } - if (bgp_aggregate->summary_only) - vty_out(vty, " summary-only"); + if (bgp_aggregate->as_set) + vty_out(vty, " as-set"); - vty_out(vty, "\n"); - } + if (bgp_aggregate->summary_only) + vty_out(vty, " summary-only"); - return 0; + vty_out(vty, "\n"); + } } -int bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi, int *write) +void bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { struct bgp_node *rn; struct bgp_distance *bdistance; @@ -11175,7 +11197,6 @@ int bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, || bgp->distance_ibgp[afi][safi] != ZEBRA_IBGP_DISTANCE_DEFAULT || bgp->distance_local[afi][safi] != ZEBRA_IBGP_DISTANCE_DEFAULT)) { - bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " distance bgp %d %d %d\n", bgp->distance_ebgp[afi][safi], bgp->distance_ibgp[afi][safi], @@ -11187,15 +11208,12 @@ int bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, if ((bdistance = rn->info) != NULL) { char buf[PREFIX_STRLEN]; - bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " distance %d %s %s\n", bdistance->distance, prefix2str(&rn->p, buf, sizeof(buf)), bdistance->access_list ? bdistance->access_list : ""); } - - return *write; } /* Allocate routing table structure and install commands. */ @@ -11275,6 +11293,7 @@ void bgp_route_init(void) /* IPv4 labeled-unicast configuration. */ install_element(VIEW_NODE, &show_ip_bgp_instance_all_cmd); install_element(VIEW_NODE, &show_ip_bgp_cmd); + install_element(VIEW_NODE, &show_ip_bgp_json_cmd); install_element(VIEW_NODE, &show_ip_bgp_route_cmd); install_element(VIEW_NODE, &show_ip_bgp_regexp_cmd); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 55f812d4a0..77d6d3e070 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -294,6 +294,8 @@ static inline int bgp_fibupd_safi(safi_t safi) } /* Prototypes. */ +extern void bgp_rib_remove(struct bgp_node *rn, struct bgp_info *ri, + struct peer *peer, afi_t afi, safi_t safi); extern void bgp_process_queue_init(void); extern void bgp_route_init(void); extern void bgp_route_finish(void); @@ -367,12 +369,9 @@ extern void bgp_process(struct bgp *, struct bgp_node *, afi_t, safi_t); * queue element with NULL bgp node. */ extern void bgp_add_eoiu_mark(struct bgp *); -extern int bgp_config_write_table_map(struct vty *, struct bgp *, afi_t, safi_t, - int *); -extern int bgp_config_write_network(struct vty *, struct bgp *, afi_t, safi_t, - int *); -extern int bgp_config_write_distance(struct vty *, struct bgp *, afi_t, safi_t, - int *); +extern void bgp_config_write_table_map(struct vty *, struct bgp *, afi_t, safi_t); +extern void bgp_config_write_network(struct vty *, struct bgp *, afi_t, safi_t); +extern void bgp_config_write_distance(struct vty *, struct bgp *, afi_t, safi_t); extern void bgp_aggregate_increment(struct bgp *, struct prefix *, struct bgp_info *, afi_t, safi_t); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2e18a6d44f..d1c6c476e4 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1279,7 +1279,7 @@ static int bgp_update_delay_deconfig_vty(struct vty *vty) return CMD_SUCCESS; } -int bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) +void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) { if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF) { vty_out(vty, " update-delay %d", bgp->v_update_delay); @@ -1287,8 +1287,6 @@ int bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) vty_out(vty, " %d", bgp->v_establish_wait); vty_out(vty, "\n"); } - - return 0; } @@ -1342,12 +1340,10 @@ static int bgp_wpkt_quanta_config_vty(struct vty *vty, const char *num, return CMD_SUCCESS; } -int bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp) +void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp) { if (bgp->wpkt_quanta != BGP_WRITE_PACKET_MAX) vty_out(vty, " write-quanta %d\n", bgp->wpkt_quanta); - - return 0; } @@ -1374,12 +1370,10 @@ DEFUN (no_bgp_wpkt_quanta, return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); } -int bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp) +void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp) { if (bgp->coalesce_time != BGP_DEFAULT_SUBGROUP_COALESCE_TIME) vty_out(vty, " coalesce-time %u\n", bgp->coalesce_time); - - return 0; } @@ -1503,17 +1497,15 @@ ALIAS_HIDDEN(no_bgp_maxpaths_ibgp, no_bgp_maxpaths_ibgp_hidden_cmd, "Number of paths\n" "Match the cluster length\n") -int bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi, int *write) +void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { if (bgp->maxpaths[afi][safi].maxpaths_ebgp != MULTIPATH_NUM) { - bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " maximum-paths %d\n", bgp->maxpaths[afi][safi].maxpaths_ebgp); } if (bgp->maxpaths[afi][safi].maxpaths_ibgp != MULTIPATH_NUM) { - bgp_config_write_family_header(vty, afi, safi, write); vty_out(vty, " maximum-paths ibgp %d", bgp->maxpaths[afi][safi].maxpaths_ibgp); if (CHECK_FLAG(bgp->maxpaths[afi][safi].ibgp_flags, @@ -1521,8 +1513,6 @@ int bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi, vty_out(vty, " equal-cluster-length"); vty_out(vty, "\n"); } - - return 0; } /* BGP timers. */ @@ -2459,7 +2449,7 @@ DEFUN (no_bgp_listen_range, return bgp_vty_return(vty, ret); } -int bgp_config_write_listen(struct vty *vty, struct bgp *bgp) +void bgp_config_write_listen(struct vty *vty, struct bgp *bgp) { struct peer_group *group; struct listnode *node, *nnode, *rnode, *nrnode; @@ -2482,8 +2472,6 @@ int bgp_config_write_listen(struct vty *vty, struct bgp *bgp) } } } - - return 0; } @@ -6424,6 +6412,59 @@ DEFUN (show_bgp_vrfs, return CMD_SUCCESS; } +static void show_address_entry(struct hash_backet *backet, void *args) +{ + struct vty *vty = (struct vty *) args; + struct bgp_addr *addr = (struct bgp_addr *) backet->data; + + vty_out(vty, "addr: %s, count: %d\n", + inet_ntoa(addr->addr), + addr->refcnt); +} + +static void show_tip_entry(struct hash_backet *backet, void *args) +{ + struct vty *vty = (struct vty *)args; + struct tip_addr *tip = (struct tip_addr *) backet->data; + + vty_out(vty, "addr: %s, count: %d\n", + inet_ntoa(tip->addr), + tip->refcnt); +} + +static void bgp_show_martian_nexthops(struct vty *vty, struct bgp *bgp) +{ + vty_out(vty, "self nexthop database:\n"); + hash_iterate(bgp->address_hash, + (void (*)(struct hash_backet *, void *))show_address_entry, + vty); + + vty_out(vty, "Tunnel-ip database:\n"); + hash_iterate(bgp->tip_hash, + (void (*)(struct hash_backet *, void *))show_tip_entry, + vty); +} + +DEFUN (show_bgp_martian_nexthop_db, + show_bgp_martian_nexthop_db_cmd, + "show bgp martian next-hop", + SHOW_STR + BGP_STR + "martian next-hops\n" + "martian next-hop database\n") +{ + struct bgp *bgp = NULL; + + bgp = bgp_get_default(); + if (!bgp) { + vty_out(vty, "%% No BGP process is configured\n"); + return CMD_WARNING; + } + bgp_show_martian_nexthops(vty, bgp); + + return CMD_SUCCESS; +} + DEFUN (show_bgp_memory, show_bgp_memory_cmd, "show [ip] bgp memory", @@ -6622,367 +6663,368 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) continue; - if (peer->afc[afi][safi]) { - if (!count) { - unsigned long ents; - char memstrbuf[MTYPE_MEMSTR_LEN]; - int vrf_id_ui; + if (!peer->afc[afi][safi]) + continue; - vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) - ? -1 - : bgp->vrf_id; + if (!count) { + unsigned long ents; + char memstrbuf[MTYPE_MEMSTR_LEN]; + int vrf_id_ui; - /* Usage summary and header */ + vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) + ? -1 + : bgp->vrf_id; + + /* Usage summary and header */ + if (use_json) { + json_object_string_add( + json, "routerId", + inet_ntoa(bgp->router_id)); + json_object_int_add(json, "as", + bgp->as); + json_object_int_add(json, "vrfId", + vrf_id_ui); + json_object_string_add( + json, "vrfName", + (bgp->inst_type + == BGP_INSTANCE_TYPE_DEFAULT) + ? "Default" + : bgp->name); + } else { + vty_out(vty, + "BGP router identifier %s, local AS number %u vrf-id %d", + inet_ntoa(bgp->router_id), + bgp->as, vrf_id_ui); + vty_out(vty, "\n"); + } + + if (bgp_update_delay_configured(bgp)) { if (use_json) { - json_object_string_add( - json, "routerId", - inet_ntoa(bgp->router_id)); - json_object_int_add(json, "as", - bgp->as); - json_object_int_add(json, "vrfId", - vrf_id_ui); - json_object_string_add( - json, "vrfName", - (bgp->inst_type - == BGP_INSTANCE_TYPE_DEFAULT) - ? "Default" - : bgp->name); - } else { - vty_out(vty, - "BGP router identifier %s, local AS number %u vrf-id %d", - inet_ntoa(bgp->router_id), - bgp->as, vrf_id_ui); - vty_out(vty, "\n"); - } + json_object_int_add( + json, + "updateDelayLimit", + bgp->v_update_delay); - if (bgp_update_delay_configured(bgp)) { - if (use_json) { + if (bgp->v_update_delay + != bgp->v_establish_wait) json_object_int_add( json, - "updateDelayLimit", - bgp->v_update_delay); + "updateDelayEstablishWait", + bgp->v_establish_wait); - if (bgp->v_update_delay - != bgp->v_establish_wait) - json_object_int_add( - json, - "updateDelayEstablishWait", - bgp->v_establish_wait); - - if (bgp_update_delay_active( - bgp)) { + if (bgp_update_delay_active( + bgp)) { + json_object_string_add( + json, + "updateDelayFirstNeighbor", + bgp->update_delay_begin_time); + json_object_boolean_true_add( + json, + "updateDelayInProgress"); + } else { + if (bgp->update_delay_over) { json_object_string_add( json, "updateDelayFirstNeighbor", bgp->update_delay_begin_time); - json_object_boolean_true_add( + json_object_string_add( json, - "updateDelayInProgress"); - } else { - if (bgp->update_delay_over) { - json_object_string_add( - json, - "updateDelayFirstNeighbor", - bgp->update_delay_begin_time); - json_object_string_add( - json, - "updateDelayBestpathResumed", - bgp->update_delay_end_time); - json_object_string_add( - json, - "updateDelayZebraUpdateResume", - bgp->update_delay_zebra_resume_time); - json_object_string_add( - json, - "updateDelayPeerUpdateResume", - bgp->update_delay_peers_resume_time); - } + "updateDelayBestpathResumed", + bgp->update_delay_end_time); + json_object_string_add( + json, + "updateDelayZebraUpdateResume", + bgp->update_delay_zebra_resume_time); + json_object_string_add( + json, + "updateDelayPeerUpdateResume", + bgp->update_delay_peers_resume_time); } - } else { + } + } else { + vty_out(vty, + "Read-only mode update-delay limit: %d seconds\n", + bgp->v_update_delay); + if (bgp->v_update_delay + != bgp->v_establish_wait) vty_out(vty, - "Read-only mode update-delay limit: %d seconds\n", - bgp->v_update_delay); - if (bgp->v_update_delay - != bgp->v_establish_wait) - vty_out(vty, - " Establish wait: %d seconds\n", - bgp->v_establish_wait); + " Establish wait: %d seconds\n", + bgp->v_establish_wait); - if (bgp_update_delay_active( - bgp)) { + if (bgp_update_delay_active( + bgp)) { + vty_out(vty, + " First neighbor established: %s\n", + bgp->update_delay_begin_time); + vty_out(vty, + " Delay in progress\n"); + } else { + if (bgp->update_delay_over) { vty_out(vty, " First neighbor established: %s\n", bgp->update_delay_begin_time); vty_out(vty, - " Delay in progress\n"); - } else { - if (bgp->update_delay_over) { - vty_out(vty, - " First neighbor established: %s\n", - bgp->update_delay_begin_time); - vty_out(vty, - " Best-paths resumed: %s\n", - bgp->update_delay_end_time); - vty_out(vty, - " zebra update resumed: %s\n", - bgp->update_delay_zebra_resume_time); - vty_out(vty, - " peers update resumed: %s\n", - bgp->update_delay_peers_resume_time); - } + " Best-paths resumed: %s\n", + bgp->update_delay_end_time); + vty_out(vty, + " zebra update resumed: %s\n", + bgp->update_delay_zebra_resume_time); + vty_out(vty, + " peers update resumed: %s\n", + bgp->update_delay_peers_resume_time); } } } + } - if (use_json) { - if (bgp_maxmed_onstartup_configured(bgp) - && bgp->maxmed_active) - json_object_boolean_true_add( - json, - "maxMedOnStartup"); - if (bgp->v_maxmed_admin) - json_object_boolean_true_add( - json, - "maxMedAdministrative"); - - json_object_int_add( - json, "tableVersion", - bgp_table_version( - bgp->rib[afi][safi])); - - ents = bgp_table_count( - bgp->rib[afi][safi]); - json_object_int_add(json, "ribCount", - ents); - json_object_int_add( - json, "ribMemory", - ents * sizeof(struct bgp_node)); + if (use_json) { + if (bgp_maxmed_onstartup_configured(bgp) + && bgp->maxmed_active) + json_object_boolean_true_add( + json, + "maxMedOnStartup"); + if (bgp->v_maxmed_admin) + json_object_boolean_true_add( + json, + "maxMedAdministrative"); - ents = listcount(bgp->peer); - json_object_int_add(json, "peerCount", - ents); - json_object_int_add( - json, "peerMemory", - ents * sizeof(struct peer)); + json_object_int_add( + json, "tableVersion", + bgp_table_version( + bgp->rib[afi][safi])); + + ents = bgp_table_count( + bgp->rib[afi][safi]); + json_object_int_add(json, "ribCount", + ents); + json_object_int_add( + json, "ribMemory", + ents * sizeof(struct bgp_node)); - if ((ents = listcount(bgp->group))) { - json_object_int_add( - json, "peerGroupCount", - ents); - json_object_int_add( - json, "peerGroupMemory", - ents * sizeof(struct - peer_group)); - } + ents = listcount(bgp->peer); + json_object_int_add(json, "peerCount", + ents); + json_object_int_add( + json, "peerMemory", + ents * sizeof(struct peer)); - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING)) - json_object_boolean_true_add( - json, - "dampeningEnabled"); - } else { - if (bgp_maxmed_onstartup_configured(bgp) - && bgp->maxmed_active) - vty_out(vty, - "Max-med on-startup active\n"); - if (bgp->v_maxmed_admin) - vty_out(vty, - "Max-med administrative active\n"); + if ((ents = listcount(bgp->group))) { + json_object_int_add( + json, "peerGroupCount", + ents); + json_object_int_add( + json, "peerGroupMemory", + ents * sizeof(struct + peer_group)); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_DAMPENING)) + json_object_boolean_true_add( + json, + "dampeningEnabled"); + } else { + if (bgp_maxmed_onstartup_configured(bgp) + && bgp->maxmed_active) vty_out(vty, - "BGP table version %" PRIu64 - "\n", - bgp_table_version( - bgp->rib[afi][safi])); - - ents = bgp_table_count( - bgp->rib[afi][safi]); + "Max-med on-startup active\n"); + if (bgp->v_maxmed_admin) vty_out(vty, - "RIB entries %ld, using %s of memory\n", - ents, - mtype_memstr( - memstrbuf, - sizeof(memstrbuf), - ents * sizeof(struct - bgp_node))); + "Max-med administrative active\n"); - /* Peer related usage */ - ents = listcount(bgp->peer); + vty_out(vty, + "BGP table version %" PRIu64 + "\n", + bgp_table_version( + bgp->rib[afi][safi])); + + ents = bgp_table_count( + bgp->rib[afi][safi]); + vty_out(vty, + "RIB entries %ld, using %s of memory\n", + ents, + mtype_memstr( + memstrbuf, + sizeof(memstrbuf), + ents * sizeof(struct + bgp_node))); + + /* Peer related usage */ + ents = listcount(bgp->peer); + vty_out(vty, + "Peers %ld, using %s of memory\n", + ents, + mtype_memstr( + memstrbuf, + sizeof(memstrbuf), + ents * sizeof(struct + peer))); + + if ((ents = listcount(bgp->group))) vty_out(vty, - "Peers %ld, using %s of memory\n", + "Peer groups %ld, using %s of memory\n", ents, mtype_memstr( memstrbuf, sizeof(memstrbuf), ents * sizeof(struct - peer))); - - if ((ents = listcount(bgp->group))) - vty_out(vty, - "Peer groups %ld, using %s of memory\n", - ents, - mtype_memstr( - memstrbuf, - sizeof(memstrbuf), - ents * sizeof(struct - peer_group))); - - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING)) - vty_out(vty, - "Dampening enabled.\n"); - vty_out(vty, "\n"); + peer_group))); - /* Subtract 8 here because 'Neighbor' is - * 8 characters */ - vty_out(vty, "Neighbor"); - vty_out(vty, "%*s", - max_neighbor_width - 8, " "); + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_DAMPENING)) vty_out(vty, - "V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd\n"); - } + "Dampening enabled.\n"); + vty_out(vty, "\n"); + + /* Subtract 8 here because 'Neighbor' is + * 8 characters */ + vty_out(vty, "Neighbor"); + vty_out(vty, "%*s", + max_neighbor_width - 8, " "); + vty_out(vty, + "V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd\n"); } + } - count++; + count++; - if (use_json) { - json_peer = json_object_new_object(); + if (use_json) { + json_peer = json_object_new_object(); - if (peer_dynamic_neighbor(peer)) - json_object_boolean_true_add( - json_peer, "dynamicPeer"); + if (peer_dynamic_neighbor(peer)) + json_object_boolean_true_add( + json_peer, "dynamicPeer"); - if (peer->hostname) - json_object_string_add(json_peer, - "hostname", - peer->hostname); + if (peer->hostname) + json_object_string_add(json_peer, + "hostname", + peer->hostname); - if (peer->domainname) - json_object_string_add( - json_peer, "domainname", - peer->domainname); + if (peer->domainname) + json_object_string_add( + json_peer, "domainname", + peer->domainname); - json_object_int_add(json_peer, "remoteAs", - peer->as); - json_object_int_add(json_peer, "version", 4); - json_object_int_add( - json_peer, "msgRcvd", - peer->open_in + peer->update_in - + peer->keepalive_in - + peer->notify_in - + peer->refresh_in - + peer->dynamic_cap_in); - json_object_int_add( - json_peer, "msgSent", - peer->open_out + peer->update_out - + peer->keepalive_out - + peer->notify_out - + peer->refresh_out - + peer->dynamic_cap_out); - - json_object_int_add(json_peer, "tableVersion", - peer->version[afi][safi]); - json_object_int_add(json_peer, "outq", - peer->obuf->count); - json_object_int_add(json_peer, "inq", 0); - peer_uptime(peer->uptime, timebuf, - BGP_UPTIME_LEN, use_json, - json_peer); - json_object_int_add( - json_peer, "prefixReceivedCount", - peer->pcount[afi][pfx_rcd_safi]); - - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) - json_object_string_add(json_peer, - "state", - "Idle (Admin)"); - else if (CHECK_FLAG( - peer->sflags, - PEER_STATUS_PREFIX_OVERFLOW)) - json_object_string_add(json_peer, - "state", - "Idle (PfxCt)"); - else - json_object_string_add( - json_peer, "state", - lookup_msg(bgp_status_msg, - peer->status, NULL)); - - if (peer->conf_if) - json_object_string_add(json_peer, - "idType", - "interface"); - else if (peer->su.sa.sa_family == AF_INET) - json_object_string_add( - json_peer, "idType", "ipv4"); - else if (peer->su.sa.sa_family == AF_INET6) - json_object_string_add( - json_peer, "idType", "ipv6"); + json_object_int_add(json_peer, "remoteAs", + peer->as); + json_object_int_add(json_peer, "version", 4); + json_object_int_add( + json_peer, "msgRcvd", + peer->open_in + peer->update_in + + peer->keepalive_in + + peer->notify_in + + peer->refresh_in + + peer->dynamic_cap_in); + json_object_int_add( + json_peer, "msgSent", + peer->open_out + peer->update_out + + peer->keepalive_out + + peer->notify_out + + peer->refresh_out + + peer->dynamic_cap_out); + + json_object_int_add(json_peer, "tableVersion", + peer->version[afi][safi]); + json_object_int_add(json_peer, "outq", + peer->obuf->count); + json_object_int_add(json_peer, "inq", 0); + peer_uptime(peer->uptime, timebuf, + BGP_UPTIME_LEN, use_json, + json_peer); + json_object_int_add( + json_peer, "prefixReceivedCount", + peer->pcount[afi][pfx_rcd_safi]); - json_object_object_add(json_peers, peer->host, - json_peer); - } else { - memset(dn_flag, '\0', sizeof(dn_flag)); - if (peer_dynamic_neighbor(peer)) { - dn_count++; - dn_flag[0] = '*'; - } + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + json_object_string_add(json_peer, + "state", + "Idle (Admin)"); + else if (CHECK_FLAG( + peer->sflags, + PEER_STATUS_PREFIX_OVERFLOW)) + json_object_string_add(json_peer, + "state", + "Idle (PfxCt)"); + else + json_object_string_add( + json_peer, "state", + lookup_msg(bgp_status_msg, + peer->status, NULL)); + + if (peer->conf_if) + json_object_string_add(json_peer, + "idType", + "interface"); + else if (peer->su.sa.sa_family == AF_INET) + json_object_string_add( + json_peer, "idType", "ipv4"); + else if (peer->su.sa.sa_family == AF_INET6) + json_object_string_add( + json_peer, "idType", "ipv6"); - if (peer->hostname - && bgp_flag_check(bgp, - BGP_FLAG_SHOW_HOSTNAME)) - len = vty_out(vty, "%s%s(%s)", dn_flag, - peer->hostname, - peer->host); + json_object_object_add(json_peers, peer->host, + json_peer); + } else { + memset(dn_flag, '\0', sizeof(dn_flag)); + if (peer_dynamic_neighbor(peer)) { + dn_count++; + dn_flag[0] = '*'; + } + + if (peer->hostname + && bgp_flag_check(bgp, + BGP_FLAG_SHOW_HOSTNAME)) + len = vty_out(vty, "%s%s(%s)", dn_flag, + peer->hostname, + peer->host); + else + len = vty_out(vty, "%s%s", dn_flag, + peer->host); + + /* pad the neighbor column with spaces */ + if (len < max_neighbor_width) + vty_out(vty, "%*s", + max_neighbor_width - len, " "); + + vty_out(vty, "4 %10u %7d %7d %8" PRIu64 + " %4d %4zd %8s", + peer->as, + peer->open_in + peer->update_in + + peer->keepalive_in + + peer->notify_in + + peer->refresh_in + + peer->dynamic_cap_in, + peer->open_out + peer->update_out + + peer->keepalive_out + + peer->notify_out + + peer->refresh_out + + peer->dynamic_cap_out, + peer->version[afi][safi], 0, + peer->obuf->count, + peer_uptime(peer->uptime, timebuf, + BGP_UPTIME_LEN, 0, NULL)); + + if (peer->status == Established) + vty_out(vty, " %12ld", + peer->pcount[afi] + [pfx_rcd_safi]); + else { + if (CHECK_FLAG(peer->flags, + PEER_FLAG_SHUTDOWN)) + vty_out(vty, " Idle (Admin)"); + else if ( + CHECK_FLAG( + peer->sflags, + PEER_STATUS_PREFIX_OVERFLOW)) + vty_out(vty, " Idle (PfxCt)"); else - len = vty_out(vty, "%s%s", dn_flag, - peer->host); - - /* pad the neighbor column with spaces */ - if (len < max_neighbor_width) - vty_out(vty, "%*s", - max_neighbor_width - len, " "); - - vty_out(vty, "4 %10u %7d %7d %8" PRIu64 - " %4d %4zd %8s", - peer->as, - peer->open_in + peer->update_in - + peer->keepalive_in - + peer->notify_in - + peer->refresh_in - + peer->dynamic_cap_in, - peer->open_out + peer->update_out - + peer->keepalive_out - + peer->notify_out - + peer->refresh_out - + peer->dynamic_cap_out, - peer->version[afi][safi], 0, - peer->obuf->count, - peer_uptime(peer->uptime, timebuf, - BGP_UPTIME_LEN, 0, NULL)); - - if (peer->status == Established) - vty_out(vty, " %12ld", - peer->pcount[afi] - [pfx_rcd_safi]); - else { - if (CHECK_FLAG(peer->flags, - PEER_FLAG_SHUTDOWN)) - vty_out(vty, " Idle (Admin)"); - else if ( - CHECK_FLAG( - peer->sflags, - PEER_STATUS_PREFIX_OVERFLOW)) - vty_out(vty, " Idle (PfxCt)"); - else - vty_out(vty, " %12s", - lookup_msg( - bgp_status_msg, - peer->status, - NULL)); - } - vty_out(vty, "\n"); + vty_out(vty, " %12s", + lookup_msg( + bgp_status_msg, + peer->status, + NULL)); } + vty_out(vty, "\n"); } } @@ -7018,26 +7060,6 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, return CMD_SUCCESS; } -/* - * Return if we have a peer configured to use this afi/safi - */ -static int bgp_show_summary_afi_safi_peer_exists(struct bgp *bgp, int afi, - int safi) -{ - struct listnode *node; - struct peer *peer; - - for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { - if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - continue; - - if (peer->afc[afi][safi]) - return 1; - } - - return 0; -} - static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, int safi, u_char use_json, json_object *json) @@ -7056,8 +7078,7 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, if (safi_wildcard) safi = 1; /* SAFI_UNICAST */ while (safi < SAFI_MAX) { - if (bgp_show_summary_afi_safi_peer_exists(bgp, afi, - safi)) { + if (bgp_afi_safi_peer_exists(bgp, afi, safi)) { json_output = true; if (is_wildcard) { /* @@ -7549,6 +7570,12 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, "defaultNotSent"); } + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + if (p->bgp->advertise_all_vni) + json_object_boolean_true_add(json_addr, + "advertiseAllVnis"); + } + if (filter->plist[FILTER_IN].name || filter->dlist[FILTER_IN].name || filter->aslist[FILTER_IN].name @@ -7814,6 +7841,12 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, vty_out(vty, " default not sent\n"); } + /* advertise-vni-all */ + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + if (p->bgp->advertise_all_vni) + vty_out(vty, " advertise-all-vni\n"); + } + if (filter->plist[FILTER_IN].name || filter->dlist[FILTER_IN].name || filter->aslist[FILTER_IN].name @@ -8598,6 +8631,46 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, json_cap, "multiprotocolExtensions", json_multi); + /* Hostname capabilities */ + json_object *json_hname = NULL; + + json_hname = json_object_new_object(); + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV)) { + json_object_string_add( + json_hname, + "advHostName", + bgp->peer_self->hostname ? + bgp->peer_self->hostname + : "n/a"); + json_object_string_add( + json_hname, + "advDomainName", + bgp->peer_self->domainname ? + bgp->peer_self->domainname + : "n/a"); + } + + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_RCV)) { + json_object_string_add( + json_hname, + "rcvHostName", + p->hostname ? + p->hostname : + "n/a"); + json_object_string_add( + json_hname, + "rcvDomainName", + p->domainname ? + p->domainname : + "n/a"); + } + + json_object_object_add(json_cap, + "hostName", + json_hname); + /* Gracefull Restart */ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || CHECK_FLAG(p->cap, @@ -8931,25 +9004,35 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, u_char use_json, } /* Hostname capability */ - if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV) - || CHECK_FLAG(p->cap, - PEER_CAP_HOSTNAME_RCV)) { + vty_out(vty, + " Hostname Capability:"); + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV)) { vty_out(vty, - " Hostname Capability:"); - if (CHECK_FLAG(p->cap, - PEER_CAP_HOSTNAME_ADV)) - vty_out(vty, " advertised"); - if (CHECK_FLAG(p->cap, - PEER_CAP_HOSTNAME_RCV)) - vty_out(vty, " %sreceived", - CHECK_FLAG( - p->cap, - PEER_CAP_HOSTNAME_ADV) - ? "and " - : ""); - vty_out(vty, "\n"); + " advertised (name: %s,domain name: %s)", + bgp->peer_self->hostname ? + bgp->peer_self->hostname + : "n/a", + bgp->peer_self->domainname ? + bgp->peer_self->domainname + : "n/a"); + } else { + vty_out(vty, " not advertised"); + } + + if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_RCV)) { + vty_out(vty, + " received (name: %s,domain name: %s)", + p->hostname ? + p->hostname : "n/a", + p->domainname ? + p->domainname : "n/a"); + } else { + vty_out(vty, " not received"); } + vty_out(vty, "\n"); + /* Gracefull Restart */ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || CHECK_FLAG(p->cap, @@ -11062,14 +11145,14 @@ DEFUN (no_bgp_redistribute_ipv6, return bgp_redistribute_unset(bgp, AFI_IP6, type, 0); } -int bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi, int *write) +void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { int i; /* Unicast redistribution only. */ if (safi != SAFI_UNICAST) - return 0; + return; for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { /* Redistribute BGP does not make sense. */ @@ -11083,11 +11166,6 @@ int bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, continue; for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) { - /* Display "address-family" when it is not yet - * diplayed. */ - bgp_config_write_family_header(vty, afi, safi, - write); - /* "redistribute" configuration. */ vty_out(vty, " redistribute %s", zebra_route_string(i)); @@ -11103,7 +11181,6 @@ int bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, } } } - return *write; } /* BGP node structure. */ @@ -12285,6 +12362,9 @@ void bgp_vty_init(void) /* "show [ip] bgp memory" commands. */ install_element(VIEW_NODE, &show_bgp_memory_cmd); + /* "show bgp martian next-hop" */ + install_element(VIEW_NODE, &show_bgp_martian_nexthop_db_cmd); + /* "show [ip] bgp views" commands. */ install_element(VIEW_NODE, &show_bgp_views_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 62bc27d507..59bc012661 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -46,10 +46,10 @@ struct bgp; extern void bgp_vty_init(void); extern const char *afi_safi_print(afi_t, safi_t); extern const char *afi_safi_json(afi_t, safi_t); -extern int bgp_config_write_update_delay(struct vty *, struct bgp *); -extern int bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); -extern int bgp_config_write_listen(struct vty *vty, struct bgp *bgp); -extern int bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp); +extern void bgp_config_write_update_delay(struct vty *, struct bgp *); +extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); +extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp); +extern void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp); extern int bgp_vty_return(struct vty *vty, int ret); extern struct peer *peer_and_group_lookup_vty(struct vty *vty, const char *peer_str); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 9a092404d5..8b9d55295d 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1022,7 +1022,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, * in * the RIB */ if (info->sub_type == BGP_ROUTE_AGGREGATE) - SET_FLAG(api.flags, ZEBRA_FLAG_BLACKHOLE); + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || info->sub_type == BGP_ROUTE_AGGREGATE) { @@ -1152,7 +1152,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (has_valid_label) SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); - if (!CHECK_FLAG(api.flags, ZEBRA_FLAG_BLACKHOLE)) + if (info->sub_type != BGP_ROUTE_AGGREGATE) api.nexthop_num = valid_nh_count; SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 11405d1c1b..7d37864f44 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -26,10 +26,10 @@ extern void bgp_zebra_init(struct thread_master *master); extern void bgp_zebra_destroy(void); extern int bgp_if_update_all(void); -extern int bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, safi_t, - int *); -extern int bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t, - safi_t, int *); +extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, + safi_t); +extern void bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t, + safi_t); extern void bgp_zebra_announce(struct bgp_node *, struct prefix *, struct bgp_info *, struct bgp *, afi_t, safi_t); extern void bgp_zebra_announce_table(struct bgp *, afi_t, safi_t); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index e3650417fa..d859420c7b 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1391,6 +1391,32 @@ void bgp_peer_conf_if_to_su_update(struct peer *peer) hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); } +static void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, + safi_t safi) +{ + struct bgp_node *rn, *nrn; + + for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; + rn = bgp_route_next(rn)) { + if (rn->info != NULL) { + /* Special handling for 2-level routing + * tables. */ + if (safi == SAFI_MPLS_VPN + || safi == SAFI_ENCAP + || safi == SAFI_EVPN) { + for (nrn = bgp_table_top(( + struct bgp_table + *)(rn->info)); + nrn; + nrn = bgp_route_next(nrn)) + bgp_process(bgp, nrn, + afi, safi); + } else + bgp_process(bgp, rn, afi, safi); + } + } +} + /* Force a bestpath recalculation for all prefixes. This is used * when 'bgp bestpath' commands are entered. */ @@ -1398,29 +1424,10 @@ void bgp_recalculate_all_bestpaths(struct bgp *bgp) { afi_t afi; safi_t safi; - struct bgp_node *rn, *nrn; for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - if (rn->info != NULL) { - /* Special handling for 2-level routing - * tables. */ - if (safi == SAFI_MPLS_VPN - || safi == SAFI_ENCAP - || safi == SAFI_EVPN) { - for (nrn = bgp_table_top(( - struct bgp_table - *)(rn->info)); - nrn; - nrn = bgp_route_next(nrn)) - bgp_process(bgp, nrn, - afi, safi); - } else - bgp_process(bgp, rn, afi, safi); - } - } + bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi); } } } @@ -1500,6 +1507,25 @@ struct peer *peer_create_accept(struct bgp *bgp) return peer; } +/* + * Return true if we have a peer configured to use this afi/safi + */ +int bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct listnode *node; + struct peer *peer; + + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + + if (peer->afc[afi][safi]) + return 1; + } + + return 0; +} + /* Change peer's AS number. */ void peer_as_change(struct peer *peer, as_t as, int as_specified) { @@ -1714,11 +1740,14 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) struct peer_group *group; struct listnode *node, *nnode; struct peer *tmp_peer; + struct bgp *bgp; /* Nothing to do if we've already activated this peer */ if (peer->afc[afi][safi]) return ret; + bgp = peer->bgp; + /* This is a peer-group so activate all of the members of the * peer-group as well */ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { @@ -1741,6 +1770,17 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) ret |= non_peergroup_activate_af(peer, afi, safi); } + /* If this is the first peer to be activated for this afi/labeled-unicast + * recalc bestpaths to trigger label allocation */ + if (safi == SAFI_LABELED_UNICAST && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) { + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_info("peer(s) are now active for labeled-unicast, allocate MPLS labels"); + + bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1; + bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); + } + return ret; } @@ -1798,6 +1838,7 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) struct peer_group *group; struct peer *tmp_peer; struct listnode *node, *nnode; + struct bgp *bgp; /* Nothing to do if we've already de-activated this peer */ if (!peer->afc[afi][safi]) @@ -1821,6 +1862,20 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) ret |= non_peergroup_deactivate_af(peer, afi, safi); } + bgp = peer->bgp; + + /* If this is the last peer to be deactivated for this afi/labeled-unicast + * recalc bestpaths to trigger label deallocation */ + if (safi == SAFI_LABELED_UNICAST && + bgp->allocate_mpls_labels[afi][SAFI_UNICAST] && + !bgp_afi_safi_peer_exists(bgp, afi, safi)) { + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_info("peer(s) are no longer active for labeled-unicast, deallocate MPLS labels"); + + bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0; + bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); + } return ret; } @@ -2761,6 +2816,21 @@ static struct bgp *bgp_create(as_t *as, const char *name, XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->host); bgp->peer_self->host = XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement"); + if (bgp->peer_self->hostname != NULL) { + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname); + bgp->peer_self->hostname = NULL; + } + if (cmd_hostname_get()) + bgp->peer_self->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, + cmd_hostname_get()); + + if (bgp->peer_self->domainname != NULL) { + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname); + bgp->peer_self->domainname = NULL; + } + if (cmd_domainname_get()) + bgp->peer_self->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, + cmd_domainname_get()); bgp->peer = list_new(); bgp->peer->cmp = (int (*)(void *, void *))peer_cmp; bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same, NULL); @@ -2933,6 +3003,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp = bgp_create(as, name, inst_type); bgp_router_id_set(bgp, &bgp->router_id_zebra); bgp_address_init(bgp); + bgp_tip_hash_init(bgp); bgp_scan_init(bgp); *bgp_val = bgp; @@ -3014,6 +3085,9 @@ void bgp_instance_down(struct bgp *bgp) /* Purge network and redistributed routes. */ bgp_purge_static_redist_routes(bgp); + + /* Cleanup registered nexthops (flags) */ + bgp_cleanup_nexthops(bgp); } /* Delete BGP instance. */ @@ -3145,6 +3219,7 @@ void bgp_free(struct bgp *bgp) bgp_scan_finish(bgp); bgp_address_destroy(bgp); + bgp_tip_hash_destroy(bgp); bgp_evpn_cleanup(bgp); @@ -6129,14 +6204,8 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, u_char use_json, return buf; } -#define afi_header_vty_out(vty, afi, safi, write, format, ...) \ - do { \ - bgp_config_write_family_header(vty, afi, safi, write); \ - vty_out(vty, format, ## __VA_ARGS__); \ - } while (0) - static void bgp_config_write_filter(struct vty *vty, struct peer *peer, - afi_t afi, safi_t safi, int *write) + afi_t afi, safi_t safi) { struct bgp_filter *filter; struct bgp_filter *gfilter = NULL; @@ -6155,16 +6224,13 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer, if (!gfilter || !gfilter->dlist[in].name || strcmp(filter->dlist[in].name, gfilter->dlist[in].name) != 0) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s distribute-list %s in\n", addr, - filter->dlist[in].name); + vty_out(vty, " neighbor %s distribute-list %s in\n", + addr, filter->dlist[in].name); } if (filter->dlist[out].name && !gfilter) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s distribute-list %s out\n", - addr, filter->dlist[out].name); + vty_out(vty, " neighbor %s distribute-list %s out\n", addr, + filter->dlist[out].name); } /* prefix-list. */ @@ -6172,19 +6238,17 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer, if (!gfilter || !gfilter->plist[in].name || strcmp(filter->plist[in].name, gfilter->plist[in].name) != 0) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s prefix-list %s in\n", - addr, filter->plist[in].name); + vty_out(vty, " neighbor %s prefix-list %s in\n", addr, + filter->plist[in].name); } - if (filter->plist[out].name) - if (!gfilter || !gfilter->plist[out].name - || strcmp(filter->plist[out].name, gfilter->plist[out].name) - != 0) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s prefix-list %s out\n", - addr, filter->plist[out].name); - } + if (filter->plist[out].name) + if (!gfilter || !gfilter->plist[out].name + || strcmp(filter->plist[out].name, gfilter->plist[out].name) + != 0) { + vty_out(vty, " neighbor %s prefix-list %s out\n", addr, + filter->plist[out].name); + } /* route-map. */ if (filter->map[RMAP_IN].name) @@ -6192,9 +6256,8 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer, || strcmp(filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s route-map %s in\n", - addr, filter->map[RMAP_IN].name); + vty_out(vty, " neighbor %s route-map %s in\n", addr, + filter->map[RMAP_IN].name); } if (filter->map[RMAP_OUT].name) @@ -6202,16 +6265,14 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer, || strcmp(filter->map[RMAP_OUT].name, gfilter->map[RMAP_OUT].name) != 0) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s route-map %s out\n", - addr, filter->map[RMAP_OUT].name); + vty_out(vty, " neighbor %s route-map %s out\n", addr, + filter->map[RMAP_OUT].name); } /* unsuppress-map */ if (filter->usmap.name && !gfilter) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s unsuppress-map %s\n", addr, - filter->usmap.name); + vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, + filter->usmap.name); } /* filter-list. */ @@ -6219,15 +6280,13 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer, if (!gfilter || !gfilter->aslist[in].name || strcmp(filter->aslist[in].name, gfilter->aslist[in].name) != 0) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s filter-list %s in\n", - addr, filter->aslist[in].name); + vty_out(vty, " neighbor %s filter-list %s in\n", addr, + filter->aslist[in].name); } if (filter->aslist[out].name && !gfilter) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s filter-list %s out\n", addr, - filter->aslist[out].name); + vty_out(vty, " neighbor %s filter-list %s out\n", addr, + filter->aslist[out].name); } } @@ -6547,8 +6606,7 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, /* BGP peer configuration display function. */ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, - struct peer *peer, afi_t afi, safi_t safi, - int *write) + struct peer *peer, afi_t afi, safi_t safi) { struct peer *g_peer = NULL; char *addr; @@ -6571,36 +6629,29 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, /* If the peer-group is active but peer is not, print a 'no * activate' */ if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { - afi_header_vty_out(vty, afi, safi, write, - " no neighbor %s activate\n", addr); + vty_out(vty, " no neighbor %s activate\n", addr); } /* If the peer-group is not active but peer is, print an 'activate' */ else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s activate\n", addr); + vty_out(vty, " neighbor %s activate\n", addr); } } else { if (peer->afc[afi][safi]) { if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s activate\n", + vty_out(vty, " neighbor %s activate\n", addr); } } else - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s activate\n", - addr); + vty_out(vty, " neighbor %s activate\n", addr); } else { if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { if (!bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) { - afi_header_vty_out( - vty, afi, safi, write, + vty_out(vty, " no neighbor %s activate\n", addr); } @@ -6611,25 +6662,20 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, /* addpath TX knobs */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ADDPATH_TX_ALL_PATHS)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s addpath-tx-all-paths\n", - addr); + vty_out(vty, " neighbor %s addpath-tx-all-paths\n", addr); } if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s addpath-tx-bestpath-per-AS\n", - addr); + vty_out(vty, " neighbor %s addpath-tx-bestpath-per-AS\n", + addr); } /* ORF capability. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) || peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_RM)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s capability orf prefix-list", - addr); + vty_out(vty, " neighbor %s capability orf prefix-list", addr); if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) @@ -6647,57 +6693,46 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, /* Route reflector client. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s route-reflector-client\n", - addr); + vty_out(vty, " neighbor %s route-reflector-client\n", addr); } /* next-hop-self force */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_FORCE_NEXTHOP_SELF)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s next-hop-self force\n", addr); + vty_out(vty, " neighbor %s next-hop-self force\n", addr); } /* next-hop-self */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s next-hop-self\n", addr); + vty_out(vty, " neighbor %s next-hop-self\n", addr); } /* remove-private-AS */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s remove-private-AS all replace-AS\n", + vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", addr); } else if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s remove-private-AS replace-AS\n", addr); + vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", + addr); } else if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s remove-private-AS all\n", - addr); + vty_out(vty, " neighbor %s remove-private-AS all\n", addr); } else if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s remove-private-AS\n", addr); + vty_out(vty, " neighbor %s remove-private-AS\n", addr); } /* as-override */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s as-override\n", addr); + vty_out(vty, " neighbor %s as-override\n", addr); } /* send-community print. */ @@ -6709,27 +6744,21 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, && peergroup_af_flag_check( peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s send-community all\n", - addr); + vty_out(vty, " neighbor %s send-community all\n", + addr); } else if (peergroup_af_flag_check( peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s send-community large\n", addr); + vty_out(vty, " neighbor %s send-community large\n", + addr); } else if (peergroup_af_flag_check( peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s send-community extended\n", + vty_out(vty, " neighbor %s send-community extended\n", addr); } else if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s send-community\n", - addr); + vty_out(vty, " neighbor %s send-community\n", addr); } } else { if (!peer_af_flag_check(peer, afi, safi, @@ -6746,9 +6775,8 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, && (!g_peer || peer_af_flag_check( g_peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))) { - afi_header_vty_out( - vty, afi, safi, write, - " no neighbor %s send-community all\n", addr); + vty_out(vty, " no neighbor %s send-community all\n", + addr); } else { if (!peer_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY) @@ -6756,8 +6784,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, || peer_af_flag_check( g_peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))) { - afi_header_vty_out( - vty, afi, safi, write, + vty_out(vty, " no neighbor %s send-community large\n", addr); } @@ -6768,8 +6795,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, || peer_af_flag_check( g_peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))) { - afi_header_vty_out( - vty, afi, safi, write, + vty_out(vty, " no neighbor %s send-community extended\n", addr); } @@ -6779,8 +6805,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, && (!g_peer || peer_af_flag_check( g_peer, afi, safi, PEER_FLAG_SEND_COMMUNITY))) { - afi_header_vty_out( - vty, afi, safi, write, + vty_out(vty, " no neighbor %s send-community\n", addr); } @@ -6798,8 +6823,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, || (peer->default_rmap[afi][safi].name && strcmp(peer->default_rmap[afi][safi].name, g_peer->default_rmap[afi][safi].name))))) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s default-originate", addr); + vty_out(vty, " neighbor %s default-originate", addr); if (peer->default_rmap[afi][safi].name) vty_out(vty, " route-map %s", peer->default_rmap[afi][safi].name); @@ -6808,9 +6832,8 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, /* Soft reconfiguration inbound. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s soft-reconfiguration inbound\n", addr); + vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", + addr); } /* maximum-prefix. */ @@ -6823,9 +6846,8 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, PEER_FLAG_MAX_PREFIX_WARNING) != CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s maximum-prefix %lu", - addr, peer->pmax[afi][safi]); + vty_out(vty, " neighbor %s maximum-prefix %lu", addr, + peer->pmax[afi][safi]); if (peer->pmax_threshold[afi][safi] != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) vty_out(vty, " %u", @@ -6842,16 +6864,13 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, /* Route server client. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_RSERVER_CLIENT)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s route-server-client\n", addr); + vty_out(vty, " neighbor %s route-server-client\n", addr); } /* Nexthop-local unchanged. */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s nexthop-local unchanged\n", - addr); + vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); } /* allowas-in <1-10> */ @@ -6862,14 +6881,11 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, || peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi]) { if (peer->allowas_in[afi][safi] == 3) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s allowas-in\n", - addr); + vty_out(vty, " neighbor %s allowas-in\n", + addr); } else { - afi_header_vty_out( - vty, afi, safi, write, - " neighbor %s allowas-in %d\n", addr, - peer->allowas_in[afi][safi]); + vty_out(vty, " neighbor %s allowas-in %d\n", + addr, peer->allowas_in[afi][safi]); } } } @@ -6880,9 +6896,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, if (!peer_group_active(peer) || !peer_af_flag_check(g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s allowas-in origin\n", - addr); + vty_out(vty, " neighbor %s allowas-in origin\n", addr); } } @@ -6892,15 +6906,13 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, || !peer_af_flag_check(g_peer, afi, safi, PEER_FLAG_WEIGHT) || peer->weight[afi][safi] != g_peer->weight[afi][safi]) { if (peer->weight[afi][safi]) { - afi_header_vty_out(vty, afi, safi, write, - " neighbor %s weight %lu\n", - addr, - peer->weight[afi][safi]); + vty_out(vty, " neighbor %s weight %lu\n", addr, + peer->weight[afi][safi]); } } /* Filter. */ - bgp_config_write_filter(vty, peer, afi, safi, write); + bgp_config_write_filter(vty, peer, afi, safi); /* atribute-unchanged. */ if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) || @@ -6915,8 +6927,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { - afi_header_vty_out( - vty, afi, safi, write, + vty_out(vty, " neighbor %s attribute-unchanged%s%s%s\n", addr, peer_af_flag_check( @@ -6937,64 +6948,52 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, } } -/* Display "address-family" configuration header. */ -void bgp_config_write_family_header(struct vty *vty, afi_t afi, safi_t safi, - int *write) +/* Address family based peer configuration display. */ +static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { - if (*write) - return; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; - vty_out(vty, " !\n address-family "); + vty_frame(vty, " !\n address-family "); if (afi == AFI_IP) { if (safi == SAFI_UNICAST) - vty_out(vty, "ipv4 unicast"); + vty_frame(vty, "ipv4 unicast"); else if (safi == SAFI_LABELED_UNICAST) - vty_out(vty, "ipv4 labeled-unicast"); + vty_frame(vty, "ipv4 labeled-unicast"); else if (safi == SAFI_MULTICAST) - vty_out(vty, "ipv4 multicast"); + vty_frame(vty, "ipv4 multicast"); else if (safi == SAFI_MPLS_VPN) - vty_out(vty, "ipv4 vpn"); + vty_frame(vty, "ipv4 vpn"); else if (safi == SAFI_ENCAP) - vty_out(vty, "ipv4 encap"); + vty_frame(vty, "ipv4 encap"); } else if (afi == AFI_IP6) { if (safi == SAFI_UNICAST) - vty_out(vty, "ipv6 unicast"); + vty_frame(vty, "ipv6 unicast"); else if (safi == SAFI_LABELED_UNICAST) - vty_out(vty, "ipv6 labeled-unicast"); + vty_frame(vty, "ipv6 labeled-unicast"); else if (safi == SAFI_MULTICAST) - vty_out(vty, "ipv6 multicast"); + vty_frame(vty, "ipv6 multicast"); else if (safi == SAFI_MPLS_VPN) - vty_out(vty, "ipv6 vpn"); + vty_frame(vty, "ipv6 vpn"); else if (safi == SAFI_ENCAP) - vty_out(vty, "ipv6 encap"); + vty_frame(vty, "ipv6 encap"); } else if (afi == AFI_L2VPN) { if (safi == SAFI_EVPN) - vty_out(vty, "l2vpn evpn"); + vty_frame(vty, "l2vpn evpn"); } - vty_out(vty, "\n"); + vty_frame(vty, "\n"); - *write = 1; -} + bgp_config_write_distance(vty, bgp, afi, safi); -/* Address family based peer configuration display. */ -static int bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) -{ - int write = 0; - struct peer *peer; - struct peer_group *group; - struct listnode *node, *nnode; + bgp_config_write_network(vty, bgp, afi, safi); - bgp_config_write_distance(vty, bgp, afi, safi, &write); - - bgp_config_write_network(vty, bgp, afi, safi, &write); - - bgp_config_write_redistribute(vty, bgp, afi, safi, &write); + bgp_config_write_redistribute(vty, bgp, afi, safi); for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) - bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi, - &write); + bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { /* Skip dynamic neighbors. */ @@ -7003,20 +7002,16 @@ static int bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, /* Do not display doppelganger peers */ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_af(vty, bgp, peer, afi, safi, - &write); + bgp_config_write_peer_af(vty, bgp, peer, afi, safi); } - bgp_config_write_maxpaths(vty, bgp, afi, safi, &write); - bgp_config_write_table_map(vty, bgp, afi, safi, &write); + bgp_config_write_maxpaths(vty, bgp, afi, safi); + bgp_config_write_table_map(vty, bgp, afi, safi); if (safi == SAFI_EVPN) - bgp_config_write_evpn_info(vty, bgp, afi, safi, &write); + bgp_config_write_evpn_info(vty, bgp, afi, safi); - if (write) - vty_out(vty, " exit-address-family\n"); - - return write; + vty_endframe(vty, " exit-address-family\n"); } int bgp_config_write(struct vty *vty) @@ -7044,11 +7039,11 @@ int bgp_config_write(struct vty *vty) vty_out(vty, "bgp route-map delay-timer %u\n", bm->rmap_update_timer); + if (write) + vty_out(vty, "!\n"); + /* BGP configuration. */ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { - if (write) - vty_out(vty, "!\n"); - /* Router bgp ASN */ vty_out(vty, "router bgp %u", bgp->as); @@ -7268,54 +7263,46 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " no auto-summary\n"); /* IPv4 unicast configuration. */ - write += - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); /* IPv4 multicast configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP, - SAFI_MULTICAST); + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); /* IPv4 labeled-unicast configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP, - SAFI_LABELED_UNICAST); + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); /* IPv4 VPN configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP, - SAFI_MPLS_VPN); + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); /* ENCAPv4 configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); /* IPv6 unicast configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_UNICAST); + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); /* IPv6 multicast configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_MULTICAST); + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); /* IPv6 labeled-unicast configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_LABELED_UNICAST); + bgp_config_write_family(vty, bgp, AFI_IP6, + SAFI_LABELED_UNICAST); /* IPv6 VPN configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_MPLS_VPN); + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); /* ENCAPv6 configuration. */ - write += bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); /* EVPN configuration. */ - write += - bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); + bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); #if ENABLE_BGP_VNC - write += bgp_rfapi_cfg_write(vty, bgp); + bgp_rfapi_cfg_write(vty, bgp); #endif - write++; + vty_out(vty, "!\n"); } - return write; + return 0; } void bgp_master_init(struct thread_master *master) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f6e7b2277f..00fce72124 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -318,6 +318,10 @@ struct bgp { struct hash *address_hash; + /* DB for all local tunnel-ips - used mainly for martian checks + Currently it only has all VxLan tunnel IPs*/ + struct hash *tip_hash; + /* Static route configuration. */ struct bgp_table *route[AFI_MAX][SAFI_MAX]; @@ -333,6 +337,9 @@ struct bgp { /* BGP redistribute configuration. */ struct list *redist[AFI_MAX][ZEBRA_ROUTE_MAX]; + /* Allocate MPLS labels */ + u_char allocate_mpls_labels[AFI_MAX][SAFI_MAX]; + /* timer to re-evaluate neighbor default-originate route-maps */ struct thread *t_rmap_def_originate_eval; #define RMAP_DEFAULT_ORIGINATE_EVAL_TIMER 5 @@ -1231,7 +1238,6 @@ extern void peer_xfer_config(struct peer *dst, struct peer *src); extern char *peer_uptime(time_t, char *, size_t, u_char, json_object *); extern int bgp_config_write(struct vty *); -extern void bgp_config_write_family_header(struct vty *, afi_t, safi_t, int *); extern void bgp_master_init(struct thread_master *master); @@ -1280,6 +1286,7 @@ extern int bgp_listen_limit_unset(struct bgp *); extern int bgp_update_delay_active(struct bgp *); extern int bgp_update_delay_configured(struct bgp *); +extern int bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi); extern void peer_as_change(struct peer *, as_t, int); extern int peer_remote_as(struct bgp *, union sockunion *, const char *, as_t *, int, afi_t, safi_t); diff --git a/configure.ac b/configure.ac index bf66e00f9f..fb73583c28 100755 --- a/configure.ac +++ b/configure.ac @@ -195,6 +195,7 @@ fi dnl always want these CFLAGS AC_C_FLAG([-fno-omit-frame-pointer]) +AC_C_FLAG([-funwind-tables]) AC_C_FLAG([-Wall]) AC_C_FLAG([-Wextra]) AC_C_FLAG([-Wmissing-prototypes]) @@ -320,6 +321,8 @@ AC_ARG_WITH(rfp-path, AS_HELP_STRING([--with-rfp-path[=DIR]],[path to replaced stub RFP used with BGP VNC])) AC_ARG_ENABLE(snmp, AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)])) +AC_ARG_ENABLE(zeromq, + AS_HELP_STRING([--enable-zeromq], [enable ZeroMQ handler (libfrrzmq)])) AC_ARG_WITH(libpam, AS_HELP_STRING([--with-libpam], [use libpam for PAM support in vtysh])) AC_ARG_ENABLE(ospfapi, @@ -1714,6 +1717,21 @@ AC_CHECK_HEADER([malloc.h], ) ], [], FRR_INCLUDES) +dnl ------ +dnl ZeroMQ +dnl ------ +if test "x$enable_zeromq" != "xno"; then + PKG_CHECK_MODULES(ZEROMQ, [libzmq >= 4.0.0], [ + AC_DEFINE(HAVE_ZEROMQ, 1, [Enable ZeroMQ support]) + ZEROMQ=true + ], [ + if test "x$enable_zeromq" = "xyes"; then + AC_MSG_ERROR([configuration specifies --enable-zeromq but libzmq was not found]) + fi + ]) +fi +AM_CONDITIONAL([ZEROMQ], test "x$ZEROMQ" = "xtrue") + dnl ---------- dnl configure date dnl ---------- @@ -1819,6 +1837,7 @@ AC_CONFIG_FILES([Makefile doc/watchfrr.8 doc/zebra.8 doc/frr.1 + doc/frr-args.8 pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh pkgsrc/eigrpd.sh]) diff --git a/debian/frr.install b/debian/frr.install index 49aeb395bb..8fc5fa5fa6 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -16,6 +16,7 @@ usr/share/man/man8/ripngd.8 usr/share/man/man8/zebra.8 usr/share/man/man8/isisd.8 usr/share/man/man8/watchfrr.8 +usr/share/man/man8/frr-args.8 usr/share/snmp/mibs/ tools/etc/* etc/ tools/*.service lib/systemd/system diff --git a/doc/Makefile.am b/doc/Makefile.am index 9016df7372..7d31e4cc47 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -52,7 +52,7 @@ info_TEXINFOS = frr.texi # because it cant just work from the png's directly it seems - contrary # to the documentation... frr.pdf: $(info_TEXINFOS) $(figures_pdf) $(frr_TEXINFOS) defines.texi - $(TEXI2PDF) -o "$@" $< + $(TEXI2PDF) -o "$@" $< || true # don't ask me why the info file is in srcdir $(srcdir)/frr.info: $(frr_TEXINFOS) defines.texi @@ -79,7 +79,7 @@ frr_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \ .dia.png: $(DIATOPNG) "$@" $< -man_MANS = frr.1 +man_MANS = frr.1 frr-args.8 if PIMD man_MANS += pimd.8 diff --git a/doc/bgpd.texi b/doc/bgpd.texi index d6b07a270f..8e0da12949 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -698,11 +698,8 @@ This command is deprecated and may be removed in a future release. Its use should be avoided. @end deffn -@c for some reason, using [all] here triggers a bug in texinfo... -@deffn {BGP} {neighbor @var{peer} next-hop-self} {} -@deffnx {BGP} {no neighbor @var{peer} next-hop-self} {} -@deffnx {BGP} {neighbor @var{peer} next-hop-self all} {} -@deffnx {BGP} {no neighbor @var{peer} next-hop-self all} {} +@deffn {BGP} {neighbor @var{peer} next-hop-self [all]} {} +@deffnx {BGP} {no neighbor @var{peer} next-hop-self [all]} {} This command specifies an announced route's nexthop as being equivalent to the address of the bgp router if it is learned via eBGP. If the optional keyword @code{all} is specified the modifiation is done diff --git a/doc/frr-args.8.in b/doc/frr-args.8.in new file mode 100644 index 0000000000..3dc84e1e22 --- /dev/null +++ b/doc/frr-args.8.in @@ -0,0 +1,248 @@ +.TH frr-args 8 "28 August 2017" "@PACKAGE_FULLNAME@ general options" "Version @PACKAGE_VERSION@" +.SH NAME +frr-args \- common command line options for all @PACKAGE_FULLNAME@ daemons. +.SH SYNOPSIS +<\fBzebra\fR|\fBbgpd\fR|\fB...\fR> +[\fB\-h\fR] [\fB\-v\fR] + +<\fBzebra\fR|\fBbgpd\fR|\fB...\fR> +[\fB\-d\fR|\fB\-t\fR|\fB\-dt\fR] +[\fB\-C\fR] +[\fB\-f\fR \fIconfig-file\fR] +[\fB\-i\fR \fIpid-file\fR] +[\fB\-z\fR \fIzclient-path\fR] +[\fB\-u\fR \fIuser\fR] +[\fB\-g\fR \fIgroup\fR] +[\fB\-A\fR \fIvty-addr\fR] +[\fB\-P\fR \fIvty-port\fR] +[\fB\-M\fR \fImodule\fR[\fB:\fIoptions\fR]] +[\fB\-N\fR \fIpathspace\fR] +[\fB\-\-vty_socket\fR \fIvty-path\fR] +[\fB\-\-moduledir\fR \fImodule-path\fR] + +.SH DESCRIPTION +@PACKAGE_NAME@ daemons share a large part of their command line options; +this man page documents these. For options on specific daemons please refer +to their respective man pages. Most of the common options are related to +process control, configuration and common library functionality. + +.SH HELP AND VERSION +.TP +\fB\-h\fR, \fB\-\-help\fR +Print a short description of the daemon's command line options. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print version and build information for the daemon. +.PP +Both of these options inhibit normal operation and will immediately exit. + +.SH PROCESS CONTROL +These options control background operation: +.TP +\fB\-d\fR, \fB\-\-daemon\fR +Launches the process in background/daemon mode, forking and detaching from +the terminal. + +The parent process will delay its exit until the daemon/child has finished +its initialization and has entered its main loop. This is important for +\fBzebra\fR startup because the other daemons will attempt to connect to +\fBzebra\fR. A return from \fBzebra -d\fR guarantees its readiness to +accept these connections. +.TP +\fB\-t\fR, \fB\-\-terminal\fR +Opens an interactive VTY session on the terminal, allowing for both state +and configuration operations. Note that the terminal starts operating after +startup has completed and the configuration file has been loaded. + +The process will exit when end of file is detected on the terminal. It is +possible to daemonize a process started with \fB-t\fR (but without \fB-d\fR) +by sending \fISIGQUIT\fR to the process (normally mapped to a \fI^\\\fR +keypress.) +.TP +\fB\-dt\fR, \fB\-\-daemon \-\-terminal\fR +This combination of the previous two options will delay the daemon from +going into background until the terminal session ends (by end of file.) + +If the process receives \fISIGINT\fR (e.g. a \fI^C\fR keypress) in this +mode, it will exit instead of daemonizing. +.PP +It is safe to suspend (\fISIGTSTP\fR / \fI^Z\fR) the terminal session +opened by the previous two options; this will only stop the terminal but +not the protocol daemon itself (which runs in a separate second process.) + +.SH CONFIGURATION AND PATHS +The following options control configuration and file system locations for +@PACKAGE_NAME@ processes: +.TP +\fB\-f\fR, \fB\-\-config_file\fR \fIconfig-file\fR +Specify a configuration file to be used instead of the default +\fB\fI@CFG_SYSCONF@/<daemon>.conf\fR file. + +Note that the daemon will attempt to write to this file if the +\fIwrite file\fR command is issued on its VTY interface or through +\fBvtysh\fR. +.TP +\fB\-C\fR, \fB\-\-dryrun\fR +Load the configuration file and check its validity, then exit. +.TP +\fB\-i\fR, \fB\-\-pid_file\fR \fIpid-file\fR +Output a pid file to a location other than the default +\fB\fI@CFG_STATE@/<daemon>.pid\fR. +.TP +\fB\-z\fR, \fB\-\-socket\fR \fIzclient-path\fR +Override the path of the ZAPI socket used to communicate between \fBzebra\fR +and the various protocol daemons. The default is +\fB\fI@CFG_STATE@/zserv.api\fR. The value of this option must be the same +across all daemons. +.TP +\fB\-N\fR, \fB\-\-pathspace\fR \fIpathspace\fR +Insert \fIpathspace\fR into all default paths, changing the defaults to: +.IP +\fB@CFG_SYSCONF@/\fIpathspace\fB/<daemon>.conf\fR +.br +\fB@CFG_STATE@/\fIpathspace\fB/<daemon>.pid\fR +.br +\fB@CFG_STATE@/\fIpathspace\fB/<daemon>.vty\fR +.br +\fB@CFG_STATE@/\fIpathspace\fB/zserv.api\fR + +\'.\' and \'/\' characters will not be accepted in \fIpathspace\fR, but the +empty string will be accepted. + +Note that this only changes the respective defaults, it has no effect on +the respective path if the \fB\-f\fR, \fB\-i\fR, \fB\-z\fR or +\fB\-\-vty_socket\fR options are used. + +The purpose of this option is to easily group all file system related +bits together for running multiple fully-separate "logical routers" on a +system, particularly with Linux network namespaces. Groups of daemons +running with distinct \fIpathspace\fR values will be completely unaware +of each other and not interact in any way. + +This option does not do any system setup (like network namespaces.) This +must be done by the user, for example by running: +.IP +\fBip netns exec \fInamespace \fB<daemon> -N \fInamespace\fR + +.SH PROCESS CREDENTIALS +.TP +\fB\-u\fR, \fB\-\-user\fR \fIuser\fR +(default: \fB@enable_user@\fR) +.TP +\fB\-g\fR, \fB\-\-group\fR \fIgroup\fR +(default: \fB@enable_group@\fR) +.IP +Change the user/group which the daemon will switch to. +.PP +Note that there is an additional group, \fB@enable_vty_group@\fR, which +controls group ownership of the VTY sockets. The name of this group cannot +currently be changed, and \fIuser\fR must be a member of this group. + +.SH VTY SETUP +These following options control the daemon's VTY (interactive command line) +interface. The interface is available over TCP, using the telnet protocol, +as well as through the \fBvtysh\fR frontend. +.TP +\fB\-A\fR, \fB--vty_addr\fR \fIvty-addr\fR +Specify an IP/IPv6 address to bind the TCP VTY interface to. It is +generally recommended to specify \fI::1\fR or \fI127.0.0.1\fR. For reasons +of backwards compatibility, the default is to listen on all interfaces. +.TP +\fB\-P\fR, \fB--vty_port\fR \fIvty-port\fR +Override the daemon's default TCP VTY port (each daemon has a different +default value upwards of 2600, listed below.) Specifying \fI0\fR disables +the TCP VTY interface. + +Default ports are: + +.ta 16m +zebra 2601 +.br +ripd 2602 +.br +ripngd 2603 +.br +ospfd 2604 +.br +bgpd 2605 +.br +ospf6d 2606 +.br +isisd 2608 +.br +babeld 2609 +.br +nhrpd 2610 +.br +pimd 2611 +.br +ldpd 2612 +.br +eigrpd 2613 + +Port 2607 is used for ospfd's Opaque LSA API, while port 2600 is used for +the (insecure) TCP-ZEBRA interface. +.TP +\fB\-\-vty_socket\fR \fIvty-path\fR +Overrides the directory used for the \fB<daemon>.vty\fR sockets. +\fBvtysh\fR connects to these sockets in order to access each daemon's +VTY. +.br +Default: \fB\fI@CFG_STATE@\fR[\fB/\fI<pathspace>\fR] + +NB: Unlike the other options, this option specifies a \fBdirectory\fR, +not a full path. + +This option is primarily used by the SNAP packaging system, its semantics +may change. It should not be neccessary in most other scenarios. + +.SH MODULE LOADING +@PACKAGE_NAME@ supports optional dynamically loadable modules, although +these can only be loaded at startup. The set of available modules may vary +across distributions and packages, and modules may be available for +installation as separate packages. +.TP +\fB\-M\fR, \fB\-\-module\fR \fImodule\fR[\fB:\fIoptions\fR] +Load a module named \fImodule\fR, optionally passing \fIoptions\fR to it. + +If there is a \'/\' character in \fImodule\fR, the value is assumed to be +a pathname to a module. + +If there is no \'/\' character, the module directory (see next option) +is searched first for a module named "\fI<daemon>\fB_\fI<module>\fB.so\fR", +then for "\fI<module>\fB.so\fR". +This allows for a module to exist in variations appropriate for particular +daemons, e.g. \fIzebra_snmp\fR and \fIbgp_snmp\fR, with the correct one +selected by \fI\-M snmp\fR. + +The meaning of \fIoptions\fR is specific to the module being loaded. Most +modules currently ignore it. + +Modules are loaded in the order as listed on the command line. This is +not generally relevant. +.TP +\fB\-\-moduledir\fR \fImodule-path\fR +Look for modules in the \fImodule-path\fR directory instead of the default +\fI@CFG_MODULE@\fR. (This path is \fBnot\fR affected by the \fB\-N\fR +option.) +.PP +The list of loaded modules can be inspected at runtime with the +\fBshow modules\fR VTY command. + + +.SH "SEE ALSO" +.BR zebra (8), +.BR vtysh (1), +.BR ripd (8), +.BR ripngd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR bgpd (8), +.BR isisd (8), +.BR babeld (8), +.BR nhrpd (8), +.BR pimd (8), +.BR ldpd (8), +.BR eigrpd (8) + +\fIhttps://frrouting.org/ diff --git a/doc/main.texi b/doc/main.texi index 265d6295ef..9e2ca5e506 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -65,6 +65,17 @@ Up or down the current interface. Set the IPv4 or IPv6 address/prefix for the interface. @end deffn +@deffn {Interface Command} {ip address @var{local-addr} peer @var{peer-addr/prefix}} {} +@deffnx {Interface Command} {no ip address @var{local-addr} peer @var{peer-addr/prefix}} {} +Configure an IPv4 Pointopoint address on the interface. +(The concept of PtP addressing does not exist for IPv6.) + +@var{local-addr} has no subnet mask since the local side in PtP +addressing is always a single (/32) address. @var{peer-addr/prefix} +can be an arbitrary subnet behind the other end of the link (or even on the +link in Point-to-Multipoint setups), though generally /32s are used. +@end deffn + @deffn {Interface Command} {ip address @var{address/prefix} secondary} {} @deffnx {Interface Command} {no ip address @var{address/prefix} secondary} {} Set the secondary flag for this address. This causes ospfd to not treat the diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index 59b1724f85..ee3e2217b3 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -306,6 +306,7 @@ int eigrp_if_up(struct eigrp_interface *ei) pe->nt = EIGRP_TOPOLOGY_TYPE_CONNECTED; ne->prefix = pe; + pe->reported_metric = metric; pe->state = EIGRP_FSM_STATE_PASSIVE; pe->fdistance = eigrp_calculate_metrics(eigrp, metric); pe->req_action |= EIGRP_FSM_NEED_UPDATE; diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index c0f8d36b49..de21cb2e73 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -93,7 +93,7 @@ static int config_write_interfaces(struct vty *vty, struct eigrp *eigrp) struct listnode *node; for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - vty_out(vty, "interface %s\n", ei->ifp->name); + vty_frame(vty, "interface %s\n", ei->ifp->name); if ((IF_DEF_PARAMS(ei->ifp)->auth_type) == EIGRP_AUTH_TYPE_MD5) { @@ -128,7 +128,7 @@ static int config_write_interfaces(struct vty *vty, struct eigrp *eigrp) } /*Separate this EIGRP interface configuration from the others*/ - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); } return 0; @@ -140,7 +140,7 @@ static int eigrp_write_interface(struct vty *vty) struct interface *ifp; for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp)) { - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); @@ -157,7 +157,7 @@ static int eigrp_write_interface(struct vty *vty) vty_out(vty, " ip hold-time eigrp %u\n", IF_DEF_PARAMS(ifp)->v_wait); - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); } return 0; diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 98f612f827..2c8b126088 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -48,14 +48,15 @@ extern struct zebra_privs_t isisd_privs; struct bpf_insn llcfilter[] = { - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, - ETHER_HDR_LEN), /* check first byte */ + /* check first byte */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5), - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, - 3), /* check second byte */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */ + /* check second byte */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN + 1), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3), + /* check third byte */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN + 2), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), BPF_STMT(BPF_RET + BPF_K, (u_int)-1), BPF_STMT(BPF_RET + BPF_K, 0)}; u_int readblen = 0; @@ -243,15 +244,14 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, u_char *ssnpa) assert(bpf_hdr->bh_caplen == bpf_hdr->bh_datalen); - offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN; + offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETH_ALEN; /* then we lose the BPF, LLC and ethernet headers */ stream_write(circuit->rcv_stream, readbuff + offset, - bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN); + bpf_hdr->bh_caplen - LLC_LEN - ETH_ALEN); stream_set_getp(circuit->rcv_stream, 0); - memcpy(ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETH_ALEN, - ETH_ALEN); + memcpy(ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETH_ALEN, ETH_ALEN); if (ioctl(circuit->fd, BIOCFLUSH, &one) < 0) zlog_warn("Flushing failed: %s", safe_strerror(errno)); @@ -265,7 +265,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) ssize_t written; size_t buflen; - buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN; + buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETH_ALEN; if (buflen > sizeof(sock_buff)) { zlog_warn( "isis_send_pdu_bcast: sock_buff size %zu is less than " @@ -291,12 +291,12 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) /* * Then the LLC */ - sock_buff[ETHER_HDR_LEN] = ISO_SAP; - sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP; - sock_buff[ETHER_HDR_LEN + 2] = 0x03; + sock_buff[ETH_ALEN] = ISO_SAP; + sock_buff[ETH_ALEN + 1] = ISO_SAP; + sock_buff[ETH_ALEN + 2] = 0x03; /* then we copy the data */ - memcpy(sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data, + memcpy(sock_buff + (LLC_LEN + ETH_ALEN), circuit->snd_stream->data, stream_get_endp(circuit->snd_stream)); /* now we can send this */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index a1aa87e396..7dd8303907 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -948,7 +948,7 @@ int isis_interface_config_write(struct vty *vty) continue; /* IF name */ - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); write++; /* IF desc */ if (ifp->desc) { @@ -1145,7 +1145,7 @@ int isis_interface_config_write(struct vty *vty) } write += circuit_write_mt_settings(circuit, vty); } - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); } return write; diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 6fa7988304..17b043444c 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -146,6 +146,6 @@ void dynhn_print_all(struct vty *vty) } vty_out(vty, " * %s %s\n", sysid_print(isis->sysid), - unix_hostname()); + cmd_hostname_get()); return; } diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 6bf6e45d92..2e1e8e5fc3 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -290,12 +290,21 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, return LSP_OLDER; } -static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer) +static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer, bool keep) { uint8_t pdu_type = (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE; struct isis_lsp_hdr *hdr = &lsp->hdr; struct stream *stream = lsp->pdu; + size_t orig_getp, orig_endp; + + if (keep) { + orig_getp = stream_get_getp(lsp->pdu); + orig_endp = stream_get_endp(lsp->pdu); + } + + stream_set_getp(lsp->pdu, 0); + stream_set_endp(lsp->pdu, 0); fill_fixed_hdr(pdu_type, stream); @@ -307,6 +316,11 @@ static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer) stream_putl(stream, hdr->seqno); stream_putw(stream, hdr->checksum); stream_putc(stream, hdr->lsp_bits); + + if (keep) { + stream_set_endp(lsp->pdu, orig_endp); + stream_set_getp(lsp->pdu, orig_getp); + } } static void lsp_add_auth(struct isis_lsp *lsp) @@ -325,8 +339,7 @@ static void lsp_pack_pdu(struct isis_lsp *lsp) lsp_add_auth(lsp); size_t len_pointer; - stream_reset(lsp->pdu); - put_lsp_hdr(lsp, &len_pointer); + put_lsp_hdr(lsp, &len_pointer, false); isis_pack_tlvs(lsp->tlvs, lsp->pdu, len_pointer, false, true); lsp->hdr.pdu_len = stream_get_endp(lsp->pdu); @@ -457,16 +470,10 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, lsp->own_lsp = 0; } + lsp_update_data(lsp, hdr, tlvs, stream, area, level); if (confusion) { - lsp_clear_data(lsp); - if (lsp->pdu != NULL) - stream_free(lsp->pdu); - lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); - lsp->age_out = ZERO_AGE_LIFETIME; - lsp->hdr.rem_lifetime = 0; - lsp_pack_pdu(lsp); - } else { - lsp_update_data(lsp, hdr, tlvs, stream, area, level); + lsp->hdr.rem_lifetime = hdr->rem_lifetime = 0; + put_lsp_hdr(lsp, NULL, true); } /* insert the lsp back into the database */ @@ -523,7 +530,7 @@ struct isis_lsp *lsp_new(struct isis_area *area, u_char *lsp_id, lsp->level = level; lsp->age_out = ZERO_AGE_LIFETIME; lsp_link_fragment(lsp, lsp0); - put_lsp_hdr(lsp, NULL); + put_lsp_hdr(lsp, NULL, false); if (isis->debugs & DEBUG_EVENTS) zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x", @@ -600,7 +607,7 @@ static void lspid_print(u_char *lsp_id, u_char *trg, char dynhost, char frag) if (dyn) sprintf((char *)id, "%.14s", dyn->hostname); else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) - sprintf((char *)id, "%.14s", unix_hostname()); + sprintf((char *)id, "%.14s", cmd_hostname_get()); else memcpy(id, sysid_print(lsp_id), 15); if (frag) @@ -880,9 +887,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) } /* Dynamic Hostname */ if (area->dynhostname) { - isis_tlvs_set_dynamic_hostname(lsp->tlvs, unix_hostname()); + isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get()); lsp_debug("ISIS (%s): Adding dynamic hostname '%s'", - area->area_tag, unix_hostname()); + area->area_tag, cmd_hostname_get()); } else { lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag); diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index e8888a5f5b..0a1d9aaa1a 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -432,24 +432,6 @@ struct in_addr newprefix2inaddr(u_char *prefix_start, u_char prefix_masklen) } /* - * Returns host.name if any, otherwise - * it returns the system hostname. - */ -const char *unix_hostname(void) -{ - static struct utsname names; - const char *hostname; - - hostname = host.name; - if (!hostname) { - uname(&names); - hostname = names.nodename; - } - - return hostname; -} - -/* * Returns the dynamic hostname associated with the passed system ID. * If no dynamic hostname found then returns formatted system ID. */ @@ -462,7 +444,7 @@ const char *print_sys_hostname(const u_char *sysid) /* For our system ID return our host name */ if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0) - return unix_hostname(); + return cmd_hostname_get(); dyn = dynhn_find_by_id(sysid); if (dyn) diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index 5a19a1ffa0..b81db34df6 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -56,7 +56,6 @@ void zlog_dump_data(void *data, int len); * misc functions */ unsigned long isis_jitter(unsigned long timer, unsigned long jitter); -const char *unix_hostname(void); /* * macros diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 9acbc21838..bc852e209f 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1294,10 +1294,10 @@ static int isis_run_spf(struct isis_area *area, int level, int family, /* * C.2.7 Step 2 */ - if (isis_vertex_queue_count(&spftree->tents) == 0) { + if (!isis_vertex_queue_count(&spftree->tents) + && (isis->debugs & DEBUG_SPF_EVENTS)) { zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s", print_sys_hostname(sysid)); - goto out; } while (isis_vertex_queue_count(&spftree->tents)) { diff --git a/isisd/isisd.c b/isisd/isisd.c index f8a9df45c7..bdc1d836db 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -1260,8 +1260,8 @@ DEFUN (show_isis_spf_ietf, if (area->spf_timer[level - 1]) { struct timeval remain = thread_timer_remain( area->spf_timer[level - 1]); - vty_out(vty, "Pending, due in %ld msec\n", - remain.tv_sec * 1000 + vty_out(vty, "Pending, due in %lld msec\n", + (long long)remain.tv_sec * 1000 + remain.tv_usec / 1000); } else { vty_out(vty, "Not scheduled\n"); @@ -1441,7 +1441,7 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level) lsp = lsp_search( lspid, area->lspdb[level]); - } else if (strncmp(unix_hostname(), + } else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) { memcpy(lspid, isis->sysid, diff --git a/lib/command.c b/lib/command.c index c86025a3bd..24095de4f0 100644 --- a/lib/command.c +++ b/lib/command.c @@ -125,6 +125,23 @@ vector cmdvec = NULL; /* Host information structure. */ struct host host; +/* + * Returns host.name if any, otherwise + * it returns the system hostname. + */ +const char *cmd_hostname_get(void) +{ + return host.name; +} + +/* + * Returns unix domainname + */ +const char *cmd_domainname_get(void) +{ + return host.domainname; +} + /* Standard command node structures. */ static struct cmd_node auth_node = { AUTH_NODE, "Password: ", @@ -475,8 +492,8 @@ static char *zencrypt(const char *passwd) /* This function write configuration of this host. */ static int config_write_host(struct vty *vty) { - if (host.name) - vty_out(vty, "hostname %s\n", host.name); + if (cmd_hostname_get()) + vty_out(vty, "hostname %s\n", cmd_hostname_get()); if (host.encrypt) { if (host.password_encrypt) @@ -1411,7 +1428,7 @@ DEFUN (show_version, "Displays zebra version\n") { vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION, - host.name ? host.name : ""); + cmd_hostname_get() ? cmd_hostname_get() : ""); vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO); vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS); @@ -1745,6 +1762,40 @@ DEFUN (show_startup_config, return CMD_SUCCESS; } +int cmd_domainname_set(const char *domainname) +{ + XFREE(MTYPE_HOST, host.domainname); + host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL; + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN (config_domainname, + domainname_cmd, + "domainname WORD", + "Set system's domain name\n" + "This system's domain name\n") +{ + struct cmd_token *word = argv[1]; + + if (!isalpha((int)word->arg[0])) { + vty_out(vty, "Please specify string starting with alphabet\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return cmd_domainname_set(word->arg); +} + +DEFUN (config_no_domainname, + no_domainname_cmd, + "no domainname [DOMAINNAME]", + NO_STR + "Reset system's domain name\n" + "domain name of this router\n") +{ + return cmd_domainname_set(NULL); +} + int cmd_hostname_set(const char *hostname) { XFREE(MTYPE_HOST, host.name); @@ -2515,9 +2566,12 @@ void install_default(enum node_type node) * terminal = -1 -- watchfrr / no logging, but minimal config control */ void cmd_init(int terminal) { + struct utsname names; + if (array_size(node_names) != NODE_TYPE_MAX) assert(!"Update the CLI node description array!"); + uname(&names); qobj_init(); varhandlers = list_new(); @@ -2526,7 +2580,15 @@ void cmd_init(int terminal) cmdvec = vector_init(VECTOR_MIN_SIZE); /* Default host value settings. */ - host.name = NULL; + host.name = XSTRDUP(MTYPE_HOST, names.nodename); +#ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME + if ((strcmp(names.domainname, "(none)") == 0)) + host.domainname = NULL; + else + host.domainname = XSTRDUP(MTYPE_HOST, names.domainname); +#else + host.domainname = NULL; +#endif host.password = NULL; host.enable = NULL; host.logfile = NULL; @@ -2579,6 +2641,8 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &hostname_cmd); install_element(CONFIG_NODE, &no_hostname_cmd); + install_element(CONFIG_NODE, &domainname_cmd); + install_element(CONFIG_NODE, &no_domainname_cmd); install_element(CONFIG_NODE, &frr_version_defaults_cmd); install_element(CONFIG_NODE, &debug_memstats_cmd); @@ -2644,6 +2708,8 @@ void cmd_terminate() if (host.name) XFREE(MTYPE_HOST, host.name); + if (host.domainname) + XFREE(MTYPE_HOST, host.domainname); if (host.password) XFREE(MTYPE_HOST, host.password); if (host.password_encrypt) diff --git a/lib/command.h b/lib/command.h index 8f12e2aabd..1c6938523c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -41,6 +41,9 @@ struct host { /* Host name of this router. */ char *name; + /* Domainname of this router */ + char *domainname; + /* Password for vty interface. */ char *password; char *password_encrypt; @@ -398,7 +401,10 @@ extern void cmd_terminate(void); extern void cmd_exit(struct vty *vty); extern int cmd_list_cmds(struct vty *vty, int do_permute); +extern int cmd_domainname_set(const char *domainname); extern int cmd_hostname_set(const char *hostname); +extern const char *cmd_hostname_get(void); +extern const char *cmd_domainname_get(void); /* NOT safe for general use; call this only if DEV_BUILD! */ extern void grammar_sandbox_init(void); diff --git a/lib/command_match.c b/lib/command_match.c index f07448d716..ad3ec2492e 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -46,8 +46,9 @@ DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack") static int add_nexthops(struct list *, struct graph_node *, struct graph_node **, size_t); -static struct list *command_match_r(struct graph_node *, vector, unsigned int, - struct graph_node **); +static enum matcher_rv command_match_r(struct graph_node *, vector, + unsigned int, struct graph_node **, + struct list **); static int score_precedence(enum cmd_token_type); @@ -80,14 +81,12 @@ static enum match_type match_variable(struct cmd_token *, const char *); static enum match_type match_mac(const char *, bool); -/* matching functions */ -static enum matcher_rv matcher_rv; - enum matcher_rv command_match(struct graph *cmdgraph, vector vline, struct list **argv, const struct cmd_element **el) { struct graph_node *stack[MAXDEPTH]; - matcher_rv = MATCHER_NO_MATCH; + enum matcher_rv status; + *argv = NULL; // prepend a dummy token to match that pesky start node vector vvline = vector_init(vline->alloced + 1); @@ -97,9 +96,8 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline, vvline->active = vline->active + 1; struct graph_node *start = vector_slot(cmdgraph->nodes, 0); - if ((*argv = command_match_r(start, vvline, 0, - stack))) // successful match - { + status = command_match_r(start, vvline, 0, stack, argv); + if (status == MATCHER_OK) { // successful match struct listnode *head = listhead(*argv); struct listnode *tail = listtail(*argv); @@ -115,6 +113,9 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline, // input, with each cmd_token->arg holding the corresponding // input assert(*el); + } else if (*argv) { + del_arglist(*argv); + *argv = NULL; } if (!*el) { @@ -129,7 +130,7 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline, // free vector vector_free(vvline); - return matcher_rv; + return status; } /** @@ -183,11 +184,15 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline, * * If no match was found, the return value is NULL. */ -static struct list *command_match_r(struct graph_node *start, vector vline, - unsigned int n, struct graph_node **stack) +static enum matcher_rv command_match_r(struct graph_node *start, vector vline, + unsigned int n, + struct graph_node **stack, + struct list **currbest) { assert(n < vector_active(vline)); + enum matcher_rv status = MATCHER_NO_MATCH; + // get the minimum match level that can count as a full match struct cmd_token *token = start->data; enum match_type minmatch = min_match_level(token->type); @@ -196,11 +201,11 @@ static struct list *command_match_r(struct graph_node *start, vector vline, * this disallows matching the same one more than once if there is a * circle in the graph (used for keyword arguments) */ if (n == MAXDEPTH) - return NULL; + return MATCHER_NO_MATCH; if (!token->allowrepeat) for (size_t s = 0; s < n; s++) if (stack[s] == start) - return NULL; + return MATCHER_NO_MATCH; // get the current operating input token char *input_token = vector_slot(vline, n); @@ -231,7 +236,7 @@ static struct list *command_match_r(struct graph_node *start, vector vline, // if we don't match this node, die if (match_token(token, input_token) < minmatch) - return NULL; + return MATCHER_NO_MATCH; stack[n] = start; @@ -244,86 +249,92 @@ static struct list *command_match_r(struct graph_node *start, vector vline, add_nexthops(next, start, NULL, 0); // determine the best match - int ambiguous = 0; - struct list *currbest = NULL; for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) { // if we've matched all input we're looking for END_TKN if (n + 1 == vector_active(vline)) { struct cmd_token *tok = gn->data; if (tok->type == END_TKN) { - if (currbest) // there is more than one END_TKN - // in the follow set - { - ambiguous = 1; + // if more than one END_TKN in the follow set + if (*currbest) { + status = MATCHER_AMBIGUOUS; break; + } else { + status = MATCHER_OK; } - currbest = list_new(); + *currbest = list_new(); // node should have one child node with the // element struct graph_node *leaf = vector_slot(gn->to, 0); // last node in the list will hold the - // cmd_element; - // this is important because list_delete() - // expects - // that all nodes have the same data type, so - // when - // deleting this list the last node must be - // manually deleted + // cmd_element; this is important because + // list_delete() expects that all nodes have + // the same data type, so when deleting this + // list the last node must be manually deleted struct cmd_element *el = leaf->data; - listnode_add(currbest, el); - currbest->del = + listnode_add(*currbest, el); + (*currbest)->del = (void (*)(void *)) & cmd_token_del; // do not break immediately; continue walking - // through the follow set - // to ensure that there is exactly one END_TKN + // through the follow set to ensure that there + // is exactly one END_TKN } continue; } // else recurse on candidate child node - struct list *result = command_match_r(gn, vline, n + 1, stack); + struct list *result = NULL; + enum matcher_rv rstat = + command_match_r(gn, vline, n + 1, stack, &result); // save the best match - if (result && currbest) { + if (result && *currbest) { // pick the best of two matches struct list *newbest = - disambiguate(currbest, result, vline, n + 1); - // set ambiguity flag - ambiguous = - !newbest || (ambiguous && newbest == currbest); + disambiguate(*currbest, result, vline, n + 1); + + // current best and result are ambiguous + if (!newbest) + status = MATCHER_AMBIGUOUS; + // current best is still the best, but ambiguous + else if (newbest == *currbest + && status == MATCHER_AMBIGUOUS) + status = MATCHER_AMBIGUOUS; + // result is better, but also ambiguous + else if (newbest == result + && rstat == MATCHER_AMBIGUOUS) + status = MATCHER_AMBIGUOUS; + // one or the other is superior and not ambiguous + else + status = MATCHER_OK; + // delete the unnecessary result struct list *todelete = - ((newbest && newbest == result) ? currbest + ((newbest && newbest == result) ? *currbest : result); del_arglist(todelete); - currbest = newbest ? newbest : currbest; - } else if (result) - currbest = result; - } - - if (currbest) { - if (ambiguous) { - del_arglist(currbest); - currbest = NULL; - matcher_rv = MATCHER_AMBIGUOUS; - } else { - // copy token, set arg and prepend to currbest - struct cmd_token *token = start->data; - struct cmd_token *copy = cmd_token_dup(token); - copy->arg = XSTRDUP(MTYPE_CMD_ARG, input_token); - listnode_add_before(currbest, currbest->head, copy); - matcher_rv = MATCHER_OK; + *currbest = newbest ? newbest : *currbest; + } else if (result) { + status = rstat; + *currbest = result; + } else if (!*currbest) { + status = MAX(rstat, status); } - } else if (n + 1 == vector_active(vline) - && matcher_rv == MATCHER_NO_MATCH) - matcher_rv = MATCHER_INCOMPLETE; + } + if (*currbest) { + // copy token, set arg and prepend to currbest + struct cmd_token *token = start->data; + struct cmd_token *copy = cmd_token_dup(token); + copy->arg = XSTRDUP(MTYPE_CMD_ARG, input_token); + listnode_add_before(*currbest, (*currbest)->head, copy); + } else if (n + 1 == vector_active(vline) && status == MATCHER_NO_MATCH) + status = MATCHER_INCOMPLETE; // cleanup list_delete(next); - return currbest; + return status; } static void stack_del(void *val) @@ -432,12 +443,12 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, * next = set of all nodes reachable from all nodes in `matched` */ - matcher_rv = idx == vector_active(vline) && next->count - ? MATCHER_OK - : MATCHER_NO_MATCH; + enum matcher_rv mrv = idx == vector_active(vline) && next->count + ? MATCHER_OK + : MATCHER_NO_MATCH; *completions = NULL; - if (!MATCHER_ERROR(matcher_rv)) { + if (!MATCHER_ERROR(mrv)) { // extract cmd_token into list *completions = list_new(); for (ALL_LIST_ELEMENTS_RO(next, node, gstack)) { @@ -448,7 +459,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, list_delete(current); list_delete(next); - return matcher_rv; + return mrv; } /** diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c new file mode 100644 index 0000000000..861f7a5f0c --- /dev/null +++ b/lib/frr_zmq.c @@ -0,0 +1,191 @@ +/* + * libzebra ZeroMQ bindings + * Copyright (C) 2015 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <zmq.h> + +#include "thread.h" +#include "memory.h" +#include "frr_zmq.h" +#include "log.h" + +DEFINE_MTYPE_STATIC(LIB, ZEROMQ_CB, "ZeroMQ callback") + +/* libzmq's context */ +void *frrzmq_context = NULL; +static unsigned frrzmq_initcount = 0; + +void frrzmq_init(void) +{ + if (frrzmq_initcount++ == 0) { + frrzmq_context = zmq_ctx_new(); + zmq_ctx_set(frrzmq_context, ZMQ_IPV6, 1); + } +} + +void frrzmq_finish(void) +{ + if (--frrzmq_initcount == 0) { + zmq_ctx_term(frrzmq_context); + frrzmq_context = NULL; + } +} + +/* read callback integration */ +struct frrzmq_cb { + struct thread *thread; + void *zmqsock; + void *arg; + int fd; + + bool cancelled; + + void (*cb_msg)(void *arg, void *zmqsock); + void (*cb_part)(void *arg, void *zmqsock, + zmq_msg_t *msg, unsigned partnum); +}; + + +static int frrzmq_read_msg(struct thread *t) +{ + struct frrzmq_cb *cb = THREAD_ARG(t); + zmq_msg_t msg; + unsigned partno; + int ret, more; + size_t moresz; + + while (1) { + zmq_pollitem_t polli = { + .socket = cb->zmqsock, + .events = ZMQ_POLLIN + }; + ret = zmq_poll(&polli, 1, 0); + + if (ret < 0) + goto out_err; + if (!(polli.revents & ZMQ_POLLIN)) + break; + + if (cb->cb_msg) { + cb->cb_msg(cb->arg, cb->zmqsock); + + if (cb->cancelled) { + XFREE(MTYPE_ZEROMQ_CB, cb); + return 0; + } + continue; + } + + partno = 0; + if (zmq_msg_init(&msg)) + goto out_err; + do { + ret = zmq_msg_recv(&msg, cb->zmqsock, ZMQ_NOBLOCK); + if (ret < 0) { + if (errno == EAGAIN) + break; + + zmq_msg_close(&msg); + goto out_err; + } + + cb->cb_part(cb->arg, cb->zmqsock, &msg, partno); + if (cb->cancelled) { + zmq_msg_close(&msg); + XFREE(MTYPE_ZEROMQ_CB, cb); + return 0; + } + + /* cb_part may have read additional parts of the + * message; don't use zmq_msg_more here */ + moresz = sizeof(more); + more = 0; + ret = zmq_getsockopt(cb->zmqsock, ZMQ_RCVMORE, + &more, &moresz); + if (ret < 0) { + zmq_msg_close(&msg); + goto out_err; + } + + partno++; + } while (more); + zmq_msg_close(&msg); + } + + funcname_thread_add_read_write(THREAD_READ, t->master, frrzmq_read_msg, + cb, cb->fd, &cb->thread, t->funcname, t->schedfrom, + t->schedfrom_line); + return 0; + +out_err: + zlog_err("ZeroMQ error: %s(%d)", strerror (errno), errno); + return 0; +} + +struct frrzmq_cb *funcname_frrzmq_thread_add_read( + struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, + zmq_msg_t *msg, unsigned partnum), + void *arg, void *zmqsock, debugargdef) +{ + int fd, events; + size_t len; + struct frrzmq_cb *cb; + + if (!(msgfunc || partfunc) || (msgfunc && partfunc)) + return NULL; + len = sizeof(fd); + if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len)) + return NULL; + len = sizeof(events); + if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len)) + return NULL; + + cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb)); + if (!cb) + return NULL; + + cb->arg = arg; + cb->zmqsock = zmqsock; + cb->cb_msg = msgfunc; + cb->cb_part = partfunc; + cb->fd = fd; + + if (events & ZMQ_POLLIN) + funcname_thread_add_event(master, + frrzmq_read_msg, cb, fd, &cb->thread, + funcname, schedfrom, fromln); + else + funcname_thread_add_read_write(THREAD_READ, master, + frrzmq_read_msg, cb, fd, &cb->thread, + funcname, schedfrom, fromln); + return cb; +} + +void frrzmq_thread_cancel(struct frrzmq_cb *cb) +{ + if (!cb->thread) { + /* canceling from within callback */ + cb->cancelled = 1; + return; + } + thread_cancel(cb->thread); + XFREE(MTYPE_ZEROMQ_CB, cb); +} diff --git a/lib/frr_zmq.h b/lib/frr_zmq.h new file mode 100644 index 0000000000..69c6f8580d --- /dev/null +++ b/lib/frr_zmq.h @@ -0,0 +1,88 @@ +/* + * libzebra ZeroMQ bindings + * Copyright (C) 2015 David Lamparter + * + * 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 _FRRZMQ_H +#define _FRRZMQ_H + +#include "thread.h" +#include <zmq.h> + +/* linking/packaging note: this is a separate library that needs to be + * linked into any daemon/library/module that wishes to use its + * functionality. The purpose of this is to encapsulate the libzmq + * dependency and not make libfrr/FRR itself depend on libzmq. + * + * libfrrzmq should be put in LDFLAGS/LIBADD *before* either libfrr or + * libzmq, and both of these should always be listed, e.g. + * foo_LDFLAGS = libfrrzmq.la libfrr.la $(ZEROMQ_LIBS) + */ + +/* libzmq's context + * + * this is mostly here as a convenience, it has IPv6 enabled but nothing + * else is tied to it; you can use a separate context without problems + */ +extern void *frrzmq_context; + +extern void frrzmq_init (void); +extern void frrzmq_finish (void); + +#define debugargdef const char *funcname, const char *schedfrom, int fromln + +/* core event registration, one of these 2 macros should be used */ +#define frrzmq_thread_add_read_msg(m,f,a,z) funcname_frrzmq_thread_add_read( \ + m,f,NULL,a,z,#f,__FILE__,__LINE__) +#define frrzmq_thread_add_read_part(m,f,a,z) funcname_frrzmq_thread_add_read( \ + m,NULL,f,a,z,#f,__FILE__,__LINE__) + +struct frrzmq_cb; + +/* Set up a POLLIN notification to be called from the libfrr main loop. + * This has the following properties: + * + * - since ZeroMQ works with edge triggered notifications, it will loop and + * dispatch as many events as ZeroMQ has pending at the time libfrr calls + * into this code + * - due to this looping (which means it non-single-issue), the callback is + * also persistent. Do _NOT_ re-register the event inside of your + * callback function. + * - either msgfunc or partfunc will be called (only one can be specified) + * - msgfunc is called once for each incoming message + * - if partfunc is specified, the message is read and partfunc is called + * for each ZeroMQ multi-part subpart. Note that you can't send replies + * before all parts have been read because that violates the ZeroMQ FSM. + * - you can safely cancel the callback from within itself + * - installing a callback will check for pending events (ZMQ_EVENTS) and + * may schedule the event to run as soon as libfrr is back in its main + * loop. + * + * TODO #1: add ZMQ_POLLERR / error callback + * TODO #2: add frrzmq_check_events() function to check for edge triggered + * things that may have happened after a zmq_send() call or so + */ +extern struct frrzmq_cb *funcname_frrzmq_thread_add_read( + struct thread_master *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, + zmq_msg_t *msg, unsigned partnum), + void *arg, void *zmqsock, debugargdef); + +extern void frrzmq_thread_cancel(struct frrzmq_cb *cb); + +#endif /* _FRRZMQ_H */ diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 89b0993d1d..264c7c48f0 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -50,6 +50,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); host.name = strdup("test"); + host.domainname = strdup("testdomainname"); vty_init(master); memory_init(); diff --git a/lib/libfrr.c b/lib/libfrr.c index 9944fdd1e1..3e2e008223 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -101,13 +101,15 @@ static const struct optspec os_always = { static const struct option lo_cfg_pid_dry[] = { {"pid_file", required_argument, NULL, 'i'}, {"config_file", required_argument, NULL, 'f'}, + {"pathspace", required_argument, NULL, 'N'}, {"dryrun", no_argument, NULL, 'C'}, {"terminal", no_argument, NULL, 't'}, {NULL}}; static const struct optspec os_cfg_pid_dry = { - "f:i:Ct", + "f:i:CtN:", " -f, --config_file Set configuration file name\n" " -i, --pid_file Set process identifier file name\n" + " -N, --pathspace Insert prefix into config & socket paths\n" " -C, --dryrun Check configuration for validity and exit\n" " -t, --terminal Open terminal session on stdio\n" " -d -t Daemonize after terminal session ends\n", @@ -351,6 +353,23 @@ static int frr_opt(int opt) return 1; di->config_file = optarg; break; + case 'N': + if (di->flags & FRR_NO_CFG_PID_DRY) + return 1; + if (di->pathspace) { + fprintf(stderr, + "-N/--pathspace option specified more than once!\n"); + errors++; + break; + } + if (strchr(optarg, '/') || strchr(optarg, '.')) { + fprintf(stderr, + "slashes or dots are not permitted in the --pathspace option.\n"); + errors++; + break; + } + di->pathspace = optarg; + break; case 'C': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; @@ -500,14 +519,25 @@ struct thread_master *frr_init(void) struct option_chain *oc; struct frrmod_runtime *module; char moderr[256]; + char p_instance[16] = "", p_pathspace[256] = ""; const char *dir; dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); - if (di->instance) + if (di->instance) { snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]", di->logname, di->instance); + snprintf(p_instance, sizeof(p_instance), "-%d", di->instance); + } + if (di->pathspace) + snprintf(p_pathspace, sizeof(p_pathspace), "/%s", + di->pathspace); + + snprintf(config_default, sizeof(config_default), "%s%s/%s%s.conf", + frr_sysconfdir, p_pathspace, di->name, p_instance); + snprintf(pidfile_default, sizeof(pidfile_default), "%s%s/%s%s.pid", + frr_vtydir, p_pathspace, di->name, p_instance); zprivs_preinit(di->privs); @@ -695,14 +725,6 @@ void frr_config_fork(void) { hook_call(frr_late_init, master); - if (di->instance) { - snprintf(config_default, sizeof(config_default), - "%s/%s-%d.conf", frr_sysconfdir, di->name, - di->instance); - snprintf(pidfile_default, sizeof(pidfile_default), - "%s/%s-%d.pid", frr_vtydir, di->name, di->instance); - } - vty_read_config(di->config_file, config_default); /* Don't start execution if we are in dry-run mode */ @@ -723,7 +745,13 @@ void frr_vty_serv(void) * (not currently set anywhere) */ if (!di->vty_path) { const char *dir; - dir = di->vty_sock_path ? di->vty_sock_path : frr_vtydir; + char defvtydir[256]; + + snprintf(defvtydir, sizeof(defvtydir), "%s%s%s", frr_vtydir, + di->pathspace ? "/" : "", + di->pathspace ? di->pathspace : ""); + + dir = di->vty_sock_path ? di->vty_sock_path : defvtydir; if (di->instance) snprintf(vtypath_default, sizeof(vtypath_default), diff --git a/lib/libfrr.h b/lib/libfrr.h index f7d69eecb3..fe6c46670a 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -54,6 +54,7 @@ struct frr_daemon_info { const char *pid_file; const char *vty_path; const char *module_path; + const char *pathspace; const char *proghelp; void (*printhelp)(FILE *target); diff --git a/lib/module.c b/lib/module.c index beef791093..b3ab91c4ea 100644 --- a/lib/module.c +++ b/lib/module.c @@ -42,8 +42,10 @@ static struct frrmod_info frrmod_default_info = { .description = "libfrr core module", }; union _frrmod_runtime_u frrmod_default = { - .r.info = &frrmod_default_info, - .r.finished_loading = 1, + .r = { + .info = &frrmod_default_info, + .finished_loading = 1, + }, }; // if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE) diff --git a/lib/nexthop.c b/lib/nexthop.c index 7180be33dd..2dba412f45 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -36,7 +36,8 @@ DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") /* check if nexthops are same, non-recursive */ -int nexthop_same_no_recurse(struct nexthop *next1, struct nexthop *next2) +int nexthop_same_no_recurse(const struct nexthop *next1, + const struct nexthop *next2) { if (next1->type != next2->type) return 0; diff --git a/lib/nexthop.h b/lib/nexthop.h index e7804379f1..781eb93413 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -43,6 +43,13 @@ enum nexthop_types_t { NEXTHOP_TYPE_BLACKHOLE, /* Null0 nexthop. */ }; +enum blackhole_type { + BLACKHOLE_UNSPEC = 0, + BLACKHOLE_NULL, + BLACKHOLE_REJECT, + BLACKHOLE_ADMINPROHIB, +}; + /* Nexthop label structure. */ struct nexthop_label { u_int8_t num_labels; @@ -69,7 +76,10 @@ struct nexthop { #define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */ /* Nexthop address */ - union g_addr gate; + union { + union g_addr gate; + enum blackhole_type bh_type; + }; union g_addr src; union g_addr rmap_src; /* Src is set via routemap */ @@ -128,8 +138,8 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, u_int8_t, void nexthop_del_labels(struct nexthop *); extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); -extern int nexthop_same_no_recurse(struct nexthop *next1, - struct nexthop *next2); +extern int nexthop_same_no_recurse(const struct nexthop *next1, + const struct nexthop *next2); extern int nexthop_labels_match(struct nexthop *nh1, struct nexthop *nh2); extern const char *nexthop2str(struct nexthop *nexthop, char *str, int size); diff --git a/lib/prefix.h b/lib/prefix.h index eab4ac2bb7..a27f46ba0a 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -125,7 +125,7 @@ struct prefix { struct in_addr adv_router; } lp; struct ethaddr prefix_eth; /* AF_ETHERNET */ - u_char val[8]; + u_char val[16]; uintptr_t ptr; struct evpn_addr prefix_evpn; /* AF_EVPN */ } u __attribute__((aligned(8))); diff --git a/lib/sockunion.h b/lib/sockunion.h index 7b1c7ba9c5..67a7a46272 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -34,6 +34,7 @@ union sockunion { struct sockaddr_in6 sin6; #ifdef __OpenBSD__ struct sockaddr_mpls smpls; + struct sockaddr_rtlabel rtlabel; #endif }; diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c index d075e70d4e..92b7620eda 100644 --- a/lib/spf_backoff.c +++ b/lib/spf_backoff.c @@ -226,8 +226,9 @@ void spf_backoff_show(struct spf_backoff *backoff, struct vty *vty, if (backoff->t_holddown) { struct timeval remain = thread_timer_remain(backoff->t_holddown); - vty_out(vty, "%s Still runs for %ld msec\n", - prefix, remain.tv_sec * 1000 + remain.tv_usec / 1000); + vty_out(vty, "%s Still runs for %lld msec\n", + prefix, (long long)remain.tv_sec * 1000 + + remain.tv_usec / 1000); } else { vty_out(vty, "%s Inactive\n", prefix); } @@ -237,8 +238,9 @@ void spf_backoff_show(struct spf_backoff *backoff, struct vty *vty, if (backoff->t_timetolearn) { struct timeval remain = thread_timer_remain(backoff->t_timetolearn); - vty_out(vty, "%s Still runs for %ld msec\n", - prefix, remain.tv_sec * 1000 + remain.tv_usec / 1000); + vty_out(vty, "%s Still runs for %lld msec\n", + prefix, (long long)remain.tv_sec * 1000 + + remain.tv_usec / 1000); } else { vty_out(vty, "%s Inactive\n", prefix); } diff --git a/lib/subdir.am b/lib/subdir.am index d6349ba22d..5f38cc7a14 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -184,6 +184,21 @@ lib_libfrrsnmp_la_SOURCES = \ # end # +# ZeroMQ support +# +if ZEROMQ +lib_LTLIBRARIES += lib/libfrrzmq.la +pkginclude_HEADERS += lib/frr_zmq.h +endif + +lib_libfrrzmq_la_CFLAGS = $(WERROR) $(ZEROMQ_CFLAGS) +lib_libfrrzmq_la_LDFLAGS = -version-info 0:0:0 +lib_libfrrzmq_la_LIBADD = lib/libfrr.la $(ZEROMQ_LIBS) +lib_libfrrzmq_la_SOURCES = \ + lib/frr_zmq.c \ + #end + +# # CLI utilities # noinst_PROGRAMS += \ diff --git a/lib/termtable.c b/lib/termtable.c index f7aec43118..ba85962cc9 100644 --- a/lib/termtable.c +++ b/lib/termtable.c @@ -31,48 +31,60 @@ struct ttable_style ttable_styles[] = { .corner = '+', .rownums_on = false, .indent = 1, - .border.top = '-', - .border.bottom = '-', - .border.left = '|', - .border.right = '|', - .border.top_on = true, - .border.bottom_on = true, - .border.left_on = true, - .border.right_on = true, - .cell.lpad = 1, - .cell.rpad = 1, - .cell.align = LEFT, - .cell.border.bottom = '-', - .cell.border.bottom_on = true, - .cell.border.top = '-', - .cell.border.top_on = false, - .cell.border.right = '|', - .cell.border.right_on = true, - .cell.border.left = '|', - .cell.border.left_on = false, + .border = { + .top = '-', + .bottom = '-', + .left = '|', + .right = '|', + .top_on = true, + .bottom_on = true, + .left_on = true, + .right_on = true, + }, + .cell = { + .lpad = 1, + .rpad = 1, + .align = LEFT, + .border = { + .bottom = '-', + .bottom_on = true, + .top = '-', + .top_on = false, + .right = '|', + .right_on = true, + .left = '|', + .left_on = false, + }, + }, }, { // blank, suitable for plaintext alignment .corner = ' ', .rownums_on = false, .indent = 1, - .border.top = ' ', - .border.bottom = ' ', - .border.left = ' ', - .border.right = ' ', - .border.top_on = false, - .border.bottom_on = false, - .border.left_on = false, - .border.right_on = false, - .cell.lpad = 0, - .cell.rpad = 3, - .cell.align = LEFT, - .cell.border.bottom = ' ', - .cell.border.bottom_on = false, - .cell.border.top = ' ', - .cell.border.top_on = false, - .cell.border.right = ' ', - .cell.border.right_on = false, - .cell.border.left = ' ', - .cell.border.left_on = false, + .border = { + .top = ' ', + .bottom = ' ', + .left = ' ', + .right = ' ', + .top_on = false, + .bottom_on = false, + .left_on = false, + .right_on = false, + }, + .cell = { + .lpad = 0, + .rpad = 3, + .align = LEFT, + .border = { + .bottom = ' ', + .bottom_on = false, + .top = ' ', + .top_on = false, + .right = ' ', + .right_on = false, + .left = ' ', + .left_on = false, + }, + } } }; /* clang-format on */ @@ -91,6 +91,25 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; static int do_log_commands = 0; +void vty_frame(struct vty *vty, const char *format, ...) +{ + va_list args; + + va_start(args, format); + vsnprintf(vty->frame + vty->frame_pos, + sizeof(vty->frame) - vty->frame_pos, + format, args); + vty->frame_pos = strlen(vty->frame); + va_end(args); +} + +void vty_endframe(struct vty *vty, const char *endtext) +{ + if (vty->frame_pos == 0 && endtext) + vty_out(vty, "%s", endtext); + vty->frame_pos = 0; +} + /* VTY standard output function. */ int vty_out(struct vty *vty, const char *format, ...) { @@ -100,6 +119,11 @@ int vty_out(struct vty *vty, const char *format, ...) char buf[1024]; char *p = NULL; + if (vty->frame_pos) { + vty->frame_pos = 0; + vty_out(vty, "%s", vty->frame); + } + if (vty_shell(vty)) { va_start(args, format); vprintf(format, args); @@ -250,16 +274,9 @@ void vty_hello(struct vty *vty) /* Put out prompt and wait input from user. */ static void vty_prompt(struct vty *vty) { - struct utsname names; - const char *hostname; - if (vty->type == VTY_TERM) { - hostname = host.name; - if (!hostname) { - uname(&names); - hostname = names.nodename; - } - vty_out(vty, cmd_prompt(vty->node), hostname); + vty_out(vty, cmd_prompt(vty->node), + cmd_hostname_get()); } } @@ -125,6 +125,12 @@ struct vty { /* What address is this vty comming from. */ char address[SU_ADDRSTRLEN]; + + /* "frame" output. This is buffered and will be printed if some + * actual output follows, or will be discarded if the frame ends + * without any output. */ + size_t frame_pos; + char frame[1024]; }; static inline void vty_push_context(struct vty *vty, int node, uint64_t id) @@ -247,7 +253,16 @@ extern void vty_terminate(void); extern void vty_reset(void); extern struct vty *vty_new(void); extern struct vty *vty_stdio(void (*atclose)(int isexit)); + +/* - vty_frame() output goes to a buffer (for context-begin markers) + * - vty_out() will first print this buffer, and clear it + * - vty_endframe() clears the buffer without printing it, and prints an + * extra string if the buffer was empty before (for context-end markers) + */ extern int vty_out(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); +extern void vty_frame(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); +extern void vty_endframe(struct vty *, const char *); + extern void vty_read_config(const char *, char *); extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); diff --git a/lib/zclient.c b/lib/zclient.c index 72fa2679b3..910e05cb47 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -621,10 +621,9 @@ static int zclient_connect(struct thread *t) * | IPv4 Nexthop address or Interface Index number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * - * Alternatively, if the flags field has ZEBRA_FLAG_BLACKHOLE or - * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_ - * nexthop information is provided, and the message describes a prefix - * to blackhole or reject route. + * Alternatively, if the route is a blackhole route, then Nexthop count + * is set to 1 and a nexthop of type NEXTHOP_TYPE_BLACKHOLE is the sole + * nexthop. * * The original struct zapi_ipv4, zapi_ipv4_route() and zread_ipv4_*() * infrastructure was built around the traditional (32-bit "gate OR @@ -692,14 +691,7 @@ int zapi_ipv4_route(u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) { - /* traditional 32-bit data units */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_BLACKHOLE)) { - stream_putc(s, 1); - stream_putc(s, NEXTHOP_TYPE_BLACKHOLE); - /* XXX assert(api->nexthop_num == 0); */ - /* XXX assert(api->ifindex_num == 0); */ - } else - stream_putc(s, api->nexthop_num + api->ifindex_num); + stream_putc(s, api->nexthop_num + api->ifindex_num); for (i = 0; i < api->nexthop_num; i++) { stream_putc(s, NEXTHOP_TYPE_IPV4); @@ -769,13 +761,7 @@ int zapi_ipv4_route_ipv6_nexthop(u_char cmd, struct zclient *zclient, /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) { - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_BLACKHOLE)) { - stream_putc(s, 1); - stream_putc(s, NEXTHOP_TYPE_BLACKHOLE); - /* XXX assert(api->nexthop_num == 0); */ - /* XXX assert(api->ifindex_num == 0); */ - } else - stream_putc(s, api->nexthop_num + api->ifindex_num); + stream_putc(s, api->nexthop_num + api->ifindex_num); for (i = 0; i < api->nexthop_num; i++) { stream_putc(s, NEXTHOP_TYPE_IPV6); @@ -855,13 +841,7 @@ int zapi_ipv6_route(u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP)) { - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_BLACKHOLE)) { - stream_putc(s, 1); - stream_putc(s, NEXTHOP_TYPE_BLACKHOLE); - /* XXX assert(api->nexthop_num == 0); */ - /* XXX assert(api->ifindex_num == 0); */ - } else - stream_putc(s, api->nexthop_num + api->ifindex_num); + stream_putc(s, api->nexthop_num + api->ifindex_num); for (i = 0; i < api->nexthop_num; i++) { stream_putc(s, NEXTHOP_TYPE_IPV6); @@ -948,6 +928,7 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api) stream_putc(s, api_nh->type); switch (api_nh->type) { case NEXTHOP_TYPE_BLACKHOLE: + stream_putc(s, api_nh->bh_type); break; case NEXTHOP_TYPE_IPV4: stream_put_in_addr(s, &api_nh->gate.ipv4); @@ -1060,6 +1041,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) api_nh->type = stream_getc(s); switch (api_nh->type) { case NEXTHOP_TYPE_BLACKHOLE: + api_nh->bh_type = stream_getc(s); break; case NEXTHOP_TYPE_IPV4: api_nh->gate.ipv4.s_addr = stream_get_ipv4(s); diff --git a/lib/zclient.h b/lib/zclient.h index 7c4780201e..e05e9d8ad9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -222,7 +222,10 @@ struct zserv_header { struct zapi_nexthop { enum nexthop_types_t type; ifindex_t ifindex; - union g_addr gate; + union { + union g_addr gate; + enum blackhole_type bh_type; + }; /* MPLS labels for BGP-LU or Segment Routing */ uint8_t label_num; @@ -429,4 +432,14 @@ extern int zclient_route_send(u_char, struct zclient *, struct zapi_route *); extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); +static inline void zapi_route_set_blackhole(struct zapi_route *api, + enum blackhole_type bh_type) +{ + api->nexthop_num = 1; + api->nexthops[0].type = NEXTHOP_TYPE_BLACKHOLE; + api->nexthops[0].bh_type = bh_type; + SET_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP); +}; + + #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/lib/zebra.h b/lib/zebra.h index 6d64bbd670..fa5fa89f77 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -404,13 +404,13 @@ extern const char *zserv_command_string(unsigned int command); /* Zebra message flags */ #define ZEBRA_FLAG_INTERNAL 0x01 #define ZEBRA_FLAG_SELFROUTE 0x02 -#define ZEBRA_FLAG_BLACKHOLE 0x04 #define ZEBRA_FLAG_IBGP 0x08 #define ZEBRA_FLAG_SELECTED 0x10 #define ZEBRA_FLAG_STATIC 0x40 -#define ZEBRA_FLAG_REJECT 0x80 #define ZEBRA_FLAG_SCOPE_LINK 0x100 #define ZEBRA_FLAG_FIB_OVERRIDE 0x200 +/* ZEBRA_FLAG_BLACKHOLE was 0x04 */ +/* ZEBRA_FLAG_REJECT was 0x80 */ /* Zebra FEC flags. */ #define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x1 diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 4b86bca5f7..495e226f15 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -99,7 +99,9 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix switch (type) { case NHRP_CACHE_NEGATIVE: - SET_FLAG(api.flags, ZEBRA_FLAG_REJECT); + zapi_route_set_blackhole(&api, BLACKHOLE_REJECT); + ifp = NULL; + nexthop = NULL; break; case NHRP_CACHE_DYNAMIC: case NHRP_CACHE_NHS: @@ -158,7 +160,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix " count %d dev %s", add ? "add" : "del", buf[0], nexthop ? inet_ntop(api.prefix.family, &api_nh->gate, buf[1], sizeof(buf[1])) : "<onlink>", - api.metric, api.nexthop_num, ifp->name); + api.metric, api.nexthop_num, ifp ? ifp->name : "none"); } zclient_route_send(add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, zclient, diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index 6d78fc1d2b..bd5b1aa6f1 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -854,7 +854,7 @@ static int interface_config_write(struct vty *vty) int i; for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), node, ifp)) { - vty_out (vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); if (ifp->desc) vty_out (vty, " description %s\n", ifp->desc); @@ -913,7 +913,7 @@ static int interface_config_write(struct vty *vty) } } - vty_out (vty, "!\n"); + vty_endframe(vty, "!\n"); } return 0; diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 649d7a1000..485937d882 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -643,9 +643,9 @@ void ospf6_area_plist_update(struct prefix_list *plist, int add) const char *name = prefix_list_name(plist); for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) { - if (!strcmp(PREFIX_NAME_IN(oa), name)) + if (PREFIX_NAME_IN(oa) && !strcmp(PREFIX_NAME_IN(oa), name)) PREFIX_LIST_IN(oa) = add ? plist : NULL; - if (!strcmp(PREFIX_NAME_OUT(oa), name)) + if (PREFIX_NAME_OUT(oa) && !strcmp(PREFIX_NAME_OUT(oa), name)) PREFIX_LIST_OUT(oa) = add ? plist : NULL; } } diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index bb63fb966e..8cfed81a81 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1755,7 +1755,7 @@ static int config_write_ospf6_interface(struct vty *vty) if (oi == NULL) continue; - vty_out(vty, "interface %s\n", oi->interface->name); + vty_frame(vty, "interface %s\n", oi->interface->name); if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); @@ -1808,7 +1808,7 @@ static int config_write_ospf6_interface(struct vty *vty) ospf6_bfd_write_config(vty, oi); - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); } return 0; } diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 387690bc8a..dc5792aa17 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -308,19 +308,24 @@ DEFUN (no_router_ospf6, /* change Router_ID commands. */ DEFUN (ospf6_router_id, ospf6_router_id_cmd, - "router-id A.B.C.D", - "Configure OSPF Router-ID\n" + "ospf6 router-id A.B.C.D", + OSPF6_STR + "Configure OSPF6 Router-ID\n" V4NOTATION_STR) { VTY_DECLVAR_CONTEXT(ospf6, o); - int idx_ipv4 = 1; + int idx = 0; int ret; + const char *router_id_str; u_int32_t router_id; - ret = inet_pton(AF_INET, argv[idx_ipv4]->arg, &router_id); + argv_find(argv, argc, "A.B.C.D", &idx); + router_id_str = argv[idx]->arg; + + ret = inet_pton(AF_INET, router_id_str, &router_id); if (ret == 0) { vty_out(vty, "malformed OSPF Router-ID: %s\n", - argv[idx_ipv4]->arg); + router_id_str); return CMD_SUCCESS; } @@ -331,6 +336,40 @@ DEFUN (ospf6_router_id, return CMD_SUCCESS; } +DEFUN (no_ospf6_router_id, + no_ospf6_router_id_cmd, + "no ospf6 router-id [A.B.C.D]", + NO_STR + OSPF6_STR + "Configure OSPF6 Router-ID\n" + V4NOTATION_STR) +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + o->router_id_static = 0; + o->router_id = 0; + + return CMD_SUCCESS; +} + +#if CONFDATE > 20180828 +CPP_NOTICE("ospf6: `router-id A.B.C.D` deprecated 2017/08/28") +#endif +ALIAS_HIDDEN(ospf6_router_id, + ospf6_router_id_hdn_cmd, + "router-id A.B.C.D", + "Configure OSPF6 Router-ID\n" + V4NOTATION_STR) + +#if CONFDATE > 20180828 +CPP_NOTICE("ospf6: `no router-id A.B.C.D` deprecated 2017/08/28") +#endif +ALIAS_HIDDEN(no_ospf6_router_id, + no_ospf6_router_id_hdn_cmd, + "no router-id [A.B.C.D]", + NO_STR + "Configure OSPF6 Router-ID\n" + V4NOTATION_STR) + DEFUN (ospf6_log_adjacency_changes, ospf6_log_adjacency_changes_cmd, "log-adjacency-changes", @@ -974,7 +1013,7 @@ static int config_write_ospf6(struct vty *vty) sizeof(router_id)); vty_out(vty, "router ospf6\n"); if (ospf6->router_id_static != 0) - vty_out(vty, " router-id %s\n", router_id); + vty_out(vty, " ospf6 router-id %s\n", router_id); /* log-adjacency-changes flag print. */ if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { @@ -1032,6 +1071,9 @@ void ospf6_top_init(void) install_default(OSPF6_NODE); install_element(OSPF6_NODE, &ospf6_router_id_cmd); + install_element(OSPF6_NODE, &no_ospf6_router_id_cmd); + install_element(OSPF6_NODE, &ospf6_router_id_hdn_cmd); + install_element(OSPF6_NODE, &no_ospf6_router_id_hdn_cmd); install_element(OSPF6_NODE, &ospf6_log_adjacency_changes_cmd); install_element(OSPF6_NODE, &ospf6_log_adjacency_changes_detail_cmd); install_element(OSPF6_NODE, &no_ospf6_log_adjacency_changes_cmd); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 2d04790d23..2372bc54b7 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -388,9 +388,9 @@ void ospf6_zebra_add_discard(struct ospf6_route *request) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF6; - api.flags = ZEBRA_FLAG_BLACKHOLE; api.safi = SAFI_UNICAST; api.prefix = *dest; + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); @@ -421,9 +421,9 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF6; - api.flags = ZEBRA_FLAG_BLACKHOLE; api.safi = SAFI_UNICAST; api.prefix = *dest; + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 96b6fe8bf9..c2a55d9b37 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -2268,7 +2268,9 @@ DEFUN (no_ospf_timers_lsa_min_arrival, return CMD_SUCCESS; } -/* Deprecated: 08/07/2017 */ +#if CONFDATE > 20180708 +CPP_NOTICE("ospf: `timers lsa arrival (0-1000)` deprecated 2017/07/08") +#endif ALIAS_HIDDEN (ospf_timers_lsa_min_arrival, ospf_timers_lsa_arrival_cmd, "timers lsa arrival (0-1000)", @@ -2277,7 +2279,9 @@ ALIAS_HIDDEN (ospf_timers_lsa_min_arrival, "ospf minimum arrival interval delay\n" "delay (msec) between accepted lsas\n"); -/* Deprecated: 08/07/2017 */ +#if CONFDATE > 20180708 +CPP_NOTICE("ospf: `no timers lsa arrival (0-1000)` deprecated 2017/07/08") +#endif ALIAS_HIDDEN (no_ospf_timers_lsa_min_arrival, no_ospf_timers_lsa_arrival_cmd, "no timers lsa arrival (0-1000)", @@ -8170,8 +8174,8 @@ static int config_write_interface(struct vty *vty) if (ifp->ifindex == IFINDEX_DELETED) continue; - vty_out(vty, "!\n"); - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "!\n"); + vty_frame(vty, "interface %s\n", ifp->name); if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); @@ -8385,6 +8389,8 @@ static int config_write_interface(struct vty *vty) } while (rn); ospf_opaque_config_write_if(vty, ifp); + + vty_endframe(vty, NULL); } return write; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index dcb392f1ad..e26a33c35f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -442,9 +442,9 @@ void ospf_zebra_add_discard(struct prefix_ipv4 *p) api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF; api.instance = ospf->instance; - api.flags = ZEBRA_FLAG_BLACKHOLE; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); @@ -462,9 +462,9 @@ void ospf_zebra_delete_discard(struct prefix_ipv4 *p) api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF; api.instance = ospf->instance; - api.flags = ZEBRA_FLAG_BLACKHOLE; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); @@ -900,17 +900,10 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: - * Maybe we should ignore reject/blackhole routes? Testing shows - * that - * there is no problems though and this is only way to - * "summarize" - * routes in ASBR at the moment. Maybe we need just a better - * generalised - * solution for these types? - * - * if ( CHECK_FLAG (api.flags, ZEBRA_FLAG_BLACKHOLE) - * || CHECK_FLAG (api.flags, ZEBRA_FLAG_REJECT)) - * return 0; + * Maybe we should ignore reject/blackhole routes? Testing + * shows that there is no problems though and this is only way + * to "summarize" routes in ASBR at the moment. Maybe we need + * just a better generalised solution for these types? */ /* Protocol tag overwrites all other tag value sent by zebra */ diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 9d65330e5f..4f5e534010 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -414,7 +414,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) struct pim_jp_agg_group *group; struct pim_interface *pim_ifp = NULL; struct pim_jp_groups *grp = NULL; - struct pim_jp *msg; + struct pim_jp *msg = NULL; struct listnode *node, *nnode; uint8_t pim_msg[10000]; uint8_t *curr_ptr = pim_msg; diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 96b9568c61..0bf2ce5d56 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -1364,7 +1364,7 @@ static int pim_upstream_register_stop_timer(struct thread *t) pim_upstream_start_register_stop_timer(up, 1); if (((up->channel_oil->cc.lastused / 100) - > PIM_KEEPALIVE_PERIOD) + > pim->keep_alive_time) && (I_am_RP(pim_ifp->pim, up->sg.grp))) { if (PIM_DEBUG_TRACE) zlog_debug( diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 8914f6eb00..7607bdf13b 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -255,10 +255,10 @@ int pim_interface_config_write(struct vty *vty) /* IF name */ if (vrf->vrf_id == VRF_DEFAULT) - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); else - vty_out(vty, "interface %s vrf %s\n", ifp->name, - vrf->name); + vty_frame(vty, "interface %s vrf %s\n", + ifp->name, vrf->name); ++writes; if (ifp->info) { @@ -363,7 +363,7 @@ int pim_interface_config_write(struct vty *vty) pim_static_write_mroute(pim, vty, ifp); pim_bfd_write_config(vty, ifp); } - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); ++writes; } } diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c index 3d104e7c7c..bf44f3c94a 100644 --- a/pimd/test_igmpv3_join.c +++ b/pimd/test_igmpv3_join.c @@ -139,7 +139,7 @@ int main(int argc, const char *argv[]) printf("%s: waiting...\n", prog_name); if (getchar() == EOF) - fprintf(stderr, "getchar failure\n"); + fprintf(stderr, "getchar failure\n"); close(fd); diff --git a/python/clidef.py b/python/clidef.py index aa6cd18b8b..6a69986323 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -36,12 +36,14 @@ class RenderHandler(object): deref = '' drop_str = False + canfail = True class StringHandler(RenderHandler): argtype = 'const char *' decl = Template('const char *$varname = NULL;') code = Template('$varname = argv[_i]->arg;') drop_str = True + canfail = False class LongHandler(RenderHandler): argtype = 'long' @@ -130,6 +132,10 @@ handlers = { } # core template invoked for each occurence of DEFPY. +# +# the "#if $..." bits are there to keep this template unified into one +# common form, without requiring a more advanced template engine (e.g. +# jinja2) templ = Template('''/* $fnname => "$cmddef" */ DEFUN_CMD_FUNC_DECL($fnname) #define funcdecl_$fnname static int ${fnname}_magic(\\ @@ -140,20 +146,31 @@ DEFUN_CMD_FUNC_DECL($fnname) funcdecl_$fnname; DEFUN_CMD_FUNC_TEXT($fnname) { +#if $nonempty /* anything to parse? */ int _i; +#if $canfail /* anything that can fail? */ unsigned _fail = 0, _failcnt = 0; +#endif $argdecls for (_i = 0; _i < argc; _i++) { if (!argv[_i]->varname) continue; - _fail = 0;$argblocks +#if $canfail /* anything that can fail? */ + _fail = 0; +#endif +$argblocks +#if $canfail /* anything that can fail? */ if (_fail) vty_out (vty, "%% invalid input for %s: %s\\n", argv[_i]->varname, argv[_i]->arg); _failcnt += _fail; +#endif } +#if $canfail /* anything that can fail? */ if (_failcnt) return CMD_WARNING; +#endif +#endif return ${fnname}_magic(self, vty, argc, argv$arglist); } @@ -196,6 +213,7 @@ def process_file(fn, ofd, dumpfd, all_defun): arglist = [] argblocks = [] doc = [] + canfail = 0 def do_add(handler, varname, attr = ''): argdefs.append(',\\\n\t%s %s%s' % (handler.argtype, varname, attr)) @@ -213,6 +231,8 @@ def process_file(fn, ofd, dumpfd, all_defun): if handler is None: continue do_add(handler, varname) code = handler.code.substitute({'varname': varname}).replace('\n', '\n\t\t\t') + if handler.canfail: + canfail = 1 strblock = '' if not handler.drop_str: do_add(StringHandler(None), '%s_str' % (varname), ' __attribute__ ((unused))') @@ -229,6 +249,8 @@ def process_file(fn, ofd, dumpfd, all_defun): params['argdecls'] = ''.join(argdecls) params['arglist'] = ''.join(arglist) params['argblocks'] = ''.join(argblocks) + params['canfail'] = canfail + params['nonempty'] = len(argblocks) ofd.write(templ.substitute(params)) if __name__ == '__main__': diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index cc0632b562..ea8a312364 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -326,7 +326,7 @@ rm -f %{buildroot}%{_libdir}/frr/modules/*.la # install /etc sources %if "%{initsystem}" == "systemd" mkdir -p %{buildroot}%{_unitdir} -install %{zeb_rh_src}/frr.service \ +install -m644 %{zeb_rh_src}/frr.service \ %{buildroot}%{_unitdir}/frr.service install %{zeb_rh_src}/frr.init \ %{buildroot}%{_sbindir}/frr diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 00b6d1cadd..1b2cbb61c3 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -1751,7 +1751,7 @@ static int rip_interface_config_write(struct vty *vty) && (!ri->auth_str) && (!ri->key_chain)) continue; - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); @@ -1807,7 +1807,7 @@ static int rip_interface_config_write(struct vty *vty) vty_out(vty, " ip rip authentication key-chain %s\n", ri->key_chain); - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); } return 0; } diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 02fab68254..5c65f522ef 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -1084,7 +1084,7 @@ static int interface_config_write(struct vty *vty) && (ri->split_horizon == ri->split_horizon_default)) continue; - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); if (ifp->desc) vty_out(vty, " description %s\n", ifp->desc); @@ -1105,7 +1105,7 @@ static int interface_config_write(struct vty *vty) } } - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); write++; } diff --git a/tests/.gitignore b/tests/.gitignore index 604ffaa8b6..41349cce24 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -45,4 +45,5 @@ __pycache__ /lib/test_timer_correctness /lib/test_timer_performance /lib/test_ttable +/lib/test_zmq /ospf6d/test_lsdb diff --git a/tests/Makefile.am b/tests/Makefile.am index 59ea3c4c69..8a8918dcb4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -72,6 +72,12 @@ check_PROGRAMS = \ $(TESTS_OSPF6D) \ # end +if ZEROMQ +check_PROGRAMS += \ + lib/test_zmq \ + # end +endif + ../vtysh/vtysh_cmd.c: $(MAKE) -C ../vtysh vtysh_cmd.c @@ -112,6 +118,8 @@ lib_test_timer_correctness_SOURCES = lib/test_timer_correctness.c \ lib_test_timer_performance_SOURCES = lib/test_timer_performance.c \ helpers/c/prng.c lib_test_ttable_SOURCES = lib/test_ttable.c +lib_test_zmq_SOURCES = lib/test_zmq.c +lib_test_zmq_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS) lib_cli_test_cli_SOURCES = lib/cli/test_cli.c lib/cli/common_cli.c lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \ lib/cli/test_commands.c \ @@ -147,6 +155,7 @@ lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD) lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD) lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) +lib_test_zmq_LDADD = ../lib/libfrrzmq.la $(ALL_TESTS_LDADD) $(ZEROMQ_LIBS) lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD) lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD) diff --git a/tests/lib/cli/test_commands.py b/tests/lib/cli/test_commands.py index bda0bbac44..d55345186a 100644 --- a/tests/lib/cli/test_commands.py +++ b/tests/lib/cli/test_commands.py @@ -8,4 +8,4 @@ class TestCommands(frrtest.TestRefOut): @pytest.mark.skipif('QUAGGA_TEST_COMMANDS' not in os.environ, reason='QUAGGA_TEST_COMMANDS not set') def test_refout(self): - return super(TestCommands, self).test_refout(self) + return super(TestCommands, self).test_refout() diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c new file mode 100644 index 0000000000..c270ec3d18 --- /dev/null +++ b/tests/lib/test_zmq.c @@ -0,0 +1,212 @@ +/* + * ZeroMQ event test + * Copyright (C) 2017 David Lamparter, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "memory.h" +#include "sigevent.h" +#include "frr_zmq.h" + +DEFINE_MTYPE_STATIC(LIB, TESTBUF, "zmq test buffer") + +static struct thread_master *master; + +static void msg_buf_free(void *data, void *hint) +{ + XFREE(MTYPE_TESTBUF, data); +} + +static void run_client(int syncfd) +{ + int i, j; + char buf[32]; + char dummy; + void *zmqctx = NULL; + void *zmqsock; + + read(syncfd, &dummy, 1); + + zmqctx = zmq_ctx_new(); + zmq_ctx_set(zmqctx, ZMQ_IPV6, 1); + + zmqsock = zmq_socket(zmqctx, ZMQ_REQ); + if (zmq_connect(zmqsock, "tcp://127.0.0.1:17171")) { + perror("zmq_connect"); + exit(1); + } + + /* single-part */ + for (i = 0; i < 8; i++) { + snprintf(buf, sizeof(buf), "msg #%d %c%c%c", + i, 'a' + i, 'b' + i, 'c' + i); + printf("client send: %s\n", buf); + fflush(stdout); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("client recv: %s\n", buf); + } + + /* multipart */ + for (i = 2; i < 5; i++) { + int more; + + printf("---\n"); + for (j = 1; j <= i; j++) { + zmq_msg_t part; + char *dyn = XMALLOC(MTYPE_TESTBUF, 32); + + snprintf(dyn, 32, "part %d/%d", j, i); + printf("client send: %s\n", dyn); + fflush(stdout); + + zmq_msg_init_data(&part, dyn, strlen(dyn) + 1, + msg_buf_free, NULL); + zmq_msg_send(&part, zmqsock, j < i ? ZMQ_SNDMORE : 0); + } + + zmq_msg_t part; + do { + char *data; + + zmq_msg_recv(&part, zmqsock, 0); + data = zmq_msg_data(&part); + more = zmq_msg_more(&part); + printf("client recv (more: %d): %s\n", more, data); + } while (more); + zmq_msg_close(&part); + } + zmq_close(zmqsock); + zmq_ctx_term(zmqctx); +} + +static struct frrzmq_cb *cb; + +static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg, + unsigned partnum) +{ + int more = zmq_msg_more(msg); + char *in = zmq_msg_data(msg); + size_t i; + zmq_msg_t reply; + char *out; + + printf("server recv part %u (more: %d): %s\n", partnum, more, in); + fflush(stdout); + /* REQ-REP doesn't allow sending a reply here */ + if (more) + return; + + out = XMALLOC(MTYPE_TESTBUF, strlen(in) + 1); + for (i = 0; i < strlen(in); i++) + out[i] = toupper(in[i]); + out[i] = '\0'; + zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL); + zmq_msg_send(&reply, zmqsock, ZMQ_SNDMORE); + + out = XMALLOC(MTYPE_TESTBUF, 32); + snprintf(out, 32, "msg# was %u", partnum); + zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL); + zmq_msg_send(&reply, zmqsock, 0); +} + +static void serverfn(void *arg, void *zmqsock) +{ + static int num = 0; + + char buf[32]; + size_t i; + zmq_recv(zmqsock, buf, sizeof(buf), 0); + + printf("server recv: %s\n", buf); + fflush(stdout); + for (i = 0; i < strlen(buf); i++) + buf[i] = toupper(buf[i]); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + + if (++num < 4) + return; + + /* change to multipart callback */ + frrzmq_thread_cancel(cb); + + cb = frrzmq_thread_add_read_part(master, serverpartfn, NULL, zmqsock); +} + +static void sigchld(void) +{ + printf("child exited.\n"); + frrzmq_thread_cancel(cb); +} + +static struct quagga_signal_t sigs[] = { + { + .signal = SIGCHLD, + .handler = sigchld, + }, +}; + +static void run_server(int syncfd) +{ + void *zmqsock; + char dummy = 0; + struct thread t; + + master = thread_master_create(NULL); + signal_init(master, array_size(sigs), sigs); + frrzmq_init(); + + zmqsock = zmq_socket(frrzmq_context, ZMQ_REP); + if (zmq_bind(zmqsock, "tcp://*:17171")) { + perror("zmq_bind"); + exit(1); + } + + cb = frrzmq_thread_add_read_msg(master, serverfn, NULL, zmqsock); + + write(syncfd, &dummy, sizeof(dummy)); + while (thread_fetch(master, &t)) + thread_call(&t); + + zmq_close(zmqsock); + frrzmq_finish(); + thread_master_free(master); + log_memstats_stderr("test"); +} + +int main(void) +{ + int syncpipe[2]; + pid_t child; + + if (pipe(syncpipe)) { + perror("pipe"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + exit(1); + } else if (child == 0) { + run_client(syncpipe[0]); + exit(0); + } + + run_server(syncpipe[1]); + exit(0); +} diff --git a/tests/lib/test_zmq.py b/tests/lib/test_zmq.py new file mode 100644 index 0000000000..1f8ee54169 --- /dev/null +++ b/tests/lib/test_zmq.py @@ -0,0 +1,11 @@ +import frrtest +import pytest +import os + +class TestZMQ(frrtest.TestRefOut): + program = './test_zmq' + + @pytest.mark.skipif('S["ZEROMQ_TRUE"]=""\n' not in open('../config.status').readlines(), + reason='ZEROMQ not enabled') + def test_refout(self): + return super(TestZMQ, self).test_refout() diff --git a/tests/lib/test_zmq.refout b/tests/lib/test_zmq.refout new file mode 100644 index 0000000000..61f45f02b1 --- /dev/null +++ b/tests/lib/test_zmq.refout @@ -0,0 +1,50 @@ +client send: msg #0 abc +server recv: msg #0 abc +client recv: MSG #0 ABC +client send: msg #1 bcd +server recv: msg #1 bcd +client recv: MSG #1 BCD +client send: msg #2 cde +server recv: msg #2 cde +client recv: MSG #2 CDE +client send: msg #3 def +server recv: msg #3 def +client recv: MSG #3 DEF +client send: msg #4 efg +server recv part 0 (more: 0): msg #4 efg +client recv: MSG #4 EFG +client send: msg #5 fgh +client recv: msg# was 0 +client send: msg #6 ghi +server recv part 0 (more: 0): msg #6 ghi +client recv: MSG #6 GHI +client send: msg #7 hij +client recv: msg# was 0 +--- +client send: part 1/2 +client send: part 2/2 +server recv part 0 (more: 1): part 1/2 +server recv part 1 (more: 0): part 2/2 +client recv (more: 1): PART 2/2 +client recv (more: 0): msg# was 1 +--- +client send: part 1/3 +client send: part 2/3 +client send: part 3/3 +server recv part 0 (more: 1): part 1/3 +server recv part 1 (more: 1): part 2/3 +server recv part 2 (more: 0): part 3/3 +client recv (more: 1): PART 3/3 +client recv (more: 0): msg# was 2 +--- +client send: part 1/4 +client send: part 2/4 +client send: part 3/4 +client send: part 4/4 +server recv part 0 (more: 1): part 1/4 +server recv part 1 (more: 1): part 2/4 +server recv part 2 (more: 1): part 3/4 +server recv part 3 (more: 0): part 4/4 +client recv (more: 1): PART 4/4 +client recv (more: 0): msg# was 3 +child exited. diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 85cbcae4df..cbfdf41127 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2295,12 +2295,12 @@ int vtysh_write_config_integrated(void) fprintf(stdout, "Building Configuration...\n"); - backup_config_file(quagga_config); - fp = fopen(quagga_config, "w"); + backup_config_file(frr_config); + fp = fopen(frr_config, "w"); if (fp == NULL) { fprintf(stdout, "%% Error: failed to open configuration file %s: %s\n", - quagga_config, safe_strerror(errno)); + frr_config, safe_strerror(errno)); return CMD_WARNING_CONFIG_FAILED; } fd = fileno(fp); @@ -2313,7 +2313,7 @@ int vtysh_write_config_integrated(void) if (fchmod(fd, CONFIGFILE_MASK) != 0) { printf("%% Warning: can't chmod configuration file %s: %s\n", - quagga_config, safe_strerror(errno)); + frr_config, safe_strerror(errno)); err++; } @@ -2345,18 +2345,18 @@ int vtysh_write_config_integrated(void) if ((uid != (uid_t)-1 || gid != (gid_t)-1) && fchown(fd, uid, gid)) { printf("%% Warning: can't chown configuration file %s: %s\n", - quagga_config, safe_strerror(errno)); + frr_config, safe_strerror(errno)); err++; } } else { - printf("%% Warning: stat() failed on %s: %s\n", quagga_config, + printf("%% Warning: stat() failed on %s: %s\n", frr_config, safe_strerror(errno)); err++; } fclose(fp); - printf("Integrated configuration saved to %s\n", quagga_config); + printf("Integrated configuration saved to %s\n", frr_config); if (err) return CMD_WARNING; @@ -2370,7 +2370,7 @@ static bool want_config_integrated(void) switch (vtysh_write_integrated) { case WRITE_INTEGRATED_UNSPECIFIED: - if (stat(quagga_config, &s) && errno == ENOENT) + if (stat(frr_config, &s) && errno == ENOENT) return false; return true; case WRITE_INTEGRATED_NO: @@ -2712,7 +2712,7 @@ static int vtysh_connect(struct vtysh_client *vclient) if (!vclient->path[0]) snprintf(vclient->path, sizeof(vclient->path), "%s/%s.vty", - vty_sock_path, vclient->name); + vtydir, vclient->name); path = vclient->path; /* Stat socket to see if we have permission to access it. */ @@ -2806,7 +2806,7 @@ static void vtysh_update_all_insances(struct vtysh_client *head_client) return; /* ls vty_sock_dir and look for all files ending in .vty */ - dir = opendir(vty_sock_path); + dir = opendir(vtydir); if (dir) { while ((file = readdir(dir)) != NULL) { if (begins_with(file->d_name, "ospfd-") @@ -2814,7 +2814,7 @@ static void vtysh_update_all_insances(struct vtysh_client *head_client) if (n == MAXIMUM_INSTANCES) { fprintf(stderr, "Parsing %s, client limit(%d) reached!\n", - vty_sock_path, n); + vtydir, n); break; } client = (struct vtysh_client *)malloc( @@ -2823,7 +2823,7 @@ static void vtysh_update_all_insances(struct vtysh_client *head_client) client->name = "ospfd"; client->flag = VTYSH_OSPFD; snprintf(client->path, sizeof(client->path), - "%s/%s", vty_sock_path, file->d_name); + "%s/%s", vtydir, file->d_name); client->next = NULL; vtysh_client_sorted_insert(head_client, client); n++; @@ -2891,21 +2891,9 @@ void vtysh_readline_init(void) char *vtysh_prompt(void) { - static struct utsname names; static char buf[100]; - const char *hostname; - extern struct host host; - - hostname = host.name; - - if (!hostname) { - if (!names.nodename[0]) - uname(&names); - hostname = names.nodename; - } - - snprintf(buf, sizeof buf, cmd_prompt(vty->node), hostname); + snprintf(buf, sizeof buf, cmd_prompt(vty->node), cmd_hostname_get()); return buf; } diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index b0866ec7f3..9d6ea4bda4 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -49,10 +49,6 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD -/* vtysh local configuration file. */ -#define VTYSH_DEFAULT_CONFIG "vtysh.conf" -#define FRR_DEFAULT_CONFIG "frr.conf" - enum vtysh_write_integrated { WRITE_INTEGRATED_UNSPECIFIED, WRITE_INTEGRATED_NO, @@ -61,7 +57,8 @@ enum vtysh_write_integrated { extern enum vtysh_write_integrated vtysh_write_integrated; -extern char *quagga_config; +extern char frr_config[]; +extern char vtydir[]; void vtysh_init_vty(void); void vtysh_init_cmd(void); @@ -93,11 +90,12 @@ void vtysh_config_init(void); void vtysh_pager_init(void); +void suid_on(void); +void suid_off(void); + /* Child process execution flag. */ extern int execute_flag; extern struct vty *vty; -extern const char *vty_sock_path; - #endif /* VTYSH_H */ diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index c561b5222f..d7e79d6b2c 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -245,7 +245,10 @@ void vtysh_config_parse_line(void *arg, const char *line) == 0 || strncmp(line, "ip extcommunity-list", strlen("ip extcommunity-list")) - == 0) + == 0 + || strncmp(line, "ip large-community-list", + strlen("ip large-community-list")) + == 0) config = config_get(COMMUNITY_LIST_NODE, line); else if (strncmp(line, "ip route", strlen("ip route")) == 0) config = config_get(IP_NODE, line); @@ -420,10 +423,9 @@ int vtysh_read_config(const char *config_default_dir) void vtysh_config_write() { char line[81]; - extern struct host host; - if (host.name) { - sprintf(line, "hostname %s", host.name); + if (cmd_hostname_get()) { + sprintf(line, "hostname %s", cmd_hostname_get()); vtysh_config_parse_line(NULL, line); } if (vtysh_write_integrated == WRITE_INTEGRATED_NO) diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 8145bf3641..961201ea90 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -44,18 +44,22 @@ /* VTY shell program name. */ char *progname; +/* SUID mode */ +static uid_t elevuid, realuid; +static gid_t elevgid, realgid; + +#define VTYSH_CONFIG_NAME "vtysh.conf" +#define FRR_CONFIG_NAME "frr.conf" + /* Configuration file name and directory. */ -static char vtysh_config_always[MAXPATHLEN] = SYSCONFDIR VTYSH_DEFAULT_CONFIG; -static char quagga_config_default[MAXPATHLEN] = SYSCONFDIR FRR_DEFAULT_CONFIG; -char *quagga_config = quagga_config_default; -char history_file[MAXPATHLEN]; +static char vtysh_config[MAXPATHLEN]; +char frr_config[MAXPATHLEN]; +char vtydir[MAXPATHLEN]; +static char history_file[MAXPATHLEN]; /* Flag for indicate executing child command. */ int execute_flag = 0; -/* VTY Socket prefix */ -const char *vty_sock_path = NULL; - /* For sigsetjmp() & siglongjmp(). */ static sigjmp_buf jmpbuf; @@ -145,6 +149,7 @@ static void usage(int status) "-m, --markfile Mark input file with context end\n" " --vty_socket Override vty socket path\n" " --config_dir Override config directory path\n" + "-N --pathspace Insert prefix into config & socket paths\n" "-w, --writeconfig Write integrated config (frr.conf) and exit\n" "-h, --help Display this help and exit\n\n" "Note that multiple commands may be executed from the command\n" @@ -174,6 +179,7 @@ struct option longopts[] = { {"noerror", no_argument, NULL, 'n'}, {"mark", no_argument, NULL, 'm'}, {"writeconfig", no_argument, NULL, 'w'}, + {"pathspace", no_argument, NULL, 'N'}, {0}}; /* Read a string, and return a pointer to it. Returns NULL on EOF. */ @@ -249,6 +255,30 @@ static void vtysh_unflock_config(void) close(flock_fd); } +void suid_on(void) +{ + if (elevuid != realuid && seteuid(elevuid)) { + perror("seteuid(on)"); + exit(1); + } + if (elevgid != realgid && setegid(elevgid)) { + perror("setegid(on)"); + exit(1); + } +} + +void suid_off(void) +{ + if (elevuid != realuid && seteuid(realuid)) { + perror("seteuid(off)"); + exit(1); + } + if (elevgid != realgid && setegid(realgid)) { + perror("setegid(off)"); + exit(1); + } +} + /* VTY shell main routine. */ int main(int argc, char **argv, char **env) { @@ -258,7 +288,6 @@ int main(int argc, char **argv, char **env) int boot_flag = 0; const char *daemon_name = NULL; const char *inputfile = NULL; - const char *vtysh_configfile_name; struct cmd_rec { char *line; struct cmd_rec *next; @@ -270,16 +299,22 @@ int main(int argc, char **argv, char **env) int writeconfig = 0; int ret = 0; char *homedir = NULL; + int ditch_suid = 0; + char sysconfdir[MAXPATHLEN]; + char pathspace[MAXPATHLEN] = ""; - /* check for restricted functionality if vtysh is run setuid */ - int restricted = (getuid() != geteuid()) || (getgid() != getegid()); + /* SUID: drop down to calling user & go back up when needed */ + elevuid = geteuid(); + elevgid = getegid(); + realuid = getuid(); + realgid = getgid(); + suid_off(); /* Preserve name of myself. */ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); - /* if logging open now */ - if ((p = getenv("VTYSH_LOG")) != NULL) - logfile = fopen(p, "a"); + strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir)); + strlcpy(vtydir, frr_vtydir, sizeof(vtydir)); /* Option handling. */ while (1) { @@ -307,63 +342,20 @@ int main(int argc, char **argv, char **env) tail = cr; } break; case OPTION_VTYSOCK: - vty_sock_path = optarg; + ditch_suid = 1; /* option disables SUID */ + strlcpy(vtydir, optarg, sizeof(vtydir)); break; case OPTION_CONFDIR: - /* - * Skip option for Config Directory if setuid - */ - if (restricted) { + ditch_suid = 1; /* option disables SUID */ + strlcpy(sysconfdir, optarg, sizeof(sysconfdir)); + break; + case 'N': + if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, - "Overriding of Config Directory blocked for vtysh with setuid"); - return 1; + "slashes or dots are not permitted in the --pathspace option.\n"); + exit(1); } - /* - * Overwrite location for vtysh.conf - */ - vtysh_configfile_name = - strrchr(VTYSH_DEFAULT_CONFIG, '/'); - if (vtysh_configfile_name) - /* skip '/' */ - vtysh_configfile_name++; - else - /* - * VTYSH_DEFAULT_CONFIG configured with relative - * path - * during config? Should really never happen for - * sensible config - */ - vtysh_configfile_name = - (char *)VTYSH_DEFAULT_CONFIG; - strlcpy(vtysh_config_always, optarg, - sizeof(vtysh_config_always)); - strlcat(vtysh_config_always, "/", - sizeof(vtysh_config_always)); - strlcat(vtysh_config_always, vtysh_configfile_name, - sizeof(vtysh_config_always)); - /* - * Overwrite location for frr.conf - */ - vtysh_configfile_name = - strrchr(FRR_DEFAULT_CONFIG, '/'); - if (vtysh_configfile_name) - /* skip '/' */ - vtysh_configfile_name++; - else - /* - * FRR_DEFAULT_CONFIG configured with relative - * path - * during config? Should really never happen for - * sensible config - */ - vtysh_configfile_name = - (char *)FRR_DEFAULT_CONFIG; - strlcpy(quagga_config_default, optarg, - sizeof(vtysh_config_always)); - strlcat(quagga_config_default, "/", - sizeof(vtysh_config_always)); - strlcat(quagga_config_default, vtysh_configfile_name, - sizeof(quagga_config_default)); + snprintf(pathspace, sizeof(pathspace), "/%s", optarg); break; case 'd': daemon_name = optarg; @@ -395,8 +387,10 @@ int main(int argc, char **argv, char **env) } } - if (!vty_sock_path) - vty_sock_path = frr_vtydir; + if (ditch_suid) { + elevuid = realuid; + elevgid = realgid; + } if (markfile + writeconfig + dryrun + boot_flag > 1) { fprintf(stderr, @@ -410,6 +404,12 @@ int main(int argc, char **argv, char **env) "NOT SUPPORTED since its\nresults are inconsistent!\n"); } + snprintf(vtysh_config, sizeof(vtysh_config), "%s%s/%s", + sysconfdir, pathspace, VTYSH_CONFIG_NAME); + snprintf(frr_config, sizeof(frr_config), "%s%s/%s", + sysconfdir, pathspace, FRR_CONFIG_NAME); + strlcat(vtydir, pathspace, sizeof(vtydir)); + /* Initialize user input buffer. */ line_read = NULL; setlinebuf(stdout); @@ -425,8 +425,11 @@ int main(int argc, char **argv, char **env) vty_init_vtysh(); - /* Read vtysh configuration file before connecting to daemons. */ - vtysh_read_config(vtysh_config_always); + /* Read vtysh configuration file before connecting to daemons. + * (file may not be readable to calling user in SUID mode) */ + suid_on(); + vtysh_read_config(vtysh_config); + suid_off(); if (markfile) { if (!inputfile) { @@ -442,7 +445,7 @@ int main(int argc, char **argv, char **env) if (inputfile) { ret = vtysh_read_config(inputfile); } else { - ret = vtysh_read_config(quagga_config_default); + ret = vtysh_read_config(frr_config); } exit(ret); @@ -486,6 +489,9 @@ int main(int argc, char **argv, char **env) } } + /* SUID: go back up elevated privs */ + suid_on(); + /* Make sure we pass authentication before proceeding. */ vtysh_auth(); @@ -498,6 +504,9 @@ int main(int argc, char **argv, char **env) exit(1); } + /* SUID: back down, don't need privs further on */ + suid_off(); + if (writeconfig) { vtysh_execute("enable"); return vtysh_write_config_integrated(); @@ -531,6 +540,17 @@ int main(int argc, char **argv, char **env) } } + if (getenv("VTYSH_LOG")) { + const char *logpath = getenv("VTYSH_LOG"); + + logfile = fopen(logpath, "a"); + if (!logfile) { + fprintf(stderr, "Failed to open logfile (%s): %s\n", + logpath, strerror(errno)); + exit(1); + } + } + /* If eval mode. */ if (cmd) { /* Enter into enable node. */ @@ -592,13 +612,13 @@ int main(int argc, char **argv, char **env) /* Boot startup configuration file. */ if (boot_flag) { - vtysh_flock_config(quagga_config); - int ret = vtysh_read_config(quagga_config); + vtysh_flock_config(frr_config); + int ret = vtysh_read_config(frr_config); vtysh_unflock_config(); if (ret) { fprintf(stderr, "Configuration file[%s] processing failure: %d\n", - quagga_config, ret); + frr_config, ret); if (no_error) exit(0); else diff --git a/zebra/connected.c b/zebra/connected.c index 701314f246..c97962647c 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -100,8 +100,10 @@ static void connected_announce(struct interface *ifp, struct connected *ifc) } /* If same interface address is already exist... */ -struct connected *connected_check(struct interface *ifp, struct prefix *p) +struct connected *connected_check(struct interface *ifp, + union prefixconstptr pu) { + const struct prefix *p = pu.p; struct connected *ifc; struct listnode *node; @@ -112,6 +114,33 @@ struct connected *connected_check(struct interface *ifp, struct prefix *p) return NULL; } +/* same, but with peer address */ +struct connected *connected_check_ptp(struct interface *ifp, + union prefixconstptr pu, + union prefixconstptr du) +{ + const struct prefix *p = pu.p; + const struct prefix *d = du.p; + struct connected *ifc; + struct listnode *node; + + /* ignore broadcast addresses */ + if (p->prefixlen != IPV4_MAX_PREFIXLEN) + d = NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + if (!prefix_same(ifc->address, p)) + continue; + if (!CONNECTED_PEER(ifc) && !d) + return ifc; + if (CONNECTED_PEER(ifc) && d + && prefix_same(ifc->destination, d)) + return ifc; + } + + return NULL; +} + /* Check if two ifc's describe the same address in the same state */ static int connected_same(struct connected *ifc1, struct connected *ifc2) { @@ -145,7 +174,8 @@ static void connected_update(struct interface *ifp, struct connected *ifc) struct connected *current; /* Check same connected route. */ - if ((current = connected_check(ifp, (struct prefix *)ifc->address))) { + current = connected_check_ptp(ifp, ifc->address, ifc->destination); + if (current) { if (CHECK_FLAG(current->conf, ZEBRA_IFC_CONFIGURED)) SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); @@ -177,6 +207,10 @@ static void connected_update(struct interface *ifp, struct connected *ifc) void connected_up_ipv4(struct interface *ifp, struct connected *ifc) { struct prefix p; + struct nexthop nh = { + .type = NEXTHOP_TYPE_IFINDEX, + .ifindex = ifp->ifindex, + }; if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -192,12 +226,10 @@ void connected_up_ipv4(struct interface *ifp, struct connected *ifc) return; rib_add(AFI_IP, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, NULL, NULL, ifp->ifindex, RT_TABLE_MAIN, ifp->metric, - 0, 0); + &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0); rib_add(AFI_IP, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, NULL, NULL, ifp->ifindex, RT_TABLE_MAIN, ifp->metric, - 0, 0); + &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( @@ -238,7 +270,8 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, p = prefix_ipv4_new(); p->family = AF_INET; p->prefix = *addr; - p->prefixlen = prefixlen; + p->prefixlen = CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_PREFIXLEN + : prefixlen; ifc->address = (struct prefix *)p; /* If there is broadcast or peer address. */ @@ -306,6 +339,10 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, void connected_down_ipv4(struct interface *ifp, struct connected *ifc) { struct prefix p; + struct nexthop nh = { + .type = NEXTHOP_TYPE_IFINDEX, + .ifindex = ifp->ifindex, + }; if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -323,10 +360,10 @@ void connected_down_ipv4(struct interface *ifp, struct connected *ifc) /* Same logic as for connected_up_ipv4(): push the changes into the * head. */ rib_delete(AFI_IP, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, NULL, ifp->ifindex, 0, 0); + &p, NULL, &nh, 0, 0); rib_delete(AFI_IP, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, NULL, ifp->ifindex, 0, 0); + 0, &p, NULL, &nh, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( @@ -350,15 +387,25 @@ void connected_delete_ipv4(struct interface *ifp, int flags, struct in_addr *addr, u_char prefixlen, struct in_addr *broad) { - struct prefix_ipv4 p; + struct prefix_ipv4 p, d; struct connected *ifc; memset(&p, 0, sizeof(struct prefix_ipv4)); p.family = AF_INET; p.prefix = *addr; - p.prefixlen = prefixlen; + p.prefixlen = CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_PREFIXLEN + : prefixlen; + + if (broad) { + memset(&d, 0, sizeof(struct prefix_ipv4)); + d.family = AF_INET; + d.prefix = *broad; + d.prefixlen = prefixlen; + ifc = connected_check_ptp(ifp, (struct prefix *)&p, + (struct prefix *)&d); + } else + ifc = connected_check_ptp(ifp, (struct prefix *)&p, NULL); - ifc = connected_check(ifp, (struct prefix *)&p); if (!ifc) return; @@ -384,6 +431,10 @@ void connected_delete_ipv4(struct interface *ifp, int flags, void connected_up_ipv6(struct interface *ifp, struct connected *ifc) { struct prefix p; + struct nexthop nh = { + .type = NEXTHOP_TYPE_IFINDEX, + .ifindex = ifp->ifindex, + }; if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -400,8 +451,7 @@ void connected_up_ipv6(struct interface *ifp, struct connected *ifc) #endif rib_add(AFI_IP6, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, NULL, NULL, ifp->ifindex, RT_TABLE_MAIN, ifp->metric, - 0, 0); + &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( @@ -422,8 +472,7 @@ void connected_up_ipv6(struct interface *ifp, struct connected *ifc) /* Add connected IPv6 route to the interface. */ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, - u_char prefixlen, struct in6_addr *broad, - const char *label) + u_char prefixlen, const char *label) { struct prefix_ipv6 *p; struct connected *ifc; @@ -446,29 +495,6 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, p->prefixlen = prefixlen; ifc->address = (struct prefix *)p; - /* If there is broadcast or peer address. */ - if (broad) { - if (IN6_IS_ADDR_UNSPECIFIED(broad)) - zlog_warn( - "warning: %s called for interface %s with unspecified " - "destination address; ignoring!", - __func__, ifp->name); - else { - p = prefix_ipv6_new(); - p->family = AF_INET6; - IPV6_ADDR_COPY(&p->prefix, broad); - p->prefixlen = prefixlen; - ifc->destination = (struct prefix *)p; - } - } - if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER) && !ifc->destination) { - zlog_warn( - "warning: %s called for interface %s " - "with peer flag set, but no peer address supplied", - __func__, ifp->name); - UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); - } - /* Label of this address. */ if (label) ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); @@ -489,6 +515,10 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, void connected_down_ipv6(struct interface *ifp, struct connected *ifc) { struct prefix p; + struct nexthop nh = { + .type = NEXTHOP_TYPE_IFINDEX, + .ifindex = ifp->ifindex, + }; if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -501,7 +531,7 @@ void connected_down_ipv6(struct interface *ifp, struct connected *ifc) return; rib_delete(AFI_IP6, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, NULL, ifp->ifindex, 0, 0); + 0, &p, NULL, &nh, 0, 0); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( @@ -521,7 +551,7 @@ void connected_down_ipv6(struct interface *ifp, struct connected *ifc) } void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, - u_char prefixlen, struct in6_addr *broad) + u_char prefixlen) { struct prefix_ipv6 p; struct connected *ifc; diff --git a/zebra/connected.h b/zebra/connected.h index eaf79fe9aa..c6da36956d 100644 --- a/zebra/connected.h +++ b/zebra/connected.h @@ -23,7 +23,10 @@ #define _ZEBRA_CONNECTED_H extern struct connected *connected_check(struct interface *ifp, - struct prefix *p); + union prefixconstptr p); +extern struct connected *connected_check_ptp(struct interface *ifp, + union prefixconstptr p, + union prefixconstptr d); extern void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, u_char prefixlen, @@ -40,10 +43,9 @@ extern void connected_down_ipv4(struct interface *, struct connected *); extern void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *address, u_char prefixlen, - struct in6_addr *broad, const char *label); + const char *label); extern void connected_delete_ipv6(struct interface *ifp, - struct in6_addr *address, u_char prefixlen, - struct in6_addr *broad); + struct in6_addr *address, u_char prefixlen); extern void connected_up_ipv6(struct interface *, struct connected *); extern void connected_down_ipv6(struct interface *ifp, struct connected *); diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index a65fcb2b8c..6396911e1b 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -214,7 +214,7 @@ static int if_getaddrs(void) dest_pnt = NULL; - if (ifap->ifa_dstaddr + if (if_is_pointopoint(ifp) && ifap->ifa_dstaddr && !IPV4_ADDR_SAME(&addr->sin_addr, &((struct sockaddr_in *) ifap->ifa_dstaddr) @@ -239,35 +239,12 @@ static int if_getaddrs(void) if (ifap->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *addr; struct sockaddr_in6 *mask; - struct sockaddr_in6 *dest; - struct in6_addr *dest_pnt; int flags = 0; addr = (struct sockaddr_in6 *)ifap->ifa_addr; mask = (struct sockaddr_in6 *)ifap->ifa_netmask; prefixlen = ip6_masklen(mask->sin6_addr); - dest_pnt = NULL; - - if (ifap->ifa_dstaddr - && !IPV6_ADDR_SAME(&addr->sin6_addr, - &((struct sockaddr_in6 *) - ifap->ifa_dstaddr) - ->sin6_addr)) { - dest = (struct sockaddr_in6 *)ifap->ifa_dstaddr; - dest_pnt = &dest->sin6_addr; - flags = ZEBRA_IFA_PEER; - } else if (ifap->ifa_broadaddr - && !IPV6_ADDR_SAME( - &addr->sin6_addr, - &((struct sockaddr_in6 *) - ifap->ifa_broadaddr) - ->sin6_addr)) { - dest = (struct sockaddr_in6 *) - ifap->ifa_broadaddr; - dest_pnt = &dest->sin6_addr; - } - #if defined(KAME) if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { addr->sin6_scope_id = @@ -279,7 +256,7 @@ static int if_getaddrs(void) #endif connected_add_ipv6(ifp, flags, &addr->sin6_addr, - prefixlen, dest_pnt, NULL); + prefixlen, NULL); } } diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index 3d53194593..9ec575b5b0 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -317,8 +317,7 @@ static int if_get_addr(struct interface *ifp, struct sockaddr *addr, (struct in_addr *)dest_pnt, label); else if (af == AF_INET6) connected_add_ipv6(ifp, flags, &SIN6(addr)->sin6_addr, - prefixlen, (struct in6_addr *)dest_pnt, - label); + prefixlen, label); return 0; } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ff69ce47cf..d5b8860d38 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -830,18 +830,24 @@ static int netlink_address(int cmd, int family, struct interface *ifp, req.ifa.ifa_family = family; req.ifa.ifa_index = ifp->ifindex; - req.ifa.ifa_prefixlen = p->prefixlen; addattr_l(&req.n, sizeof req, IFA_LOCAL, &p->u.prefix, bytelen); - if (family == AF_INET && cmd == RTM_NEWADDR) { - if (!CONNECTED_PEER(ifc) && ifc->destination) { + if (family == AF_INET) { + if (CONNECTED_PEER(ifc)) { + p = ifc->destination; + addattr_l(&req.n, sizeof req, IFA_ADDRESS, + &p->u.prefix, bytelen); + } else if (cmd == RTM_NEWADDR && ifc->destination) { p = ifc->destination; addattr_l(&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix, bytelen); } } + /* p is now either ifc->address or ifc->destination */ + req.ifa.ifa_prefixlen = p->prefixlen; + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY); @@ -995,12 +1001,10 @@ int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h, & (IFA_F_DADFAILED | IFA_F_TENTATIVE))) connected_add_ipv6( ifp, flags, (struct in6_addr *)addr, - ifa->ifa_prefixlen, - (struct in6_addr *)broad, label); + ifa->ifa_prefixlen, label); } else connected_delete_ipv6(ifp, (struct in6_addr *)addr, - ifa->ifa_prefixlen, - (struct in6_addr *)broad); + ifa->ifa_prefixlen); } return 0; diff --git a/zebra/interface.c b/zebra/interface.c index 12312ff43a..1bafb4c59d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -242,7 +242,7 @@ int if_subnet_add(struct interface *ifp, struct connected *ifc) /* Get address derived subnet node and associated address list, while marking address secondary attribute appropriately. */ - cp = *ifc->address; + cp = *CONNECTED_PREFIX(ifc); apply_mask(&cp); rn = route_node_get(zebra_if->ipv4_subnets, &cp); @@ -267,12 +267,16 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc) struct route_node *rn; struct zebra_if *zebra_if; struct list *addr_list; + struct prefix cp; assert(ifp && ifp->info && ifc); zebra_if = ifp->info; + cp = *CONNECTED_PREFIX(ifc); + apply_mask(&cp); + /* Get address derived subnet node. */ - rn = route_node_lookup(zebra_if->ipv4_subnets, ifc->address); + rn = route_node_lookup(zebra_if->ipv4_subnets, &cp); if (!(rn && rn->info)) { zlog_warn( "Trying to remove an address from an unknown subnet." @@ -565,7 +569,7 @@ static void if_uninstall_connected(struct interface *ifp) static void if_delete_connected(struct interface *ifp) { struct connected *ifc; - struct prefix *p; + struct prefix cp; struct route_node *rn; struct zebra_if *zebra_if; @@ -578,11 +582,13 @@ static void if_delete_connected(struct interface *ifp) while ((node = (last ? last->next : listhead(ifp->connected)))) { ifc = listgetdata(node); - p = ifc->address; - if (p->family == AF_INET + cp = *CONNECTED_PREFIX(ifc); + apply_mask(&cp); + + if (cp.family == AF_INET && (rn = route_node_lookup(zebra_if->ipv4_subnets, - p))) { + &cp))) { struct listnode *anode; struct listnode *next; struct listnode *first; @@ -635,7 +641,7 @@ static void if_delete_connected(struct interface *ifp) list_delete(addr_list); rn->info = NULL; route_unlock_node(rn); - } else if (p->family == AF_INET6) { + } else if (cp.family == AF_INET6) { connected_down_ipv6(ifp, ifc); zebra_interface_address_delete_update(ifp, ifc); @@ -873,7 +879,8 @@ void if_up(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = zif->link; + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + zif->link_ifindex); if (link_if) zebra_vxlan_svi_up(ifp, link_if); } @@ -901,7 +908,8 @@ void if_down(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = zif->link; + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + zif->link_ifindex); if (link_if) zebra_vxlan_svi_down(ifp, link_if); } @@ -966,6 +974,8 @@ static void connected_dump_vty(struct vty *vty, struct connected *connected) vty_out(vty, (CONNECTED_PEER(connected) ? " peer " : " broadcast ")); prefix_vty_out(vty, connected->destination); + if (CONNECTED_PEER(connected)) + vty_out(vty, "/%d", connected->destination->prefixlen); } if (CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY)) @@ -2365,38 +2375,56 @@ static int ip_address_install(struct vty *vty, struct interface *ifp, const char *label) { struct zebra_if *if_data; - struct prefix_ipv4 cp; + struct prefix_ipv4 lp, pp; struct connected *ifc; struct prefix_ipv4 *p; int ret; if_data = ifp->info; - ret = str2prefix_ipv4(addr_str, &cp); + ret = str2prefix_ipv4(addr_str, &lp); if (ret <= 0) { vty_out(vty, "%% Malformed address \n"); return CMD_WARNING_CONFIG_FAILED; } - if (ipv4_martian(&cp.prefix)) { + if (ipv4_martian(&lp.prefix)) { vty_out(vty, "%% Invalid address\n"); return CMD_WARNING_CONFIG_FAILED; } - ifc = connected_check(ifp, (struct prefix *)&cp); + if (peer_str) { + if (lp.prefixlen != 32) { + vty_out(vty, + "%% Local prefix length for P-t-P address must be /32\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = str2prefix_ipv4(peer_str, &pp); + if (ret <= 0) { + vty_out(vty, "%% Malformed peer address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + ifc = connected_check_ptp(ifp, &lp, peer_str ? &pp : NULL); if (!ifc) { ifc = connected_new(); ifc->ifp = ifp; /* Address. */ p = prefix_ipv4_new(); - *p = cp; + *p = lp; ifc->address = (struct prefix *)p; - /* Broadcast. */ - if (p->prefixlen <= IPV4_MAX_PREFIXLEN - 2) { + if (peer_str) { + SET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + p = prefix_ipv4_new(); + *p = pp; + ifc->destination = (struct prefix *)p; + } else if (p->prefixlen <= IPV4_MAX_PREFIXLEN - 2) { p = prefix_ipv4_new(); - *p = cp; + *p = lp; p->prefix.s_addr = ipv4_broadcast_addr(p->prefix.s_addr, p->prefixlen); ifc->destination = (struct prefix *)p; @@ -2445,19 +2473,33 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label) { - struct prefix_ipv4 cp; + struct prefix_ipv4 lp, pp; struct connected *ifc; int ret; /* Convert to prefix structure. */ - ret = str2prefix_ipv4(addr_str, &cp); + ret = str2prefix_ipv4(addr_str, &lp); if (ret <= 0) { vty_out(vty, "%% Malformed address \n"); return CMD_WARNING_CONFIG_FAILED; } + if (peer_str) { + if (lp.prefixlen != 32) { + vty_out(vty, + "%% Local prefix length for P-t-P address must be /32\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = str2prefix_ipv4(peer_str, &pp); + if (ret <= 0) { + vty_out(vty, "%% Malformed peer address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + /* Check current interface address. */ - ifc = connected_check(ifp, (struct prefix *)&cp); + ifc = connected_check_ptp(ifp, &lp, peer_str ? &pp : NULL); if (!ifc) { vty_out(vty, "%% Can't find address\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2517,6 +2559,32 @@ DEFUN (no_ip_address, NULL, NULL); } +DEFUN (ip_address_peer, + ip_address_peer_cmd, + "ip address A.B.C.D peer A.B.C.D/M", + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "Local IP (e.g. 10.0.0.1) for P-t-P address\n" + "Specify P-t-P address\n" + "Peer IP address (e.g. 10.0.0.1/8)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + return ip_address_install(vty, ifp, argv[2]->arg, argv[4]->arg, NULL); +} + +DEFUN (no_ip_address_peer, + no_ip_address_peer_cmd, + "no ip address A.B.C.D peer A.B.C.D/M", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "Local IP (e.g. 10.0.0.1) for P-t-P address\n" + "Specify P-t-P address\n" + "Peer IP address (e.g. 10.0.0.1/8)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + return ip_address_uninstall(vty, ifp, argv[3]->arg, argv[5]->arg, NULL); +} #ifdef HAVE_NETLINK DEFUN (ip_address_label, @@ -2791,7 +2859,7 @@ static int if_config_write(struct vty *vty) vrf = vrf_lookup_by_id(ifp->vrf_id); if (ifp->vrf_id == VRF_DEFAULT) - vty_out(vty, "interface %s\n", ifp->name); + vty_frame(vty, "interface %s\n", ifp->name); else vty_out(vty, "interface %s vrf %s\n", ifp->name, vrf->name); @@ -2820,7 +2888,16 @@ static int if_config_write(struct vty *vty) p = ifc->address; vty_out(vty, " ip%s address %s", p->family == AF_INET ? "" : "v6", - prefix2str(p, buf, sizeof(buf))); + inet_ntop(p->family, &p->u.prefix, + buf, sizeof(buf))); + if (CONNECTED_PEER (ifc)) { + p = ifc->destination; + vty_out(vty, " peer %s", + inet_ntop(p->family, + &p->u.prefix, + buf, sizeof(buf))); + } + vty_out (vty, "/%d", p->prefixlen); if (ifc->label) vty_out(vty, " label %s", ifc->label); @@ -2842,7 +2919,7 @@ static int if_config_write(struct vty *vty) link_params_config_write(vty, ifp); - vty_out(vty, "!\n"); + vty_endframe(vty, "!\n"); } return 0; } @@ -2876,6 +2953,8 @@ void zebra_if_init(void) install_element(INTERFACE_NODE, &no_bandwidth_if_cmd); install_element(INTERFACE_NODE, &ip_address_cmd); install_element(INTERFACE_NODE, &no_ip_address_cmd); + install_element(INTERFACE_NODE, &ip_address_peer_cmd); + install_element(INTERFACE_NODE, &no_ip_address_peer_cmd); install_element(INTERFACE_NODE, &ipv6_address_cmd); install_element(INTERFACE_NODE, &no_ipv6_address_cmd); #ifdef HAVE_NETLINK diff --git a/zebra/interface.h b/zebra/interface.h index 7b56dcd4a4..6bc05e21c5 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -183,12 +183,12 @@ struct rtadvconf { /* Zebra interface type - ones of interest. */ typedef enum { - ZEBRA_IF_VXLAN, /* VxLAN interface */ - ZEBRA_IF_VRF, /* VRF device */ - ZEBRA_IF_BRIDGE, /* bridge device */ - ZEBRA_IF_VLAN, /* VLAN sub-interface */ - ZEBRA_IF_MACVLAN, /* MAC VLAN interface*/ - ZEBRA_IF_OTHER, /* Anything else */ + ZEBRA_IF_OTHER = 0, /* Anything else */ + ZEBRA_IF_VXLAN, /* VxLAN interface */ + ZEBRA_IF_VRF, /* VRF device */ + ZEBRA_IF_BRIDGE, /* bridge device */ + ZEBRA_IF_VLAN, /* VLAN sub-interface */ + ZEBRA_IF_MACVLAN, /* MAC VLAN interface*/ } zebra_iftype_t; /* Zebra "slave" interface type */ diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 4404bb4483..58118ce006 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -179,10 +179,15 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) { int ret; struct ifaliasreq addreq; - struct sockaddr_in addr; - struct sockaddr_in mask; + struct sockaddr_in addr, mask, peer; struct prefix_ipv4 *p; + /* don't configure PtP addresses on broadcast ifs or reverse */ + if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) { + errno = EINVAL; + return -1; + } + p = (struct prefix_ipv4 *)ifc->address; rib_lookup_and_pushup(p, ifp->vrf_id); @@ -197,6 +202,18 @@ int if_set_prefix(struct interface *ifp, struct connected *ifc) #endif memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in)); + if (CONNECTED_PEER(ifc)) { + p = (struct prefix_ipv4 *)ifc->destination; + memset(&mask, 0, sizeof(struct sockaddr_in)); + peer.sin_addr = p->prefix; + peer.sin_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + peer.sin_len = sizeof(struct sockaddr_in); +#endif + memcpy(&addreq.ifra_broadaddr, &peer, + sizeof(struct sockaddr_in)); + } + memset(&mask, 0, sizeof(struct sockaddr_in)); masklen2ip(p->prefixlen, &mask.sin_addr); mask.sin_family = p->family; @@ -217,10 +234,15 @@ int if_unset_prefix(struct interface *ifp, struct connected *ifc) { int ret; struct ifaliasreq addreq; - struct sockaddr_in addr; - struct sockaddr_in mask; + struct sockaddr_in addr, mask, peer; struct prefix_ipv4 *p; + /* this would probably wreak havoc */ + if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) { + errno = EINVAL; + return -1; + } + p = (struct prefix_ipv4 *)ifc->address; memset(&addreq, 0, sizeof addreq); @@ -234,6 +256,18 @@ int if_unset_prefix(struct interface *ifp, struct connected *ifc) #endif memcpy(&addreq.ifra_addr, &addr, sizeof(struct sockaddr_in)); + if (CONNECTED_PEER(ifc)) { + p = (struct prefix_ipv4 *)ifc->destination; + memset(&mask, 0, sizeof(struct sockaddr_in)); + peer.sin_addr = p->prefix; + peer.sin_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + peer.sin_len = sizeof(struct sockaddr_in); +#endif + memcpy(&addreq.ifra_broadaddr, &peer, + sizeof(struct sockaddr_in)); + } + memset(&mask, 0, sizeof(struct sockaddr_in)); masklen2ip(p->prefixlen, &mask.sin_addr); mask.sin_family = p->family; diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 84d01bca6f..554ca4f77b 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -88,13 +88,10 @@ extern struct zebra_privs_t zserv_privs; #if !defined(ROUNDUP) /* - * It's a bug for a platform not to define rounding/alignment for - * sockaddrs on the routing socket. This warning really is - * intentional, to provoke filing bug reports with operating systems - * that don't define RT_ROUNDUP or equivalent. + * If you're porting to a platform that changed RT_ROUNDUP but doesn't + * have it in its headers, this will break rather obviously and you'll + * have to fix it here. */ -#warning \ - "net/route.h does not define RT_ROUNDUP; making unwarranted assumptions!" /* OS X (Xcode as of 2014-12) is known not to define RT_ROUNDUP */ #ifdef __APPLE__ @@ -444,6 +441,12 @@ int ifm_read(struct if_msghdr *ifm) RTA_ADDR_GET(NULL, RTA_IFA, ifm->ifm_addrs, cp); RTA_ADDR_GET(NULL, RTA_AUTHOR, ifm->ifm_addrs, cp); RTA_ADDR_GET(NULL, RTA_BRD, ifm->ifm_addrs, cp); +#ifdef RTA_LABEL + RTA_ATTR_GET(NULL, RTA_LABEL, ifm->ifm_addrs, cp); +#endif +#ifdef RTA_SRC + RTA_ADDR_GET(NULL, RTA_SRC, ifm->ifm_addrs, cp); +#endif if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: sdl ifname %s", __func__, @@ -664,6 +667,12 @@ static void ifam_read_mesg(struct ifa_msghdr *ifm, union sockunion *addr, RTA_ADDR_GET(addr, RTA_IFA, ifm->ifam_addrs, pnt); RTA_ADDR_GET(NULL, RTA_AUTHOR, ifm->ifam_addrs, pnt); RTA_ADDR_GET(brd, RTA_BRD, ifm->ifam_addrs, pnt); +#ifdef RTA_LABEL + RTA_ATTR_GET(NULL, RTA_LABEL, ifm->ifam_addrs, pnt); +#endif +#ifdef RTA_SRC + RTA_ADDR_GET(NULL, RTA_SRC, ifm->ifam_addrs, pnt); +#endif if (IS_ZEBRA_DEBUG_KERNEL) { int family = sockunion_family(addr); @@ -765,12 +774,10 @@ int ifam_read(struct ifa_msghdr *ifam) if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv6(ifp, flags, &addr.sin6.sin6_addr, ip6_masklen(mask.sin6.sin6_addr), - &brd.sin6.sin6_addr, (isalias ? ifname : NULL)); else connected_delete_ipv6(ifp, &addr.sin6.sin6_addr, - ip6_masklen(mask.sin6.sin6_addr), - &brd.sin6.sin6_addr); + ip6_masklen(mask.sin6.sin6_addr)); break; default: /* Unsupported family silently ignore... */ @@ -832,6 +839,17 @@ static int rtm_read_mesg(struct rt_msghdr *rtm, union sockunion *dest, RTA_ADDR_GET(NULL, RTA_IFA, rtm->rtm_addrs, pnt); RTA_ADDR_GET(NULL, RTA_AUTHOR, rtm->rtm_addrs, pnt); RTA_ADDR_GET(NULL, RTA_BRD, rtm->rtm_addrs, pnt); +#ifdef RTA_LABEL +#if 0 + union sockunion label; + memset(&label, 0, sizeof(label)); + RTA_ATTR_GET(&label, RTA_LABEL, rtm->rtm_addrs, pnt); +#endif + RTA_ATTR_GET(NULL, RTA_LABEL, rtm->rtm_addrs, pnt); +#endif +#ifdef RTA_SRC + RTA_ADDR_GET(NULL, RTA_SRC, rtm->rtm_addrs, pnt); +#endif /* If there is netmask information set it's family same as destination family*/ @@ -852,6 +870,7 @@ void rtm_read(struct rt_msghdr *rtm) union sockunion dest, mask, gate; char ifname[INTERFACE_NAMSIZ + 1]; short ifnlen = 0; + struct nexthop nh; zebra_flags = 0; @@ -889,11 +908,15 @@ void rtm_read(struct rt_msghdr *rtm) if (flags & RTF_STATIC) SET_FLAG(zebra_flags, ZEBRA_FLAG_STATIC); + memset(&nh, 0, sizeof(nh)); /* This is a reject or blackhole route */ - if (flags & RTF_REJECT) - SET_FLAG(zebra_flags, ZEBRA_FLAG_REJECT); - if (flags & RTF_BLACKHOLE) - SET_FLAG(zebra_flags, ZEBRA_FLAG_BLACKHOLE); + if (flags & RTF_REJECT) { + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.bh_type = BLACKHOLE_REJECT; + } else if (flags & RTF_BLACKHOLE) { + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.bh_type = BLACKHOLE_NULL; + } if (dest.sa.sa_family == AF_INET) { struct prefix p; @@ -1018,19 +1041,23 @@ 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, 0); + ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, + NULL, NULL, 0, 0); + + if (!nh.type) { + nh.type = NEXTHOP_TYPE_IPV4; + nh.gate.ipv4 = gate.sin.sin_addr; + } - union g_addr ggate = {.ipv4 = gate.sin.sin_addr}; if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &ggate, NULL, 0, 0, 0, 0, 0); + &nh, 0, 0, 0, 0); else rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &ggate, 0, 0, 0); + &nh, 0, 0); } if (dest.sa.sa_family == AF_INET6) { /* One day we might have a debug section here like one in the @@ -1060,19 +1087,25 @@ 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, 0); + ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, + NULL, NULL, 0, 0); + + if (!nh.type) { + nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX + : NEXTHOP_TYPE_IPV6; + nh.gate.ipv6 = gate.sin6.sin6_addr; + nh.ifindex = ifindex; + } - union g_addr ggate = {.ipv6 = gate.sin6.sin6_addr}; if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &ggate, NULL, ifindex, 0, 0, 0, 0); + &nh, 0, 0, 0, 0); else rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, - &ggate, ifindex, 0, 0); + &nh, 0, 0); } } @@ -1082,7 +1115,7 @@ void rtm_read(struct rt_msghdr *rtm) */ int rtm_write(int message, union sockunion *dest, union sockunion *mask, union sockunion *gate, union sockunion *mpls, unsigned int index, - int zebra_flags, int metric) + enum blackhole_type bh_type, int metric) { int ret; caddr_t pnt; @@ -1172,11 +1205,16 @@ int rtm_write(int message, union sockunion *dest, union sockunion *mask, /* Tagging route with flags */ msg.rtm.rtm_flags |= (RTF_PROTO1); - /* Additional flags. */ - if (zebra_flags & ZEBRA_FLAG_BLACKHOLE) - msg.rtm.rtm_flags |= RTF_BLACKHOLE; - if (zebra_flags & ZEBRA_FLAG_REJECT) + switch (bh_type) { + case BLACKHOLE_UNSPEC: + break; + case BLACKHOLE_REJECT: msg.rtm.rtm_flags |= RTF_REJECT; + break; + default: + msg.rtm.rtm_flags |= RTF_BLACKHOLE; + break; + } #define SOCKADDRSET(X, R) \ diff --git a/zebra/kernel_socket.h b/zebra/kernel_socket.h index 41b322185f..096a21f782 100644 --- a/zebra/kernel_socket.h +++ b/zebra/kernel_socket.h @@ -34,8 +34,8 @@ extern void rtm_read(struct rt_msghdr *); extern int ifam_read(struct ifa_msghdr *); extern int ifm_read(struct if_msghdr *); extern int rtm_write(int, union sockunion *, union sockunion *, - union sockunion *, union sockunion *, unsigned int, int, - int); + union sockunion *, union sockunion *, unsigned int, + enum blackhole_type, int); extern const struct message rtm_type_str[]; #endif /* __ZEBRA_KERNEL_SOCKET_H */ diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 6f08b5b5a0..fdfaa69571 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -502,8 +502,6 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, struct route_entry *newre; struct route_entry *same; struct prefix p; - struct nexthop *nhop; - union g_addr *gate; route_map_result_t ret = RMAP_MATCH; if (rmap_name) @@ -535,17 +533,10 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, if (re->nexthop_num == 1) { - nhop = re->nexthop; - if (nhop->type == NEXTHOP_TYPE_IFINDEX) - gate = NULL; - else - gate = (union g_addr *)&nhop->gate.ipv4; - rib_add(AFI_IP, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, 0, &p, - NULL, gate, - (union g_addr *)&nhop->src.ipv4, - nhop->ifindex, zebrad.rtm_table_default, + NULL, re->nexthop, + zebrad.rtm_table_default, re->metric, re->mtu, zebra_import_table_distance[AFI_IP] [re->table]); @@ -586,7 +577,7 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) p.u.prefix4 = rn->p.u.prefix4; rib_delete(AFI_IP, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, - re->table, re->flags, &p, NULL, NULL, 0, + re->table, re->flags, &p, NULL, NULL, zebrad.rtm_table_default, re->metric); } /* DD: Add IPv6 code */ diff --git a/zebra/rib.h b/zebra/rib.h index 495b731e88..9b1ce23b08 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -231,7 +231,8 @@ typedef enum { extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *, ifindex_t); -extern struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *); +extern struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *, + enum blackhole_type); extern struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *, struct in_addr *, struct in_addr *); @@ -294,18 +295,17 @@ extern int rib_uninstall_kernel(struct route_node *rn, struct route_entry *re); * also implicitly withdraw equal prefix of same type. */ extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, - struct prefix_ipv6 *src_p, union g_addr *gate, - union g_addr *src, ifindex_t ifindex, u_int32_t table_id, - u_int32_t metric, u_int32_t mtu, u_char distance); + struct prefix_ipv6 *src_p, const struct nexthop *nh, + u_int32_t table_id, u_int32_t metric, u_int32_t mtu, + u_char distance); extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *, struct prefix_ipv6 *src_p, struct route_entry *); extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, - struct prefix_ipv6 *src_p, union g_addr *gate, - ifindex_t ifindex, u_int32_t table_id, - u_int32_t metric); + struct prefix_ipv6 *src_p, const struct nexthop *nh, + u_int32_t table_id, u_int32_t metric); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t, union g_addr *, diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index c2e8349bf5..d1fd1a968a 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -240,13 +240,27 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, void *gate = NULL; void *prefsrc = NULL; /* IPv4 preferred source host address */ void *src = NULL; /* IPv6 srcdest source prefix */ + enum blackhole_type bh_type = BLACKHOLE_UNSPEC; rtm = NLMSG_DATA(h); if (startup && h->nlmsg_type != RTM_NEWROUTE) return 0; - if (startup && rtm->rtm_type != RTN_UNICAST) + switch (rtm->rtm_type) { + case RTN_UNICAST: + break; + case RTN_BLACKHOLE: + bh_type = BLACKHOLE_NULL; + break; + case RTN_UNREACHABLE: + bh_type = BLACKHOLE_REJECT; + break; + case RTN_PROHIBIT: + bh_type = BLACKHOLE_ADMINPROHIB; + break; + default: return 0; + } len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); if (len < 0) @@ -365,11 +379,33 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, afi = AFI_IP6; if (h->nlmsg_type == RTM_NEWROUTE) { - if (!tb[RTA_MULTIPATH]) + if (!tb[RTA_MULTIPATH]) { + struct nexthop nh; + size_t sz = (afi == AFI_IP) ? 4 : 16; + + memset(&nh, 0, sizeof(nh)); + if (index && !gate) + nh.type = NEXTHOP_TYPE_IFINDEX; + else if (index && gate) + nh.type = (afi == AFI_IP) + ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + else if (!index && gate) + nh.type = (afi == AFI_IP) + ? NEXTHOP_TYPE_IPV4 + : NEXTHOP_TYPE_IPV6; + else { + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.bh_type = bh_type; + } + nh.ifindex = index; + if (prefsrc) + memcpy(&nh.src, prefsrc, sz); + if (gate) + memcpy(&nh.gate, gate, sz); rib_add(afi, SAFI_UNICAST, vrf_id, ZEBRA_ROUTE_KERNEL, - 0, flags, &p, NULL, gate, prefsrc, index, table, - metric, mtu, 0); - else { + 0, flags, &p, NULL, &nh, table, metric, mtu, 0); + } else { /* This is a multipath route */ struct route_entry *re; @@ -444,41 +480,35 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, NULL, re); } } else { - if (!tb[RTA_MULTIPATH]) + if (!tb[RTA_MULTIPATH]) { + struct nexthop nh; + size_t sz = (afi == AFI_IP) ? 4 : 16; + + memset(&nh, 0, sizeof(nh)); + if (index && !gate) + nh.type = NEXTHOP_TYPE_IFINDEX; + else if (index && gate) + nh.type = (afi == AFI_IP) + ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + else if (!index && gate) + nh.type = (afi == AFI_IP) + ? NEXTHOP_TYPE_IPV4 + : NEXTHOP_TYPE_IPV6; + else + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.ifindex = index; + if (gate) + memcpy(&nh.gate, gate, sz); rib_delete(afi, SAFI_UNICAST, vrf_id, - ZEBRA_ROUTE_KERNEL, 0, flags, &p, NULL, gate, - index, table, metric); - else { - struct rtnexthop *rtnh = - (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]); - - len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); - - for (;;) { - if (len < (int)sizeof(*rtnh) - || rtnh->rtnh_len > len) - break; - - gate = NULL; - if (rtnh->rtnh_len > sizeof(*rtnh)) { - memset(tb, 0, sizeof(tb)); - netlink_parse_rtattr( - tb, RTA_MAX, RTNH_DATA(rtnh), - rtnh->rtnh_len - sizeof(*rtnh)); - if (tb[RTA_GATEWAY]) - gate = RTA_DATA( - tb[RTA_GATEWAY]); - } - - if (gate) - rib_delete(afi, SAFI_UNICAST, vrf_id, - ZEBRA_ROUTE_KERNEL, 0, flags, - &p, NULL, gate, index, - table, metric); - - len -= NLMSG_ALIGN(rtnh->rtnh_len); - rtnh = RTNH_NEXT(rtnh); - } + ZEBRA_ROUTE_KERNEL, 0, flags, &p, NULL, &nh, + table, metric); + } else { + /* XXX: need to compare the entire list of nexthops + * here for NLM_F_APPEND stupidity */ + rib_delete(afi, SAFI_UNICAST, vrf_id, + ZEBRA_ROUTE_KERNEL, 0, flags, &p, NULL, + NULL, table, metric); } } @@ -609,18 +639,10 @@ int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h, if (len < 0) return -1; - switch (rtm->rtm_type) { - case RTN_UNICAST: - netlink_route_change_read_unicast(snl, h, ns_id, startup); - break; - case RTN_MULTICAST: + if (rtm->rtm_type == RTN_MULTICAST) netlink_route_change_read_multicast(snl, h, ns_id, startup); - break; - default: - return 0; - break; - } - + else + netlink_route_change_read_unicast(snl, h, ns_id, startup); return 0; } @@ -1207,7 +1229,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, struct sockaddr_nl snl; struct nexthop *nexthop = NULL; unsigned int nexthop_num; - int discard; + int discard = 0; int family = PREFIX_FAMILY(p); const char *routedesc; int setsrc = 0; @@ -1238,24 +1260,23 @@ static int netlink_route_multipath(int cmd, struct prefix *p, req.r.rtm_src_len = src_p ? src_p->prefixlen : 0; req.r.rtm_protocol = get_rt_proto(re->type); req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; - if ((re->flags & ZEBRA_FLAG_BLACKHOLE) - || (re->flags & ZEBRA_FLAG_REJECT)) + if (re->nexthop_num == 1 + && re->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { discard = 1; - else - discard = 0; - - if (cmd == RTM_NEWROUTE) { - if (discard) { - if (re->flags & ZEBRA_FLAG_BLACKHOLE) - req.r.rtm_type = RTN_BLACKHOLE; - else if (re->flags & ZEBRA_FLAG_REJECT) - req.r.rtm_type = RTN_UNREACHABLE; - else - assert(RTN_BLACKHOLE - != RTN_UNREACHABLE); /* false */ - } else - req.r.rtm_type = RTN_UNICAST; + + switch (re->nexthop->bh_type) { + case BLACKHOLE_ADMINPROHIB: + req.r.rtm_type = RTN_PROHIBIT; + break; + case BLACKHOLE_REJECT: + req.r.rtm_type = RTN_UNREACHABLE; + break; + default: + req.r.rtm_type = RTN_BLACKHOLE; + break; + } } addattr_l(&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); @@ -1280,6 +1301,9 @@ static int netlink_route_multipath(int cmd, struct prefix *p, addattr32(&req.n, sizeof req, RTA_TABLE, re->table); } + if (discard) + goto skip; + if (re->mtu || re->nexthop_mtu) { char buf[NL_PKT_BUF_SIZE]; struct rtattr *rta = (void *)buf; @@ -1293,21 +1317,6 @@ static int netlink_route_multipath(int cmd, struct prefix *p, RTA_PAYLOAD(rta)); } - if (discard) { - if (cmd == RTM_NEWROUTE) - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { - /* We shouldn't encounter recursive nexthops on - * discard routes, - * but it is probably better to handle that case - * correctly anyway. - */ - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - } - goto skip; - } - /* Count overall nexthops so we can decide whether to use singlepath * or multipath case. */ nexthop_num = 0; @@ -2007,7 +2016,8 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, * itself */ if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = zif->link; + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + zif->link_ifindex); if (!link_if) return 0; } else if (IS_ZEBRA_IF_BRIDGE(ifp)) diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 32ae41b917..10c7109194 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -42,11 +42,6 @@ extern struct zebra_privs_t zserv_privs; -/* kernel socket export */ -extern int rtm_write(int message, union sockunion *dest, union sockunion *mask, - union sockunion *gate, union sockunion *mpls, - unsigned int index, int zebra_flags, int metric); - #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN /* Adjust netmask socket length. Return value is a adjusted sin_len value. */ @@ -108,6 +103,7 @@ static int kernel_rtm_ipv4(int cmd, struct prefix *p, struct route_entry *re) int gate = 0; int error; char prefix_buf[PREFIX_STRLEN]; + enum blackhole_type bh_type = BLACKHOLE_UNSPEC; if (IS_ZEBRA_DEBUG_RIB) prefix2str(p, prefix_buf, sizeof(prefix_buf)); @@ -155,6 +151,7 @@ static int kernel_rtm_ipv4(int cmd, struct prefix *p, struct route_entry *re) struct in_addr loopback; loopback.s_addr = htonl(INADDR_LOOPBACK); sin_gate.sin_addr = loopback; + bh_type = nexthop->bh_type; gate = 1; } @@ -182,7 +179,7 @@ static int kernel_rtm_ipv4(int cmd, struct prefix *p, struct route_entry *re) cmd, (union sockunion *)&sin_dest, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, - smplsp, ifindex, re->flags, re->metric); + smplsp, ifindex, bh_type, re->metric); if (IS_ZEBRA_DEBUG_RIB) { if (!gate) { @@ -292,6 +289,7 @@ static int kernel_rtm_ipv6(int cmd, struct prefix *p, struct route_entry *re) ifindex_t ifindex = 0; int gate = 0; int error; + enum blackhole_type bh_type = BLACKHOLE_UNSPEC; memset(&sin_dest, 0, sizeof(struct sockaddr_in6)); sin_dest.sin6_family = AF_INET6; @@ -331,6 +329,9 @@ static int kernel_rtm_ipv6(int cmd, struct prefix *p, struct route_entry *re) || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) ifindex = nexthop->ifindex; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) + bh_type = nexthop->bh_type; + if (cmd == RTM_ADD) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } @@ -369,7 +370,7 @@ static int kernel_rtm_ipv6(int cmd, struct prefix *p, struct route_entry *re) error = rtm_write(cmd, (union sockunion *)&sin_dest, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, - smplsp, ifindex, re->flags, re->metric); + smplsp, ifindex, bh_type, re->metric); #if 0 if (error) diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 1bba003a0a..62f3224b6e 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -75,8 +75,8 @@ static void handle_route_entry(mib2_ipRouteEntry_t *routeEntry) { struct prefix prefix; - struct in_addr tmpaddr, gateway; - union g_addr *ggateway; + struct in_addr tmpaddr; + struct nexthop nh; u_char zebra_flags = 0; if (routeEntry->ipRouteInfo.re_ire_type & IRE_CACHETABLE) @@ -93,11 +93,12 @@ static void handle_route_entry(mib2_ipRouteEntry_t *routeEntry) tmpaddr.s_addr = routeEntry->ipRouteMask; prefix.prefixlen = ip_masklen(tmpaddr); - gateway.s_addr = routeEntry->ipRouteNextHop; - ggateway = (union g_addr *)&gateway; + memset(&nh, 0, sizeof(nh)); + nh.type = NEXTHOP_TYPE_IPV4; + nh.gate.ipv4.s_addr = routeEntry->ipRouteNextHop; rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, - zebra_flags, &prefix, NULL, ggateway, NULL, 0, 0, 0, 0, 0); + zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0); } void route_read(struct zebra_ns *zns) diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 28f7956639..27c7891372 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -222,7 +222,6 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, rib_dest_t *dest, struct route_entry *re) { struct nexthop *nexthop; - int discard; memset(ri, 0, sizeof(*ri)); @@ -247,30 +246,9 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, } ri->rtm_protocol = netlink_proto_from_route_type(re->type); - - if ((re->flags & ZEBRA_FLAG_BLACKHOLE) - || (re->flags & ZEBRA_FLAG_REJECT)) - discard = 1; - else - discard = 0; - - if (cmd == RTM_NEWROUTE) { - if (discard) { - if (re->flags & ZEBRA_FLAG_BLACKHOLE) - ri->rtm_type = RTN_BLACKHOLE; - else if (re->flags & ZEBRA_FLAG_REJECT) - ri->rtm_type = RTN_UNREACHABLE; - else - assert(0); - } else - ri->rtm_type = RTN_UNICAST; - } - + ri->rtm_type = RTN_UNICAST; ri->metric = &re->metric; - if (discard) - return 1; - for (ALL_NEXTHOPS(re->nexthop, nexthop)) { if (ri->num_nhs >= multipath_num) break; @@ -278,6 +256,22 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + switch (nexthop->bh_type) { + case BLACKHOLE_ADMINPROHIB: + ri->rtm_type = RTN_PROHIBIT; + break; + case BLACKHOLE_REJECT: + ri->rtm_type = RTN_UNREACHABLE; + break; + case BLACKHOLE_NULL: + default: + ri->rtm_type = RTN_BLACKHOLE; + break; + } + return 1; + } + if ((cmd == RTM_NEWROUTE && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index 450ad5b0bf..b850f1fb1e 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -141,7 +141,6 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, struct route_entry *re) { Fpm__AddRoute *msg; - int discard; struct nexthop *nexthop; uint num_nhs, u; struct nexthop *nexthops[MULTIPATH_NUM]; @@ -164,26 +163,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; msg->key = fpm_route_key_create(allocator, rib_dest_prefix(dest)); qpb_protocol_set(&msg->protocol, re->type); - - if ((re->flags & ZEBRA_FLAG_BLACKHOLE) - || (re->flags & ZEBRA_FLAG_REJECT)) - discard = 1; - else - discard = 0; - - if (discard) { - if (re->flags & ZEBRA_FLAG_BLACKHOLE) { - msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE; - } else if (re->flags & ZEBRA_FLAG_REJECT) { - msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE; - } else { - assert(0); - } - return msg; - } else { - msg->route_type = FPM__ROUTE_TYPE__NORMAL; - } - + msg->route_type = FPM__ROUTE_TYPE__NORMAL; msg->metric = re->metric; /* @@ -197,6 +177,19 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, if (num_nhs >= ZEBRA_NUM_OF(nexthops)) break; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE; + break; + case BLACKHOLE_NULL: + default: + msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE; + break; + } + return msg; + } + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 5d059a4502..3d505857c2 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -224,12 +224,19 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp, zif->brslave_info.bridge_ifindex = bridge_ifindex; /* Set up or remove link with master */ - if (bridge_ifindex != IFINDEX_INTERNAL) + if (bridge_ifindex != IFINDEX_INTERNAL) { zebra_l2_map_slave_to_bridge(&zif->brslave_info); - else if (old_bridge_ifindex != IFINDEX_INTERNAL) + /* In the case of VxLAN, invoke the handler for EVPN. */ + if (zif->zif_type == ZEBRA_IF_VXLAN) + zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); + } else if (old_bridge_ifindex != IFINDEX_INTERNAL) { + /* + * In the case of VxLAN, invoke the handler for EVPN. + * Note that this should be done *prior* + * to unmapping the interface from the bridge. + */ + if (zif->zif_type == ZEBRA_IF_VXLAN) + zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); zebra_l2_unmap_slave_from_bridge(&zif->brslave_info); - - /* In the case of VxLAN, invoke the handler for EVPN. */ - if (zif->zif_type == ZEBRA_IF_VXLAN) - zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); + } } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 63ac06b160..c7a2564ab4 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -291,13 +291,14 @@ struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re, return nexthop; } -struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re) +struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re, + enum blackhole_type bh_type) { struct nexthop *nexthop; nexthop = nexthop_new(); nexthop->type = NEXTHOP_TYPE_BLACKHOLE; - SET_FLAG(re->flags, ZEBRA_FLAG_BLACKHOLE); + nexthop->bh_type = bh_type; route_entry_nexthop_add(re, nexthop); @@ -471,12 +472,6 @@ static int nexthop_active(afi_t afi, struct route_entry *re, continue; } - /* If the longest prefix match for the nexthop yields - * a blackhole, mark it as inactive. */ - if (CHECK_FLAG(match->flags, ZEBRA_FLAG_BLACKHOLE) - || CHECK_FLAG(match->flags, ZEBRA_FLAG_REJECT)) - return 0; - if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ newhop = match->nexthop; @@ -488,41 +483,46 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_INTERNAL)) { resolved = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) - if (CHECK_FLAG(newhop->flags, NEXTHOP_FLAG_FIB) - && !CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) { - if (set) { - SET_FLAG( - nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - SET_FLAG( - re->status, - ROUTE_ENTRY_NEXTHOPS_CHANGED); - - nexthop_set_resolved( - afi, newhop, nexthop); - } - resolved = 1; + for (ALL_NEXTHOPS(match->nexthop, newhop)) { + if (newhop->type == NEXTHOP_TYPE_BLACKHOLE) + continue; + if (!CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_FIB)) + continue; + if (CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (set) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(re->status, + ROUTE_ENTRY_NEXTHOPS_CHANGED); + nexthop_set_resolved(afi, newhop, + nexthop); } + resolved = 1; + } if (resolved && set) re->nexthop_mtu = match->mtu; return resolved; } else if (re->type == ZEBRA_ROUTE_STATIC) { resolved = 0; - for (ALL_NEXTHOPS(match->nexthop, newhop)) - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_FIB)) { - if (set) { - SET_FLAG( - nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); + for (ALL_NEXTHOPS(match->nexthop, newhop)) { + if (newhop->type == NEXTHOP_TYPE_BLACKHOLE) + continue; + if (!CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_FIB)) + continue; - nexthop_set_resolved( - afi, newhop, nexthop); - } - resolved = 1; + if (set) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, newhop, + nexthop); } + resolved = 1; + } if (resolved && set) re->nexthop_mtu = match->mtu; return resolved; @@ -2273,16 +2273,15 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, - struct prefix_ipv6 *src_p, union g_addr *gate, - ifindex_t ifindex, u_int32_t table_id, - u_int32_t metric) + struct prefix_ipv6 *src_p, const struct nexthop *nh, + u_int32_t table_id, u_int32_t metric) { struct route_table *table; struct route_node *rn; struct route_entry *re; struct route_entry *fib = NULL; struct route_entry *same = NULL; - struct nexthop *nexthop; + struct nexthop *rtnh; char buf2[INET6_ADDRSTRLEN]; assert(!src_p || afi == AFI_IP6); @@ -2332,9 +2331,9 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric) continue; - if (re->type == ZEBRA_ROUTE_CONNECT && (nexthop = re->nexthop) - && nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->ifindex != ifindex) + if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->nexthop) + && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) { + if (rtnh->ifindex != nh->ifindex) continue; if (re->refcnt) { re->refcnt--; @@ -2347,14 +2346,12 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, } /* Make sure that the route found has the same gateway. */ else { - if (gate == NULL) { + if (nh == NULL) { same = re; break; } - for (ALL_NEXTHOPS(re->nexthop, nexthop)) - if (IPV4_ADDR_SAME(&nexthop->gate.ipv4, &gate->ipv4) - || IPV6_ADDR_SAME(&nexthop->gate.ipv6, - gate)) { + for (ALL_NEXTHOPS(re->nexthop, rtnh)) + if (nexthop_same_no_recurse(rtnh, nh)) { same = re; break; } @@ -2375,9 +2372,9 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, } if (allow_delete) { /* Unset flags. */ - for (nexthop = fib->nexthop; nexthop; - nexthop = nexthop->next) - UNSET_FLAG(nexthop->flags, + for (rtnh = fib->nexthop; rtnh; + rtnh = rtnh->next) + UNSET_FLAG(rtnh->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG(fib->status, @@ -2391,22 +2388,22 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, } } else { if (IS_ZEBRA_DEBUG_RIB) { - if (gate) + if (nh) rnode_debug( rn, vrf_id, "via %s ifindex %d type %d " "doesn't exist in rib", inet_ntop( - family2afi(afi), gate, + family2afi(afi), &nh->gate, buf2, INET_ADDRSTRLEN), /* FIXME */ - ifindex, type); + nh->ifindex, type); else rnode_debug( rn, vrf_id, - "ifindex %d type %d doesn't exist in rib", - ifindex, type); + "type %d doesn't exist in rib", + type); } route_unlock_node(rn); return; @@ -2423,15 +2420,14 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, - union g_addr *gate, union g_addr *src, ifindex_t ifindex, - u_int32_t table_id, u_int32_t metric, u_int32_t mtu, - u_char distance) + const struct nexthop *nh, u_int32_t table_id, u_int32_t metric, + u_int32_t mtu, u_char distance) { struct route_entry *re; struct route_entry *same = NULL; struct route_table *table; struct route_node *rn; - struct nexthop *nexthop; + struct nexthop *rtnh; assert(!src_p || afi == AFI_IP6); @@ -2480,9 +2476,9 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, break; } /* Duplicate system route comes in. */ - else if ((nexthop = re->nexthop) - && nexthop->type == NEXTHOP_TYPE_IFINDEX - && nexthop->ifindex == ifindex + else if ((rtnh = re->nexthop) + && rtnh->type == NEXTHOP_TYPE_IFINDEX + && rtnh->ifindex == nh->ifindex && !CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) { re->refcnt++; return 0; @@ -2503,29 +2499,14 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance, re->nexthop_num = 0; re->uptime = time(NULL); - /* Nexthop settings. */ - if (gate) { - if (afi == AFI_IP6) { - if (ifindex) - route_entry_nexthop_ipv6_ifindex_add( - re, &gate->ipv6, ifindex); - else - route_entry_nexthop_ipv6_add(re, &gate->ipv6); - } else { - if (ifindex) - route_entry_nexthop_ipv4_ifindex_add( - re, &gate->ipv4, &src->ipv4, ifindex); - else - route_entry_nexthop_ipv4_add(re, &gate->ipv4, - &src->ipv4); - } - } else - route_entry_nexthop_ifindex_add(re, ifindex); + rtnh = nexthop_new(); + *rtnh = *nh; + route_entry_nexthop_add(re, rtnh); /* If this route is kernel route, set FIB flag to the route. */ if (RIB_SYSTEM_ROUTE(re)) - for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + for (rtnh = re->nexthop; rtnh; rtnh = rtnh->next) + SET_FLAG(rtnh->flags, NEXTHOP_FLAG_FIB); /* Link new rib to node.*/ if (IS_ZEBRA_DEBUG_RIB) { diff --git a/zebra/zebra_static.c b/zebra/zebra_static.c index 6815916faf..51bbf2a0cc 100644 --- a/zebra/zebra_static.c +++ b/zebra/zebra_static.c @@ -91,7 +91,8 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, si->ifindex); break; case STATIC_BLACKHOLE: - nexthop = route_entry_nexthop_blackhole_add(re); + nexthop = route_entry_nexthop_blackhole_add(re, + si->bh_type); break; case STATIC_IPV6_GATEWAY: nexthop = route_entry_nexthop_ipv6_add(re, @@ -166,7 +167,8 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, si->ifindex); break; case STATIC_BLACKHOLE: - nexthop = route_entry_nexthop_blackhole_add(re); + nexthop = route_entry_nexthop_blackhole_add(re, + si->bh_type); break; case STATIC_IPV6_GATEWAY: nexthop = route_entry_nexthop_ipv6_add(re, @@ -187,9 +189,6 @@ void static_install_route(afi_t afi, safi_t safi, struct prefix *p, si->snh_label.num_labels, &si->snh_label.label[0]); - /* Save the flags of this static routes (reject, blackhole) */ - re->flags = si->flags; - if (IS_ZEBRA_DEBUG_RIB) { char buf[INET6_ADDRSTRLEN]; if (IS_ZEBRA_DEBUG_RIB) { @@ -364,7 +363,7 @@ void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p, int static_add_route(afi_t afi, safi_t safi, u_char type, struct prefix *p, struct prefix_ipv6 *src_p, union g_addr *gate, - const char *ifname, u_char flags, + const char *ifname, enum blackhole_type bh_type, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label) { @@ -405,7 +404,7 @@ int static_add_route(afi_t afi, safi_t safi, u_char type, struct prefix *p, if ((distance == si->distance) && (tag == si->tag) && !memcmp(&si->snh_label, snh_label, sizeof(struct static_nh_label)) - && si->flags == flags) { + && si->bh_type == bh_type) { route_unlock_node(rn); return 0; } else @@ -424,7 +423,7 @@ int static_add_route(afi_t afi, safi_t safi, u_char type, struct prefix *p, si->type = type; si->distance = distance; - si->flags = flags; + si->bh_type = bh_type; si->tag = tag; si->vrf_id = zvrf_id(zvrf); if (ifname) diff --git a/zebra/zebra_static.h b/zebra/zebra_static.h index ff9f0f59e7..458594a289 100644 --- a/zebra/zebra_static.h +++ b/zebra/zebra_static.h @@ -61,18 +61,12 @@ struct static_route { /* * Nexthop value. */ + enum blackhole_type bh_type; union g_addr addr; ifindex_t ifindex; char ifname[INTERFACE_NAMSIZ + 1]; - /* bit flags */ - u_char flags; - /* - see ZEBRA_FLAG_REJECT - ZEBRA_FLAG_BLACKHOLE - */ - /* Label information */ struct static_nh_label snh_label; }; @@ -86,7 +80,7 @@ extern void static_uninstall_route(afi_t afi, safi_t safi, struct prefix *p, extern int static_add_route(afi_t, safi_t safi, u_char type, struct prefix *p, struct prefix_ipv6 *src_p, union g_addr *gate, - const char *ifname, u_char flags, + const char *ifname, enum blackhole_type bh_type, route_tag_t tag, u_char distance, struct zebra_vrf *zvrf, struct static_nh_label *snh_label); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 7649d5f358..eeca7bceda 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -74,7 +74,7 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, union g_addr gate; union g_addr *gatep = NULL; struct in_addr mask; - u_char flag = 0; + enum blackhole_type bh_type = 0; route_tag_t tag = 0; struct zebra_vrf *zvrf; u_char type; @@ -165,28 +165,18 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, } } - /* Null0 static route. */ - if ((ifname != NULL) - && (strncasecmp(ifname, "Null0", strlen(ifname)) == 0)) { - if (flag_str) { - vty_out(vty, "%% can not have flag %s with Null0\n", - flag_str); - return CMD_WARNING_CONFIG_FAILED; - } - SET_FLAG(flag, ZEBRA_FLAG_BLACKHOLE); - ifname = NULL; - } - /* Route flags */ if (flag_str) { switch (flag_str[0]) { case 'r': case 'R': /* XXX */ - SET_FLAG(flag, ZEBRA_FLAG_REJECT); + bh_type = BLACKHOLE_REJECT; break; + case 'n': + case 'N' /* XXX */: case 'b': case 'B': /* XXX */ - SET_FLAG(flag, ZEBRA_FLAG_BLACKHOLE); + bh_type = BLACKHOLE_NULL; break; default: vty_out(vty, "%% Malformed flag %s \n", flag_str); @@ -221,7 +211,7 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, if (!negate) static_add_route(afi, safi, type, &p, src_p, gatep, ifname, - flag, tag, distance, zvrf, &snh_label); + bh_type, tag, distance, zvrf, &snh_label); else static_delete_route(afi, safi, type, &p, src_p, gatep, ifname, tag, distance, zvrf, &snh_label); @@ -346,8 +336,7 @@ DEFPY (ip_route, <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask>\ <\ {A.B.C.D$gate|INTERFACE$ifname}\ - |null0$ifname\ - |<reject|blackhole>$flag\ + |<null0|reject|blackhole>$flag\ >\ [{\ tag (1-4294967295)\ @@ -417,10 +406,6 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, vty_out(vty, ", best"); if (re->refcnt) vty_out(vty, ", refcnt %ld", re->refcnt); - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out(vty, ", blackhole"); - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_REJECT)) - vty_out(vty, ", reject"); vty_out(vty, "\n"); if (re->type == ZEBRA_ROUTE_RIP || re->type == ZEBRA_ROUTE_OSPF @@ -485,7 +470,20 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, re->vrf_id)); break; case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " directly connected, Null0"); + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } break; default: break; @@ -580,12 +578,6 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_int_add(json_route, "metric", re->metric); } - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_BLACKHOLE)) - json_object_boolean_true_add(json_route, "blackhole"); - - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_REJECT)) - json_object_boolean_true_add(json_route, "reject"); - if (re->type == ZEBRA_ROUTE_RIP || re->type == ZEBRA_ROUTE_OSPF || re->type == ZEBRA_ROUTE_ISIS || re->type == ZEBRA_ROUTE_NHRP @@ -671,7 +663,26 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, break; case NEXTHOP_TYPE_BLACKHOLE: json_object_boolean_true_add(json_nexthop, - "blackhole"); + "unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + json_object_boolean_true_add( + json_nexthop, + "reject"); + break; + case BLACKHOLE_ADMINPROHIB: + json_object_boolean_true_add( + json_nexthop, + "admin-prohibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add( + json_nexthop, + "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; + } break; default: break; @@ -796,7 +807,20 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, ifindex2ifname(nexthop->ifindex, re->vrf_id)); break; case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " is directly connected, Null0"); + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } break; default: break; @@ -839,11 +863,6 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, sizeof buf, 1)); } - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out(vty, ", bh"); - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_REJECT)) - vty_out(vty, ", rej"); - if (re->type == ZEBRA_ROUTE_RIP || re->type == ZEBRA_ROUTE_OSPF || re->type == ZEBRA_ROUTE_ISIS || re->type == ZEBRA_ROUTE_NHRP @@ -1696,12 +1715,15 @@ static int static_config(struct vty *vty, afi_t afi, safi_t safi, case STATIC_IFNAME: vty_out(vty, " %s", si->ifname); break; - /* blackhole and Null0 mean the same thing */ case STATIC_BLACKHOLE: - if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT)) + switch (si->bh_type) { + case BLACKHOLE_REJECT: vty_out(vty, " reject"); - else - vty_out(vty, " Null0"); + break; + default: + vty_out(vty, " blackhole"); + break; + } break; case STATIC_IPV4_GATEWAY_IFNAME: vty_out(vty, " %s %s", @@ -1719,19 +1741,6 @@ static int static_config(struct vty *vty, afi_t afi, safi_t safi, break; } - /* flags are incompatible with STATIC_BLACKHOLE - */ - if (si->type != STATIC_BLACKHOLE) { - if (CHECK_FLAG(si->flags, - ZEBRA_FLAG_REJECT)) - vty_out(vty, " %s", "reject"); - - if (CHECK_FLAG(si->flags, - ZEBRA_FLAG_BLACKHOLE)) - vty_out(vty, " %s", - "blackhole"); - } - if (si->tag) vty_out(vty, " tag %" ROUTE_TAG_PRI, si->tag); @@ -1766,8 +1775,7 @@ DEFPY (ipv6_route, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M]\ <\ {X:X::X:X$gate|INTERFACE$ifname}\ - |null0$ifname\ - |<reject|blackhole>$flag\ + |<null0|reject|blackhole>$flag\ >\ [{\ tag (1-4294967295)\ diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index ef24e533d0..4e5612f4f8 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -204,13 +204,18 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) if (json == NULL) { vty_out(vty, " Remote VTEP: %s", inet_ntoa(n->r_vtep_ip)); - vty_out(vty, " State: %s", IS_ZEBRA_NEIGH_ACTIVE(n) - ? "Active" - : "Inactive"); } else json_object_string_add(json, "remoteVtep", inet_ntoa(n->r_vtep_ip)); } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (!json) { + vty_out(vty, "\n"); + vty_out(vty, " State: %s", + IS_ZEBRA_NEIGH_ACTIVE(n) ? "Active" + : "Inactive"); + } + } if (json == NULL) vty_out(vty, "\n"); } @@ -384,8 +389,8 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt) } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { vty_out(vty, " Auto Mac "); } - vty_out(vty, " ARP ref: %u\n", mac->neigh_refcnt); + vty_out(vty, "\n"); /* print all the associated neigh */ vty_out(vty, " Neighbors:\n"); if (!listcount(mac->neigh_list)) @@ -1355,9 +1360,6 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, memcpy(&n->emac, macaddr, ETH_ALEN); n->ifindex = ifp->ifindex; - /* We have a neigh associated to mac increment the refcnt*/ - mac->neigh_refcnt++; - if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP", @@ -1435,13 +1437,22 @@ static void zvni_gw_macip_del_for_vni_hash(struct hash_backet *backet, struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; + struct interface *ifp; /* Add primary SVI MAC*/ zvni = (zebra_vni_t *)backet->data; if (!zvni) return; - zif = zvni->vxlan_if->info; + ifp = zvni->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + zl2_info = zif->l2info.vxl; vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, @@ -1468,6 +1479,7 @@ static void zvni_gw_macip_add_for_vni_hash(struct hash_backet *backet, struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; + struct interface *ifp = NULL; zvni = (zebra_vni_t *)backet->data; if (!zvni) @@ -1476,7 +1488,14 @@ static void zvni_gw_macip_add_for_vni_hash(struct hash_backet *backet, if (!advertise_gw_macip_enabled(zvrf, zvni)) return; - zif = zvni->vxlan_if->info; + ifp = zvni->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; zl2_info = zif->l2info.vxl; vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, @@ -1762,6 +1781,9 @@ static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if) vlanid_t vid = 0; zebra_vni_t *zvni; + if (!br_if) + return NULL; + /* Make sure the linked interface is a bridge. */ if (!IS_ZEBRA_IF_BRIDGE(br_if)) return NULL; @@ -1831,6 +1853,10 @@ static struct interface *zvni_map_to_svi(struct zebra_vrf *zvrf, vlanid_t vid, struct zebra_l2info_vlan *vl; u_char bridge_vlan_aware; + /* Defensive check, caller expected to invoke only with valid bridge. */ + if (!br_if) + return NULL; + /* Determine if bridge is VLAN-aware or not */ zif = br_if->info; assert(zif); @@ -1947,10 +1973,8 @@ static void zvni_install_mac_hash(struct hash_backet *backet, void *ctxt) static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall) { - if (mac->neigh_refcnt) - mac->neigh_refcnt--; - - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO) || mac->neigh_refcnt > 0) + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO) || + !list_isempty(mac->neigh_list)) return; if (uninstall) @@ -2857,7 +2881,7 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp, /* see if the AUTO mac needs to be deleted */ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) - || !listcount(zmac->neigh_list)) + && !listcount(zmac->neigh_list)) zvni_mac_del(zvni, zmac); return 0; @@ -2876,10 +2900,9 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, zebra_vni_t *zvni; zebra_neigh_t *n; struct zebra_vrf *zvrf; - zebra_mac_t *zmac; + zebra_mac_t *zmac, *old_zmac; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - int send_upd = 1, send_del = 0; /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. @@ -2934,19 +2957,35 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0) { - if (n->ifindex == ifp->ifindex) - /* we're not interested in whatever has - * changed. */ - return 0; - /* client doesn't care about a purely local - * change. */ - send_upd = 0; - } else - /* If the MAC has changed, issue a delete first - * as this means a - * different MACIP route. + /* Update any params and return - client doesn't + * care about a purely local change. */ - send_del = 1; + n->ifindex = ifp->ifindex; + return 0; + } + + /* If the MAC has changed, + * need to issue a delete first + * as this means a different MACIP route. + * Also, need to do some unlinking/relinking. + */ + zvni_neigh_send_del_to_client(zvrf, zvni->vni, + &n->ip, &n->emac, + 0); + old_zmac = zvni_mac_lookup(zvni, &n->emac); + if (old_zmac) { + listnode_delete(old_zmac->neigh_list, + n); + zvni_deref_ip2mac(zvni, old_zmac, 0); + } + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + memcpy(&n->emac, macaddr, ETH_ALEN); + + /* Link to new MAC */ + listnode_add_sort(zmac->neigh_list, n); } else if (ext_learned) /* The neighbor is remote and that is the notification we got. */ @@ -2958,6 +2997,8 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, { UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); n->r_vtep_ip.s_addr = 0; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; } } else { n = zvni_neigh_add(zvni, ip, macaddr); @@ -2969,17 +3010,11 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, ifp->name, ifp->ifindex, zvni->vni); return -1; } + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; } - /* Issue delete for older info, if needed. */ - if (send_del) - zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac, - 0); - - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - /* Before we program this in BGP, we need to check if MAC is locally * learnt as well */ if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { @@ -2993,23 +3028,20 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp, return 0; } - /* Inform BGP if required. */ - if (send_upd) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u", - ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - - ZEBRA_NEIGH_SET_ACTIVE(n); - return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, - macaddr, 0); - } + /* Inform BGP. */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u", + ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zvni->vni); - return 0; + ZEBRA_NEIGH_SET_ACTIVE(n); + return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, + macaddr, 0); } + /* * Handle message from client to delete a remote MACIP for a VNI. */ @@ -3027,6 +3059,8 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, u_short l = 0, ipa_len; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; s = client->ibuf; @@ -3069,12 +3103,18 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, zvrf_id(zvrf), vni); continue; } - if (!zvni->vxlan_if) { + ifp = zvni->vxlan_if; + if (!ifp) { zlog_err( "VNI %u hash %p doesn't have intf upon remote MACIP DEL", vni, zvni); continue; } + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + continue; /* The remote VTEP specified is normally expected to exist, but * it is @@ -3087,12 +3127,6 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, if (!zvni_vtep_find(zvni, &vtep_ip)) continue; - /* If the local VxLAN interface is not up (should be a transient - * event), there's nothing more to do. - */ - if (!if_is_operative(zvni->vxlan_if)) - continue; - mac = zvni_mac_lookup(zvni, &macaddr); if (ipa_len) n = zvni_neigh_lookup(zvni, &ip); @@ -3136,7 +3170,7 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length, zvni_process_neigh_on_remote_mac_del(zvrf, zvni, mac); - if (!mac->neigh_refcnt) { + if (list_isempty(mac->neigh_list)) { zvni_mac_uninstall(zvni, mac, 0); zvni_mac_del(zvni, mac); } else @@ -3170,6 +3204,8 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; u_char sticky; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; assert(EVPN_ENABLED(zvrf)); @@ -3217,16 +3253,17 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, zvrf_id(zvrf), vni); continue; } - if (!zvni->vxlan_if) { + ifp = zvni->vxlan_if; + if (!ifp) { zlog_err( "VNI %u hash %p doesn't have intf upon remote MACIP add", vni, zvni); continue; } - /* If the local VxLAN interface is not up (should be a transient - * event), there's nothing more to do. - */ - if (!if_is_operative(zvni->vxlan_if)) + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; /* The remote VTEP specified should normally exist, but it is @@ -3276,9 +3313,6 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, /* Is this MAC created for a MACIP? */ if (ipa_len) SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - /* Moving from local to remote, issue delete. */ - zvni_mac_uninstall(zvni, mac, 1); } /* Set "auto" and "remote" forwarding info. */ @@ -3330,16 +3364,16 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length, return -1; } - /* New neighbor referring to this MAC. */ - mac->neigh_refcnt++; } else if (memcmp(&n->emac, &macaddr, sizeof(macaddr)) != 0) { - /* MAC change, update ref counts for old and new - * MAC. */ + /* MAC change, update neigh list for old and new + * mac */ old_mac = zvni_mac_lookup(zvni, &n->emac); - if (old_mac) + if (old_mac) { + listnode_delete(old_mac->neigh_list, n); zvni_deref_ip2mac(zvni, old_mac, 1); - mac->neigh_refcnt++; + } + listnode_add_sort(mac->neigh_list, n); memcpy(&n->emac, &macaddr, ETH_ALEN); } @@ -3691,6 +3725,8 @@ int zebra_vxlan_remote_vtep_del(struct zserv *client, int sock, u_short length, struct in_addr vtep_ip; zebra_vni_t *zvni; zebra_vtep_t *zvtep; + struct interface *ifp; + struct zebra_if *zif; s = client->ibuf; @@ -3717,6 +3753,18 @@ int zebra_vxlan_remote_vtep_del(struct zserv *client, int sock, u_short length, continue; } + ifp = zvni->vxlan_if; + if (!ifp) { + zlog_err("VNI %u hash %p doesn't have intf upon remote VTEP DEL", + zvni->vni, zvni); + continue; + } + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + continue; + /* If the remote VTEP does not exist, there's nothing more to * do. * Otherwise, uninstall any remote MACs pointing to this VTEP @@ -3747,6 +3795,8 @@ int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length, vni_t vni; struct in_addr vtep_ip; zebra_vni_t *zvni; + struct interface *ifp; + struct zebra_if *zif; assert(EVPN_ENABLED(zvrf)); @@ -3772,24 +3822,24 @@ int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length, zvrf_id(zvrf), vni); continue; } - if (!zvni->vxlan_if) { + + ifp = zvni->vxlan_if; + if (!ifp) { zlog_err( "VNI %u hash %p doesn't have intf upon remote VTEP ADD", zvni->vni, zvni); continue; } + zif = ifp->info; - /* If the remote VTEP already exists, or the local VxLAN - * interface is - * not up (should be a transient event), there's nothing more - * to do. - * Otherwise, add and install the entry. - */ - if (zvni_vtep_find(zvni, &vtep_ip)) + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; - if (!if_is_operative(zvni->vxlan_if)) + /* If the remote VTEP already exists, + there's nothing more to do. */ + if (zvni_vtep_find(zvni, &vtep_ip)) continue; if (zvni_vtep_add(zvni, &vtep_ip) == NULL) { @@ -3843,7 +3893,11 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, if (!ifp_zif) return -1; - svi_if = ifp_zif->link; + /* + * for a MACVLAN interface the link represents the svi_if + */ + svi_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp_zif->link_ifindex); if (!svi_if) { zlog_err("%u:MACVLAN %s(%u) without link information", ifp->vrf_id, ifp->name, ifp->ifindex); @@ -3851,19 +3905,39 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, } if (IS_ZEBRA_IF_VLAN(svi_if)) { + /* + * If it is a vlan aware bridge then the link gives the + * bridge information + */ + struct interface *svi_if_link = NULL; + svi_if_zif = svi_if->info; - if (svi_if_zif) - zvni = zvni_map_svi(svi_if, svi_if_zif->link); + if (svi_if_zif) { + svi_if_link = if_lookup_by_index_per_ns( + zebra_ns_lookup( + NS_DEFAULT), + svi_if_zif->link_ifindex); + zvni = zvni_map_svi(svi_if, svi_if_link); + } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { + /* + * If it is a vlan unaware bridge then svi is the bridge + * itself + */ zvni = zvni_map_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = - NULL; /* Zebra daemon specific info for SVI*/ + NULL; /* Zebra daemon specific info for SVI */ + struct interface *svi_if_link = + NULL; /* link info for the SVI = bridge info */ svi_if_zif = ifp->info; - if (svi_if_zif) - zvni = zvni_map_svi(ifp, svi_if_zif->link); + svi_if_link = + if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + svi_if_zif->link_ifindex); + if (svi_if_zif && svi_if_link) + zvni = zvni_map_svi(ifp, svi_if_link); } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { zvni = zvni_map_svi(ifp, ifp); } @@ -4145,7 +4219,7 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) vxl->access_vlan, inet_ntoa(vxl->vtep_ip), zif->brslave_info.bridge_ifindex, chgflags); - /* Removed from bridge? */ + /* Removed from bridge? Cleanup and return */ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ @@ -4154,7 +4228,11 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags) zvni_neigh_del_all(zvrf, zvni, 1, 0, DEL_ALL_NEIGH); zvni_mac_del_all(zvrf, zvni, 1, 0, DEL_ALL_MAC); zvni_vtep_del_all(zvni, 1); - } else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { + return 0; + } + + /* Handle other changes. */ + if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { /* Remove all existing local neighbors and MACs for this VNI * (including from BGP) */ @@ -4272,6 +4350,7 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock, int advertise; vni_t vni = 0; zebra_vni_t *zvni = NULL; + struct interface *ifp = NULL; s = client->ibuf; advertise = stream_getc(s); @@ -4322,7 +4401,16 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock, zvni->advertise_gw_macip = advertise; - zif = zvni->vxlan_if->info; + ifp = zvni->vxlan_if; + if (!ifp) + return 0; + + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return 0; + zl2_info = zif->l2info.vxl; vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan, diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 8539311eab..fa7d0e9457 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -114,8 +114,6 @@ struct zebra_mac_t_ { struct in_addr r_vtep_ip; } fwd_info; - u_int32_t neigh_refcnt; - /* List of neigh associated with this mac */ struct list *neigh_list; }; diff --git a/zebra/zserv.c b/zebra/zserv.c index 98494cff07..d23e902e9b 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -626,6 +626,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, api_nh->type = nexthop->type; switch (nexthop->type) { case NEXTHOP_TYPE_BLACKHOLE: + api_nh->bh_type = nexthop->bh_type; break; case NEXTHOP_TYPE_IPV4: api_nh->gate.ipv4 = nexthop->gate.ipv4; @@ -1093,7 +1094,8 @@ static int zread_route_add(struct zserv *client, u_short length, api_nh->ifindex); break; case NEXTHOP_TYPE_BLACKHOLE: - route_entry_nexthop_blackhole_add(re); + route_entry_nexthop_blackhole_add(re, + api_nh->bh_type); break; } @@ -1163,7 +1165,7 @@ static int zread_route_del(struct zserv *client, u_short length, src_p = &api.src_prefix; rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &api.prefix, src_p, NULL, 0, zvrf->table_id, + api.flags, &api.prefix, src_p, NULL, zvrf->table_id, api.metric); /* Stats */ @@ -1198,9 +1200,10 @@ static int zread_ipv4_add(struct zserv *client, u_short length, ifindex_t ifindex; safi_t safi; int ret; - enum lsp_types_t label_type; + enum lsp_types_t label_type = ZEBRA_LSP_NONE; mpls_label_t label; struct nexthop *nexthop; + enum blackhole_type bh_type = BLACKHOLE_NULL; /* Get input stream. */ s = client->ibuf; @@ -1264,7 +1267,7 @@ static int zread_ipv4_add(struct zserv *client, u_short length, stream_forward_getp(s, IPV6_MAX_BYTELEN); break; case NEXTHOP_TYPE_BLACKHOLE: - route_entry_nexthop_blackhole_add(re); + route_entry_nexthop_blackhole_add(re, bh_type); break; } } @@ -1329,7 +1332,7 @@ static int zread_ipv4_delete(struct zserv *client, u_short length, table_id = zvrf->table_id; rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, NULL, NULL, 0, table_id, 0); + api.flags, &p, NULL, NULL, table_id, 0); client->v4_route_del_cnt++; return 0; } @@ -1367,6 +1370,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, enum lsp_types_t label_type = ZEBRA_LSP_NONE; mpls_label_t label; struct nexthop *nexthop; + enum blackhole_type bh_type = BLACKHOLE_NULL; /* Get input stream. */ s = client->ibuf; @@ -1434,7 +1438,7 @@ static int zread_ipv4_route_ipv6_nexthop_add(struct zserv *client, } break; case NEXTHOP_TYPE_BLACKHOLE: - route_entry_nexthop_blackhole_add(re); + route_entry_nexthop_blackhole_add(re, bh_type); break; } } @@ -1516,6 +1520,7 @@ static int zread_ipv6_add(struct zserv *client, u_short length, enum lsp_types_t label_type = ZEBRA_LSP_NONE; mpls_label_t label; struct nexthop *nexthop; + enum blackhole_type bh_type = BLACKHOLE_NULL; /* Get input stream. */ s = client->ibuf; @@ -1594,7 +1599,7 @@ static int zread_ipv6_add(struct zserv *client, u_short length, } break; case NEXTHOP_TYPE_BLACKHOLE: - route_entry_nexthop_blackhole_add(re); + route_entry_nexthop_blackhole_add(re, bh_type); break; } } @@ -1689,7 +1694,7 @@ static int zread_ipv6_delete(struct zserv *client, u_short length, src_pp = NULL; rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &p, src_pp, NULL, 0, client->rtm_table, 0); + api.flags, &p, src_pp, NULL, client->rtm_table, 0); client->v6_route_del_cnt++; return 0; |
