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; }