summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_clist.c80
-rw-r--r--bgpd/bgp_community.c2
-rw-r--r--bgpd/bgp_community.h1
-rw-r--r--bgpd/bgp_route.c42
-rw-r--r--bgpd/bgp_routemap.c5
-rw-r--r--doc/user/bgp.rst6
-rw-r--r--lib/command.h3
-rw-r--r--tests/topotests/bgp_blackhole_community/__init__.py0
-rw-r--r--tests/topotests/bgp_blackhole_community/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_blackhole_community/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_blackhole_community/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_blackhole_community/r3/bgpd.conf5
-rw-r--r--tests/topotests/bgp_blackhole_community/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py123
15 files changed, 226 insertions, 85 deletions
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
index 6ac6cf56dd..e17cce3ff6 100644
--- a/bgpd/bgp_clist.c
+++ b/bgpd/bgp_clist.c
@@ -654,86 +654,6 @@ static bool ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg)
return false;
}
-#if 0
-/* Delete community attribute using regular expression match. Return
- modified communites attribute. */
-static struct community *
-community_regexp_delete (struct community *com, regex_t * reg)
-{
- int i;
- uint32_t comval;
- /* Maximum is "65535:65535" + '\0'. */
- char c[12];
- const char *str;
-
- if (!com)
- return NULL;
-
- i = 0;
- while (i < com->size)
- {
- memcpy (&comval, com_nthval (com, i), sizeof(uint32_t));
- comval = ntohl (comval);
-
- switch (comval) {
- case COMMUNITY_INTERNET:
- str = "internet";
- break;
- case COMMUNITY_ACCEPT_OWN:
- str = "accept-own";
- break;
- case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
- str = "route-filter-translated-v4";
- break;
- case COMMUNITY_ROUTE_FILTER_v4:
- str = "route-filter-v4";
- break;
- case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
- str = "route-filter-translated-v6";
- break;
- case COMMUNITY_ROUTE_FILTER_v6:
- str = "route-filter-v6";
- break;
- case COMMUNITY_LLGR_STALE:
- str = "llgr-stale";
- break;
- case COMMUNITY_NO_LLGR:
- str = "no-llgr";
- break;
- case COMMUNITY_ACCEPT_OWN_NEXTHOP:
- str = "accept-own-nexthop";
- break;
- case COMMUNITY_BLACKHOLE:
- str = "blackhole";
- break;
- case COMMUNITY_NO_EXPORT:
- str = "no-export";
- break;
- case COMMUNITY_NO_ADVERTISE:
- str = "no-advertise";
- break;
- case COMMUNITY_LOCAL_AS:
- str = "local-AS";
- break;
- case COMMUNITY_NO_PEER:
- str = "no-peer";
- break;
- default:
- sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF,
- comval & 0xFFFF);
- str = c;
- break;
- }
-
- if (regexec (reg, str, 0, NULL, 0) == 0)
- community_del_val (com, com_nthval (com, i));
- else
- i++;
- }
- return com;
-}
-#endif
-
/* When given community attribute matches to the community-list return
1 else return 0. */
bool community_list_match(struct community *com, struct community_list *list)
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
index f722a8dbc7..43138b82f6 100644
--- a/bgpd/bgp_community.c
+++ b/bgpd/bgp_community.c
@@ -56,7 +56,7 @@ void community_free(struct community **com)
}
/* Add one community value to the community. */
-static void community_add_val(struct community *com, uint32_t val)
+void community_add_val(struct community *com, uint32_t val)
{
com->size++;
if (com->val)
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
index b99f38ab64..2a1fbf526a 100644
--- a/bgpd/bgp_community.h
+++ b/bgpd/bgp_community.h
@@ -88,6 +88,7 @@ extern struct community *community_delete(struct community *,
struct community *);
extern struct community *community_dup(struct community *);
extern bool community_include(struct community *, uint32_t);
+extern void community_add_val(struct community *com, uint32_t val);
extern void community_del_val(struct community *, uint32_t *);
extern unsigned long community_count(void);
extern struct hash *community_hash(void);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 1d1f3d9b26..6735c1a952 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -3505,6 +3505,34 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
return ret;
}
+static void bgp_attr_add_no_advertise_community(struct attr *attr)
+{
+ struct community *old;
+ struct community *new;
+ struct community *merge;
+ struct community *noadv;
+
+ old = attr->community;
+ noadv = community_str2com("no-advertise");
+
+ if (old) {
+ merge = community_merge(community_dup(old), noadv);
+
+ if (!old->refcnt)
+ community_free(&old);
+
+ new = community_uniq_sort(merge);
+ community_free(&merge);
+ } else {
+ new = community_dup(noadv);
+ }
+
+ community_free(&noadv);
+
+ attr->community = new;
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES);
+}
+
int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
struct attr *attr, afi_t afi, safi_t safi, int type,
int sub_type, struct prefix_rd *prd, mpls_label_t *label,
@@ -3697,6 +3725,20 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (peer->sort == BGP_PEER_EBGP) {
+ /* rfc7999:
+ * A BGP speaker receiving an announcement tagged with the
+ * BLACKHOLE community SHOULD add the NO_ADVERTISE or
+ * NO_EXPORT community as defined in RFC1997, or a
+ * similar community, to prevent propagation of the
+ * prefix outside the local AS. The community to prevent
+ * propagation SHOULD be chosen according to the operator's
+ * routing policy.
+ */
+ if (new_attr.community
+ && community_include(new_attr.community,
+ COMMUNITY_BLACKHOLE))
+ bgp_attr_add_no_advertise_community(&new_attr);
+
/* If we receive the graceful-shutdown community from an eBGP
* peer we must lower local-preference */
if (new_attr.community
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 32af9ed027..3dc2cfbd5c 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -4828,6 +4828,11 @@ DEFUN (set_community,
buffer_putstr(b, "no-export");
continue;
}
+ if (strncmp(argv[i]->arg, "blackhole", strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "blackhole");
+ continue;
+ }
if (strncmp(argv[i]->arg, "graceful-shutdown",
strlen(argv[i]->arg))
== 0) {
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 613faa4660..173daa9b22 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1985,9 +1985,9 @@ is 4 octet long. The following format is used to define the community value.
``0xFFFF029A`` ``65535:666``. :rfc:`7999` documents sending prefixes to
EBGP peers and upstream for the purpose of blackholing traffic.
Prefixes tagged with the this community should normally not be
- re-advertised from neighbors of the originating network. It is
- recommended upon receiving prefixes tagged with this community to
- add ``NO_EXPORT`` and ``NO_ADVERTISE``.
+ re-advertised from neighbors of the originating network. Upon receiving
+ ``BLACKHOLE`` community from a BGP speaker, ``NO_ADVERTISE`` community
+ is added automatically.
``no-export``
``no-export`` represents well-known communities value ``NO_EXPORT``
diff --git a/lib/command.h b/lib/command.h
index bfe64a7235..b002e79f09 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -414,7 +414,8 @@ struct cmd_node {
"<neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr>"
#define AREA_TAG_STR "[area tag]\n"
#define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n"
-#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n"
+#define COMMUNITY_VAL_STR \
+ "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet|graceful-shutdown|accept-own-nexthop|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|blackhole|no-peer or additive\n"
#define MPLS_TE_STR "MPLS-TE specific commands\n"
#define LINK_PARAMS_STR "Configure interface link parameters\n"
#define OSPF_RI_STR "OSPF Router Information specific commands\n"
diff --git a/tests/topotests/bgp_blackhole_community/__init__.py b/tests/topotests/bgp_blackhole_community/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/__init__.py
diff --git a/tests/topotests/bgp_blackhole_community/r1/bgpd.conf b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf
new file mode 100644
index 0000000000..be86bd6b2c
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf
@@ -0,0 +1,13 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor r1-eth0 interface remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor r1-eth0 route-map r2 out
+ exit-address-family
+ !
+!
+route-map r2 permit 10
+ set community blackhole
+!
diff --git a/tests/topotests/bgp_blackhole_community/r1/zebra.conf b/tests/topotests/bgp_blackhole_community/r1/zebra.conf
new file mode 100644
index 0000000000..70dc5e516d
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r1/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+ip forwarding
+!
+
diff --git a/tests/topotests/bgp_blackhole_community/r2/bgpd.conf b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf
new file mode 100644
index 0000000000..a4fb45e1ff
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor r2-eth0 interface remote-as external
+ neighbor r2-eth1 interface remote-as external
+!
diff --git a/tests/topotests/bgp_blackhole_community/r2/zebra.conf b/tests/topotests/bgp_blackhole_community/r2/zebra.conf
new file mode 100644
index 0000000000..307e5187ca
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface r2-eth0
+ ip address 192.168.0.2/24
+!
+interface r2-eth1
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_blackhole_community/r3/bgpd.conf b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf
new file mode 100644
index 0000000000..f0635cb8c9
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf
@@ -0,0 +1,5 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor r3-eth0 interface remote-as external
+!
diff --git a/tests/topotests/bgp_blackhole_community/r3/zebra.conf b/tests/topotests/bgp_blackhole_community/r3/zebra.conf
new file mode 100644
index 0000000000..05ab56d6f1
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.1.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py
new file mode 100644
index 0000000000..b61ad354e2
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test if 172.16.255.254/32 tagged with BLACKHOLE community is not
+re-advertised downstream.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_blackhole_community():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show ip bgp 172.16.255.254/32 json")
+ )
+ expected = {"paths": [{"community": {"list": ["blackhole", "noAdvertise"]}}]}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_no_advertise():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd(
+ "show ip bgp neighbor r2-eth1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {},
+ "totalPrefixCounter": 0,
+ "filteredPrefixCounter": 0,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"])
+
+ test_func = functools.partial(_bgp_no_advertise)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Advertised blackhole tagged prefix in "{}"'.format(
+ tgen.gears["r2"]
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))