From 73ac816057d972d9d13236fbe1a740167ad26f37 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 19 May 2015 17:40:34 -0700 Subject: [PATCH] bgpd: bgpd-table-map.patch COMMAND: table-map DESCRIPTION: This feature is used to apply a route-map on route updates from BGP to Zebra. All the applicable match operations are allowed, such as match on prefix, next-hop, communities, etc. Set operations for this attach-point are limited to metric and next-hop only. Any operation of this feature does not affect BGPs internal RIB. Supported for ipv4 and ipv6 address families. It works on multi-paths as well, however, metric setting is based on the best-path only. IMPLEMENTATION NOTES: The route-map application at this point is not supposed to modify any of BGP route's attributes (anything in bgp_info for that matter). To achieve that, creating a copy of the bgp_attr was inevitable. Implementation tries to keep the memory footprint low, code comments do point out the rationale behind a few choices made. bgp_zebra_announce() was already a big routine, adding this feature would extend it further. Patch has created a few smaller routines/macros whereever possible to keep the size of the routine in check without compromising on the readability of the code/flow inside this routine. For updating a partially filtered route (with its nexthops), BGP to Zebra replacement semantic of the next-hops serves the purpose well. However, with this patch there could be some redundant withdraws each time BGP announces a route thats (all the nexthops) gets denied by the route-map application. Handling of this case could be optimized by keeping state with the prefix and the nexthops in BGP. The patch doesn't optimizing that case, as even with the redundant withdraws the total number of updates to zebra are still be capped by the total number of routes in the table. Signed-off-by: Vipin Kumar Reviewed-by: Pradosh Mohapatra --- bgpd/bgp_attr.c | 59 ++++++- bgpd/bgp_attr.h | 2 + bgpd/bgp_mpath.h | 3 + bgpd/bgp_route.c | 90 ++++++++++- bgpd/bgp_route.h | 2 + bgpd/bgp_routemap.c | 50 +++++- bgpd/bgp_vty.c | 3 +- bgpd/bgp_zebra.c | 373 +++++++++++++++++++++++++++++++------------- bgpd/bgp_zebra.h | 4 +- bgpd/bgpd.c | 2 + bgpd/bgpd.h | 16 +- doc/bgpd.texi | 11 ++ 12 files changed, 491 insertions(+), 124 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index fcf82551cc..4e38bca200 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -150,7 +150,6 @@ cluster_free (struct cluster_list *cluster) XFREE (MTYPE_CLUSTER, cluster); } -#if 0 static struct cluster_list * cluster_dup (struct cluster_list *cluster) { @@ -169,7 +168,6 @@ cluster_dup (struct cluster_list *cluster) return new; } -#endif static struct cluster_list * cluster_intern (struct cluster_list *cluster) @@ -219,6 +217,23 @@ transit_free (struct transit *transit) XFREE (MTYPE_TRANSIT, transit); } +static struct transit * +transit_dup (struct transit *transit) +{ + struct transit *new; + + new = XCALLOC (MTYPE_TRANSIT, sizeof (struct transit)); + new->length = transit->length; + if (new->length) + { + new->val = XMALLOC (MTYPE_TRANSIT_VAL, transit->length); + memcpy (new->val, transit->val, transit->length); + } + else + new->val = NULL; + + return new; +} static void * transit_hash_alloc (void *p) @@ -343,6 +358,46 @@ bgp_attr_dup (struct attr *new, struct attr *orig) } } +void +bgp_attr_deep_dup (struct attr *new, struct attr *orig) +{ + if (orig->aspath) + new->aspath = aspath_dup(orig->aspath); + + if (orig->community) + new->community = community_dup(orig->community); + + if (orig->extra) + { + if (orig->extra->ecommunity) + new->extra->ecommunity = ecommunity_dup(orig->extra->ecommunity); + if (orig->extra->cluster) + new->extra->cluster = cluster_dup(orig->extra->cluster); + if (orig->extra->transit) + new->extra->transit = transit_dup(orig->extra->transit); + } +} + +void +bgp_attr_deep_free (struct attr *attr) +{ + if (attr->aspath) + aspath_free(attr->aspath); + + if (attr->community) + community_free(attr->community); + + if (attr->extra) + { + if (attr->extra->ecommunity) + ecommunity_free(&attr->extra->ecommunity); + if (attr->extra->cluster) + cluster_free(attr->extra->cluster); + if (attr->extra->transit) + transit_free(attr->extra->transit); + } +} + unsigned long int attr_count (void) { diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 7edf684021..2d796b5b70 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -155,6 +155,8 @@ extern int bgp_attr_check (struct peer *, struct attr *); extern struct attr_extra *bgp_attr_extra_get (struct attr *); extern void bgp_attr_extra_free (struct attr *); extern void bgp_attr_dup (struct attr *, struct attr *); +extern void bgp_attr_deep_dup (struct attr *, struct attr *); +extern void bgp_attr_deep_free (struct attr *); extern struct attr *bgp_attr_intern (struct attr *attr); extern void bgp_attr_unintern_sub (struct attr *); extern void bgp_attr_unintern (struct attr **); diff --git a/bgpd/bgp_mpath.h b/bgpd/bgp_mpath.h index 0645e6c276..0c8137041a 100644 --- a/bgpd/bgp_mpath.h +++ b/bgpd/bgp_mpath.h @@ -24,6 +24,9 @@ #ifndef _QUAGGA_BGP_MPATH_H #define _QUAGGA_BGP_MPATH_H +/* Limit on number of configured maxpaths */ +#define BGP_MAXIMUM_MAXPATHS 255 + /* BGP default maximum-paths */ #define BGP_DEFAULT_MAXPATHS 1 diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index b554e195e2..134c7c07dc 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1598,7 +1598,7 @@ bgp_process_main (struct work_queue *wq, void *data) { if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED) || CHECK_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG)) - bgp_zebra_announce (p, old_select, bgp, safi); + bgp_zebra_announce (p, old_select, bgp, afi, safi); UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG); UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); @@ -1629,7 +1629,7 @@ bgp_process_main (struct work_queue *wq, void *data) if (new_select && new_select->type == ZEBRA_ROUTE_BGP && new_select->sub_type == BGP_ROUTE_NORMAL) - bgp_zebra_announce (p, new_select, bgp, safi); + bgp_zebra_announce (p, new_select, bgp, afi, safi); else { /* Withdraw the route from the kernel. */ @@ -4035,6 +4035,84 @@ bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str, return CMD_SUCCESS; } +static int +bgp_table_map_set (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, + const char *rmap_name) +{ + struct bgp_rmap *rmap; + + rmap = &bgp->table_map[afi][safi]; + if (rmap_name) + { + if (rmap->name) + free (rmap->name); + rmap->name = strdup (rmap_name); + rmap->map = route_map_lookup_by_name (rmap_name); + } + else + { + if (rmap->name) + free (rmap->name); + rmap->name = NULL; + rmap->map = NULL; + } + + bgp_zebra_announce_table(bgp, afi, safi); + + return CMD_SUCCESS; +} + +static int +bgp_table_map_unset (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, + const char *rmap_name) +{ + struct bgp_rmap *rmap; + + rmap = &bgp->table_map[afi][safi]; + if (rmap->name) + free (rmap->name); + rmap->name = NULL; + rmap->map = NULL; + + bgp_zebra_announce_table(bgp, afi, safi); + + return CMD_SUCCESS; +} + +int +bgp_config_write_table_map (struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, int *write) +{ + if (bgp->table_map[afi][safi].name) + { + bgp_config_write_family_header (vty, afi, safi, write); + vty_out (vty, " table-map %s%s", + bgp->table_map[afi][safi].name, VTY_NEWLINE); + } + + return 0; +} + + +DEFUN (bgp_table_map, + bgp_table_map_cmd, + "table-map WORD", + "BGP table to RIB route download filter\n" + "Name of the route map\n") +{ + return bgp_table_map_set (vty, vty->index, + bgp_node_afi (vty), bgp_node_safi (vty), argv[0]); +} +DEFUN (no_bgp_table_map, + no_bgp_table_map_cmd, + "no table-map WORD", + "BGP table to RIB route download filter\n" + "Name of the route map\n") +{ + return bgp_table_map_unset (vty, vty->index, + bgp_node_afi (vty), bgp_node_safi (vty), argv[0]); +} + DEFUN (bgp_network, bgp_network_cmd, "network A.B.C.D/M", @@ -12671,6 +12749,7 @@ bgp_route_init (void) bgp_distance_table = bgp_table_init (AFI_IP, SAFI_UNICAST); /* IPv4 BGP commands. */ + install_element (BGP_NODE, &bgp_table_map_cmd); install_element (BGP_NODE, &bgp_network_cmd); install_element (BGP_NODE, &bgp_network_mask_cmd); install_element (BGP_NODE, &bgp_network_mask_natural_cmd); @@ -12680,6 +12759,7 @@ bgp_route_init (void) install_element (BGP_NODE, &bgp_network_backdoor_cmd); install_element (BGP_NODE, &bgp_network_mask_backdoor_cmd); install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_cmd); + install_element (BGP_NODE, &no_bgp_table_map_cmd); install_element (BGP_NODE, &no_bgp_network_cmd); install_element (BGP_NODE, &no_bgp_network_mask_cmd); install_element (BGP_NODE, &no_bgp_network_mask_natural_cmd); @@ -12712,12 +12792,14 @@ bgp_route_init (void) install_element (BGP_NODE, &no_aggregate_address_mask_summary_as_set_cmd); /* IPv4 unicast configuration. */ + install_element (BGP_IPV4_NODE, &bgp_table_map_cmd); install_element (BGP_IPV4_NODE, &bgp_network_cmd); install_element (BGP_IPV4_NODE, &bgp_network_mask_cmd); install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_cmd); install_element (BGP_IPV4_NODE, &bgp_network_route_map_cmd); install_element (BGP_IPV4_NODE, &bgp_network_mask_route_map_cmd); install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_table_map_cmd); install_element (BGP_IPV4_NODE, &no_bgp_network_cmd); install_element (BGP_IPV4_NODE, &no_bgp_network_mask_cmd); install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_cmd); @@ -12747,12 +12829,14 @@ bgp_route_init (void) install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_summary_as_set_cmd); /* IPv4 multicast configuration. */ + install_element (BGP_IPV4M_NODE, &bgp_table_map_cmd); install_element (BGP_IPV4M_NODE, &bgp_network_cmd); install_element (BGP_IPV4M_NODE, &bgp_network_mask_cmd); install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_cmd); install_element (BGP_IPV4M_NODE, &bgp_network_route_map_cmd); install_element (BGP_IPV4M_NODE, &bgp_network_mask_route_map_cmd); install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_table_map_cmd); install_element (BGP_IPV4M_NODE, &no_bgp_network_cmd); install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_cmd); install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_cmd); @@ -13026,8 +13110,10 @@ bgp_route_init (void) install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_prefix_counts_cmd); /* New config IPv6 BGP commands. */ + install_element (BGP_IPV6_NODE, &bgp_table_map_cmd); install_element (BGP_IPV6_NODE, &ipv6_bgp_network_cmd); install_element (BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd); + install_element (BGP_IPV6_NODE, &no_bgp_table_map_cmd); install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd); install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_route_map_cmd); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index b50a0bab3a..7f31a38204 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -237,6 +237,8 @@ extern int bgp_withdraw (struct peer *, struct prefix *, struct attr *, /* for bgp_nexthop and bgp_damp */ extern void bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t); +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 *); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 24f3af767a..a2c740078c 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -45,6 +45,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_attr.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_zebra.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_clist.h" @@ -52,6 +53,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_debug.h" /* Memo of route-map commands. @@ -2409,6 +2411,23 @@ bgp_route_map_update (const char *unused) } } + /* For table route-map updates. */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (bgp->table_map[afi][safi].name) + { + bgp->table_map[afi][safi].map = + route_map_lookup_by_name (bgp->table_map[afi][safi].name); + bgp_zebra_announce_table(bgp, afi, safi); + } + else + bgp->table_map[afi][safi].map = NULL; + } + } + /* For network route-map updates. */ for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) { @@ -2443,6 +2462,32 @@ bgp_route_map_update (const char *unused) } } +static void +bgp_route_map_add (const char *unused) +{ + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("received route-map add"); + + bgp_route_map_update(unused); +} +static void +bgp_route_map_delete (const char *unused) +{ + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("received route-map delete"); + + bgp_route_map_update(unused); +} +static void +bgp_route_map_event (route_map_event_t event, const char *unused) +{ + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("received route-map event"); + + bgp_route_map_update(unused); +} + + DEFUN (match_peer, match_peer_cmd, "match peer (A.B.C.D|X:X::X:X)", @@ -3897,8 +3942,9 @@ bgp_route_map_init (void) { route_map_init (); route_map_init_vty (); - route_map_add_hook (bgp_route_map_update); - route_map_delete_hook (bgp_route_map_update); + route_map_add_hook (bgp_route_map_add); + route_map_delete_hook (bgp_route_map_delete); + route_map_event_hook (bgp_route_map_event); route_map_install_match (&route_match_peer_cmd); route_map_install_match (&route_match_ip_address_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 93797aa089..fc93c81212 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -672,7 +672,8 @@ bgp_maxpaths_config_vty (struct vty *vty, int peer_type, char *mpaths, if (set) { - VTY_GET_INTEGER_RANGE ("maximum-paths", maxpaths, mpaths, 1, 255); + VTY_GET_INTEGER_RANGE ("maximum-paths", maxpaths, mpaths, 1, + BGP_MAXIMUM_MAXPATHS); ret = bgp_maximum_paths_set (bgp, afi, safi, peer_type, maxpaths, options); } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 6ba4f4bf2b..bea28bee55 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -48,6 +48,43 @@ struct in_addr router_id_zebra; struct stream *bgp_nexthop_buf = NULL; struct stream *bgp_ifindices_buf = NULL; +/* These array buffers are used in making a copy of the attributes for + route-map apply. Arrays are being used here to minimize mallocs and + frees for the temporary copy of the attributes. + Given the zapi api expects the nexthop buffer to contain pointer to + pointers for nexthops, we couldnt have used a single nexthop variable + on the stack, hence we had two options: + 1. maintain a linked-list and free it after zapi_*_route call + 2. use an array to avoid number of mallocs. + Number of supported next-hops are finite, use of arrays should be ok. */ +struct attr attr_cp[BGP_MAXIMUM_MAXPATHS]; +struct attr_extra attr_extra_cp[BGP_MAXIMUM_MAXPATHS]; +int attr_index = 0; + +/* Once per address-family initialization of the attribute array */ +#define BGP_INFO_ATTR_BUF_INIT()\ +do {\ + memset(attr_cp, 0, BGP_MAXIMUM_MAXPATHS * sizeof(struct attr));\ + memset(attr_extra_cp, 0, BGP_MAXIMUM_MAXPATHS * sizeof(struct attr_extra));\ + attr_index = 0;\ +} while (0) + +#define BGP_INFO_ATTR_BUF_COPY(info_src, info_dst)\ +do { \ + *info_dst = *info_src; \ + assert(attr_index != BGP_MAXIMUM_MAXPATHS);\ + attr_cp[attr_index].extra = &attr_extra_cp[attr_index]; \ + bgp_attr_dup (&attr_cp[attr_index], info_src->attr); \ + bgp_attr_deep_dup (&attr_cp[attr_index], info_src->attr); \ + info_dst->attr = &attr_cp[attr_index]; \ + attr_index++;\ +} while (0) + +#define BGP_INFO_ATTR_BUF_FREE(info) \ +do { \ + bgp_attr_deep_free(info->attr); \ +} while (0) + /* Router-id update message from zebra. */ static int bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length) @@ -671,15 +708,72 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, return ret; } +static struct in6_addr * +bgp_info_to_ipv6_nexthop (struct bgp_info *info) +{ + struct in6_addr *nexthop = NULL; + + /* Only global address nexthop exists. */ + if (info->attr->extra->mp_nexthop_len == 16) + nexthop = &info->attr->extra->mp_nexthop_global; + + /* If both global and link-local address present. */ + if (info->attr->extra->mp_nexthop_len == 32) + { + /* Workaround for Cisco's nexthop bug. */ + if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global) + && info->peer->su_remote->sa.sa_family == AF_INET6) + nexthop = &info->peer->su_remote->sin6.sin6_addr; + else + nexthop = &info->attr->extra->mp_nexthop_local; + } + + return nexthop; +} + +static int +bgp_table_map_apply (struct route_map *map, struct prefix *p, + struct bgp_info *info) +{ + if (route_map_apply(map, p, RMAP_BGP, info) != RMAP_DENYMATCH) + return 1; + + if (BGP_DEBUG(zebra, ZEBRA)) + { + if (p->family == AF_INET) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("Zebra rmap deny: IPv4 route %s/%d nexthop %s", + inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), + p->prefixlen, + inet_ntop(AF_INET, &info->attr->nexthop, buf[1], + sizeof(buf[1]))); + } + if (p->family == AF_INET6) + { + char buf[2][INET6_ADDRSTRLEN]; + zlog_debug("Zebra rmap deny: IPv6 route %s/%d nexthop %s", + inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), + p->prefixlen, + inet_ntop(AF_INET6, bgp_info_to_ipv6_nexthop(info), buf[1], + sizeof(buf[1]))); + } + } + return 0; +} + void -bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, safi_t safi) +bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, + afi_t afi, safi_t safi) { int flags; u_char distance; struct peer *peer; struct bgp_info *mpinfo; size_t oldsize, newsize; - u_int32_t nhcount; + u_int32_t nhcount, metric; + struct bgp_info local_info; + struct bgp_info *info_cp = &local_info; if (zclient->sock < 0) return; @@ -706,6 +800,8 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa { struct zapi_ipv4 api; struct in_addr *nexthop; + char buf[2][INET_ADDRSTRLEN]; + int valid_nh_count = 0; /* resize nexthop buffer size if necessary */ if ((oldsize = stream_get_size (bgp_nexthop_buf)) < @@ -720,26 +816,72 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa } } stream_reset (bgp_nexthop_buf); + nexthop = NULL; + + /* Metric is currently based on the best-path only. */ + metric = info->attr->med; + + if (bgp->table_map[afi][safi].name) + { + BGP_INFO_ATTR_BUF_INIT(); + + /* Copy info and attributes, so the route-map apply doesn't modify the + BGP route info. */ + BGP_INFO_ATTR_BUF_COPY(info, info_cp); + if (bgp_table_map_apply(bgp->table_map[afi][safi].map, p, info_cp)) + { + metric = info_cp->attr->med; + nexthop = &info_cp->attr->nexthop; + } + BGP_INFO_ATTR_BUF_FREE(info_cp); + } + else + { + nexthop = &info->attr->nexthop; + } + + if (nexthop) + { + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); + valid_nh_count++; + } - api.flags = flags; - nexthop = &info->attr->nexthop; - stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); for (mpinfo = bgp_info_mpath_first (info); mpinfo; - mpinfo = bgp_info_mpath_next (mpinfo)) - { - nexthop = &mpinfo->attr->nexthop; - stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); - } + mpinfo = bgp_info_mpath_next (mpinfo)) + { + nexthop = NULL; + if (bgp->table_map[afi][safi].name) + { + /* Copy info and attributes, so the route-map apply doesn't modify the + BGP route info. */ + BGP_INFO_ATTR_BUF_COPY(mpinfo, info_cp); + if (bgp_table_map_apply(bgp->table_map[afi][safi].map, p, info_cp)) + nexthop = &info_cp->attr->nexthop; + BGP_INFO_ATTR_BUF_FREE(info_cp); + } + else + { + nexthop = &mpinfo->attr->nexthop; + } + + if (nexthop == NULL) + continue; + + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); + valid_nh_count++; + } + + api.flags = flags; api.type = ZEBRA_ROUTE_BGP; api.message = 0; api.safi = safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = nhcount; + api.nexthop_num = valid_nh_count; api.nexthop = (struct in_addr **)STREAM_DATA (bgp_nexthop_buf); api.ifindex_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = info->attr->med; + api.metric = metric; distance = bgp_distance_apply (p, info, bgp); @@ -750,23 +892,19 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa } if (BGP_DEBUG(zebra, ZEBRA)) - { - int i; - char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Zebra send: IPv4 route add %s/%d nexthop %s metric %u" - " count %d", - inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), - p->prefixlen, - inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])), - api.metric, api.nexthop_num); - for (i = 1; i < api.nexthop_num; i++) - zlog_debug("Zebra send: IPv4 route add [nexthop %d] %s", - i, inet_ntop(AF_INET, api.nexthop[i], buf[1], - sizeof(buf[1]))); - } + { + int i; + zlog_debug("Zebra send: IPv4 route %s %s/%d metric %u" + " count %d", (valid_nh_count ? "add":"delete"), + inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), + p->prefixlen, api.metric, api.nexthop_num); + for (i = 0; i < api.nexthop_num; i++) + zlog_debug(" IPv4 [nexthop %d] %s", i+1, + inet_ntop(AF_INET, api.nexthop[i], buf[1], sizeof(buf[1]))); + } - zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, - (struct prefix_ipv4 *) p, &api); + zapi_ipv4_route (valid_nh_count ? ZEBRA_IPV4_ROUTE_ADD: ZEBRA_IPV4_ROUTE_DELETE, + zclient, (struct prefix_ipv4 *) p, &api); } #ifdef HAVE_IPV6 @@ -777,6 +915,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa struct in6_addr *nexthop; struct zapi_ipv6 api; int valid_nh_count = 0; + char buf[2][INET6_ADDRSTRLEN]; /* resize nexthop buffer size if necessary */ if ((oldsize = stream_get_size (bgp_nexthop_buf)) < @@ -810,94 +949,86 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa nexthop = NULL; assert (info->attr->extra); - - /* Only global address nexthop exists. */ - if (info->attr->extra->mp_nexthop_len == 16) - nexthop = &info->attr->extra->mp_nexthop_global; - - /* If both global and link-local address present. */ - if (info->attr->extra->mp_nexthop_len == 32) - { - /* Workaround for Cisco's nexthop bug. */ - if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global) - && peer->su_remote->sa.sa_family == AF_INET6) - nexthop = &peer->su_remote->sin6.sin6_addr; - else - nexthop = &info->attr->extra->mp_nexthop_local; - if (info->peer->nexthop.ifp) - ifindex = info->peer->nexthop.ifp->ifindex; - } + /* Metric is currently based on the best-path only. */ + metric = info->attr->med; - if (nexthop == NULL) - return; + if (bgp->table_map[afi][safi].name) + { + BGP_INFO_ATTR_BUF_INIT(); - if (!ifindex) - { - if (info->peer->ifname) - ifindex = if_nametoindex (info->peer->ifname); - else if (info->peer->nexthop.ifp) - ifindex = info->peer->nexthop.ifp->ifindex; - } - stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); - stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); - valid_nh_count++; + /* Copy info and attributes, so the route-map apply doesn't modify the + BGP route info. */ + BGP_INFO_ATTR_BUF_COPY(info, info_cp); + if (bgp_table_map_apply(bgp->table_map[afi][safi].map, p, info_cp)) + { + metric = info_cp->attr->med; + nexthop = bgp_info_to_ipv6_nexthop(info_cp); + } + BGP_INFO_ATTR_BUF_FREE(info_cp); + } + else + { + nexthop = bgp_info_to_ipv6_nexthop(info); + } + + if (nexthop) + { + if (info->attr->extra->mp_nexthop_len == 32) + if (info->peer->nexthop.ifp) + ifindex = info->peer->nexthop.ifp->ifindex; + + if (!ifindex) + if (info->peer->ifname) + ifindex = if_nametoindex (info->peer->ifname); + else if (info->peer->nexthop.ifp) + ifindex = info->peer->nexthop.ifp->ifindex; + + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); + stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); + valid_nh_count++; + } for (mpinfo = bgp_info_mpath_first (info); mpinfo; mpinfo = bgp_info_mpath_next (mpinfo)) - { - ifindex = 0; - - /* Only global address nexthop exists. */ - if (mpinfo->attr->extra->mp_nexthop_len == 16) - nexthop = &mpinfo->attr->extra->mp_nexthop_global; + { + ifindex = 0; + nexthop = NULL; - /* If both global and link-local address present. */ - if (mpinfo->attr->extra->mp_nexthop_len == 32) + if (bgp->table_map[afi][safi].name) { - /* Workaround for Cisco's nexthop bug. */ - if (IN6_IS_ADDR_UNSPECIFIED (&mpinfo->attr->extra->mp_nexthop_global) - && mpinfo->peer->su_remote->sa.sa_family == AF_INET6) - { - nexthop = &mpinfo->peer->su_remote->sin6.sin6_addr; - } - else - { - nexthop = &mpinfo->attr->extra->mp_nexthop_local; - } - - if (mpinfo->peer->nexthop.ifp) - { - ifindex = mpinfo->peer->nexthop.ifp->ifindex; - } + /* Copy info and attributes, so the route-map apply doesn't modify the + BGP route info. */ + BGP_INFO_ATTR_BUF_COPY(mpinfo, info_cp); + if (bgp_table_map_apply(bgp->table_map[afi][safi].map, p, info_cp)) + nexthop = bgp_info_to_ipv6_nexthop(info_cp); + BGP_INFO_ATTR_BUF_FREE(info_cp); + } + else + { + nexthop = bgp_info_to_ipv6_nexthop(mpinfo); } - if (nexthop == NULL) - { - continue; - } + if (nexthop == NULL) + continue; + + if (mpinfo->attr->extra->mp_nexthop_len == 32) + if (mpinfo->peer->nexthop.ifp) + ifindex = mpinfo->peer->nexthop.ifp->ifindex; if (!ifindex) - { - if (mpinfo->peer->ifname) - { - ifindex = if_nametoindex (mpinfo->peer->ifname); - } - else if (mpinfo->peer->nexthop.ifp) - { - ifindex = mpinfo->peer->nexthop.ifp->ifindex; - } - } - - if (ifindex == 0) - { - continue; - } + if (mpinfo->peer->ifname) + ifindex = if_nametoindex (mpinfo->peer->ifname); + else if (mpinfo->peer->nexthop.ifp) + ifindex = mpinfo->peer->nexthop.ifp->ifindex; + + if (ifindex == 0) + continue; stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); valid_nh_count++; - } + } /* Make Zebra API structure. */ api.flags = flags; @@ -911,24 +1042,44 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa api.ifindex_num = valid_nh_count; api.ifindex = (unsigned int *)STREAM_DATA (bgp_ifindices_buf); SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = info->attr->med; + api.metric = metric; if (BGP_DEBUG(zebra, ZEBRA)) - { - char buf[2][INET6_ADDRSTRLEN]; - zlog_debug("Zebra send: IPv6 route add %s/%d nexthop %s metric %u", - inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), - p->prefixlen, - inet_ntop(AF_INET6, nexthop, buf[1], sizeof(buf[1])), - api.metric); - } + { + int i; + zlog_debug("Zebra send: IPv6 route %s %s/%d metric %u", + valid_nh_count ? "add" : "delete", + inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), + p->prefixlen, api.metric); + for (i = 0; i < api.nexthop_num; i++) + zlog_debug(" IPv6 [nexthop %d] %s", i+1, + inet_ntop(AF_INET6, api.nexthop[i], buf[1], sizeof(buf[1]))); + } - zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, - (struct prefix_ipv6 *) p, &api); + zapi_ipv6_route (valid_nh_count ? ZEBRA_IPV6_ROUTE_ADD : ZEBRA_IPV6_ROUTE_DELETE, + zclient, (struct prefix_ipv6 *) p, &api); } #endif /* HAVE_IPV6 */ } +/* Announce all routes of a table to zebra */ +void +bgp_zebra_announce_table (struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_table *table; + struct bgp_info *ri; + + table = bgp->rib[afi][safi]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + for (ri = rn->info; ri; ri = ri->next) + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL) + bgp_zebra_announce (&rn->p, ri, bgp, afi, safi); +} + void bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) { diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 466758ec3d..34e1216706 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -33,7 +33,9 @@ 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_zebra_announce (struct prefix *, struct bgp_info *, struct bgp *, safi_t); +extern void bgp_zebra_announce (struct prefix *, struct bgp_info *, struct bgp *, + afi_t, safi_t); +extern void bgp_zebra_announce_table (struct bgp *, afi_t, safi_t); extern void bgp_zebra_withdraw (struct prefix *, struct bgp_info *, safi_t); extern int bgp_redistribute_set (struct bgp *, afi_t, int); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 3c2f82c6be..2635b729b9 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -5196,6 +5196,7 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi, } bgp_config_write_maxpaths (vty, bgp, afi, safi, &write); + bgp_config_write_table_map (vty, bgp, afi, safi, &write); if (write) vty_out (vty, " exit-address-family%s", VTY_NEWLINE); @@ -5378,6 +5379,7 @@ bgp_config_write (struct vty *vty) /* maximum-paths */ bgp_config_write_maxpaths (vty, bgp, AFI_IP, SAFI_UNICAST, &write); + bgp_config_write_table_map (vty, bgp, AFI_IP, SAFI_UNICAST, &write); /* Distance configuration. */ bgp_config_write_distance (vty, bgp); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 4fb6afefc0..b2d421ed1f 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -62,6 +62,13 @@ struct bgp_master #define BGP_OPT_NO_LISTEN (1 << 3) }; +/* BGP route-map structure. */ +struct bgp_rmap +{ + char *name; + struct route_map *map; +}; + /* BGP instance structure. */ struct bgp { @@ -153,6 +160,9 @@ struct bgp /* BGP routing information base. */ struct bgp_table *rib[AFI_MAX][SAFI_MAX]; + /* BGP table route-map. */ + struct bgp_rmap table_map[AFI_MAX][SAFI_MAX]; + /* BGP redistribute configuration. */ u_char redist[AFI_MAX][ZEBRA_ROUTE_MAX]; @@ -161,11 +171,7 @@ struct bgp u_int32_t redist_metric[AFI_MAX][ZEBRA_ROUTE_MAX]; /* BGP redistribute route-map. */ - struct - { - char *name; - struct route_map *map; - } rmap[AFI_MAX][ZEBRA_ROUTE_MAX]; + struct bgp_rmap rmap[AFI_MAX][ZEBRA_ROUTE_MAX]; /* BGP distance configuration. */ u_char distance_ebgp; diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 10fe13ca22..5dd4ed616d 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -247,6 +247,17 @@ and generates updates to its peers. Default max-delay is 0, i.e. the feature is off by default. @end deffn +@deffn {BGP} {table-map @var{route-map-name}} {} +This feature is used to apply a route-map on route updates from BGP to Zebra. +All the applicable match operations are allowed, such as match on prefix, +next-hop, communities, etc. Set operations for this attach-point are limited +to metric and next-hop only. Any operation of this feature does not affect +BGPs internal RIB. + +Supported for ipv4 and ipv6 address families. It works on multi-paths as well, +however, metric setting is based on the best-path only. +@end deffn + @node BGP Peer @section BGP Peer -- 2.39.5