summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_nht.c18
-rw-r--r--bgpd/bgp_open.c1
-rw-r--r--doc/user/bgp.rst9
-rw-r--r--zebra/interface.c51
-rw-r--r--zebra/interface.h5
-rw-r--r--zebra/rtadv.c47
6 files changed, 116 insertions, 15 deletions
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 7eba0eda44..dd1ffe9f3b 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -415,6 +415,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
bnc->change_flags |= BGP_NEXTHOP_CHANGED;
if (nhr.nexthop_num) {
+ struct peer *peer = bnc->nht_info;
+
/* notify bgp fsm if nbr ip goes from invalid->valid */
if (!bnc->nexthop_num)
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
@@ -430,6 +432,22 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
+ /*
+ * Turn on RA for the v6 nexthops
+ * we receive from bgp. This is to allow us
+ * to work with v4 routing over v6 nexthops
+ */
+ if (peer &&
+ CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)
+ && nhr.prefix.family == AF_INET6) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ nexthop->vrf_id);
+ zclient_send_interface_radv_req(
+ zclient, nexthop->vrf_id, ifp, true,
+ BGP_UNNUM_DEFAULT_RA_INTERVAL);
+ }
/* There is at least one label-switched path */
if (nexthop->nh_label &&
nexthop->nh_label->num_labels) {
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 62b412af0c..cf5901df5a 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -1331,7 +1331,6 @@ void bgp_open_capability(struct stream *s, struct peer *peer)
*/
if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)
&& peer->su.sa.sa_family == AF_INET6
- && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr)
&& afi == AFI_IP
&& (safi == SAFI_UNICAST
|| safi == SAFI_LABELED_UNICAST)) {
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 14f2c8dc9a..d9d496f7fc 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -845,6 +845,15 @@ Configuring Peers
specified number of hops away will be allowed to become neighbors. This
command is mutually exclusive with *ebgp-multihop*.
+.. index:: [no] neighbor PEER capability extended-nexthop
+.. clicmd:: [no] neighbor PEER capability extended-nexthop
+
+ Allow bgp to negotiate the extended-nexthop capability with it's peer.
+ If you are peering over a v6 LL address then this capability is turned
+ on automatically. If you are peering over a v6 Global Address then
+ turning on this command will allow BGP to install v4 routes with
+ v6 nexthops if you do not have v4 configured on interfaces.
+
.. index:: [no] bgp fast-external-failover
.. clicmd:: [no] bgp fast-external-failover
diff --git a/zebra/interface.c b/zebra/interface.c
index 8e492b8069..96b244635d 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -51,6 +51,8 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_errors.h"
+DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information")
+
#define ZEBRA_PTM_SUPPORT
DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp),
@@ -99,7 +101,7 @@ static int if_zebra_new_hook(struct interface *ifp)
{
struct zebra_if *zebra_if;
- zebra_if = XCALLOC(MTYPE_TMP, sizeof(struct zebra_if));
+ zebra_if = XCALLOC(MTYPE_ZINFO, sizeof(struct zebra_if));
zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC;
zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF;
@@ -136,6 +138,8 @@ static int if_zebra_new_hook(struct interface *ifp)
}
#endif /* HAVE_RTADV */
+ memset(&zebra_if->neigh_mac[0], 0, 6);
+
/* Initialize installed address chains tree. */
zebra_if->ipv4_subnets =
route_table_init_with_delegate(&zebra_if_table_delegate);
@@ -175,7 +179,7 @@ static int if_zebra_delete_hook(struct interface *ifp)
THREAD_OFF(zebra_if->speed_update);
- XFREE(MTYPE_TMP, zebra_if);
+ XFREE(MTYPE_ZINFO, zebra_if);
}
return 0;
@@ -802,19 +806,32 @@ static void ipv6_ll_address_to_mac(struct in6_addr *address, uint8_t *mac)
mac[5] = address->s6_addr[15];
}
-void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
- struct in6_addr *address, int add)
+static bool mac_is_same(char *mac1, char *mac2)
+{
+ if (mac1[0] == mac2[0] &&
+ mac1[1] == mac2[1] &&
+ mac1[2] == mac2[2] &&
+ mac1[3] == mac2[3] &&
+ mac1[4] == mac2[4] &&
+ mac1[5] == mac2[5])
+ return true;
+ else
+ return false;
+}
+
+void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *ifp,
+ char mac[6],
+ struct in6_addr *address,
+ int add)
{
struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id);
struct zebra_if *zif = ifp->info;
char buf[16] = "169.254.0.1";
struct in_addr ipv4_ll;
- char mac[6];
ns_id_t ns_id;
inet_pton(AF_INET, buf, &ipv4_ll);
- ipv6_ll_address_to_mac(address, (uint8_t *)mac);
ns_id = zvrf->zns->ns_id;
/*
@@ -823,10 +840,16 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
*
* supported message types are RTM_NEWNEIGH and RTM_DELNEIGH
*/
- kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
+ if (!mac_is_same(zif->neigh_mac, mac)) {
+ kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr,
+ mac, 6, ns_id);
- /* Add arp record */
- kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
+ /* Add arp record */
+ kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr,
+ mac, 6, ns_id);
+ }
+
+ memcpy(&zif->neigh_mac[0], &mac[0], 6);
/*
* We need to note whether or not we originated a v6
@@ -840,6 +863,16 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
zvrf->neigh_updates++;
}
+void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
+ struct in6_addr *address, int add)
+{
+
+ char mac[6];
+
+ ipv6_ll_address_to_mac(address, (uint8_t *)mac);
+ if_nbr_mac_to_ipv4ll_neigh_update(ifp, mac, address, add);
+}
+
static void if_nbr_ipv6ll_to_ipv4ll_neigh_add_all(struct interface *ifp)
{
if (listhead(ifp->nbr_connected)) {
diff --git a/zebra/interface.h b/zebra/interface.h
index c6d8b24b01..e4c05e8dc4 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -279,6 +279,7 @@ struct zebra_if {
* for bgp unnumbered?
*/
bool v6_2_v4_ll_neigh_entry;
+ char neigh_mac[6];
struct in6_addr v6_2_v4_ll_addr6;
};
@@ -332,6 +333,10 @@ extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *);
extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int);
extern void if_unlink_per_ns(struct interface *);
+extern void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *fip,
+ char mac[6],
+ struct in6_addr *address,
+ int add);
extern void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
struct in6_addr *address,
int add);
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
index f9bd5ad1bb..3bb75f3446 100644
--- a/zebra/rtadv.c
+++ b/zebra/rtadv.c
@@ -455,6 +455,38 @@ static void rtadv_process_solicit(struct interface *ifp)
rtadv_send_packet(zns->rtadv.sock, ifp);
}
+/*
+ * This function processes optional attributes off of
+ * end of a RA packet received. At this point in
+ * time we only care about this in one situation
+ * which is when a interface does not have a LL
+ * v6 address. We still need to be able to install
+ * the mac address for v4 to v6 resolution
+ */
+static void rtadv_process_optional(uint8_t *optional, unsigned int len,
+ struct interface *ifp,
+ struct sockaddr_in6 *addr)
+{
+ char *mac;
+
+ while (len > 0) {
+ struct nd_opt_hdr *opt_hdr = (struct nd_opt_hdr *)optional;
+
+ switch(opt_hdr->nd_opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ mac = (char *)(optional+2);
+ if_nbr_mac_to_ipv4ll_neigh_update(ifp, mac,
+ &addr->sin6_addr, 1);
+ break;
+ default:
+ break;
+ }
+
+ len -= 8 * opt_hdr->nd_opt_len;
+ optional += 8 * opt_hdr->nd_opt_len;
+ }
+}
+
static void rtadv_process_advert(uint8_t *msg, unsigned int len,
struct interface *ifp,
struct sockaddr_in6 *addr)
@@ -469,14 +501,19 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len,
inet_ntop(AF_INET6, &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN);
if (len < sizeof(struct nd_router_advert)) {
- zlog_debug("%s(%u): Rx RA with invalid length %d from %s",
- ifp->name, ifp->ifindex, len, addr_str);
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s(%u): Rx RA with invalid length %d from %s",
+ ifp->name, ifp->ifindex, len, addr_str);
return;
}
+
if (!IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) {
- zlog_debug(
- "%s(%u): Rx RA with non-linklocal source address from %s",
- ifp->name, ifp->ifindex, addr_str);
+ rtadv_process_optional(msg + sizeof(struct nd_router_advert),
+ len - sizeof(struct nd_router_advert),
+ ifp, addr);
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug("%s(%u): Rx RA with non-linklocal source address from %s",
+ ifp->name, ifp->ifindex, addr_str);
return;
}