From: Donatas Abraitis Date: Fri, 6 Jan 2023 12:33:03 +0000 (+0200) Subject: bgpd: Add `neighbor path-attribute discard` command X-Git-Tag: base_8.5~67^2 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=a5c6a9b18ec94c01256f2c3abab2bf143a9c3622;p=matthieu%2Ffrr.git bgpd: Add `neighbor path-attribute discard` command The idea is to drop unwanted attributes from the BGP UPDATE messages and continue by just ignoring them. This improves the security, flexiblity, etc. This is the command that Cisco has also. Signed-off-by: Donatas Abraitis --- diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 908c024e9d..4306d3bd12 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -33,6 +33,7 @@ #include "filter.h" #include "command.h" #include "srv6.h" +#include "frrstr.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -1850,6 +1851,7 @@ stream_failure: /* Atomic aggregate. */ static int bgp_attr_atomic(struct bgp_attr_parser_args *args) { + struct peer *const peer = args->peer; struct attr *const attr = args->attr; const bgp_size_t length = args->length; @@ -1862,10 +1864,22 @@ static int bgp_attr_atomic(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto atomic_ignore; + /* Set atomic aggregate flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE); return BGP_ATTR_PARSE_PROCEED; + +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; } /* Aggregator attribute */ @@ -1891,6 +1905,9 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto aggregator_ignore; + if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) aggregator_as = stream_getl(peer->curr); else @@ -1917,6 +1934,15 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) } return BGP_ATTR_PARSE_PROCEED; + +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; } /* New Aggregator attribute */ @@ -1937,6 +1963,9 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, 0); } + if (peer->discard_attrs[args->type]) + goto as4_aggregator_ignore; + aggregator_as = stream_getl(peer->curr); *as4_aggregator_as = aggregator_as; @@ -1960,6 +1989,15 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, } return BGP_ATTR_PARSE_PROCEED; + +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; } /* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH. @@ -2079,6 +2117,9 @@ bgp_attr_community(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto community_ignore; + bgp_attr_set_community( attr, community_parse((uint32_t *)stream_pnt(peer->curr), length)); @@ -2094,6 +2135,15 @@ bgp_attr_community(struct bgp_attr_parser_args *args) args->total); return BGP_ATTR_PARSE_PROCEED; + +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; } /* Originator ID attribute. */ @@ -2117,11 +2167,23 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto originator_id_ignore; + attr->originator_id.s_addr = stream_get_ipv4(peer->curr); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID); return BGP_ATTR_PARSE_PROCEED; + +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; } /* Cluster list attribute. */ @@ -2144,6 +2206,9 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto cluster_list_ignore; + bgp_attr_set_cluster( attr, cluster_parse((struct in_addr *)stream_pnt(peer->curr), length)); @@ -2154,6 +2219,15 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST); return BGP_ATTR_PARSE_PROCEED; + +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; } /* Multiprotocol reachability information parse. */ @@ -2413,6 +2487,9 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto large_community_ignore; + bgp_attr_set_lcommunity( attr, lcommunity_parse(stream_pnt(peer->curr), length)); /* XXX: fix ecommunity_parse to use stream API */ @@ -2423,6 +2500,15 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args) args->total); return BGP_ATTR_PARSE_PROCEED; + +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; } /* Extended Community attribute. */ @@ -2514,6 +2600,9 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto ipv6_ext_community_ignore; + ipv6_ecomm = ecommunity_parse_ipv6( stream_pnt(peer->curr), length, CHECK_FLAG(peer->flags, @@ -2528,6 +2617,15 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) args->total); return BGP_ATTR_PARSE_PROCEED; + +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; } /* Parse Tunnel Encap attribute in an UPDATE */ @@ -3202,6 +3300,9 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args) goto aigp_ignore; } + if (peer->discard_attrs[args->type]) + goto aigp_ignore; + if (!bgp_attr_aigp_valid(s, length)) goto aigp_ignore; @@ -3212,6 +3313,10 @@ 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; } @@ -3230,6 +3335,9 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args) args->total); } + if (peer->discard_attrs[args->type]) + goto otc_ignore; + attr->otc = stream_getl(peer->curr); if (!attr->otc) { flog_err(EC_BGP_ATTR_MAL_AS_PATH, "OTC attribute value is 0"); @@ -3240,6 +3348,15 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args) attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC); return BGP_ATTR_PARSE_PROCEED; + +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; } /* BGP unknown attribute treatment. */ @@ -3263,6 +3380,14 @@ 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 any of the mandatory well-known attributes are not recognized, then the Error Subcode is set to Unrecognized Well-known Attribute. The Data field contains the unrecognized attribute @@ -4970,3 +5095,62 @@ void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi, len = stream_get_endp(s) - cp - 2; stream_putw_at(s, cp, len); } + +void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer, + const char *discard_attrs) +{ + int i, num_attributes; + char **attributes; + afi_t afi; + safi_t safi; + + if (discard_attrs) { + frrstr_split(discard_attrs, " ", &attributes, &num_attributes); + + for (i = 0; i < BGP_ATTR_MAX; i++) + peer->discard_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 discard 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 discard path-attribute %s only for eBGP, ignoring.\n", + lookup_msg(attr_str, attr_num, NULL)); + continue; + } + + peer->discard_attrs[attr_num] = true; + } + XFREE(MTYPE_TMP, attributes); + + /* Configuring path attributes to be discarded 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); + } +} diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index a34da1a6de..cb0bf4a43c 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -414,6 +414,8 @@ extern unsigned int attrhash_key_make(const void *p); extern void attr_show_all(struct vty *vty); 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); /* 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 4acf4f76aa..9724c3533a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -8808,6 +8808,32 @@ DEFPY( return CMD_SUCCESS; } +DEFPY(neighbor_path_attribute_discard, + neighbor_path_attribute_discard_cmd, + "neighbor $neighbor path-attribute discard (1-255)...", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Manipulate path attributes from incoming UPDATE messages\n" + "Drop specified attributes from incoming UPDATE messages\n" + "Attribute number\n") +{ + struct peer *peer; + int idx = 0; + const char *discard_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) + discard_attrs = argv_concat(argv, argc, idx); + + bgp_path_attribute_discard_vty(vty, peer, discard_attrs); + + return CMD_SUCCESS; +} + static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, struct ecommunity **list, bool is_rt6) { @@ -17444,6 +17470,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", addr); + /* path-attribute discard */ + char discard_attrs_str[BUFSIZ] = {0}; + bool discard_attrs = bgp_path_attribute_discard( + peer, discard_attrs_str, sizeof(discard_attrs_str)); + + if (discard_attrs) + vty_out(vty, " neighbor %s path-attribute discard %s\n", addr, + discard_attrs_str); + if (!CHECK_FLAG(peer->peer_gr_new_status_flag, PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { @@ -19559,6 +19594,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd); install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd); + /* "neighbor path-attribute discard" commands. */ + install_element(BGP_NODE, &neighbor_path_attribute_discard_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 9b4aa38d7a..1a4634531c 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4203,6 +4203,25 @@ static void peer_drop_dynamic_neighbor(struct peer *peer) peer->group->name, dncount); } +bool bgp_path_attribute_discard(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->discard_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 8697912314..e162cae0e1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1806,6 +1806,10 @@ struct peer { bool shut_during_cfg; +#define BGP_ATTR_MAX 255 + /* Path attributes discard */ + bool discard_attrs[BGP_ATTR_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(peer); @@ -2669,6 +2673,8 @@ extern void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, safi_t safi); 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); #ifdef _FRR_ATTRIBUTE_PRINTFRR /* clang-format off */ #pragma FRR printfrr_ext "%pBP" (struct peer *)