From d68e74b41cc3575986a5269fe04ebe01c71ffc0e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jakub=20Urba=C5=84czyk?= Date: Thu, 6 Aug 2020 13:07:01 +0200 Subject: [PATCH] lib, zebra: add support for sending ARP requests MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit We can make the Linux kernel send an ARP/NDP request by adding a neighbour with the 'NUD_INCOMPLETE' state and the 'NTF_USE' flag. This commit adds new dataplane operation as well as new zapi message to allow other daemons send ARP/NDP requests. Signed-off-by: Jakub Urbańczyk --- doc/developer/zebra.rst | 4 +++- doc/user/sharp.rst | 6 ++++++ lib/log.c | 3 ++- lib/zclient.c | 20 ++++++++++++++++++++ lib/zclient.h | 5 +++++ zebra/kernel_netlink.c | 1 + zebra/kernel_socket.c | 1 + zebra/rt_netlink.c | 5 +++++ zebra/zapi_msg.c | 39 +++++++++++++++++++++++++++++++++++++-- zebra/zebra_dplane.c | 29 ++++++++++++++++++++++++----- zebra/zebra_dplane.h | 11 +++++++++++ zebra/zebra_nhg.c | 1 + zebra/zebra_rib.c | 1 + 13 files changed, 117 insertions(+), 9 deletions(-) diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst index edd8b9ae29..d51cbc9a14 100644 --- a/doc/developer/zebra.rst +++ b/doc/developer/zebra.rst @@ -382,9 +382,11 @@ Zebra Protocol Commands +------------------------------------+-------+ | ZEBRA_OPAQUE_UNREGISTER | 109 | +------------------------------------+-------+ +| ZEBRA_NEIGH_DISCOVER | 110 | ++------------------------------------+-------+ Dataplane batching -========================= +================== Dataplane batching is an optimization feature that reduces the processing time involved in the user space to kernel space transition for every message we diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index 5a27be22b9..57ef141c7e 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -138,3 +138,9 @@ keyword. At present, no sharp commands will be preserved in the config. Remove a test zapi client session that was created with the specified session id. + +.. index:: sharp neigh discover +.. clicmd:: sharp neigh discover [vrf NAME] IFNAME + + Send an ARP/NDP request to trigger the addition of a neighbor in the ARP + table. diff --git a/lib/log.c b/lib/log.c index 202d6d858f..4054185019 100644 --- a/lib/log.c +++ b/lib/log.c @@ -451,7 +451,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES), DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE), DESC_ENTRY(ZEBRA_OPAQUE_REGISTER), - DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER)}; + DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_DISCOVER)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/zclient.c b/lib/zclient.c index 6449fe15b9..808aa18bbe 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3953,3 +3953,23 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) stream_failure: return 0; } + +int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p) +{ + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf_id); + stream_putl(s, ifp->ifindex); + + stream_putc(s, p->family); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, prefix_blen(p)); + + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} diff --git a/lib/zclient.h b/lib/zclient.h index bcdae85823..dab384d5ec 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -214,6 +214,7 @@ typedef enum { ZEBRA_OPAQUE_MESSAGE, ZEBRA_OPAQUE_REGISTER, ZEBRA_OPAQUE_UNREGISTER, + ZEBRA_NEIGH_DISCOVER, } zebra_message_types_t; enum zebra_error_types { @@ -956,6 +957,10 @@ enum zapi_opaque_registry { */ extern int zclient_send_hello(struct zclient *client); +extern int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p); + #ifdef __cplusplus } #endif diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index b04076b945..d0c1bc812d 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -1417,6 +1417,7 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: return netlink_put_neigh_update_msg(bth, ctx); case DPLANE_OP_RULE_ADD: diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 9e56e2988d..4c29b999f0 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1526,6 +1526,7 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: res = kernel_neigh_update_ctx(ctx); break; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 190af3f59d..4a6839d3b1 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -147,6 +147,8 @@ static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags) flags |= NTF_EXT_LEARNED; if (dplane_flags & DPLANE_NTF_ROUTER) flags |= NTF_ROUTER; + if (dplane_flags & DPLANE_NTF_USE) + flags |= NTF_USE; return flags; } @@ -166,6 +168,8 @@ static uint16_t neigh_state_to_netlink(uint16_t dplane_state) state |= NUD_NOARP; if (dplane_state & DPLANE_NUD_PROBE) state |= NUD_PROBE; + if (dplane_state & DPLANE_NUD_INCOMPLETE) + state |= NUD_INCOMPLETE; return state; } @@ -3631,6 +3635,7 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); break; case DPLANE_OP_NEIGH_DELETE: diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 17114f820c..fbed99dc59 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2962,6 +2962,41 @@ stream_failure: return; } +static inline void zread_neigh_discover(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + ifindex_t ifindex; + struct interface *ifp; + struct prefix p; + struct ipaddr ip; + + s = msg; + + STREAM_GETL(s, ifindex); + + ifp = if_lookup_by_index_per_ns(zvrf->zns, ifindex); + if (!ifp) { + zlog_debug("Failed to lookup ifindex: %u", ifindex); + return; + } + + STREAM_GETC(s, p.family); + STREAM_GETC(s, p.prefixlen); + STREAM_GET(&p.u.prefix, s, prefix_blen(&p)); + + if (p.family == AF_INET) + SET_IPADDR_V4(&ip); + else + SET_IPADDR_V6(&ip); + + memcpy(&ip.ip.addr, &p.u.prefix, prefix_blen(&p)); + + dplane_neigh_discover(ifp, &ip); + +stream_failure: + return; +} + static void zsend_error_msg(struct zserv *client, enum zebra_error_types error, struct zmsghdr *bad_hdr) { @@ -3065,8 +3100,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, - [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities -}; + [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities, + [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover}; #if defined(HANDLE_ZAPI_FUZZING) extern struct zebra_privs_t zserv_privs; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index d34e1433ce..8ab7a7f5bc 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -185,7 +185,7 @@ struct dplane_mac_info { }; /* - * EVPN neighbor info for the dataplane + * Neighbor info for the dataplane */ struct dplane_neigh_info { struct ipaddr ip_addr; @@ -607,6 +607,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_DELETE: case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } @@ -839,6 +840,10 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_RULE_UPDATE: ret = "RULE_UPDATE"; break; + + case DPLANE_OP_NEIGH_DISCOVER: + ret = "NEIGH_DISCOVER"; + break; } return ret; @@ -3223,8 +3228,19 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, return result; } +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip) +{ + enum zebra_dplane_result result; + + result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip, + DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0); + + return result; +} + /* - * Common helper api for evpn neighbor updates + * Common helper api for neighbor updates */ static enum zebra_dplane_result neigh_update_internal(enum dplane_op_e op, @@ -3243,9 +3259,8 @@ neigh_update_internal(enum dplane_op_e op, char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN]; zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s", - dplane_op2str(op), + dplane_op2str(op), ifp->name, prefix_mac2str(mac, buf1, sizeof(buf1)), - ifp->name, ipaddr2str(ip, buf2, sizeof(buf2))); } @@ -3446,7 +3461,9 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed) out_max = atomic_load_explicit(&prov->dp_out_max, memory_order_relaxed); - vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", out: %"PRIu64", q_max: %"PRIu64"\n", + vty_out(vty, + "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64 + ", out: %" PRIu64 ", q_max: %" PRIu64 "\n", prov->dp_name, prov->dp_id, in, in_max, out, out_max); DPLANE_LOCK(); @@ -3780,6 +3797,7 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, sizeof(buf)); @@ -3886,6 +3904,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, memory_order_relaxed); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 3b5eda2486..c68a617f38 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -149,6 +149,9 @@ enum dplane_op_e { DPLANE_OP_RULE_ADD, DPLANE_OP_RULE_DELETE, DPLANE_OP_RULE_UPDATE, + + /* Link layer address discovery */ + DPLANE_OP_NEIGH_DISCOVER, }; /* @@ -160,12 +163,14 @@ enum dplane_op_e { /* Neighbor cache flags */ #define DPLANE_NTF_EXT_LEARNED 0x01 #define DPLANE_NTF_ROUTER 0x02 +#define DPLANE_NTF_USE 0x04 /* Neighbor cache states */ #define DPLANE_NUD_REACHABLE 0x01 #define DPLANE_NUD_STALE 0x02 #define DPLANE_NUD_NOARP 0x04 #define DPLANE_NUD_PROBE 0x08 +#define DPLANE_NUD_INCOMPLETE 0x10 /* MAC update flags - dplane_mac_info.update_flags */ #define DPLANE_MAC_REMOTE (1 << 0) @@ -572,6 +577,12 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, const struct in_addr *ip, vni_t vni); +/* + * Enqueue a neighbour discovery request for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip); + /* Forward ref of zebra_pbr_rule */ struct zebra_pbr_rule; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 1c5b843539..b8faaa43fd 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2663,6 +2663,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_DELETE: case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 55dccc4a12..d1d56f2cdb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3764,6 +3764,7 @@ static int rib_process_dplane_results(struct thread *thread) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: /* Don't expect this: just return the struct? */ dplane_ctx_fini(&ctx); -- 2.39.5