diff options
author | Mark Stapp <mjs@voltanet.io> | 2021-04-13 14:02:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-13 14:02:56 +0200 |
commit | f3dbd9d3efe7f2d4c0e83bf0890c6dd0eaa6b806 (patch) | |
tree | 05b18d72d1927fbcb9c01ea379422f0397eb9186 | |
parent | Merge pull request #8389 from idryzhov/route-map-optimization-nb (diff) | |
parent | zebra, lib: replace ZEBRA_ROUTE_NEIGH with simplified version (diff) | |
download | frr-f3dbd9d3efe7f2d4c0e83bf0890c6dd0eaa6b806.tar.xz frr-f3dbd9d3efe7f2d4c0e83bf0890c6dd0eaa6b806.zip |
Merge pull request #8145 from pguibert6WIND/nhrp_use_zebra
nhrp: use zebra
35 files changed, 1365 insertions, 239 deletions
@@ -464,7 +464,15 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST), DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY), DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_ADD), - DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL)}; + DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_ADDED), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_REMOVED), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_GET), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_REGISTER), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_IP_ADD), + DESC_ENTRY(ZEBRA_NEIGH_IP_DEL), + DESC_ENTRY(ZEBRA_CONFIGURE_ARP)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/zclient.c b/lib/zclient.c index c78937c1e..d613906d8 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3916,6 +3916,21 @@ static int zclient_read(struct thread *thread) (*zclient->zebra_client_close_notify)(command, zclient, length, vrf_id); break; + case ZEBRA_NHRP_NEIGH_ADDED: + if (zclient->neighbor_added) + (*zclient->neighbor_added)(command, zclient, length, + vrf_id); + break; + case ZEBRA_NHRP_NEIGH_REMOVED: + if (zclient->neighbor_removed) + (*zclient->neighbor_removed)(command, zclient, length, + vrf_id); + break; + case ZEBRA_NHRP_NEIGH_GET: + if (zclient->neighbor_get) + (*zclient->neighbor_get)(command, zclient, length, + vrf_id); + break; default: break; } @@ -4181,3 +4196,59 @@ char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, size_t len) return buf; } + +static int zclient_neigh_ip_read_entry(struct stream *s, struct ipaddr *add) +{ + uint8_t family; + + STREAM_GETC(s, family); + if (family != AF_INET && family != AF_INET6) + return -1; + + STREAM_GET(&add->ip.addr, s, family2addrsize(family)); + add->ipa_type = family; + return 0; + stream_failure: + return -1; +} + +int zclient_neigh_ip_encode(struct stream *s, + uint16_t cmd, + union sockunion *in, + union sockunion *out, + struct interface *ifp) +{ + int ret = 0; + + zclient_create_header(s, cmd, ifp->vrf_id); + stream_putc(s, sockunion_family(in)); + stream_write(s, sockunion_get_addr(in), sockunion_get_addrlen(in)); + if (out && sockunion_family(out) != AF_UNSPEC) { + stream_putc(s, sockunion_family(out)); + stream_write(s, sockunion_get_addr(out), + sockunion_get_addrlen(out)); + } else + stream_putc(s, AF_UNSPEC); + stream_putl(s, ifp->ifindex); + if (out) + stream_putl(s, ZEBRA_NEIGH_STATE_REACHABLE); + else + stream_putl(s, ZEBRA_NEIGH_STATE_FAILED); + return ret; +} + +int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api) +{ + int ret; + + ret = zclient_neigh_ip_read_entry(s, &api->ip_in); + if (ret < 0) + return -1; + zclient_neigh_ip_read_entry(s, &api->ip_out); + + STREAM_GETL(s, api->index); + STREAM_GETL(s, api->ndm_state); + return 0; + stream_failure: + return -1; +} diff --git a/lib/zclient.h b/lib/zclient.h index bd952ea1e..90240e40b 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -23,6 +23,7 @@ /* For struct zapi_route. */ #include "prefix.h" +#include "ipaddr.h" /* For struct interface and struct connected. */ #include "if.h" @@ -223,6 +224,14 @@ typedef enum { ZEBRA_NEIGH_DISCOVER, ZEBRA_ROUTE_NOTIFY_REQUEST, ZEBRA_CLIENT_CLOSE_NOTIFY, + ZEBRA_NHRP_NEIGH_ADDED, + ZEBRA_NHRP_NEIGH_REMOVED, + ZEBRA_NHRP_NEIGH_GET, + ZEBRA_NHRP_NEIGH_REGISTER, + ZEBRA_NHRP_NEIGH_UNREGISTER, + ZEBRA_NEIGH_IP_ADD, + ZEBRA_NEIGH_IP_DEL, + ZEBRA_CONFIGURE_ARP, } zebra_message_types_t; enum zebra_error_types { @@ -381,6 +390,9 @@ struct zclient { int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS); int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS); int (*zebra_client_close_notify)(ZAPI_CALLBACK_ARGS); + void (*neighbor_added)(ZAPI_CALLBACK_ARGS); + void (*neighbor_removed)(ZAPI_CALLBACK_ARGS); + void (*neighbor_get)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ @@ -794,6 +806,27 @@ struct zclient_options { extern struct zclient_options zclient_options_default; +/* link layer representation for GRE like interfaces + * ip_in is the underlay IP, ip_out is the tunnel dest + * index stands for the index of the interface + * ndm state stands for the NDM value in netlink + */ +#define ZEBRA_NEIGH_STATE_REACHABLE (0x02) +#define ZEBRA_NEIGH_STATE_FAILED (0x20) +struct zapi_neigh_ip { + int cmd; + struct ipaddr ip_in; + struct ipaddr ip_out; + ifindex_t index; + uint32_t ndm_state; +}; +int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api); +int zclient_neigh_ip_encode(struct stream *s, + uint16_t cmd, + union sockunion *in, + union sockunion *out, + struct interface *ifp); + /* * We reserve the top 4 bits for l2-NHG, everything else * is for zebra/proto l3-NHG. diff --git a/nhrpd/linux.c b/nhrpd/linux.c index 59c82b1c5..9cabdbf06 100644 --- a/nhrpd/linux.c +++ b/nhrpd/linux.c @@ -154,7 +154,6 @@ int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af) break; } ret |= linux_configure_arp(ifname, 1); - ret |= netlink_configure_arp(ifindex, af); return ret; } diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c index dc4697cda..ecea0a9ec 100644 --- a/nhrpd/netlink_arp.c +++ b/nhrpd/netlink_arp.c @@ -19,6 +19,8 @@ #include <linux/netfilter/nfnetlink_log.h> #include "thread.h" +#include "stream.h" +#include "prefix.h" #include "nhrpd.h" #include "netlink.h" #include "znl.h" @@ -27,129 +29,11 @@ int netlink_req_fd = -1; int netlink_nflog_group; static int netlink_log_fd = -1; static struct thread *netlink_log_thread; -static int netlink_listen_fd = -1; - -typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb); void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma) { - struct nlmsghdr *n; - struct ndmsg *ndm; - struct zbuf *zb = zbuf_alloc(512); - - n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, - NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); - ndm = znl_push(zb, sizeof(*ndm)); - *ndm = (struct ndmsg){ - .ndm_family = sockunion_family(proto), - .ndm_ifindex = ifp->ifindex, - .ndm_type = RTN_UNICAST, - .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED, - }; - znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), - family2addrsize(sockunion_family(proto))); - if (nbma) - znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), - family2addrsize(sockunion_family(nbma))); - znl_nlmsg_complete(zb, n); - zbuf_send(zb, netlink_req_fd); - zbuf_recv(zb, netlink_req_fd); - zbuf_free(zb); -} - -static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) -{ - struct ndmsg *ndm; - struct rtattr *rta; - struct nhrp_cache *c; - struct interface *ifp; - struct zbuf payload; - union sockunion addr, lladdr; - size_t len; - int state; - - memset(&lladdr, 0, sizeof(lladdr)); - ndm = znl_pull(zb, sizeof(*ndm)); - if (!ndm) - return; - - sockunion_family(&addr) = AF_UNSPEC; - while ((rta = znl_rta_pull(zb, &payload)) != NULL) { - len = zbuf_used(&payload); - switch (rta->rta_type) { - case NDA_DST: - sockunion_set(&addr, ndm->ndm_family, - zbuf_pulln(&payload, len), len); - break; - case NDA_LLADDR: - sockunion_set(&lladdr, ndm->ndm_family, - zbuf_pulln(&payload, len), len); - break; - } - } - - ifp = if_lookup_by_index(ndm->ndm_ifindex, VRF_DEFAULT); - if (!ifp || sockunion_family(&addr) == AF_UNSPEC) - return; - - c = nhrp_cache_get(ifp, &addr, 0); - if (!c) - return; - - debugf(NHRP_DEBUG_KERNEL, - "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", - (msg->nlmsg_type == RTM_GETNEIGH) - ? "who-has" - : (msg->nlmsg_type == RTM_NEWNEIGH) ? "new-neigh" - : "del-neigh", - &addr, ifp->name, &lladdr, ndm->ndm_state, c->used, c->cur.type); - - if (msg->nlmsg_type == RTM_GETNEIGH) { - if (c->cur.type >= NHRP_CACHE_CACHED) { - nhrp_cache_set_used(c, 1); - debugf(NHRP_DEBUG_KERNEL, - "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", - &addr, ifp->name, &c->cur.remote_nbma_natoa, - &c->cur.peer->vc->remote.nbma, &lladdr); - /* In case of shortcuts, nbma is given by lladdr, not - * vc->remote.nbma. - */ - netlink_update_binding(ifp, &addr, &lladdr); - } - } else { - state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state - : NUD_FAILED; - nhrp_cache_set_used(c, state == NUD_REACHABLE); - } -} - -static int netlink_route_recv(struct thread *t) -{ - uint8_t buf[ZNL_BUFFER_SIZE]; - int fd = THREAD_FD(t); - struct zbuf payload, zb; - struct nlmsghdr *n; - - zbuf_init(&zb, buf, sizeof(buf), 0); - while (zbuf_recv(&zb, fd) > 0) { - while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) { - debugf(NHRP_DEBUG_KERNEL, - "Netlink: Received msg_type %u, msg_flags %u", - n->nlmsg_type, n->nlmsg_flags); - switch (n->nlmsg_type) { - case RTM_GETNEIGH: - case RTM_NEWNEIGH: - case RTM_DELNEIGH: - netlink_neigh_msg(n, &payload); - break; - } - } - } - - thread_add_read(master, netlink_route_recv, 0, fd, NULL); - - return 0; + nhrp_send_zebra_nbr(proto, nbma, ifp); } static void netlink_log_register(int fd, int group) @@ -265,47 +149,64 @@ void netlink_set_nflog_group(int nlgroup) } } -void netlink_init(void) +void nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS) { - netlink_req_fd = znl_open(NETLINK_ROUTE, 0); - if (netlink_req_fd < 0) - return; + union sockunion addr = {}, lladdr = {}; + struct interface *ifp; + int state, ndm_state; + struct nhrp_cache *c; + struct zapi_neigh_ip api = {}; - netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH); - if (netlink_listen_fd < 0) + zclient_neigh_ip_decode(zclient->ibuf, &api); + if (api.ip_in.ipa_type == AF_UNSPEC) return; + sockunion_family(&addr) = api.ip_in.ipa_type; + memcpy((uint8_t *)sockunion_get_addr(&addr), &api.ip_in.ip.addr, + family2addrsize(api.ip_in.ipa_type)); - thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd, NULL); -} + sockunion_family(&lladdr) = api.ip_out.ipa_type; + if (api.ip_out.ipa_type != AF_UNSPEC) + memcpy((uint8_t *)sockunion_get_addr(&lladdr), + &api.ip_out.ip.addr, + family2addrsize(api.ip_out.ipa_type)); -int netlink_configure_arp(unsigned int ifindex, int pf) -{ - struct nlmsghdr *n; - struct ndtmsg *ndtm; - struct rtattr *rta; - struct zbuf *zb = zbuf_alloc(512); - int r; - - n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE); - ndtm = znl_push(zb, sizeof(*ndtm)); - *ndtm = (struct ndtmsg){ - .ndtm_family = pf, - }; + ifp = if_lookup_by_index(api.index, vrf_id); + ndm_state = api.ndm_state; - znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", - 10); - - rta = znl_rta_nested_push(zb, NDTA_PARMS); - znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex); - znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1); - znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0); - znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0); - znl_rta_nested_complete(zb, rta); - - znl_nlmsg_complete(zb, n); - r = zbuf_send(zb, netlink_req_fd); - zbuf_recv(zb, netlink_req_fd); - zbuf_free(zb); + if (!ifp) + return; + c = nhrp_cache_get(ifp, &addr, 0); + if (!c) + return; + debugf(NHRP_DEBUG_KERNEL, + "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", + (cmd == ZEBRA_NHRP_NEIGH_GET) + ? "who-has" + : (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? "new-neigh" + : "del-neigh", + &addr, ifp->name, &lladdr, ndm_state, c->used, c->cur.type); + if (cmd == ZEBRA_NHRP_NEIGH_GET) { + if (c->cur.type >= NHRP_CACHE_CACHED) { + nhrp_cache_set_used(c, 1); + debugf(NHRP_DEBUG_KERNEL, + "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", + &addr, ifp->name, &c->cur.remote_nbma_natoa, + &c->cur.peer->vc->remote.nbma, &lladdr); + /* In case of shortcuts, nbma is given by lladdr, not + * vc->remote.nbma. + */ + netlink_update_binding(ifp, &addr, &lladdr); + } + } else { + state = (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? ndm_state + : ZEBRA_NEIGH_STATE_FAILED; + nhrp_cache_set_used(c, state == ZEBRA_NEIGH_STATE_REACHABLE); + } +} - return r; +void netlink_init(void) +{ + netlink_req_fd = znl_open(NETLINK_ROUTE, 0); + if (netlink_req_fd < 0) + return; } diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index a6880054f..f6d89b141 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -298,6 +298,7 @@ void nhrp_interface_update(struct interface *ifp) if (!if_ad->configured) { os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi)); + nhrp_send_zebra_configure_arp(ifp, afi2family(afi)); if_ad->configured = 1; nhrp_interface_update_address(ifp, afi, 1); } diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index c1f615d0a..a44190d29 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -192,10 +192,10 @@ static void *nhrp_peer_create(void *data) return p; } -static void do_peer_hash_free(struct hash_bucket *hb, - void *arg __attribute__((__unused__))) +static void do_peer_hash_free(void *hb_data) { - struct nhrp_peer *p = hb->data; + struct nhrp_peer *p = (struct nhrp_peer *)hb_data; + nhrp_peer_check_delete(p); } @@ -207,9 +207,10 @@ void nhrp_peer_interface_del(struct interface *ifp) nifp->peer_hash ? nifp->peer_hash->count : 0); if (nifp->peer_hash) { - hash_iterate(nifp->peer_hash, do_peer_hash_free, NULL); + hash_clean(nifp->peer_hash, do_peer_hash_free); assert(nifp->peer_hash->count == 0); hash_free(nifp->peer_hash); + nifp->peer_hash = NULL; } } diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 7a4c57b5d..23fa0771e 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -79,6 +79,24 @@ static void nhrp_route_update_zebra(const struct prefix *p, } } +static void nhrp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, reg ? ZEBRA_NHRP_NEIGH_REGISTER : + ZEBRA_NHRP_NEIGH_UNREGISTER, + vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp) { struct route_node *rn; @@ -344,6 +362,8 @@ static void nhrp_zebra_connected(struct zclient *zclient) ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, true); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, true); } void nhrp_zebra_init(void) @@ -357,7 +377,9 @@ void nhrp_zebra_init(void) zclient->interface_address_delete = nhrp_interface_address_delete; zclient->redistribute_route_add = nhrp_route_read; zclient->redistribute_route_del = nhrp_route_read; - + zclient->neighbor_added = nhrp_neighbor_operation; + zclient->neighbor_removed = nhrp_neighbor_operation; + zclient->neighbor_get = nhrp_neighbor_operation; zclient_init(zclient, ZEBRA_ROUTE_NHRP, 0, &nhrpd_privs); } @@ -370,8 +392,47 @@ static void nhrp_table_node_cleanup(struct route_table *table, XFREE(MTYPE_NHRP_ROUTE, node->info); } +void nhrp_send_zebra_configure_arp(struct interface *ifp, int family) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) { + debugf(NHRP_DEBUG_COMMON, "%s() : zclient not ready", + __func__); + return; + } + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, + ZEBRA_CONFIGURE_ARP, + ifp->vrf_id); + stream_putc(s, family); + stream_putl(s, ifp->ifindex); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + +void nhrp_send_zebra_nbr(union sockunion *in, + union sockunion *out, + struct interface *ifp) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + s = zclient->obuf; + stream_reset(s); + zclient_neigh_ip_encode(s, out ? ZEBRA_NEIGH_IP_ADD : + ZEBRA_NEIGH_IP_DEL, in, out, + ifp); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + void nhrp_zebra_terminate(void) { + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, false); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, false); zclient_stop(zclient); zclient_free(zclient); diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index e4afb22f8..136f855df 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -88,7 +88,12 @@ static inline int notifier_active(struct notifier_list *l) void nhrp_zebra_init(void); void nhrp_zebra_terminate(void); - +void nhrp_send_zebra_configure_arp(struct interface *ifp, int family); +void nhrp_send_zebra_nbr(union sockunion *in, + union sockunion *out, + struct interface *ifp); +void nhrp_send_zebra_configure_arp(struct interface *ifp, + int family); struct zbuf; struct nhrp_vc; struct nhrp_cache; @@ -326,6 +331,7 @@ int nhrp_interface_up(ZAPI_CALLBACK_ARGS); int nhrp_interface_down(ZAPI_CALLBACK_ARGS); int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS); int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS); +void nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS); void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn); diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 9b117ddf0..c21e01601 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -470,10 +470,44 @@ static void vici_register_event(struct vici_conn *vici, const char *name) vici_submit(vici, obuf); } +static bool vici_charon_filepath_done; +static bool vici_charon_not_found; + +static char *vici_get_charon_filepath(void) +{ + static char buff[1200]; + FILE *fp; + char *ptr; + char line[1024]; + + if (vici_charon_filepath_done) + return (char *)buff; + fp = popen("ipsec --piddir", "r"); + if (!fp) { + if (!vici_charon_not_found) { + flog_err(EC_NHRP_SWAN, + "VICI: Failed to retrieve charon file path"); + vici_charon_not_found = true; + } + return NULL; + } + /* last line of output is used to get vici path */ + while (fgets(line, sizeof(line), fp) != NULL) { + ptr = strchr(line, '\n'); + if (ptr) + *ptr = '\0'; + snprintf(buff, sizeof(buff), "%s/charon.vici", line); + } + pclose(fp); + vici_charon_filepath_done = true; + return buff; +} + static int vici_reconnect(struct thread *t) { struct vici_conn *vici = THREAD_ARG(t); int fd; + char *file_path; vici->t_reconnect = NULL; if (vici->fd >= 0) @@ -481,6 +515,11 @@ static int vici_reconnect(struct thread *t) fd = sock_open_unix(VICI_SOCKET); if (fd < 0) { + file_path = vici_get_charon_filepath(); + if (file_path) + fd = sock_open_unix(file_path); + } + if (fd < 0) { debugf(NHRP_DEBUG_VICI, "%s: failure connecting VICI socket: %s", __func__, strerror(errno)); diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c index a23526af4..43ce97481 100644 --- a/nhrpd/zbuf.c +++ b/nhrpd/zbuf.c @@ -27,7 +27,7 @@ struct zbuf *zbuf_alloc(size_t size) { struct zbuf *zb; - zb = XMALLOC(MTYPE_ZBUF_DATA, sizeof(*zb) + size); + zb = XCALLOC(MTYPE_ZBUF_DATA, sizeof(*zb) + size); zbuf_init(zb, zb + 1, size, 0); zb->allocated = 1; diff --git a/tests/topotests/nhrp-topo/r1/nhrp4_cache.json b/tests/topotests/nhrp-topo/r1/nhrp4_cache.json new file mode 100644 index 000000000..6426a939b --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/nhrp4_cache.json @@ -0,0 +1,29 @@ +{ + "attr":{ + "entriesCount":2 + }, + "table":[ + { + "interface":"r1-gre0", + "type":"nhs", + "protocol":"10.255.255.2", + "nbma":"10.2.1.2", + "claimed_nbma":"10.2.1.2", + "used":false, + "timeout":true, + "auth":false, + "identity":"" + }, + { + "interface":"r1-gre0", + "type":"local", + "protocol":"10.255.255.1", + "nbma":"10.1.1.1", + "claimed_nbma":"10.1.1.1", + "used":false, + "timeout":false, + "auth":false, + "identity":"-" + } + ] +} diff --git a/tests/topotests/nhrp-topo/r1/nhrp_route4.json b/tests/topotests/nhrp-topo/r1/nhrp_route4.json new file mode 100644 index 000000000..68b5a6ece --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/nhrp_route4.json @@ -0,0 +1,25 @@ +{ + "10.255.255.2\/32":[ + { + "prefix":"10.255.255.2\/32", + "protocol":"nhrp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":10, + "metric":0, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-gre0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/nhrp-topo/r1/nhrpd.conf b/tests/topotests/nhrp-topo/r1/nhrpd.conf new file mode 100644 index 000000000..04114bdbe --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/nhrpd.conf @@ -0,0 +1,10 @@ +log stdout debugging +debug nhrp all +interface r1-gre0 + ip nhrp holdtime 500 + ip nhrp shortcut + ip nhrp network-id 42 + ip nhrp nhs dynamic nbma 10.2.1.2 + ip nhrp registration no-unique + tunnel source r1-eth0 +exit diff --git a/tests/topotests/nhrp-topo/r1/zebra.conf b/tests/topotests/nhrp-topo/r1/zebra.conf new file mode 100644 index 000000000..b45670fcb --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/zebra.conf @@ -0,0 +1,12 @@ +interface r1-eth0 + ip address 10.1.1.1/24 +! +ip route 10.2.1.0/24 10.1.1.3 +interface r1-gre0 + ip address 10.255.255.1/32 + no link-detect + ipv6 nd suppress-ra +exit +interface r1-eth1 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/nhrp-topo/r2/nhrp4_cache.json b/tests/topotests/nhrp-topo/r2/nhrp4_cache.json new file mode 100644 index 000000000..34558e0c2 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/nhrp4_cache.json @@ -0,0 +1,29 @@ +{ + "attr":{ + "entriesCount":2 + }, + "table":[ + { + "interface":"r2-gre0", + "type":"local", + "protocol":"10.255.255.2", + "nbma":"10.2.1.2", + "claimed_nbma":"10.2.1.2", + "used":false, + "timeout":false, + "auth":false, + "identity":"-" + }, + { + "interface":"r2-gre0", + "type":"dynamic", + "protocol":"10.255.255.1", + "nbma":"10.1.1.1", + "claimed_nbma":"10.1.1.1", + "used":false, + "timeout":true, + "auth":false, + "identity":"" + } + ] +} diff --git a/tests/topotests/nhrp-topo/r2/nhrp_route4.json b/tests/topotests/nhrp-topo/r2/nhrp_route4.json new file mode 100644 index 000000000..7393cba89 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/nhrp_route4.json @@ -0,0 +1,25 @@ +{ + "10.255.255.1\/32":[ + { + "prefix":"10.255.255.1\/32", + "protocol":"nhrp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":10, + "metric":0, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-gre0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/nhrp-topo/r2/nhrpd.conf b/tests/topotests/nhrp-topo/r2/nhrpd.conf new file mode 100644 index 000000000..e4f6fb744 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/nhrpd.conf @@ -0,0 +1,10 @@ +debug nhrp all +log stdout debugging +nhrp nflog-group 1 +interface r2-gre0 + ip nhrp holdtime 500 + ip nhrp redirect + ip nhrp network-id 42 + ip nhrp registration no-unique + tunnel source r2-eth0 +exit diff --git a/tests/topotests/nhrp-topo/r2/zebra.conf b/tests/topotests/nhrp-topo/r2/zebra.conf new file mode 100644 index 000000000..9f40d4d72 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/zebra.conf @@ -0,0 +1,12 @@ +interface r2-eth0 + ip address 10.2.1.2/24 +! +ip route 10.1.1.0/24 10.2.1.3 +interface r2-gre0 + ip address 10.255.255.2/32 + no link-detect + ipv6 nd suppress-ra +! +interface r2-eth1 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/nhrp-topo/r3/zebra.conf b/tests/topotests/nhrp-topo/r3/zebra.conf new file mode 100644 index 000000000..6d3d26797 --- /dev/null +++ b/tests/topotests/nhrp-topo/r3/zebra.conf @@ -0,0 +1,11 @@ +debug zebra kernel +debug zebra rib +debug zebra events +debug zebra packet +ip forwarding +interface r3-eth0 + ip address 10.1.1.3/24 +! +interface r3-eth1 + ip address 10.2.1.3/24 +exit diff --git a/tests/topotests/nhrp-topo/test_nhrp_topo.dot b/tests/topotests/nhrp-topo/test_nhrp_topo.dot new file mode 100644 index 000000000..6b68fb398 --- /dev/null +++ b/tests/topotests/nhrp-topo/test_nhrp_topo.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n2001:db8:4::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; + + r2 -- sw3 [label="eth2"]; + r4 -- sw3 [label="eth0"]; +} diff --git a/tests/topotests/nhrp-topo/test_nhrp_topo.py b/tests/topotests/nhrp-topo/test_nhrp_topo.py new file mode 100644 index 000000000..1687961f3 --- /dev/null +++ b/tests/topotests/nhrp-topo/test_nhrp_topo.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +# +# test_nhrp_topo.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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_nhrp_topo.py: Test the FRR/Quagga NHRP daemon +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class NHRPTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 3 routers. + 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['r3']) + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2']) + switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['r1']) + + +def _populate_iface(): + tgen = get_topogen() + cmds_tot_hub = ['ip tunnel add {0}-gre0 mode gre ttl 64 key 42 dev {0}-eth0 local 10.2.1.{1} remote 0.0.0.0', + 'ip link set dev {0}-gre0 up', + 'echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6'] + + cmds_tot = ['ip tunnel add {0}-gre0 mode gre ttl 64 key 42 dev {0}-eth0 local 10.1.1.{1} remote 0.0.0.0', + 'ip link set dev {0}-gre0 up', + 'echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6'] + + for cmd in cmds_tot_hub: + input = cmd.format('r2', '2') + logger.info('input: '+cmd) + output = tgen.net['r2'].cmd(cmd.format('r2', '2')) + logger.info('output: '+output); + + for cmd in cmds_tot: + input = cmd.format('r1', '1') + logger.info('input: '+cmd) + output = tgen.net['r1'].cmd(cmd.format('r1', '1')) + logger.info('output: '+output); + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(NHRPTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + ) + if rname in ('r1', 'r2'): + router.load_config( + TopoRouter.RD_NHRP, + os.path.join(CWD, '{}/nhrpd.conf'.format(rname)) + ) + + # Initialize all routers. + logger.info('Launching BGP, NHRP') + for name in router_list: + router = tgen.gears[name] + router.start() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged before checking for the NHRP + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking NHRP cache and IPv4 routes for convergence") + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + if rname == 'r3': + continue + + json_file = '{}/{}/nhrp4_cache.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip nhrp cache json', expected) + _, result = topotest.run_and_expect(test_func, None, count=40, + wait=0.5) + + output = router.vtysh_cmd('show ip nhrp cache') + logger.info(output) + + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + for rname, router in router_list.iteritems(): + if rname == 'r3': + continue + + json_file = '{}/{}/nhrp_route4.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route nhrp json', expected) + _, result = topotest.run_and_expect(test_func, None, count=40, + wait=0.5) + + output = router.vtysh_cmd('show ip route nhrp') + logger.info(output) + + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + for rname, router in router_list.iteritems(): + if rname == 'r3': + continue + logger.info('Dump neighbor information on {}-gre0'.format(rname)) + output = router.run('ip neigh show') + logger.info(output) + + +def test_nhrp_connection(): + "Assert that the NHRP peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pingrouter = tgen.gears['r1'] + logger.info('Check Ping IPv4 from R1 to R2 = 10.255.255.2)') + output = pingrouter.run('ping 10.255.255.2 -f -c 1000') + logger.info(output) + if '1000 packets transmitted, 1000 received' not in output: + assertmsg = 'expected ping IPv4 from R1 to R2 should be ok' + assert 0, assertmsg + else: + logger.info('Check Ping IPv4 from R1 to R2 OK') + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/zebra/interface.c b/zebra/interface.c index 3eeed9ac9..4b708496a 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -898,7 +898,8 @@ void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *ifp, * Remove and re-add any existing neighbor entry for this address, * since Netlink doesn't currently offer update message types. */ - kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); + kernel_neigh_update(0, ifp->ifindex, (void *)&ipv4_ll.s_addr, mac, 6, + ns_id, AF_INET, true); /* Add new neighbor entry. * @@ -910,8 +911,8 @@ void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *ifp, * they'll be useless to us. */ if (add) - kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, - ns_id); + kernel_neigh_update(add, ifp->ifindex, (void *)&ipv4_ll.s_addr, + mac, 6, ns_id, AF_INET, true); memcpy(&zif->neigh_mac[0], &mac[0], 6); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index e71e66245..adb61023c 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -1335,6 +1335,9 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: return netlink_put_neigh_update_msg(bth, ctx); case DPLANE_OP_RULE_ADD: diff --git a/zebra/rt.h b/zebra/rt.h index 48f1df286..daaa926a7 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -68,8 +68,11 @@ kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); #endif /* !HAVE_NETLINK */ -extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id); +extern int kernel_neigh_update(int cmd, int ifindex, void *addr, char *lla, + int llalen, ns_id_t ns_id, uint8_t family, + bool permanent); +extern int kernel_neigh_register(vrf_id_t vrf_id, struct zserv *client, + bool reg); extern int kernel_interface_set_master(struct interface *master, struct interface *slave); @@ -78,6 +81,9 @@ extern int mpls_kernel_init(void); extern uint32_t kernel_get_speed(struct interface *ifp, int *error); extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); +extern int kernel_configure_if_link(struct interface *ifp, + struct interface *link_ifp, ns_id_t ns_id); + /* * Southbound Initialization routines to get initial starting * state. diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 55e0775a8..d2ec7da57 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -185,6 +185,10 @@ static uint16_t neigh_state_to_netlink(uint16_t dplane_state) state |= NUD_PROBE; if (dplane_state & DPLANE_NUD_INCOMPLETE) state |= NUD_INCOMPLETE; + if (dplane_state & DPLANE_NUD_PERMANENT) + state |= NUD_PERMANENT; + if (dplane_state & DPLANE_NUD_FAILED) + state |= NUD_FAILED; return state; } @@ -1537,10 +1541,10 @@ static void _netlink_mpls_debug(int cmd, uint32_t label, const char *routedesc) routedesc, nl_msg_type_to_str(cmd), label); } -static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id) +static int netlink_neigh_update(int cmd, int ifindex, void *addr, char *lla, + int llalen, ns_id_t ns_id, uint8_t family, + bool permanent, uint8_t protocol) { - uint8_t protocol = RTPROT_ZEBRA; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -1556,15 +1560,24 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; - req.ndm.ndm_family = AF_INET; - req.ndm.ndm_state = NUD_PERMANENT; + req.ndm.ndm_family = family; req.ndm.ndm_ifindex = ifindex; req.ndm.ndm_type = RTN_UNICAST; + if (cmd == RTM_NEWNEIGH) { + if (!permanent) + req.ndm.ndm_state = NUD_REACHABLE; + else + req.ndm.ndm_state = NUD_PERMANENT; + } else + req.ndm.ndm_state = NUD_FAILED; nl_attr_put(&req.n, sizeof(req), NDA_PROTOCOL, &protocol, sizeof(protocol)); - nl_attr_put32(&req.n, sizeof(req), NDA_DST, addr); - nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); + req.ndm.ndm_type = RTN_UNICAST; + nl_attr_put(&req.n, sizeof(req), NDA_DST, addr, + family2addrsize(family)); + if (lla) + nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); @@ -2679,11 +2692,12 @@ int netlink_nexthop_read(struct zebra_ns *zns) } -int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id) +int kernel_neigh_update(int add, int ifindex, void *addr, char *lla, int llalen, + ns_id_t ns_id, uint8_t family, bool permanent) { return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex, - addr, lla, llalen, ns_id); + addr, lla, llalen, ns_id, family, permanent, + RTPROT_ZEBRA); } /** @@ -2694,7 +2708,9 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, * entry. * @ctx: Dataplane context * @cmd: Netlink command (RTM_NEWNEIGH or RTM_DELNEIGH) - * @mac: A neighbor cache link layer address + * @lla: A pointer to neighbor cache link layer address + * @llalen: Length of the pointer to neighbor cache link layer + * address * @ip: A neighbor cache n/w layer destination address * In the case of bridge FDB, this represnts the remote * VTEP IP. @@ -2706,18 +2722,18 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, * @state: NUD_* states * @data: data buffer pointer * @datalen: total amount of data buffer space + * @protocol: protocol information * * Return: 0 when the msg doesn't fit entirely in the buffer * otherwise the number of bytes written to buf. */ static ssize_t netlink_neigh_update_msg_encode( - const struct zebra_dplane_ctx *ctx, int cmd, const struct ethaddr *mac, - const struct ipaddr *ip, bool replace_obj, uint8_t family, uint8_t type, - uint8_t flags, uint16_t state, uint32_t nhg_id, bool nfy, + const struct zebra_dplane_ctx *ctx, int cmd, const void *lla, + int llalen, const struct ipaddr *ip, bool replace_obj, uint8_t family, + uint8_t type, uint8_t flags, uint16_t state, uint32_t nhg_id, bool nfy, uint8_t nfy_flags, bool ext, uint32_t ext_flags, void *data, - size_t datalen) + size_t datalen, uint8_t protocol) { - uint8_t protocol = RTPROT_ZEBRA; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -2749,8 +2765,8 @@ static ssize_t netlink_neigh_update_msg_encode( sizeof(protocol))) return 0; - if (mac) { - if (!nl_attr_put(&req->n, datalen, NDA_LLADDR, mac, 6)) + if (lla) { + if (!nl_attr_put(&req->n, datalen, NDA_LLADDR, lla, llalen)) return 0; } @@ -2814,12 +2830,17 @@ netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, void *buf, size_t buflen) { struct ethaddr dst_mac = {.octet = {0}}; + int proto = RTPROT_ZEBRA; + + if (dplane_ctx_get_type(ctx) != 0) + proto = zebra2proto(dplane_ctx_get_type(ctx)); return netlink_neigh_update_msg_encode( - ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false, - PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, - false /*nfy*/, 0 /*nfy_flags*/, false /*ext*/, 0 /*ext_flags*/, - buf, buflen); + ctx, cmd, (const void *)&dst_mac, ETH_ALEN, + dplane_ctx_neigh_get_ipaddr(ctx), false, PF_BRIDGE, 0, NTF_SELF, + (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, false /*nfy*/, + 0 /*nfy_flags*/, false /*ext*/, 0 /*ext_flags*/, buf, buflen, + proto); } #ifndef NDA_RTA @@ -3185,6 +3206,10 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, uint32_t update_flags; bool nfy = false; uint8_t nfy_flags = 0; + int proto = RTPROT_ZEBRA; + + if (dplane_ctx_get_type(ctx) != 0) + proto = zebra2proto(dplane_ctx_get_type(ctx)); cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL ? RTM_NEWNEIGH : RTM_DELNEIGH; @@ -3251,9 +3276,10 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, } total = netlink_neigh_update_msg_encode( - ctx, cmd, dplane_ctx_mac_get_addr(ctx), &vtep_ip, true, - AF_BRIDGE, 0, flags, state, nhg_id, nfy, nfy_flags, - false /*ext*/, 0 /*ext_flags*/, data, datalen); + ctx, cmd, (const void *)dplane_ctx_mac_get_addr(ctx), ETH_ALEN, + &vtep_ip, true, AF_BRIDGE, 0, flags, state, nhg_id, nfy, + nfy_flags, false /*ext*/, 0 /*ext_flags*/, data, datalen, + proto); return total; } @@ -3307,6 +3333,8 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) bool local_inactive; uint32_t ext_flags = 0; bool dp_static = false; + int l2_len = 0; + int cmd; ndm = NLMSG_DATA(h); @@ -3348,6 +3376,43 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) if (h->nlmsg_type == RTM_NEWNEIGH && !(ndm->ndm_state & NUD_VALID)) netlink_handle_5549(ndm, zif, ifp, &ip, true); + /* we send link layer information to client: + * - nlmsg_type = RTM_DELNEIGH|NEWNEIGH|GETNEIGH + * - struct ipaddr ( for DEL and GET) + * - struct ethaddr mac; (for NEW) + */ + if (h->nlmsg_type == RTM_NEWNEIGH) + cmd = ZEBRA_NHRP_NEIGH_ADDED; + else if (h->nlmsg_type == RTM_GETNEIGH) + cmd = ZEBRA_NHRP_NEIGH_GET; + else if (h->nlmsg_type == RTM_DELNEIGH) + cmd = ZEBRA_NHRP_NEIGH_REMOVED; + else { + zlog_debug("%s(): unknown nlmsg type %u", __func__, + h->nlmsg_type); + return 0; + } + if (tb[NDA_LLADDR]) { + /* copy LLADDR information */ + l2_len = RTA_PAYLOAD(tb[NDA_LLADDR]); + memcpy(&mac, RTA_DATA(tb[NDA_LLADDR]), l2_len); + } + if (l2_len == IPV4_MAX_BYTELEN || l2_len == 0) { + union sockunion link_layer_ipv4; + + if (l2_len) { + sockunion_family(&link_layer_ipv4) = AF_INET; + memcpy((void *)sockunion_get_addr(&link_layer_ipv4), + &mac, l2_len); + } else + sockunion_family(&link_layer_ipv4) = AF_UNSPEC; + zsend_nhrp_neighbor_notify(cmd, ifp, &ip, ndm->ndm_state, + &link_layer_ipv4); + } + + if (h->nlmsg_type == RTM_GETNEIGH) + return 0; + /* The neighbor is present on an SVI. From this, we locate the * underlying * bridge because we're only interested in neighbors on a VxLAN bridge. @@ -3615,7 +3680,8 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) int len; struct ndmsg *ndm; - if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH)) + if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH + || h->nlmsg_type == RTM_GETNEIGH)) return 0; /* Length validity. */ @@ -3656,19 +3722,42 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, void *buf, size_t buflen) { const struct ipaddr *ip; - const struct ethaddr *mac; + const struct ethaddr *mac = NULL; + const struct ipaddr *link_ip = NULL; + const void *link_ptr = NULL; + char buf2[ETHER_ADDR_STRLEN]; + + int llalen; uint8_t flags; uint16_t state; uint8_t family; uint32_t update_flags; uint32_t ext_flags = 0; bool ext = false; + int proto = RTPROT_ZEBRA; + + if (dplane_ctx_get_type(ctx) != 0) + proto = zebra2proto(dplane_ctx_get_type(ctx)); ip = dplane_ctx_neigh_get_ipaddr(ctx); - mac = dplane_ctx_neigh_get_mac(ctx); - if (is_zero_mac(mac)) - mac = NULL; + if (dplane_ctx_get_op(ctx) == DPLANE_OP_NEIGH_IP_INSTALL + || dplane_ctx_get_op(ctx) == DPLANE_OP_NEIGH_IP_DELETE) { + link_ip = dplane_ctx_neigh_get_link_ip(ctx); + llalen = IPADDRSZ(link_ip); + link_ptr = (const void *)&(link_ip->ip.addr); + ipaddr2str(link_ip, buf2, sizeof(buf2)); + } else { + mac = dplane_ctx_neigh_get_mac(ctx); + llalen = ETH_ALEN; + link_ptr = (const void *)mac; + if (is_zero_mac(mac)) + mac = NULL; + if (mac) + prefix_mac2str(mac, buf2, sizeof(buf2)); + else + snprintf(buf2, sizeof(buf2), "null"); + } update_flags = dplane_ctx_neigh_get_update_flags(ctx); flags = neigh_flags_to_netlink(dplane_ctx_neigh_get_flags(ctx)); state = neigh_state_to_netlink(dplane_ctx_neigh_get_state(ctx)); @@ -3682,7 +3771,7 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, */ if (update_flags & DPLANE_NEIGH_WAS_STATIC) ext = true; - } else { + } else if (!(update_flags & DPLANE_NEIGH_NO_EXTENSION)) { ext = true; /* local neigh */ if (update_flags & DPLANE_NEIGH_SET_STATIC) @@ -3690,15 +3779,63 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Tx %s family %s IF %s(%u) Neigh %pIA MAC %pEA flags 0x%x state 0x%x %sext_flags 0x%x", + "Tx %s family %s IF %s(%u) Neigh %pIA %s %s flags 0x%x state 0x%x %sext_flags 0x%x", nl_msg_type_to_str(cmd), nl_family_to_str(family), dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), - ip, mac, flags, state, ext ? "ext " : "", ext_flags); + ip, link_ip ? "Link " : "MAC ", buf2, flags, state, + ext ? "ext " : "", ext_flags); return netlink_neigh_update_msg_encode( - ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state, - 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, ext, ext_flags, buf, - buflen); + ctx, cmd, link_ptr, llalen, ip, true, family, RTN_UNICAST, + flags, state, 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, ext, + ext_flags, buf, buflen, proto); +} + +static int netlink_neigh_table_update_ctx(const struct zebra_dplane_ctx *ctx, + void *data, size_t datalen) +{ + struct { + struct nlmsghdr n; + struct ndtmsg ndtm; + char buf[]; + } *req = data; + struct rtattr *nest; + uint8_t family; + ifindex_t idx; + uint32_t val; + + if (datalen < sizeof(*req)) + return 0; + memset(req, 0, sizeof(*req)); + family = dplane_ctx_neightable_get_family(ctx); + idx = dplane_ctx_get_ifindex(ctx); + + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE; + req->n.nlmsg_type = RTM_SETNEIGHTBL; + req->ndtm.ndtm_family = family; + + nl_attr_put(&req->n, datalen, NDTA_NAME, + family == AF_INET ? "arp_cache" : "ndisc_cache", 10); + nest = nl_attr_nest(&req->n, datalen, NDTA_PARMS); + if (nest == NULL) + return 0; + if (!nl_attr_put(&req->n, datalen, NDTPA_IFINDEX, &idx, sizeof(idx))) + return 0; + val = dplane_ctx_neightable_get_app_probes(ctx); + if (!nl_attr_put(&req->n, datalen, NDTPA_APP_PROBES, &val, sizeof(val))) + return 0; + val = dplane_ctx_neightable_get_mcast_probes(ctx); + if (!nl_attr_put(&req->n, datalen, NDTPA_MCAST_PROBES, &val, + sizeof(val))) + return 0; + val = dplane_ctx_neightable_get_ucast_probes(ctx); + if (!nl_attr_put(&req->n, datalen, NDTPA_UCAST_PROBES, &val, + sizeof(val))) + return 0; + nl_attr_nest_end(&req->n, nest); + + return NLMSG_ALIGN(req->n.nlmsg_len); } static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, @@ -3710,9 +3847,11 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); break; case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_DELETE: ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); break; case DPLANE_OP_VTEP_ADD: @@ -3723,6 +3862,9 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + ret = netlink_neigh_table_update_ctx(ctx, buf, buflen); + break; default: ret = -1; } diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index a0f401c33..ada828d01 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -362,8 +362,14 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) return ZEBRA_DPLANE_REQUEST_SUCCESS; } -int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id) +int kernel_neigh_register(vrf_id_t vrf_id, struct zserv *client, bool reg) +{ + /* TODO */ + return 0; +} + +int kernel_neigh_update(int add, int ifindex, void *addr, char *lla, int llalen, + ns_id_t ns_id, uint8_t family, bool permanent) { /* TODO */ return 0; @@ -388,6 +394,12 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx) return ZEBRA_DPLANE_REQUEST_SUCCESS; } +int kernel_configure_if_link(struct interface *ifp, struct interface *link_ifp, + ns_id_t ns_id) +{ + return 0; +} + extern int kernel_interface_set_master(struct interface *master, struct interface *slave) { diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index b48291441..e854d7ff3 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -974,6 +974,37 @@ void zsend_ipset_entry_notify_owner(const struct zebra_dplane_ctx *ctx, zserv_send_message(client, s); } +void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4) +{ + struct stream *s; + struct listnode *node, *nnode; + struct zserv *client; + afi_t afi; + union sockunion ip; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Notifying Neighbor entry (%u)", + __PRETTY_FUNCTION__, cmd); + + sockunion_family(&ip) = ipaddr_family(ipaddr); + afi = family2afi(sockunion_family(&ip)); + memcpy((char *)sockunion_get_addr(&ip), &ipaddr->ip.addr, + family2addrsize(sockunion_family(&ip))); + + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + if (!vrf_bitmap_check(client->nhrp_neighinfo[afi], ifp->vrf_id)) + continue; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + zclient_neigh_ip_encode(s, cmd, &ip, link_layer_ipv4, ifp); + stream_putw_at(s, 0, stream_get_endp(s)); + zserv_send_message(client, s); + } +} + + /* Router-id is updated. Send ZEBRA_ROUTER_ID_UPDATE to client. */ int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p, vrf_id_t vrf_id) @@ -2277,6 +2308,7 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) vrf_bitmap_unset(client->redist[afi][i], zvrf_id(zvrf)); vrf_bitmap_unset(client->redist_default[afi], zvrf_id(zvrf)); vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); } } @@ -3167,6 +3199,97 @@ stream_failure: return; } + +static inline void zebra_neigh_register(ZAPI_HANDLER_ARGS) +{ + afi_t afi; + + STREAM_GETW(msg, afi); + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while registering for neighbors notifications", + afi); + goto stream_failure; + } + vrf_bitmap_set(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); +stream_failure: + return; +} + +static inline void zebra_neigh_unregister(ZAPI_HANDLER_ARGS) +{ + afi_t afi; + + STREAM_GETW(msg, afi); + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while unregistering from neighbor notifications", + afi); + goto stream_failure; + } + vrf_bitmap_unset(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); +stream_failure: + return; +} + +static inline void zebra_configure_arp(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + uint8_t fam; + ifindex_t idx; + struct interface *ifp; + + s = msg; + STREAM_GETC(s, fam); + if (fam != AF_INET && fam != AF_INET6) + return; + STREAM_GETL(s, idx); + ifp = if_lookup_by_index_per_ns(zvrf->zns, idx); + if (!ifp) + return; + dplane_neigh_table_update(ifp, fam, 1, 0, 0); +stream_failure: + return; +} + +static inline void zebra_neigh_ip_add(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_neigh_ip api = {}; + int ret; + const struct interface *ifp; + + s = msg; + ret = zclient_neigh_ip_decode(s, &api); + if (ret < 0) + return; + ifp = if_lookup_by_index(api.index, zvrf_id(zvrf)); + if (!ifp) + return; + dplane_neigh_ip_update(DPLANE_OP_NEIGH_IP_INSTALL, ifp, &api.ip_out, + &api.ip_in, api.ndm_state, client->proto); +} + + +static inline void zebra_neigh_ip_del(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_neigh_ip api = {}; + int ret; + struct interface *ifp; + + s = msg; + ret = zclient_neigh_ip_decode(s, &api); + if (ret < 0) + return; + ifp = if_lookup_by_index(api.index, zvrf_id(zvrf)); + if (!ifp) + return; + dplane_neigh_ip_update(DPLANE_OP_NEIGH_IP_DELETE, ifp, &api.ip_out, + &api.ip_in, api.ndm_state, client->proto); +} + + static inline void zread_iptable(ZAPI_HANDLER_ARGS) { struct zebra_pbr_iptable *zpi = @@ -3352,6 +3475,11 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ROUTE_NOTIFY_REQUEST] = zread_route_notify_request, [ZEBRA_EVPN_REMOTE_NH_ADD] = zebra_evpn_proc_remote_nh, [ZEBRA_EVPN_REMOTE_NH_DEL] = zebra_evpn_proc_remote_nh, + [ZEBRA_NEIGH_IP_ADD] = zebra_neigh_ip_add, + [ZEBRA_NEIGH_IP_DEL] = zebra_neigh_ip_del, + [ZEBRA_NHRP_NEIGH_REGISTER] = zebra_neigh_register, + [ZEBRA_NHRP_NEIGH_UNREGISTER] = zebra_neigh_unregister, + [ZEBRA_CONFIGURE_ARP] = zebra_configure_arp, }; /* diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index ca471f8d9..0beb3cc10 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -104,6 +104,9 @@ extern int zsend_label_manager_connect_response(struct zserv *client, extern int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, char *name, int status); +extern void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4); extern int zsend_client_close_notify(struct zserv *client, struct zserv *closed_client); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 18fe0a7e8..c8ee8f905 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -220,13 +220,26 @@ struct dplane_mac_info { */ struct dplane_neigh_info { struct ipaddr ip_addr; - struct ethaddr mac; + union { + struct ethaddr mac; + struct ipaddr ip_addr; + } link; uint32_t flags; uint16_t state; uint32_t update_flags; }; /* + * Neighbor Table + */ +struct dplane_neigh_table { + uint8_t family; + uint32_t app_probes; + uint32_t ucast_probes; + uint32_t mcast_probes; +}; + +/* * Policy based routing rule info for the dataplane */ struct dplane_ctx_rule { @@ -313,6 +326,7 @@ struct zebra_dplane_ctx { struct zebra_pbr_ipset_entry entry; struct zebra_pbr_ipset_info info; } ipset_entry; + struct dplane_neigh_table neightable; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -452,6 +466,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_ipset_entry_in; _Atomic uint32_t dg_ipset_entry_errors; + _Atomic uint32_t dg_neightable_in; + _Atomic uint32_t dg_neightable_errors; + /* Dataplane pthread */ struct frr_pthread *dg_pthread; @@ -496,12 +513,11 @@ static enum zebra_dplane_result mac_update_common( vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, uint32_t update_flags); -static enum zebra_dplane_result neigh_update_internal( - enum dplane_op_e op, - const struct interface *ifp, - const struct ethaddr *mac, - const struct ipaddr *ip, - uint32_t flags, uint16_t state, uint32_t update_flags); +static enum zebra_dplane_result +neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, + const void *link, int link_family, + const struct ipaddr *ip, uint32_t flags, uint16_t state, + uint32_t update_flags, int protocol); /* * Public APIs @@ -669,6 +685,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_UPDATE: case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_NONE: case DPLANE_OP_IPSET_ADD: case DPLANE_OP_IPSET_DELETE: @@ -677,6 +695,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + break; case DPLANE_OP_IPTABLE_ADD: case DPLANE_OP_IPTABLE_DELETE: if (ctx->u.iptable.interface_name_list) { @@ -950,6 +970,15 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_IPSET_ENTRY_DELETE: ret = "IPSET_ENTRY_DELETE"; break; + case DPLANE_OP_NEIGH_IP_INSTALL: + ret = "NEIGH_IP_INSTALL"; + break; + case DPLANE_OP_NEIGH_IP_DELETE: + ret = "NEIGH_IP_DELETE"; + break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + ret = "NEIGH_TABLE_UPDATE"; + break; } return ret; @@ -1711,11 +1740,18 @@ const struct ipaddr *dplane_ctx_neigh_get_ipaddr( return &(ctx->u.neigh.ip_addr); } +const struct ipaddr * +dplane_ctx_neigh_get_link_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.neigh.link.ip_addr); +} + const struct ethaddr *dplane_ctx_neigh_get_mac( const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.neigh.mac); + return &(ctx->u.neigh.link.mac); } uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx) @@ -1978,6 +2014,37 @@ uint32_t dplane_intf_extra_get_status(const struct dplane_intf_extra *ptr) return ptr->status; } +uint8_t dplane_ctx_neightable_get_family(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.family; +} + +uint32_t +dplane_ctx_neightable_get_app_probes(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.app_probes; +} + +uint32_t +dplane_ctx_neightable_get_ucast_probes(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.ucast_probes; +} + +uint32_t +dplane_ctx_neightable_get_mcast_probes(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.mcast_probes; +} + /* * End of interface extra info accessors */ @@ -3436,6 +3503,41 @@ enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, } /* + * API to configure link local with either MAC address or IP information + */ +enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, + const struct interface *ifp, + struct ipaddr *link_ip, + struct ipaddr *ip, + uint32_t ndm_state, int protocol) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + uint16_t state = 0; + uint32_t update_flags; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[PREFIX_STRLEN], buf2[PREFIX_STRLEN]; + + ipaddr2str(link_ip, buf1, sizeof(buf1)); + ipaddr2str(ip, buf2, sizeof(buf2)); + zlog_debug("init link ctx %s: ifp %s, ip %s link %s", + dplane_op2str(op), ifp->name, buf1, buf2); + } + if (ndm_state == ZEBRA_NEIGH_STATE_REACHABLE) + state = DPLANE_NUD_REACHABLE; + else if (ndm_state == ZEBRA_NEIGH_STATE_FAILED) + state = DPLANE_NUD_FAILED; + + update_flags = DPLANE_NEIGH_NO_EXTENSION; + + result = neigh_update_internal(op, ifp, (const void *)link_ip, + ipaddr_family(link_ip), ip, 0, state, + update_flags, protocol); + + return result; +} + +/* * Enqueue local mac add (or update). */ enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, @@ -3584,9 +3686,9 @@ enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, if (was_static) update_flags |= DPLANE_NEIGH_WAS_STATIC; - result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, flags, DPLANE_NUD_NOARP, - update_flags); + result = neigh_update_internal( + DPLANE_OP_NEIGH_INSTALL, ifp, (const void *)mac, AF_ETHERNET, + ip, flags, DPLANE_NUD_NOARP, update_flags, 0); return result; } @@ -3618,9 +3720,9 @@ enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, if (set_router) ntf |= DPLANE_NTF_ROUTER; - result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, ntf, - state, update_flags); + result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, ifp, + (const void *)mac, AF_ETHERNET, ip, ntf, + state, update_flags, 0); return result; } @@ -3636,8 +3738,8 @@ enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, update_flags |= DPLANE_NEIGH_REMOTE; - result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, - ifp, NULL, ip, 0, 0, update_flags); + result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, ifp, NULL, + AF_ETHERNET, ip, 0, 0, update_flags, 0); return result; } @@ -3660,8 +3762,8 @@ enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, SET_IPADDR_V4(&addr); addr.ipaddr_v4 = *ip; - result = neigh_update_internal(DPLANE_OP_VTEP_ADD, - ifp, &mac, &addr, 0, 0, 0); + result = neigh_update_internal(DPLANE_OP_VTEP_ADD, ifp, &mac, + AF_ETHERNET, &addr, 0, 0, 0, 0); return result; } @@ -3685,8 +3787,9 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, SET_IPADDR_V4(&addr); addr.ipaddr_v4 = *ip; - result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, - ifp, &mac, &addr, 0, 0, 0); + result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, ifp, + (const void *)&mac, AF_ETHERNET, &addr, + 0, 0, 0, 0); return result; } @@ -3696,8 +3799,65 @@ enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, { enum zebra_dplane_result result; - result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip, - DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0); + result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, + AF_ETHERNET, ip, DPLANE_NTF_USE, + DPLANE_NUD_INCOMPLETE, 0, 0); + + return result; +} + +enum zebra_dplane_result dplane_neigh_table_update(const struct interface *ifp, + const uint8_t family, + const uint32_t app_probes, + const uint32_t ucast_probes, + const uint32_t mcast_probes) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret; + struct zebra_dplane_ctx *ctx = NULL; + struct zebra_ns *zns; + enum dplane_op_e op = DPLANE_OP_NEIGH_TABLE_UPDATE; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + zlog_debug("set neigh ctx %s: ifp %s, family %s", + dplane_op2str(op), ifp->name, family2str(family)); + } + + ctx = dplane_ctx_alloc(); + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf_id; + + zns = zebra_ns_lookup(ifp->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; + + /* Init the neighbor-specific data area */ + memset(&ctx->u.neightable, 0, sizeof(ctx->u.neightable)); + + ctx->u.neightable.family = family; + ctx->u.neightable.app_probes = app_probes; + ctx->u.neightable.ucast_probes = ucast_probes; + ctx->u.neightable.mcast_probes = mcast_probes; + + /* Enqueue for processing on the dplane pthread */ + ret = dplane_update_enqueue(ctx); + + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_neightable_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + /* Error counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_neightable_errors, 1, + memory_order_relaxed); + dplane_ctx_free(&ctx); + } return result; } @@ -3706,27 +3866,43 @@ enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, * Common helper api for neighbor updates */ static enum zebra_dplane_result -neigh_update_internal(enum dplane_op_e op, - const struct interface *ifp, - const struct ethaddr *mac, - const struct ipaddr *ip, - uint32_t flags, uint16_t state, - uint32_t update_flags) +neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, + const void *link, const int link_family, + const struct ipaddr *ip, uint32_t flags, uint16_t state, + uint32_t update_flags, int protocol) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; struct zebra_dplane_ctx *ctx = NULL; struct zebra_ns *zns; + const struct ethaddr *mac = NULL; + const struct ipaddr *link_ip = NULL; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("init neigh ctx %s: ifp %s, mac %pEA, ip %pIA", - dplane_op2str(op), ifp->name, mac, ip); + if (link_family == AF_ETHERNET) + mac = (const struct ethaddr *)link; + else + link_ip = (const struct ipaddr *)link; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[PREFIX_STRLEN]; + + buf1[0] = '\0'; + if (link_family == AF_ETHERNET) + prefix_mac2str(mac, buf1, sizeof(buf1)); + else + ipaddr2str(link_ip, buf1, sizeof(buf1)); + zlog_debug("init neigh ctx %s: ifp %s, %s %s, ip %pIA", + dplane_op2str(op), ifp->name, + link_family == AF_ETHERNET ? "mac " : "link ", + buf1, ip); + } ctx = dplane_ctx_alloc(); ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; ctx->zd_vrf_id = ifp->vrf_id; + dplane_ctx_set_type(ctx, protocol); zns = zebra_ns_lookup(ifp->vrf_id); dplane_ctx_ns_init(ctx, zns, false); @@ -3739,7 +3915,10 @@ neigh_update_internal(enum dplane_op_e op, ctx->u.neigh.ip_addr = *ip; if (mac) - ctx->u.neigh.mac = *mac; + ctx->u.neigh.link.mac = *mac; + else if (link_ip) + ctx->u.neigh.link.ip_addr = *link_ip; + ctx->u.neigh.flags = flags; ctx->u.neigh.state = state; ctx->u.neigh.update_flags = update_flags; @@ -4048,6 +4227,13 @@ int dplane_show_helper(struct vty *vty, bool detailed) memory_order_relaxed); vty_out(vty, "IPset entry updates: %" PRIu64 "\n", incoming); vty_out(vty, "IPset entry errors: %" PRIu64 "\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_neightable_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_neightable_errors, + memory_order_relaxed); + vty_out(vty, "Neighbor Table updates: %"PRIu64"\n", incoming); + vty_out(vty, "Neighbor Table errors: %"PRIu64"\n", errs); return CMD_SUCCESS; } @@ -4433,6 +4619,8 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, sizeof(buf)); @@ -4485,6 +4673,13 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) dplane_op2str(dplane_ctx_get_op(ctx)), ipent.unique, ctx); } break; + + case DPLANE_OP_NEIGH_TABLE_UPDATE: + zlog_debug("Dplane neigh table op %s, ifp %s, family %s", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifname(ctx), + family2str(dplane_ctx_neightable_get_family(ctx))); + break; } } @@ -4568,6 +4763,8 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, memory_order_relaxed); @@ -4604,6 +4801,13 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) memory_order_relaxed); break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit( + &zdplane_info.dg_neightable_errors, 1, + memory_order_relaxed); + break; + /* Ignore 'notifications' - no-op */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 4913ca251..8d51d93cd 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -165,6 +165,12 @@ enum dplane_op_e { DPLANE_OP_IPSET_DELETE, DPLANE_OP_IPSET_ENTRY_ADD, DPLANE_OP_IPSET_ENTRY_DELETE, + + /* LINK LAYER IP address update */ + DPLANE_OP_NEIGH_IP_INSTALL, + DPLANE_OP_NEIGH_IP_DELETE, + + DPLANE_OP_NEIGH_TABLE_UPDATE, }; /* @@ -184,6 +190,8 @@ enum dplane_op_e { #define DPLANE_NUD_NOARP 0x04 #define DPLANE_NUD_PROBE 0x08 #define DPLANE_NUD_INCOMPLETE 0x10 +#define DPLANE_NUD_PERMANENT 0x20 +#define DPLANE_NUD_FAILED 0x40 /* MAC update flags - dplane_mac_info.update_flags */ #define DPLANE_MAC_REMOTE (1 << 0) @@ -196,6 +204,7 @@ enum dplane_op_e { #define DPLANE_NEIGH_WAS_STATIC (1 << 1) #define DPLANE_NEIGH_SET_STATIC (1 << 2) #define DPLANE_NEIGH_SET_INACTIVE (1 << 3) +#define DPLANE_NEIGH_NO_EXTENSION (1 << 4) #define DPLANE_BR_PORT_NON_DF (1 << 0) @@ -458,6 +467,8 @@ const struct ipaddr *dplane_ctx_neigh_get_ipaddr( const struct zebra_dplane_ctx *ctx); const struct ethaddr *dplane_ctx_neigh_get_mac( const struct zebra_dplane_ctx *ctx); +const struct ipaddr * +dplane_ctx_neigh_get_link_ip(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx); @@ -507,6 +518,15 @@ dplane_ctx_get_br_port_sph_filters(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_br_port_backup_nhg_id(const struct zebra_dplane_ctx *ctx); +/* Accessors for neighbor table information */ +uint8_t dplane_ctx_neightable_get_family(const struct zebra_dplane_ctx *ctx); +uint32_t +dplane_ctx_neightable_get_app_probes(const struct zebra_dplane_ctx *ctx); +uint32_t +dplane_ctx_neightable_get_mcast_probes(const struct zebra_dplane_ctx *ctx); +uint32_t +dplane_ctx_neightable_get_ucast_probes(const struct zebra_dplane_ctx *ctx); + /* Namespace info - esp. for netlink communication */ const struct zebra_dplane_info *dplane_ctx_get_ns( const struct zebra_dplane_ctx *ctx); @@ -585,6 +605,16 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, const struct connected *ifc); /* + * Link layer operations for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, + const struct interface *ifp, + struct ipaddr *link_ip, + struct ipaddr *ip, + uint32_t ndm_state, + int protocol); + +/* * Enqueue evpn mac operations for the dataplane. */ enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, @@ -656,6 +686,15 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, const struct ipaddr *ip); +/* + * Enqueue a neighbor table parameter set + */ +enum zebra_dplane_result dplane_neigh_table_update(const struct interface *ifp, + const uint8_t family, + const uint32_t app_probes, + const uint32_t ucast_probes, + const uint32_t mcast_probes); + /* Forward ref of zebra_pbr_rule */ struct zebra_pbr_rule; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 12ed024a6..7edf02289 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2898,6 +2898,8 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_RULE_ADD: @@ -2912,6 +2914,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_IPSET_DELETE: case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: break; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ffe4be855..82a0e6d01 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3924,10 +3924,13 @@ static int rib_process_dplane_results(struct thread *thread) case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_NONE: /* Don't expect this: just return the struct? */ dplane_ctx_fini(&ctx); diff --git a/zebra/zserv.c b/zebra/zserv.c index 6c5eebe6f..f89b6fe47 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -638,6 +638,7 @@ static void zserv_client_free(struct zserv *client) vrf_bitmap_free(client->redist_default[afi]); vrf_bitmap_free(client->ridinfo[afi]); + vrf_bitmap_free(client->nhrp_neighinfo[afi]); } /* @@ -760,6 +761,7 @@ static struct zserv *zserv_client_create(int sock) client->redist[afi][i] = vrf_bitmap_init(); client->redist_default[afi] = vrf_bitmap_init(); client->ridinfo[afi] = vrf_bitmap_init(); + client->nhrp_neighinfo[afi] = vrf_bitmap_init(); } /* Add this client to linked list. */ diff --git a/zebra/zserv.h b/zebra/zserv.h index c60799b8b..203670ac1 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -137,6 +137,9 @@ struct zserv { /* Router-id information. */ vrf_bitmap_t ridinfo[AFI_MAX]; + /* Router-id information. */ + vrf_bitmap_t nhrp_neighinfo[AFI_MAX]; + bool notify_owner; /* Indicates if client is synchronous. */ |