From 7723e8d3fd86a2e00b99df6fbb64083c23c1e637 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 12 Dec 2019 16:06:59 +0100 Subject: [PATCH] zebra: link layer config and notification, implementation in zebra zebra implements zebra api for configuring link layer information. that can be an arp entry (for ipv4) or ipv6 neighbor discovery entry. This can also be an ipv4/ipv6 entry associated to an underlay ipv4 address, as it is used in gre point to multipoint interfaces. this api will also be used as monitoring. an hash list is instantiated into zebra (this is the vrf bitmap). each client interested in those entries in a specific vrf, will listen for following messages: entries added, removed, or who-has messages. Signed-off-by: Philippe Guibert --- lib/log.c | 4 ++- lib/zclient.h | 2 ++ zebra/rt.h | 2 ++ zebra/rt_netlink.c | 33 ++++++++++++++++++- zebra/rt_socket.c | 6 ++++ zebra/zapi_msg.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ zebra/zapi_msg.h | 3 ++ zebra/zserv.c | 2 ++ zebra/zserv.h | 3 ++ 9 files changed, 134 insertions(+), 2 deletions(-) diff --git a/lib/log.c b/lib/log.c index 9a8a91b004..6e8df99512 100644 --- a/lib/log.c +++ b/lib/log.c @@ -467,7 +467,9 @@ static const struct zebra_desc_table command_types[] = { 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_GET), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_REGISTER), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_UNREGISTER)}; #undef DESC_ENTRY diff --git a/lib/zclient.h b/lib/zclient.h index a99f2eba16..d0d77f93c2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -226,6 +226,8 @@ typedef enum { ZEBRA_NHRP_NEIGH_ADDED, ZEBRA_NHRP_NEIGH_REMOVED, ZEBRA_NHRP_NEIGH_GET, + ZEBRA_NHRP_NEIGH_REGISTER, + ZEBRA_NHRP_NEIGH_UNREGISTER, } zebra_message_types_t; enum zebra_error_types { diff --git a/zebra/rt.h b/zebra/rt.h index 48f1df2868..00ff378753 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -70,6 +70,8 @@ kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); 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_register(vrf_id_t vrf_id, struct zserv *client, + bool reg); extern int kernel_interface_set_master(struct interface *master, struct interface *slave); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 55e0775a8c..82ef78d299 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -3307,6 +3307,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 +3350,34 @@ 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) + zsend_nhrp_neighbor_notify(cmd, ifp, &ip, ndm->ndm_state, + &mac, l2_len); + + 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 +3645,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. */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index a0f401c334..5fdf589624 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -362,6 +362,12 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) return ZEBRA_DPLANE_REQUEST_SUCCESS; } +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, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index b482914418..304a6a03f1 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -974,6 +974,52 @@ 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, + void *mac, int macsize) +{ + struct stream *s; + struct listnode *node, *nnode; + struct zserv *client; + afi_t afi; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Notifying Neighbor entry (%u)", + __PRETTY_FUNCTION__, cmd); + + if (ipaddr->ipa_type == IPADDR_V4) + afi = AFI_IP; + else if (ipaddr->ipa_type == IPADDR_V6) + afi = AFI_IP6; + else + return; + 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_create_header(s, cmd, ifp->vrf_id); + stream_putl(s, ifp->ifindex); + if (ipaddr->ipa_type == IPADDR_V4) { + stream_putw(s, AF_INET); + stream_put(s, &ipaddr->ip._v4_addr, IPV4_MAX_BYTELEN); + } else if (ipaddr->ipa_type == IPADDR_V6) { + stream_putw(s, AF_INET6); + stream_put(s, &ipaddr->ip._v6_addr, IPV6_MAX_BYTELEN); + } else + return; + stream_putl(s, ndm_state); + stream_putl(s, macsize); + if (mac) + stream_put(s, mac, macsize); + 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 +2323,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 +3214,38 @@ 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 zread_iptable(ZAPI_HANDLER_ARGS) { struct zebra_pbr_iptable *zpi = @@ -3352,6 +3431,8 @@ 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_NHRP_NEIGH_REGISTER] = zebra_neigh_register, + [ZEBRA_NHRP_NEIGH_UNREGISTER] = zebra_neigh_unregister, }; /* diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index ca471f8d98..2822619da9 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, + void *mac, int macsize); extern int zsend_client_close_notify(struct zserv *client, struct zserv *closed_client); diff --git a/zebra/zserv.c b/zebra/zserv.c index 6c5eebe6fe..f89b6fe478 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 c60799b8ba..203670ac1d 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. */