diff options
| author | Donald Sharp <sharpd@cumulusnetworks.com> | 2021-01-22 08:36:27 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-22 08:36:27 -0500 | 
| commit | f939c3a69f1eb7f3a99885132809104b95518c17 (patch) | |
| tree | a4322548a419e9e279ec120da00fc4958371f7c7 | |
| parent | 75691bb7c4c7c16bb200421ca5defd94abdfd594 (diff) | |
| parent | 3130cfd7420d7510317cdd9a127da1ca63381647 (diff) | |
Merge pull request #7899 from ton31337/fix/bgpd_blackhole_communitybase_7.6
bgpd: Massage Blackhole community
| -rw-r--r-- | bgpd/bgp_clist.c | 80 | ||||
| -rw-r--r-- | bgpd/bgp_community.c | 2 | ||||
| -rw-r--r-- | bgpd/bgp_community.h | 1 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 42 | ||||
| -rw-r--r-- | bgpd/bgp_routemap.c | 5 | ||||
| -rw-r--r-- | doc/user/bgp.rst | 6 | ||||
| -rw-r--r-- | lib/command.h | 3 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/__init__.py | 0 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/r1/bgpd.conf | 13 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/r1/zebra.conf | 10 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/r2/bgpd.conf | 6 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/r2/zebra.conf | 9 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/r3/bgpd.conf | 5 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/r3/zebra.conf | 6 | ||||
| -rw-r--r-- | tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py | 123 | 
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))  | 
