+// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP attributes management routines.
* Copyright (C) 1996, 97, 98, 1999 Kunihiro Ishiguro
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra 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, or (at your option) any
- * later version.
- *
- * GNU Zebra 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 "filter.h"
#include "command.h"
#include "srv6.h"
+#include "frrstr.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
&& attr1->med == attr2->med
&& attr1->local_pref == attr2->local_pref
&& attr1->rmap_change_flags == attr2->rmap_change_flags) {
- if (attr1->aggregator_as == attr2->aggregator_as &&
- attr1->aggregator_addr.s_addr ==
- attr2->aggregator_addr.s_addr &&
- attr1->weight == attr2->weight &&
- attr1->tag == attr2->tag &&
- attr1->label_index == attr2->label_index &&
- attr1->mp_nexthop_len == attr2->mp_nexthop_len &&
- bgp_attr_get_ecommunity(attr1) ==
- bgp_attr_get_ecommunity(attr2) &&
- bgp_attr_get_ipv6_ecommunity(attr1) ==
- bgp_attr_get_ipv6_ecommunity(attr2) &&
- bgp_attr_get_lcommunity(attr1) ==
- bgp_attr_get_lcommunity(attr2) &&
- bgp_attr_get_cluster(attr1) ==
- bgp_attr_get_cluster(attr2) &&
- bgp_attr_get_transit(attr1) ==
- bgp_attr_get_transit(attr2) &&
- bgp_attr_get_aigp_metric(attr1) ==
- bgp_attr_get_aigp_metric(attr2) &&
- attr1->rmap_table_id == attr2->rmap_table_id &&
- (attr1->encap_tunneltype == attr2->encap_tunneltype) &&
- encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs)
+ if (attr1->aggregator_as == attr2->aggregator_as
+ && attr1->aggregator_addr.s_addr
+ == attr2->aggregator_addr.s_addr
+ && attr1->weight == attr2->weight
+ && attr1->tag == attr2->tag
+ && attr1->label_index == attr2->label_index
+ && attr1->mp_nexthop_len == attr2->mp_nexthop_len
+ && bgp_attr_get_ecommunity(attr1)
+ == bgp_attr_get_ecommunity(attr2)
+ && bgp_attr_get_ipv6_ecommunity(attr1)
+ == bgp_attr_get_ipv6_ecommunity(attr2)
+ && bgp_attr_get_lcommunity(attr1)
+ == bgp_attr_get_lcommunity(attr2)
+ && bgp_attr_get_cluster(attr1)
+ == bgp_attr_get_cluster(attr2)
+ && bgp_attr_get_transit(attr1)
+ == bgp_attr_get_transit(attr2)
+ && bgp_attr_get_aigp_metric(attr1)
+ == bgp_attr_get_aigp_metric(attr2)
+ && attr1->rmap_table_id == attr2->rmap_table_id
+ && (attr1->encap_tunneltype == attr2->encap_tunneltype)
+ && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs)
#ifdef ENABLE_BGP_VNC
&& encap_same(bgp_attr_get_vnc_subtlvs(attr1),
bgp_attr_get_vnc_subtlvs(attr2))
#endif
&& IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
- &attr2->mp_nexthop_global) &&
- IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
- &attr2->mp_nexthop_local) &&
- IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
- &attr2->mp_nexthop_global_in) &&
- IPV4_ADDR_SAME(&attr1->originator_id,
- &attr2->originator_id) &&
- overlay_index_same(attr1, attr2) &&
- !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) &&
- attr1->es_flags == attr2->es_flags &&
- attr1->mm_sync_seqnum == attr2->mm_sync_seqnum &&
- attr1->df_pref == attr2->df_pref &&
- attr1->df_alg == attr2->df_alg &&
- attr1->nh_ifindex == attr2->nh_ifindex &&
- attr1->nh_flag == attr2->nh_flag &&
- attr1->nh_lla_ifindex == attr2->nh_lla_ifindex &&
- attr1->distance == attr2->distance &&
- srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) &&
- srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) &&
- attr1->srte_color == attr2->srte_color &&
- attr1->nh_type == attr2->nh_type &&
- attr1->bh_type == attr2->bh_type &&
- attr1->otc == attr2->otc)
+ &attr2->mp_nexthop_global)
+ && IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
+ &attr2->mp_nexthop_local)
+ && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
+ &attr2->mp_nexthop_global_in)
+ && IPV4_ADDR_SAME(&attr1->originator_id,
+ &attr2->originator_id)
+ && overlay_index_same(attr1, attr2)
+ && !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t))
+ && attr1->es_flags == attr2->es_flags
+ && attr1->mm_sync_seqnum == attr2->mm_sync_seqnum
+ && attr1->df_pref == attr2->df_pref
+ && attr1->df_alg == attr2->df_alg
+ && attr1->nh_ifindex == attr2->nh_ifindex
+ && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex
+ && attr1->distance == attr2->distance
+ && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn)
+ && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)
+ && attr1->srte_color == attr2->srte_color
+ && attr1->nh_type == attr2->nh_type
+ && attr1->bh_type == attr2->bh_type
+ && attr1->otc == attr2->otc)
return true;
}
attr->origin = origin;
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN);
- attr->aspath = aspath_empty();
+ attr->aspath = aspath_empty(bgp->asnotation);
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
attr->weight = BGP_ATTR_DEFAULT_WEIGHT;
attr->tag = 0;
if (aspath)
attr.aspath = aspath_intern(aspath);
else
- attr.aspath = aspath_empty();
+ attr.aspath = aspath_empty(bgp->asnotation);
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
/* Next hop attribute. */
struct attr *const attr = args->attr;
struct peer *const peer = args->peer;
const bgp_size_t length = args->length;
+ enum asnotation_mode asnotation;
+ asnotation = bgp_get_asnotation(
+ args->peer && args->peer->bgp ? args->peer->bgp : NULL);
/*
* peer with AS4 => will get 4Byte ASnums
* otherwise, will get 16 Bit
*/
- attr->aspath = aspath_parse(
- peer->curr, length,
- CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
- && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV));
+ attr->aspath =
+ aspath_parse(peer->curr, length,
+ CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) &&
+ CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV),
+ asnotation);
/* In case of IBGP, length will be zero. */
if (!attr->aspath) {
* such messages, conformant BGP speakers SHOULD use the "Treat-as-
* withdraw" error handling behavior as per [RFC7606].
*/
- if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) {
+ if (peer->bgp && peer->bgp->reject_as_sets &&
+ aspath_check_as_sets(attr->aspath)) {
flog_err(EC_BGP_ATTR_MAL_AS_PATH,
"AS_SET and AS_CONFED_SET are deprecated from %pBP",
peer);
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
+ enum asnotation_mode asnotation;
+
+ asnotation = bgp_get_asnotation(peer->bgp);
- *as4_path = aspath_parse(peer->curr, length, 1);
+ *as4_path = aspath_parse(peer->curr, length, 1, asnotation);
/* In case of IBGP, length will be zero. */
if (!*as4_path) {
/* 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;
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_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);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Aggregator attribute */
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
+ goto aggregator_ignore;
+
if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV))
aggregator_as = stream_getl(peer->curr);
else
}
return BGP_ATTR_PARSE_PROCEED;
+
+aggregator_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* New Aggregator attribute */
0);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
+ goto as4_aggregator_ignore;
+
aggregator_as = stream_getl(peer->curr);
*as4_aggregator_as = aggregator_as;
}
return BGP_ATTR_PARSE_PROCEED;
+
+as4_aggregator_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
+ goto community_ignore;
+
bgp_attr_set_community(
attr,
community_parse((uint32_t *)stream_pnt(peer->curr), length));
args->total);
return BGP_ATTR_PARSE_PROCEED;
+
+community_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Originator ID attribute. */
args->total);
}
+ 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);
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
return BGP_ATTR_PARSE_PROCEED;
+
+originator_id_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Cluster list attribute. */
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
+ goto cluster_list_ignore;
+
bgp_attr_set_cluster(
attr, cluster_parse((struct in_addr *)stream_pnt(peer->curr),
length));
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST);
return BGP_ATTR_PARSE_PROCEED;
+
+cluster_list_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Multiprotocol reachability information parse. */
return BGP_ATTR_PARSE_WITHDRAW;
}
attr->nh_ifindex = peer->nexthop.ifp->ifindex;
- if (if_is_operative(peer->nexthop.ifp))
- SET_FLAG(attr->nh_flag,
- BGP_ATTR_NH_IF_OPERSTATE);
- else
- UNSET_FLAG(attr->nh_flag,
- BGP_ATTR_NH_IF_OPERSTATE);
}
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
return BGP_ATTR_PARSE_WITHDRAW;
}
attr->nh_ifindex = peer->nexthop.ifp->ifindex;
- if (if_is_operative(peer->nexthop.ifp))
- SET_FLAG(attr->nh_flag,
- BGP_ATTR_NH_IF_OPERSTATE);
- else
- UNSET_FLAG(attr->nh_flag,
- BGP_ATTR_NH_IF_OPERSTATE);
}
if (attr->mp_nexthop_len
== BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_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 */
args->total);
return BGP_ATTR_PARSE_PROCEED;
+
+large_community_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Extended Community attribute. */
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
+ goto ipv6_ext_community_ignore;
+
ipv6_ecomm = ecommunity_parse_ipv6(
stream_pnt(peer->curr), length,
CHECK_FLAG(peer->flags,
args->total);
return BGP_ATTR_PARSE_PROCEED;
+
+ipv6_ext_community_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* Parse Tunnel Encap attribute in an UPDATE */
goto aigp_ignore;
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_attrs[args->type])
+ goto aigp_ignore;
+
if (!bgp_attr_aigp_valid(s, length))
goto aigp_ignore;
aigp_ignore:
stream_forward_getp(peer->curr, length);
- return BGP_ATTR_PARSE_PROCEED;
+ return bgp_attr_ignore(peer, args->type);
}
/* OTC attribute. */
args->total);
}
+ if (peer->discard_attrs[args->type] || peer->withdraw_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");
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
return BGP_ATTR_PARSE_PROCEED;
+
+otc_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ return bgp_attr_ignore(peer, args->type);
}
/* BGP unknown attribute treatment. */
/* Forward read pointer of input stream. */
stream_forward_getp(peer->curr, length);
+ 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
Attribute. The Data field contains the unrecognized attribute
}
} break;
case SAFI_MPLS_VPN: {
- if (attr->mp_nexthop_len
- == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
- stream_putc(s, 48);
- stream_putl(s, 0); /* RD = 0, per RFC */
- stream_putl(s, 0);
- stream_put(s, &attr->mp_nexthop_global,
- IPV6_MAX_BYTELEN);
+ if (attr->mp_nexthop_len ==
+ BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+ stream_putc(s, attr->mp_nexthop_len);
+ else
+ stream_putc(s, BGP_ATTR_NHLEN_VPNV6_GLOBAL);
+ stream_putl(s, 0); /* RD = 0, per RFC */
+ stream_putl(s, 0);
+ stream_put(s, &attr->mp_nexthop_global,
+ IPV6_MAX_BYTELEN);
+ if (attr->mp_nexthop_len ==
+ BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
stream_putl(s, 0); /* RD = 0, per RFC */
stream_putl(s, 0);
stream_put(s, &attr->mp_nexthop_local,
IPV6_MAX_BYTELEN);
- } else {
- stream_putc(s, 24);
- stream_putl(s, 0); /* RD = 0, per RFC */
- stream_putl(s, 0);
- stream_put(s, &attr->mp_nexthop_global,
- IPV6_MAX_BYTELEN);
}
} break;
case SAFI_ENCAP:
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, 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 (!discard_attrs) {
+ for (i = 0; i < BGP_ATTR_MAX; i++)
+ peer->discard_attrs[i] = false;
+ goto discard_soft_clear;
+ }
+
+ if (discard_attrs) {
+ frrstr_split(discard_attrs, " ", &attributes, &num_attributes);
+
+ if (set)
+ 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] = set;
+ }
+ XFREE(MTYPE_TMP, attributes);
+ discard_soft_clear:
+ /* 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);
+ }
+}
+
+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;
+}