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++; + } + + for (mpinfo = bgp_info_mpath_first (info); mpinfo; + 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; - 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 *)); - } - 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++; - - 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; - - /* If both global and link-local address present. */ - if (mpinfo->attr->extra->mp_nexthop_len == 32) + /* 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)) { - /* 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; - } + 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 == NULL) - { - continue; - } + if (nexthop) + { + if (info->attr->extra->mp_nexthop_len == 32) + if (info->peer->nexthop.ifp) + ifindex = info->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 (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; + 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 = 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 (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; + + 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