From e2863b4ff5963f1794ea176e9182bb235356d165 Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Tue, 24 Jan 2023 11:02:24 +0200 Subject: [PATCH] bgpd: Add `neighbor path-attribute treat-as-withdraw` command To filter out routes with unwanted prefixes. Signed-off-by: Donatas Abraitis --- bgpd/bgp_attr.c | 172 +++++++++++++++++++++++++++++------------------- bgpd/bgp_attr.h | 4 ++ bgpd/bgp_vty.c | 69 +++++++++++++++++++ bgpd/bgpd.c | 20 ++++++ bgpd/bgpd.h | 5 ++ 5 files changed, 203 insertions(+), 67 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 392b558805..572475f068 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1864,7 +1864,7 @@ static int bgp_attr_atomic(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto atomic_ignore; /* Set atomic aggregate flag. */ @@ -1875,11 +1875,7 @@ static int bgp_attr_atomic(struct bgp_attr_parser_args *args) atomic_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Aggregator attribute */ @@ -1905,7 +1901,7 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto aggregator_ignore; if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) @@ -1938,11 +1934,7 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) aggregator_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* New Aggregator attribute */ @@ -1963,7 +1955,7 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, 0); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto as4_aggregator_ignore; aggregator_as = stream_getl(peer->curr); @@ -1993,11 +1985,7 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, as4_aggregator_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH. @@ -2117,7 +2105,7 @@ bgp_attr_community(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto community_ignore; bgp_attr_set_community( @@ -2139,11 +2127,7 @@ bgp_attr_community(struct bgp_attr_parser_args *args) community_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Originator ID attribute. */ @@ -2167,7 +2151,7 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto originator_id_ignore; attr->originator_id.s_addr = stream_get_ipv4(peer->curr); @@ -2179,11 +2163,7 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args) originator_id_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Cluster list attribute. */ @@ -2206,7 +2186,7 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto cluster_list_ignore; bgp_attr_set_cluster( @@ -2223,11 +2203,7 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) cluster_list_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Multiprotocol reachability information parse. */ @@ -2487,7 +2463,7 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto large_community_ignore; bgp_attr_set_lcommunity( @@ -2504,11 +2480,7 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args) large_community_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Extended Community attribute. */ @@ -2600,7 +2572,7 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto ipv6_ext_community_ignore; ipv6_ecomm = ecommunity_parse_ipv6( @@ -2621,11 +2593,7 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) ipv6_ext_community_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* Parse Tunnel Encap attribute in an UPDATE */ @@ -3300,7 +3268,7 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args) goto aigp_ignore; } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto aigp_ignore; if (!bgp_attr_aigp_valid(s, length)) @@ -3313,11 +3281,7 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args) aigp_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* OTC attribute. */ @@ -3335,7 +3299,7 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args) args->total); } - if (peer->discard_attrs[args->type]) + if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type]) goto otc_ignore; attr->otc = stream_getl(peer->curr); @@ -3352,11 +3316,7 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args) otc_ignore: stream_forward_getp(peer->curr, length); - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_ignore(peer, args->type); } /* BGP unknown attribute treatment. */ @@ -3380,13 +3340,8 @@ bgp_attr_unknown(struct bgp_attr_parser_args *args) /* Forward read pointer of input stream. */ stream_forward_getp(peer->curr, length); - if (peer->discard_attrs[type]) { - if (bgp_debug_update(peer, NULL, NULL, 1)) - zlog_debug("%pBP: Ignoring attribute %s", peer, - lookup_msg(attr_str, args->type, NULL)); - - return BGP_ATTR_PARSE_PROCEED; - } + if (peer->discard_attrs[type] || peer->withdraw_attrs[type]) + return bgp_attr_ignore(peer, type); /* If any of the mandatory well-known attributes are not recognized, then the Error Subcode is set to Unrecognized Well-known @@ -5165,3 +5120,86 @@ void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer, peer_clear_soft(peer, afi, safi, BGP_CLEAR_SOFT_IN); } } + +void bgp_path_attribute_withdraw_vty(struct vty *vty, struct peer *peer, + const char *withdraw_attrs, bool set) +{ + int i, num_attributes; + char **attributes; + afi_t afi; + safi_t safi; + + /* If `no` command specified without arbitrary attributes, + * then flush all. + */ + if (!withdraw_attrs) { + for (i = 0; i < BGP_ATTR_MAX; i++) + peer->withdraw_attrs[i] = false; + goto withdraw_soft_clear; + } + + if (withdraw_attrs) { + frrstr_split(withdraw_attrs, " ", &attributes, &num_attributes); + + if (set) + for (i = 0; i < BGP_ATTR_MAX; i++) + peer->withdraw_attrs[i] = false; + + for (i = 0; i < num_attributes; i++) { + uint8_t attr_num = strtoul(attributes[i], NULL, 10); + + XFREE(MTYPE_TMP, attributes[i]); + + /* Some of the attributes, just can't be ignored. */ + if (attr_num == BGP_ATTR_ORIGIN || + attr_num == BGP_ATTR_AS_PATH || + attr_num == BGP_ATTR_NEXT_HOP || + attr_num == BGP_ATTR_MULTI_EXIT_DISC || + attr_num == BGP_ATTR_MP_REACH_NLRI || + attr_num == BGP_ATTR_MP_UNREACH_NLRI || + attr_num == BGP_ATTR_EXT_COMMUNITIES) { + vty_out(vty, + "%% Can't treat-as-withdraw path-attribute %s, ignoring.\n", + lookup_msg(attr_str, attr_num, NULL)); + continue; + } + + /* Ignore local-pref, originator-id, cluster-list only + * for eBGP. + */ + if (peer->sort != BGP_PEER_EBGP && + (attr_num == BGP_ATTR_LOCAL_PREF || + attr_num == BGP_ATTR_ORIGINATOR_ID || + attr_num == BGP_ATTR_CLUSTER_LIST)) { + vty_out(vty, + "%% Can treat-as-withdraw path-attribute %s only for eBGP, ignoring.\n", + lookup_msg(attr_str, attr_num, NULL)); + continue; + } + + peer->withdraw_attrs[attr_num] = set; + } + XFREE(MTYPE_TMP, attributes); + withdraw_soft_clear: + /* Configuring path attributes to be treated as withdraw will + * trigger + * an inbound Route Refresh to ensure that the routing table + * is up to date. + */ + FOREACH_AFI_SAFI (afi, safi) + peer_clear_soft(peer, afi, safi, BGP_CLEAR_SOFT_IN); + } +} + +enum bgp_attr_parse_ret bgp_attr_ignore(struct peer *peer, uint8_t type) +{ + bool discard = peer->discard_attrs[type]; + bool withdraw = peer->withdraw_attrs[type]; + + if (bgp_debug_update(peer, NULL, NULL, 1) && (discard || withdraw)) + zlog_debug("%pBP: Ignoring attribute %s (%s)", peer, + lookup_msg(attr_str, type, NULL), + withdraw ? "treat-as-withdraw" : "discard"); + + return withdraw ? BGP_ATTR_PARSE_WITHDRAW : BGP_ATTR_PARSE_PROCEED; +} diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 33283f4bf6..e3499079d5 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -416,6 +416,10 @@ extern unsigned long int attr_count(void); extern unsigned long int attr_unknown_count(void); extern void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer, const char *discard_attrs, bool set); +extern void bgp_path_attribute_withdraw_vty(struct vty *vty, struct peer *peer, + const char *withdraw_attrs, + bool set); +extern enum bgp_attr_parse_ret bgp_attr_ignore(struct peer *peer, uint8_t type); /* Cluster list prototypes. */ extern bool cluster_loop_check(struct cluster_list *cluster, diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index d837601f32..0ba3c760c3 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -8819,6 +8819,59 @@ DEFPY(no_neighbor_path_attribute_discard, return CMD_SUCCESS; } +DEFPY(neighbor_path_attribute_treat_as_withdraw, + neighbor_path_attribute_treat_as_withdraw_cmd, + "neighbor $neighbor path-attribute treat-as-withdraw (1-255)...", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Manipulate path attributes from incoming UPDATE messages\n" + "Treat-as-withdraw any incoming BGP UPDATE messages that contain the specified attribute\n" + "Attribute number\n") +{ + struct peer *peer; + int idx = 0; + const char *withdraw_attrs = NULL; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + argv_find(argv, argc, "(1-255)", &idx); + if (idx) + withdraw_attrs = argv_concat(argv, argc, idx); + + bgp_path_attribute_withdraw_vty(vty, peer, withdraw_attrs, true); + + return CMD_SUCCESS; +} + +DEFPY(no_neighbor_path_attribute_treat_as_withdraw, + no_neighbor_path_attribute_treat_as_withdraw_cmd, + "no neighbor $neighbor path-attribute treat-as-withdraw (1-255)...", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Manipulate path attributes from incoming UPDATE messages\n" + "Treat-as-withdraw any incoming BGP UPDATE messages that contain the specified attribute\n" + "Attribute number\n") +{ + struct peer *peer; + int idx = 0; + const char *withdraw_attrs = NULL; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + argv_find(argv, argc, "(1-255)", &idx); + if (idx) + withdraw_attrs = argv_concat(argv, argc, idx); + + bgp_path_attribute_withdraw_vty(vty, peer, withdraw_attrs, false); + + return CMD_SUCCESS; +} + static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, struct ecommunity **list, bool is_rt6) { @@ -17459,6 +17512,16 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " neighbor %s path-attribute discard %s\n", addr, discard_attrs_str); + /* path-attribute treat-as-withdraw */ + char withdraw_attrs_str[BUFSIZ] = {0}; + bool withdraw_attrs = bgp_path_attribute_treat_as_withdraw( + peer, withdraw_attrs_str, sizeof(withdraw_attrs_str)); + + if (withdraw_attrs) + vty_out(vty, + " neighbor %s path-attribute treat-as-withdraw %s\n", + addr, withdraw_attrs_str); + if (!CHECK_FLAG(peer->peer_gr_new_status_flag, PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { @@ -19570,6 +19633,12 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_path_attribute_discard_cmd); install_element(BGP_NODE, &no_neighbor_path_attribute_discard_cmd); + /* "neighbor path-attribute treat-as-withdraw" commands. */ + install_element(BGP_NODE, + &neighbor_path_attribute_treat_as_withdraw_cmd); + install_element(BGP_NODE, + &no_neighbor_path_attribute_treat_as_withdraw_cmd); + /* "neighbor passive" commands. */ install_element(BGP_NODE, &neighbor_passive_cmd); install_element(BGP_NODE, &no_neighbor_passive_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 39010e76f9..f4e823e212 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4213,6 +4213,26 @@ bool bgp_path_attribute_discard(struct peer *peer, char *buf, size_t size) return false; } +bool bgp_path_attribute_treat_as_withdraw(struct peer *peer, char *buf, + size_t size) +{ + if (!buf) + return false; + + buf[0] = '\0'; + + for (unsigned int i = 0; i < BGP_ATTR_MAX; i++) { + if (peer->withdraw_attrs[i]) + snprintf(buf + strlen(buf), size - strlen(buf), "%s%d", + (strlen(buf) > 0) ? " " : "", i); + } + + if (strlen(buf) > 0) + return true; + + return false; +} + /* If peer is configured at least one address family return 1. */ bool peer_active(struct peer *peer) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 4c4c81f997..8f7040dce6 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1768,6 +1768,9 @@ struct peer { /* Path attributes discard */ bool discard_attrs[BGP_ATTR_MAX]; + /* Path attributes treat-as-withdraw */ + bool withdraw_attrs[BGP_ATTR_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(peer); @@ -2620,6 +2623,8 @@ extern void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi, int outbound); extern bool bgp_path_attribute_discard(struct peer *peer, char *buf, size_t size); +extern bool bgp_path_attribute_treat_as_withdraw(struct peer *peer, char *buf, + size_t size); #ifdef _FRR_ATTRIBUTE_PRINTFRR /* clang-format off */ #pragma FRR printfrr_ext "%pBP" (struct peer *) -- 2.39.5