zebra: Add Generic Netlink socket

zebra already supports several Netlink sockets which allow it to
communicate with the kernel. Each Netlink socket has a specific purpose:
we have a socket for incoming events from the kernel, a socket for
programming the dataplane, a socket for the kernel messages, a socket
used as the command channel. All the currently supported sockets are
based on the `NETLINK_ROUTE` protocol.

This commit adds a new Netlink socket that allows zebra to send
commands to the kernel using the `Generic Netlink` protocol.

Signed-off-by: Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
This commit is contained in:
Carmine Scarpitta 2022-11-17 00:15:40 +01:00 committed by Carmine Scarpitta
parent f998057fb3
commit e14d1dcdbc
3 changed files with 66 additions and 3 deletions

View file

@ -316,9 +316,6 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
int sock; int sock;
int namelen; int namelen;
if (nl_family != NETLINK_ROUTE)
return -1;
frr_with_privs(&zserv_privs) { frr_with_privs(&zserv_privs) {
sock = ns_socket(AF_NETLINK, SOCK_RAW, nl_family, ns_id); sock = ns_socket(AF_NETLINK, SOCK_RAW, nl_family, ns_id);
if (sock < 0) { if (sock < 0) {
@ -1230,6 +1227,33 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
return netlink_talk_info(filter, n, &dp_info, startup); return netlink_talk_info(filter, n, &dp_info, startup);
} }
/*
* Synchronous version of netlink_talk_info. Converts args to suit the
* common version, which is suitable for both sync and async use.
*/
int ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct zebra_ns *zns, bool startup)
{
struct zebra_dplane_info dp_info;
if (zns->ge_netlink_cmd.sock < 0)
return -1;
/* Increment sequence number before capturing snapshot of ns socket
* info.
*/
zns->ge_netlink_cmd.seq = zebra_router_get_next_sequence();
/* Capture info in intermediate info struct */
dp_info.ns_id = zns->ns_id;
dp_info.is_cmd = true;
dp_info.sock = zns->ge_netlink_cmd.sock;
dp_info.seq = zns->ge_netlink_cmd.seq;
return netlink_talk_info(filter, n, &dp_info, startup);
}
/* Issue request message to kernel via netlink socket. GET messages /* Issue request message to kernel via netlink socket. GET messages
* are issued through this interface. * are issued through this interface.
*/ */
@ -1808,6 +1832,19 @@ void kernel_init(struct zebra_ns *zns)
kernel_netlink_nlsock_insert(&zns->netlink_dplane_in); kernel_netlink_nlsock_insert(&zns->netlink_dplane_in);
/* Generic Netlink socket. */
snprintf(zns->ge_netlink_cmd.name, sizeof(zns->ge_netlink_cmd.name),
"generic-netlink-cmd (NS %u)", zns->ns_id);
zns->ge_netlink_cmd.sock = -1;
if (netlink_socket(&zns->ge_netlink_cmd, 0, 0, 0, zns->ns_id,
NETLINK_GENERIC) < 0) {
zlog_warn("Failure to create %s socket",
zns->ge_netlink_cmd.name);
}
if (zns->ge_netlink_cmd.sock >= 0)
kernel_netlink_nlsock_insert(&zns->ge_netlink_cmd);
/* /*
* SOL_NETLINK is not available on all platforms yet * SOL_NETLINK is not available on all platforms yet
* apparently. It's in bits/socket.h which I am not * apparently. It's in bits/socket.h which I am not
@ -1846,6 +1883,15 @@ void kernel_init(struct zebra_ns *zns)
zlog_notice("Registration for extended dp ACK failed : %d %s", zlog_notice("Registration for extended dp ACK failed : %d %s",
errno, safe_strerror(errno)); errno, safe_strerror(errno));
if (zns->ge_netlink_cmd.sock >= 0) {
one = 1;
ret = setsockopt(zns->ge_netlink_cmd.sock, SOL_NETLINK,
NETLINK_EXT_ACK, &one, sizeof(one));
if (ret < 0)
zlog_err("Registration for extended generic netlink cmd ACK failed : %d %s",
errno, safe_strerror(errno));
}
/* /*
* Trim off the payload of the original netlink message in the * Trim off the payload of the original netlink message in the
* acknowledgment. This option is available since Linux 4.2, so if * acknowledgment. This option is available since Linux 4.2, so if
@ -1878,12 +1924,22 @@ void kernel_init(struct zebra_ns *zns)
zns->netlink_dplane_in.name, safe_strerror(errno), zns->netlink_dplane_in.name, safe_strerror(errno),
errno); errno);
if (zns->ge_netlink_cmd.sock >= 0) {
if (fcntl(zns->ge_netlink_cmd.sock, F_SETFL, O_NONBLOCK) < 0)
zlog_err("Can't set %s socket error: %s(%d)",
zns->ge_netlink_cmd.name, safe_strerror(errno),
errno);
}
/* Set receive buffer size if it's set from command line */ /* Set receive buffer size if it's set from command line */
if (rcvbufsize) { if (rcvbufsize) {
netlink_recvbuf(&zns->netlink, rcvbufsize); netlink_recvbuf(&zns->netlink, rcvbufsize);
netlink_recvbuf(&zns->netlink_cmd, rcvbufsize); netlink_recvbuf(&zns->netlink_cmd, rcvbufsize);
netlink_recvbuf(&zns->netlink_dplane_out, rcvbufsize); netlink_recvbuf(&zns->netlink_dplane_out, rcvbufsize);
netlink_recvbuf(&zns->netlink_dplane_in, rcvbufsize); netlink_recvbuf(&zns->netlink_dplane_in, rcvbufsize);
if (zns->ge_netlink_cmd.sock >= 0)
netlink_recvbuf(&zns->ge_netlink_cmd, rcvbufsize);
} }
/* Set filter for inbound sockets, to exclude events we've generated /* Set filter for inbound sockets, to exclude events we've generated
@ -1926,6 +1982,8 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
kernel_nlsock_fini(&zns->netlink_dplane_in); kernel_nlsock_fini(&zns->netlink_dplane_in);
kernel_nlsock_fini(&zns->ge_netlink_cmd);
/* During zebra shutdown, we need to leave the dataplane socket /* During zebra shutdown, we need to leave the dataplane socket
* around until all work is done. * around until all work is done.
*/ */

View file

@ -98,6 +98,9 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup);
extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct nlsock *nl, struct nlmsghdr *n, struct nlsock *nl,
struct zebra_ns *zns, bool startup); struct zebra_ns *zns, bool startup);
extern int
ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct zebra_ns *zns, bool startup);
extern int netlink_request(struct nlsock *nl, void *req); extern int netlink_request(struct nlsock *nl, void *req);
enum netlink_msg_status { enum netlink_msg_status {

View file

@ -49,6 +49,8 @@ struct zebra_ns {
struct nlsock netlink_dplane_out; struct nlsock netlink_dplane_out;
struct nlsock netlink_dplane_in; struct nlsock netlink_dplane_in;
struct event *t_netlink; struct event *t_netlink;
struct nlsock ge_netlink_cmd; /* command channel for generic netlink */
#endif #endif
struct route_table *if_table; struct route_table *if_table;