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)) |
