From 2f6197b044edea555348f4c0c5acd2d3ab5feabf Mon Sep 17 00:00:00 2001 From: Biswajit Sadhu Date: Wed, 24 Apr 2019 00:40:01 -0700 Subject: [PATCH] bgpd: Prevent IPv6 routes received via a ibgp session with own ip as nexthop Prevent IPv6 routes received via a ibgp session with one of its own interface ip as nexthop from getting installed in the BGP table. Implemented IPV6 HASH table, where we need to add any ipv6 address as they gets configured and delete them from the HASH table as the ipv6 addresses get unconfigured. The above hash table is used to verify if any route learned via BGP has nexthop which is equal to one of its its connected ipv6 interface. Signed-off-by: Biswajit Sadhu sadhub@vmware.com --- bgpd/bgp_nexthop.c | 133 +++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_nexthop.h | 10 ++++ bgpd/bgp_route.c | 4 +- bgpd/bgpd.c | 1 + bgpd/bgpd.h | 3 + lib/jhash.c | 15 +++++ lib/jhash.h | 2 + 7 files changed, 167 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index de97b73c72..cf67a36238 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -47,6 +47,11 @@ DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Address Intf String"); +static void bgp_ipv6_address_add(struct bgp *bgp, struct connected *ifc, + struct prefix *p); +static void bgp_ipv6_address_del(struct bgp *bgp, struct connected *ifc, + struct prefix *p); + char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) { prefix2str(&(bnc->node->p), buf, size); @@ -379,6 +384,8 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; + bgp_ipv6_address_add(bgp, ifc, addr); + rn = bgp_node_get(bgp->connected_table[AFI_IP6], (struct prefix *)&p); @@ -419,6 +426,8 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; + bgp_ipv6_address_del(bgp, ifc, addr); + rn = bgp_node_lookup(bgp->connected_table[AFI_IP6], (struct prefix *)&p); } @@ -772,3 +781,127 @@ void bgp_scan_finish(struct bgp *bgp) bgp->import_check_table[afi] = NULL; } } + +static void *bgp_ipv6_address_hash_alloc(void *p) +{ + const struct in6_addr *v6addr = (const struct in6_addr *)p; + struct bgp_addrv6 *addr; + + addr = XMALLOC(MTYPE_BGP_ADDR, sizeof(struct bgp_addrv6)); + addr->addrv6 = *v6addr; + + addr->ifp_name_list = list_new(); + addr->ifp_name_list->del = bgp_address_hash_string_del; + + return addr; +} + +static void bgp_ipv6_address_hash_free(void *data) +{ + struct bgp_addrv6 *v6addr = data; + + list_delete(&v6addr->ifp_name_list); + XFREE(MTYPE_BGP_ADDR, v6addr); +} + +static unsigned int bgp_ipv6_address_hash_key_make(void *p) +{ + const struct bgp_addrv6 *v6 = p; + + return __ipv6_addr_jhash(&v6->addrv6, 0); +} + +static bool bgp_ipv6_address_hash_cmp(const void *p1, + const void *p2) +{ + const struct bgp_addrv6 *addr1 = p1; + const struct bgp_addrv6 *addr2 = p2; + + return(!memcmp(&addr1->addrv6, &addr2->addrv6, + sizeof(struct in6_addr))); +} + +void bgp_ipv6_address_init(struct bgp *bgp) +{ + bgp->ipv6_address_hash = hash_create(bgp_ipv6_address_hash_key_make, + bgp_ipv6_address_hash_cmp, + "BGP IPV6 Address Hash"); +} + +void bgp_ipv6_address_destroy(struct bgp *bgp) +{ + if (bgp->ipv6_address_hash == NULL) + return; + hash_clean(bgp->ipv6_address_hash, + bgp_ipv6_address_hash_free); + + hash_free(bgp->ipv6_address_hash); + bgp->ipv6_address_hash = NULL; +} + +static void bgp_ipv6_address_add(struct bgp *bgp, struct connected *ifc, + struct prefix *p) +{ + struct bgp_addrv6 tmp = {0}; + struct bgp_addrv6 *addr = NULL; + struct listnode *node = NULL; + char *name = 0; + + tmp.addrv6 = p->u.prefix6; + + addr = hash_get(bgp->ipv6_address_hash, &tmp, + bgp_ipv6_address_hash_alloc); + if (!addr) + return; + + for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { + if (strcmp(ifc->ifp->name, name) == 0) + break; + } + + if (!node) { + name = XSTRDUP(MTYPE_MARTIAN_STRING, ifc->ifp->name); + listnode_add(addr->ifp_name_list, name); + } +} + + +static void bgp_ipv6_address_del(struct bgp *bgp, struct connected *ifc, + struct prefix *p) +{ + struct bgp_addrv6 tmp; + struct bgp_addrv6 *addr; + struct listnode *node; + char *name; + + + tmp.addrv6 = p->u.prefix6; + + addr = hash_lookup(bgp->ipv6_address_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { + if (strcmp(ifc->ifp->name, name) == 0) + break; + } + + if (node) { + list_delete_node(addr->ifp_name_list, node); + XFREE(MTYPE_MARTIAN_STRING, name); + } + + if (addr->ifp_name_list->count == 0) { + hash_release(bgp->ipv6_address_hash, addr); + list_delete(&addr->ifp_name_list); + XFREE(MTYPE_BGP_ADDR, addr); + } +} +int bgp_nexthop_self_ipv6(struct bgp *bgp, struct in6_addr *addr) +{ + struct bgp_addrv6 tmp; + + tmp.addrv6 = *addr; + return (!!hash_lookup(bgp->ipv6_address_hash, &tmp)); +} diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index f06fae5706..c26295f810 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -74,6 +74,12 @@ struct tip_addr { int refcnt; }; +/* ipv6 connected address info structure */ +struct bgp_addrv6 { + struct in6_addr addrv6; + struct list *ifp_name_list; +}; + extern void bgp_connected_add(struct bgp *bgp, struct connected *c); extern void bgp_connected_delete(struct bgp *bgp, struct connected *c); extern int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, @@ -96,4 +102,8 @@ extern void bgp_tip_hash_init(struct bgp *bgp); extern void bgp_tip_hash_destroy(struct bgp *bgp); extern void bgp_nexthop_show_address_hash(struct vty *vty, struct bgp *bgp); +extern void bgp_ipv6_address_init(struct bgp *bgp); +extern void bgp_ipv6_address_destroy(struct bgp *bgp); +extern int bgp_nexthop_self_ipv6(struct bgp *bgp, + struct in6_addr *addr); #endif /* _QUAGGA_BGP_NEXTHOP_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7850666085..5fb6d70119 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2932,7 +2932,9 @@ static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, ret = (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) || IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) || IN6_IS_ADDR_MULTICAST( - &attr->mp_nexthop_global)); + &attr->mp_nexthop_global) + || bgp_nexthop_self_ipv6(bgp, + &attr->mp_nexthop_global)); break; default: diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index b2925cd512..730cc8bde7 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3213,6 +3213,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp->vrf_id = vrf_generate_id(); bgp_router_id_set(bgp, &bgp->router_id_zebra); bgp_address_init(bgp); + bgp_ipv6_address_init(bgp); bgp_tip_hash_init(bgp); bgp_scan_init(bgp); *bgp_val = bgp; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b0f6567534..cf03328f8c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -393,6 +393,9 @@ struct bgp { struct hash *address_hash; + /* ipv6 connected address hash table pointer */ + struct hash *ipv6_address_hash; + /* DB for all local tunnel-ips - used mainly for martian checks Currently it only has all VxLan tunnel IPs*/ struct hash *tip_hash; diff --git a/lib/jhash.c b/lib/jhash.c index 0d561ef3a4..d0aff57d87 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -185,3 +185,18 @@ uint32_t jhash_1word(uint32_t a, uint32_t initval) { return jhash_3words(a, 0, 0, initval); } + +/* ipv6 hash function */ +uint32_t __ipv6_addr_jhash(const struct in6_addr *a, const uint32_t initval) +{ + uint32_t v = 0; + uint32_t y[4] = {0}; + + /* Since s6_addr32 is not available is few os like FreeBSD, NetBDS, + * OMIBDS & OpenBDS. So recreating in uint32_t format. + */ + memcpy(y, a->s6_addr, sizeof(struct in6_addr)); + v = y[0] ^ y[1]; + + return jhash_3words(v, y[2], y[3], initval); +} diff --git a/lib/jhash.h b/lib/jhash.h index 977421495c..1b6f879369 100644 --- a/lib/jhash.h +++ b/lib/jhash.h @@ -45,6 +45,8 @@ extern uint32_t jhash_3words(uint32_t a, uint32_t b, uint32_t c, uint32_t initval); extern uint32_t jhash_2words(uint32_t a, uint32_t b, uint32_t initval); extern uint32_t jhash_1word(uint32_t a, uint32_t initval); +extern uint32_t __ipv6_addr_jhash(const struct in6_addr *a, + const uint32_t initval); #ifdef __cplusplus } -- 2.39.5