obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
- hook_register(zapi_client_close, release_daemon_label_chunks);
+ hook_register(zserv_client_close, release_daemon_label_chunks);
}
/**
/* Needed for BSD routing socket. */
pid = getpid();
+ /* Intialize pthread library */
frr_pthread_init();
- /* This must be done only after locking pidfile (bug #403). */
- zebra_zserv_socket_init(zserv_path);
+ /* Start Zebra API server */
+ zserv_start(zserv_path);
/* Init label manager */
label_manager_init(lblmgr_path);
return;
tbl_mgr.lc_list = list_new();
tbl_mgr.lc_list->del = delete_table_chunk;
- hook_register(zapi_client_close, release_daemon_table_chunks);
+ hook_register(zserv_client_close, release_daemon_table_chunks);
}
/**
zserv_encode_interface(s, ifp);
client->ifadd_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* Interface deletion from zebra daemon. */
zserv_encode_interface(s, ifp);
client->ifdel_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
int zsend_vrf_add(struct zserv *client, struct zebra_vrf *zvrf)
zserv_encode_vrf(s, zvrf);
client->vrfadd_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* VRF deletion from zebra daemon. */
zserv_encode_vrf(s, zvrf);
client->vrfdel_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
int zsend_interface_link_params(struct zserv *client, struct interface *ifp)
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* Interface address is added/deleted. Send ZEBRA_INTERFACE_ADDRESS_ADD or
stream_putw_at(s, 0, stream_get_endp(s));
client->connected_rt_add_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
static int zsend_interface_nbr_address(int cmd, struct zserv *client,
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* Interface address addition. */
stream_putw_at(s, 0, stream_get_endp(s));
client->if_vrfchg_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* Add new nbr connected IPv6 address */
else
client->ifdown_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p,
zebra_route_string(api.type), api.vrf_id,
buf_prefix);
}
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
struct stream *s;
uint8_t blen;
- client = zebra_find_client(re->type, re->instance);
+ client = zserv_find_client(re->type, re->instance);
if (!client || !client->notify_owner) {
if (IS_ZEBRA_DEBUG_PACKET) {
char buff[PREFIX_STRLEN];
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
stream_putw_at(s, 0, stream_get_endp(s));
- zebra_server_send_message(client, s);
+ zserv_send_message(client, s);
}
void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset,
stream_put(s, ipset->ipset_name, ZEBRA_IPSET_NAME_SIZE);
stream_putw_at(s, 0, stream_get_endp(s));
- zebra_server_send_message(client, s);
+ zserv_send_message(client, s);
}
void zsend_ipset_entry_notify_owner(struct zebra_pbr_ipset_entry *ipset,
stream_put(s, ipset->backpointer->ipset_name, ZEBRA_IPSET_NAME_SIZE);
stream_putw_at(s, 0, stream_get_endp(s));
- zebra_server_send_message(client, s);
+ zserv_send_message(client, s);
}
void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable,
stream_putl(s, iptable->unique);
stream_putw_at(s, 0, stream_get_endp(s));
- zebra_server_send_message(client, s);
+ zserv_send_message(client, s);
}
/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
/* Put length at the first point of the stream. */
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* Send response to a get label chunk request to client */
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
static int zsend_table_manager_connect_response(struct zserv *client,
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* Inbound message handling ------------------------------------------------ */
stream_putl(s, multipath_num);
stream_putw_at(s, 0, stream_get_endp(s));
- zebra_server_send_message(client, s);
+ zserv_send_message(client, s);
}
/* Tie up route-type and client->sock */
stream_put_prefix(s, &rn->p);
stream_putl(s, fec->label);
stream_putw_at(s, 0, stream_get_endp(s));
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
if (!mpls_processq_init(&zebrad))
mpls_enabled = 1;
- hook_register(zapi_client_close, zebra_mpls_cleanup_fecs_for_client);
+ hook_register(zserv_client_close, zebra_mpls_cleanup_fecs_for_client);
}
stream_putl(s, suc);
stream_putw_at(s, 0, stream_get_endp(s));
- zebra_server_send_message(client, s);
+ zserv_send_message(client, s);
}
ptm_cb.ptm_sock = -1;
- hook_register(zapi_client_close, zebra_ptm_bfd_client_deregister);
+ hook_register(zserv_client_close, zebra_ptm_bfd_client_deregister);
}
void zebra_ptm_finish(void)
stream_putw_at(s, 0, stream_get_endp(s));
client->if_bfd_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
void zebra_interface_bfd_update(struct interface *ifp, struct prefix *dp,
stream_putw_at(s, 0, stream_get_endp(s));
client->bfd_peer_replay_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
void zebra_bfd_peer_replay_req(void)
RB_INIT(zebra_pw_head, &zvrf->pseudowires);
RB_INIT(zebra_static_pw_head, &zvrf->static_pseudowires);
- hook_register(zapi_client_close, zebra_pw_client_close);
+ hook_register(zserv_client_close, zebra_pw_client_close);
}
void zebra_pw_exit(struct zebra_vrf *zvrf)
void zebra_rnh_init(void)
{
- hook_register(zapi_client_close, zebra_client_cleanup_rnh);
+ hook_register(zserv_client_close, zebra_client_cleanup_rnh);
}
static inline struct route_table *get_rnh_table(vrf_id_t vrfid, int family,
client->nh_last_upd_time = monotime(NULL);
client->last_write_cmd = cmd;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
static void print_nh(struct nexthop *nexthop, struct vty *vty)
struct zserv *client = NULL;
struct stream *s = NULL;
- client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
else
client->macipdel_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
struct zserv *client;
struct stream *s;
- client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
zebra_route_string(client->proto));
client->vniadd_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
struct zserv *client;
struct stream *s;
- client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
zebra_route_string(client->proto));
client->vnidel_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
struct ethaddr rmac;
char buf[ETHER_ADDR_STRLEN];
- client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
zebra_route_string(client->proto));
client->l3vniadd_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/*
struct stream *s = NULL;
struct zserv *client = NULL;
- client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
zebra_route_string(client->proto));
client->l3vnidel_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
struct stream *s = NULL;
char buf[PREFIX_STRLEN];
- client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
if (!client)
return 0;
else
client->prefixdel_cnt++;
- return zebra_server_send_message(client, s);
+ return zserv_send_message(client, s);
}
/* re-add remote rmac if needed */
*
* These are used almost exclusively by client threads to drive their own event
* loops. The only exception is in zebra_client_create(), which pushes an
- * initial ZEBRA_READ event to start the API handler loop.
+ * initial ZSERV_CLIENT_READ event to start the API handler loop.
*/
-enum event { ZEBRA_READ, ZEBRA_WRITE };
+enum zserv_client_event {
+ /* Schedule a socket read */
+ ZSERV_CLIENT_READ,
+ /* Schedule a buffer write */
+ ZSERV_CLIENT_WRITE,
+ /* Schedule a buffer flush */
+ ZSERV_CLIENT_FLUSH_DATA,
+};
-/* Forward declarations for functions used from both threads. */
-static void zebra_event(struct zserv *client, enum event event);
-static int zebra_client_handle_close(struct thread *thread);
-static int zserv_process_messages(struct thread *thread);
+/*
+ * Main thread events.
+ *
+ * These are used by client threads to notify the main thread about various
+ * events and to make processing requests.
+ */
+enum zserv_event {
+ /* Schedule listen job on Zebra API socket */
+ ZSERV_ACCEPT,
+ /* The calling client has packets on its input buffer */
+ ZSERV_PROCESS_MESSAGES,
+ /* The calling client wishes to be killed */
+ ZSERV_HANDLE_CLOSE,
+};
+
+/*
+ * Zebra server event driver for all client threads.
+ *
+ * This is essentially a wrapper around thread_add_event() that centralizes
+ * those scheduling calls into one place.
+ *
+ * All calls to this function schedule an event on the pthread running the
+ * provided client.
+ *
+ * client
+ * the client in question, and thread target
+ *
+ * event
+ * the event to notify them about
+ */
+static void zserv_client_event(struct zserv *client,
+ enum zserv_client_event event);
+
+/*
+ * Zebra server event driver for the main thread.
+ *
+ * This is essentially a wrapper around thread_add_event() that centralizes
+ * those scheduling calls into one place.
+ *
+ * All calls to this function schedule an event on Zebra's main pthread.
+ *
+ * client
+ * the client in question
+ *
+ * event
+ * the event to notify the main thread about
+ */
+static void zserv_event(struct zserv *client, enum zserv_event event);
/* Client thread lifecycle -------------------------------------------------- */
zlog_hexdump(msg->data, STREAM_READABLE(msg));
}
-
/*
* Gracefully shut down a client connection.
*
*
* Must be called from the client pthread, never the main thread.
*/
-static void zebra_client_close(struct zserv *client)
+static void zserv_client_close(struct zserv *client)
{
THREAD_OFF(client->t_read);
THREAD_OFF(client->t_write);
- thread_add_event(zebrad.master, zebra_client_handle_close, client, 0,
- NULL);
+ zserv_event(client, ZSERV_HANDLE_CLOSE);
}
static int zserv_flush_data(struct thread *thread)
zlog_warn(
"%s: buffer_flush_available failed on zserv client fd %d, closing",
__func__, client->sock);
- zebra_client_close(client);
+ zserv_client_close(client);
client = NULL;
break;
case BUFFER_PENDING:
- client->t_write = NULL;
- thread_add_write(client->pthread->master, zserv_flush_data, client,
- client->sock, &client->t_write);
+ zserv_client_event(client, ZSERV_CLIENT_FLUSH_DATA);
break;
case BUFFER_EMPTY:
break;
client->sock);
zlog_warn("%s: closing connection to %s", __func__,
zebra_route_string(client->proto));
- zebra_client_close(client);
+ zserv_client_close(client);
return -1;
case BUFFER_PENDING:
- thread_add_write(client->pthread->master, zserv_flush_data,
- client, client->sock, &client->t_write);
+ zserv_client_event(client, ZSERV_CLIENT_FLUSH_DATA);
break;
case BUFFER_EMPTY:
break;
pthread_mutex_lock(&client->obuf_mtx);
{
if (client->obuf_fifo->count)
- zebra_event(client, ZEBRA_WRITE);
+ zserv_client_event(client, ZSERV_CLIENT_WRITE);
}
pthread_mutex_unlock(&client->obuf_mtx);
}
#endif
-
/*
* Read and process data from a client socket.
*
zebrad.packets_to_process - p2p);
/* Schedule job to process those packets */
- thread_add_event(zebrad.master, zserv_process_messages, client, 0,
- NULL);
+ zserv_event(client, ZSERV_PROCESS_MESSAGES);
/* Reschedule ourselves */
- zebra_event(client, ZEBRA_READ);
+ zserv_client_event(client, ZSERV_CLIENT_READ);
return 0;
zread_fail:
- zebra_client_close(client);
+ zserv_client_close(client);
return -1;
}
-static void zebra_event(struct zserv *client, enum event event)
+static void zserv_client_event(struct zserv *client,
+ enum zserv_client_event event)
{
switch (event) {
- case ZEBRA_READ:
+ case ZSERV_CLIENT_READ:
thread_add_read(client->pthread->master, zserv_read, client,
client->sock, &client->t_read);
break;
- case ZEBRA_WRITE:
+ case ZSERV_CLIENT_WRITE:
thread_add_write(client->pthread->master, zserv_write, client,
client->sock, &client->t_write);
break;
+ case ZSERV_CLIENT_FLUSH_DATA:
+ thread_add_write(client->pthread->master, zserv_flush_data,
+ client, client->sock, &client->t_write);
+ break;
}
}
/* Main thread lifecycle ---------------------------------------------------- */
-
/*
* Read and process messages from a client.
*
pthread_mutex_lock(&client->ibuf_mtx);
{
if (client->ibuf_fifo->count)
- thread_add_event(zebrad.master, zserv_process_messages,
- client, 0, NULL);
+ zserv_event(client, ZSERV_PROCESS_MESSAGES);
}
pthread_mutex_unlock(&client->ibuf_mtx);
return 0;
}
-int zebra_server_send_message(struct zserv *client, struct stream *msg)
+int zserv_send_message(struct zserv *client, struct stream *msg)
{
pthread_mutex_lock(&client->obuf_mtx);
{
stream_fifo_push(client->obuf_fifo, msg);
- zebra_event(client, ZEBRA_WRITE);
+ zserv_client_event(client, ZSERV_CLIENT_WRITE);
}
pthread_mutex_unlock(&client->obuf_mtx);
return 0;
/* Hooks for client connect / disconnect */
-DEFINE_HOOK(zapi_client_connect, (struct zserv *client), (client));
-DEFINE_KOOH(zapi_client_close, (struct zserv *client), (client));
+DEFINE_HOOK(zserv_client_connect, (struct zserv *client), (client));
+DEFINE_KOOH(zserv_client_close, (struct zserv *client), (client));
/*
* Deinitialize zebra client.
* managed by the owning pthread and any tasks associated with them must have
* been stopped prior to invoking this function.
*/
-static void zebra_client_free(struct zserv *client)
+static void zserv_client_free(struct zserv *client)
{
- hook_call(zapi_client_close, client);
+ hook_call(zserv_client_close, client);
/* Close file descriptor. */
if (client->sock) {
* terminate the client thread, update relevant internal datastructures and
* free any resources allocated by the main thread.
*/
-static int zebra_client_handle_close(struct thread *thread)
+static int zserv_handle_client_close(struct thread *thread)
{
struct zserv *client = THREAD_ARG(thread);
client->pthread = NULL;
listnode_delete(zebrad.client_list, client);
- zebra_client_free(client);
+ zserv_client_free(client);
return 0;
}
* sock
* client's socket file descriptor
*/
-static void zebra_client_create(int sock)
+static void zserv_client_create(int sock)
{
struct zserv *client;
int i;
zebra_vrf_update_all(client);
/* start read loop */
- zebra_event(client, ZEBRA_READ);
+ zserv_client_event(client, ZSERV_CLIENT_READ);
/* call callbacks */
- hook_call(zapi_client_connect, client);
+ hook_call(zserv_client_connect, client);
/* start pthread */
frr_pthread_run(client->pthread, NULL);
}
-/* Accept code of zebra server socket. */
-static int zebra_accept(struct thread *thread)
+/*
+ * Accept socket connection.
+ */
+static int zserv_accept(struct thread *thread)
{
int accept_sock;
int client_sock;
accept_sock = THREAD_FD(thread);
/* Reregister myself. */
- thread_add_read(zebrad.master, zebra_accept, NULL, accept_sock, NULL);
+ zserv_event(NULL, ZSERV_ACCEPT);
len = sizeof(struct sockaddr_in);
client_sock = accept(accept_sock, (struct sockaddr *)&client, &len);
set_nonblocking(client_sock);
/* Create new zebra client. */
- zebra_client_create(client_sock);
+ zserv_client_create(client_sock);
return 0;
}
-/* Make zebra server socket, wiping any existing one (see bug #403). */
-void zebra_zserv_socket_init(char *path)
+void zserv_start(char *path)
{
int ret;
- int sock;
mode_t old_mask;
struct sockaddr_storage sa;
socklen_t sa_len;
old_mask = umask(0077);
/* Make UNIX domain socket. */
- sock = socket(sa.ss_family, SOCK_STREAM, 0);
- if (sock < 0) {
+ zebrad.sock = socket(sa.ss_family, SOCK_STREAM, 0);
+ if (zebrad.sock < 0) {
zlog_warn("Can't create zserv socket: %s",
safe_strerror(errno));
zlog_warn(
}
if (sa.ss_family != AF_UNIX) {
- sockopt_reuseaddr(sock);
- sockopt_reuseport(sock);
+ sockopt_reuseaddr(zebrad.sock);
+ sockopt_reuseport(zebrad.sock);
} else {
struct sockaddr_un *suna = (struct sockaddr_un *)&sa;
if (suna->sun_path[0])
}
zserv_privs.change(ZPRIVS_RAISE);
- setsockopt_so_recvbuf(sock, 1048576);
- setsockopt_so_sendbuf(sock, 1048576);
+ setsockopt_so_recvbuf(zebrad.sock, 1048576);
+ setsockopt_so_sendbuf(zebrad.sock, 1048576);
zserv_privs.change(ZPRIVS_LOWER);
if (sa.ss_family != AF_UNIX && zserv_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
- ret = bind(sock, (struct sockaddr *)&sa, sa_len);
+ ret = bind(zebrad.sock, (struct sockaddr *)&sa, sa_len);
if (ret < 0) {
zlog_warn("Can't bind zserv socket on %s: %s", path,
safe_strerror(errno));
zlog_warn(
"zebra can't provide full functionality due to above error");
- close(sock);
+ close(zebrad.sock);
+ zebrad.sock = -1;
return;
}
if (sa.ss_family != AF_UNIX && zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
- ret = listen(sock, 5);
+ ret = listen(zebrad.sock, 5);
if (ret < 0) {
zlog_warn("Can't listen to zserv socket %s: %s", path,
safe_strerror(errno));
zlog_warn(
"zebra can't provide full functionality due to above error");
- close(sock);
+ close(zebrad.sock);
+ zebrad.sock = -1;
return;
}
umask(old_mask);
- thread_add_read(zebrad.master, zebra_accept, NULL, sock, NULL);
+ zserv_event(NULL, ZSERV_ACCEPT);
}
+void zserv_event(struct zserv *client, enum zserv_event event)
+{
+ switch (event) {
+ case ZSERV_ACCEPT:
+ thread_add_read(zebrad.master, zserv_accept, NULL, zebrad.sock,
+ NULL);
+ break;
+ case ZSERV_PROCESS_MESSAGES:
+ thread_add_event(zebrad.master, zserv_process_messages, client,
+ 0, NULL);
+ break;
+ case ZSERV_HANDLE_CLOSE:
+ thread_add_event(zebrad.master, zserv_handle_client_close,
+ client, 0, NULL);
+ }
+}
+
+
/* General purpose ---------------------------------------------------------- */
#define ZEBRA_TIME_BUF 32
client->v6_route_del_cnt);
}
-struct zserv *zebra_find_client(uint8_t proto, unsigned short instance)
+struct zserv *zserv_find_client(uint8_t proto, unsigned short instance)
{
struct listnode *node, *nnode;
struct zserv *client;
{
/* Client list init. */
zebrad.client_list = list_new();
- zebrad.client_list->del = (void (*)(void *))zebra_client_free;
+ zebrad.client_list->del = (void (*)(void *)) zserv_client_free;
+
+ /* Misc init. */
+ zebrad.sock = -1;
install_element(ENABLE_NODE, &show_zebra_client_cmd);
install_element(ENABLE_NODE, &show_zebra_client_summary_cmd);
struct zebra_vrf *zvrf
/* Hooks for client connect / disconnect */
-DECLARE_HOOK(zapi_client_connect, (struct zserv *client), (client));
-DECLARE_KOOH(zapi_client_close, (struct zserv *client), (client));
+DECLARE_HOOK(zserv_client_connect, (struct zserv *client), (client));
+DECLARE_KOOH(zserv_client_close, (struct zserv *client), (client));
/* Zebra instance */
struct zebra_t {
struct thread_master *master;
struct list *client_list;
+ /* Socket */
+ int sock;
+
/* default table */
uint32_t rtm_table_default;
extern struct zebra_t zebrad;
extern unsigned int multipath_num;
-/* Prototypes. */
+/*
+ * Initialize Zebra API server.
+ *
+ * Installs CLI commands and creates the client list.
+ */
extern void zserv_init(void);
-extern void zebra_zserv_socket_init(char *path);
-extern int zebra_server_send_message(struct zserv *client, struct stream *msg);
-extern struct zserv *zebra_find_client(uint8_t proto, unsigned short instance);
+/*
+ * Start Zebra API server.
+ *
+ * Allocates resources, creates the server socket and begins listening on the
+ * socket.
+ *
+ * path
+ * where to place the Unix domain socket
+ */
+extern void zserv_start(char *path);
+
+/*
+ * Send a message to a connected Zebra API client.
+ *
+ * client
+ * the client to send to
+ *
+ * msg
+ * the message to send
+ */
+extern int zserv_send_message(struct zserv *client, struct stream *msg);
+
+/*
+ * Retrieve a client by its protocol and instance number.
+ *
+ * proto
+ * protocol number
+ *
+ * instance
+ * instance number
+ *
+ * Returns:
+ * The Zebra API client.
+ */
+extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance);
#if defined(HANDLE_ZAPI_FUZZING)
extern void zserv_read_file(char *input);