kernel-netlink: Add simple wrapper for Netlink event sockets

This commit is contained in:
Tobias Brunner
2023-02-16 13:25:35 +01:00
parent e323539428
commit cb0bdb847d
3 changed files with 168 additions and 5 deletions
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2022 Tobias Brunner
* Copyright (C) 2008-2023 Tobias Brunner
* Copyright (C) 2014 Martin Willi
*
* Copyright (C) secunet Security Networks AG
@@ -58,6 +58,7 @@
#endif
typedef struct private_netlink_socket_t private_netlink_socket_t;
typedef struct private_netlink_event_socket_t private_netlink_event_socket_t;
/**
* Private variables and functions of netlink_socket_t class.
@@ -125,6 +126,37 @@ struct private_netlink_socket_t {
bool ignore_retransmit_errors;
};
/**
* Private data of netlink_event_socket_t class
*/
struct private_netlink_event_socket_t {
/**
* Public interface
*/
netlink_event_socket_t public;
/**
* Registered callback
*/
netlink_event_cb_t cb;
/**
* User data to pass to callback
*/
void *user;
/**
* Netlink socket
*/
int socket;
/**
* Buffer size for received Netlink messages
*/
u_int buflen;
};
/**
* #definable hook to simulate request message loss
*/
@@ -700,6 +732,92 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
return &this->public;
}
CALLBACK(watch_event, bool,
private_netlink_event_socket_t *this, int fd, watcher_event_t event)
{
char buf[this->buflen];
struct nlmsghdr *hdr = (struct nlmsghdr*)buf;
struct sockaddr_nl addr;
socklen_t addr_len = sizeof(addr);
int len;
len = recvfrom(this->socket, buf, sizeof(buf), MSG_DONTWAIT,
(struct sockaddr*)&addr, &addr_len);
if (len < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
DBG1(DBG_KNL, "netlink event read error: %s", strerror(errno));
}
return TRUE;
}
else if (addr.nl_pid != 0)
{ /* ignore non-kernel messages */
return TRUE;
}
while (NLMSG_OK(hdr, len))
{
this->cb(this->user, hdr);
hdr = NLMSG_NEXT(hdr, len);
}
return TRUE;
}
METHOD(netlink_event_socket_t, destroy_event, void,
private_netlink_event_socket_t *this)
{
if (this->socket != -1)
{
lib->watcher->remove(lib->watcher, this->socket);
close(this->socket);
}
free(this);
}
/*
* Described in header
*/
netlink_event_socket_t *netlink_event_socket_create(int protocol, uint32_t groups,
netlink_event_cb_t cb, void *user)
{
private_netlink_event_socket_t *this;
struct sockaddr_nl addr = {
.nl_family = AF_NETLINK,
.nl_groups = groups,
};
INIT(this,
.public = {
.destroy = _destroy_event,
},
.cb = cb,
.user = user,
.buflen = netlink_get_buflen(),
);
this->socket = socket(AF_NETLINK, SOCK_RAW, protocol);
if (this->socket == -1)
{
DBG1(DBG_KNL, "unable to create netlink event socket: %s (%d)",
strerror(errno), errno);
destroy_event(this);
return NULL;
}
if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)))
{
DBG1(DBG_KNL, "unable to bind netlink event socket: %s (%d)",
strerror(errno), errno);
destroy_event(this);
return NULL;
}
lib->watcher->add(lib->watcher, this->socket, WATCHER_READ, watch_event, this);
return &this->public;
}
/*
* Described in header
*/