kernel-netlink: Use event socket wrapper for XFRM and networking events

This commit is contained in:
Tobias Brunner 2023-01-26 17:50:51 +01:00
parent cb0bdb847d
commit 77a5c9514c
2 changed files with 68 additions and 188 deletions

View File

@ -108,11 +108,6 @@
*/ */
#define XFRM_LIMIT(x) ((x) == 0 ? XFRM_INF : (x)) #define XFRM_LIMIT(x) ((x) == 0 ? XFRM_INF : (x))
/**
* Create ORable bitfield of XFRM NL groups
*/
#define XFRMNLGRP(x) (1<<(XFRMNLGRP_##x-1))
/** /**
* Returns a pointer to the first rtattr following the nlmsghdr *nlh and the * Returns a pointer to the first rtattr following the nlmsghdr *nlh and the
* 'usual' netlink data x like 'struct xfrm_usersa_info' * 'usual' netlink data x like 'struct xfrm_usersa_info'
@ -344,7 +339,7 @@ struct private_kernel_netlink_ipsec_t {
/** /**
* Netlink xfrm socket to receive acquire and expire events * Netlink xfrm socket to receive acquire and expire events
*/ */
int socket_xfrm_events; netlink_event_socket_t *socket_xfrm_events;
/** /**
* Whether to install routes along policies * Whether to install routes along policies
@ -1111,67 +1106,28 @@ static void process_mapping(private_kernel_netlink_ipsec_t *this,
} }
} }
/** CALLBACK(receive_events, void,
* Receives events from kernel private_kernel_netlink_ipsec_t *this, struct nlmsghdr *hdr)
*/
static bool receive_events(private_kernel_netlink_ipsec_t *this, int fd,
watcher_event_t event)
{ {
char response[netlink_get_buflen()]; switch (hdr->nlmsg_type)
struct nlmsghdr *hdr = (struct nlmsghdr*)response;
struct sockaddr_nl addr;
socklen_t addr_len = sizeof(addr);
int len;
len = recvfrom(this->socket_xfrm_events, response, sizeof(response),
MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len);
if (len < 0)
{ {
switch (errno) case XFRM_MSG_ACQUIRE:
{ process_acquire(this, hdr);
case EINTR: break;
/* interrupted, try again */ case XFRM_MSG_EXPIRE:
return TRUE; process_expire(this, hdr);
case EAGAIN: break;
/* no data ready, select again */ case XFRM_MSG_MIGRATE:
return TRUE; process_migrate(this, hdr);
default: break;
DBG1(DBG_KNL, "unable to receive from XFRM event socket: %s " case XFRM_MSG_MAPPING:
"(%d)", strerror(errno), errno); process_mapping(this, hdr);
sleep(1); break;
return TRUE; default:
} DBG1(DBG_KNL, "received unknown event from XFRM event "
"socket: %d", hdr->nlmsg_type);
break;
} }
if (addr.nl_pid != 0)
{ /* not from kernel. not interested, try another one */
return TRUE;
}
while (NLMSG_OK(hdr, len))
{
switch (hdr->nlmsg_type)
{
case XFRM_MSG_ACQUIRE:
process_acquire(this, hdr);
break;
case XFRM_MSG_EXPIRE:
process_expire(this, hdr);
break;
case XFRM_MSG_MIGRATE:
process_migrate(this, hdr);
break;
case XFRM_MSG_MAPPING:
process_mapping(this, hdr);
break;
default:
DBG1(DBG_KNL, "received unknown event from XFRM event "
"socket: %d", hdr->nlmsg_type);
break;
}
hdr = NLMSG_NEXT(hdr, len);
}
return TRUE;
} }
METHOD(kernel_ipsec_t, get_features, kernel_feature_t, METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
@ -3637,11 +3593,7 @@ METHOD(kernel_ipsec_t, destroy, void,
array_destroy_function(this->bypass, array_destroy_function(this->bypass,
(array_callback_t)remove_port_bypass, this); (array_callback_t)remove_port_bypass, this);
if (this->socket_xfrm_events > 0) DESTROY_IF(this->socket_xfrm_events);
{
lib->watcher->remove(lib->watcher, this->socket_xfrm_events);
close(this->socket_xfrm_events);
}
DESTROY_IF(this->socket_xfrm); DESTROY_IF(this->socket_xfrm);
enumerator = this->policies->create_enumerator(this->policies); enumerator = this->policies->create_enumerator(this->policies);
while (enumerator->enumerate(enumerator, &policy, &policy)) while (enumerator->enumerate(enumerator, &policy, &policy))
@ -3764,7 +3716,7 @@ static void setup_spd_hash_thresh(private_kernel_netlink_ipsec_t *this,
kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
{ {
private_kernel_netlink_ipsec_t *this; private_kernel_netlink_ipsec_t *this;
struct sockaddr_nl addr; uint32_t groups;
INIT(this, INIT(this,
.public = { .public = {
@ -3816,29 +3768,15 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
setup_spd_hash_thresh(this, "ipv4", XFRMA_SPD_IPV4_HTHRESH, 32); setup_spd_hash_thresh(this, "ipv4", XFRMA_SPD_IPV4_HTHRESH, 32);
setup_spd_hash_thresh(this, "ipv6", XFRMA_SPD_IPV6_HTHRESH, 128); setup_spd_hash_thresh(this, "ipv6", XFRMA_SPD_IPV6_HTHRESH, 128);
memset(&addr, 0, sizeof(addr)); groups = nl_group(XFRMNLGRP_ACQUIRE) | nl_group(XFRMNLGRP_EXPIRE) |
addr.nl_family = AF_NETLINK; nl_group(XFRMNLGRP_MIGRATE) | nl_group(XFRMNLGRP_MAPPING);
this->socket_xfrm_events = netlink_event_socket_create(NETLINK_XFRM, groups,
/* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */ receive_events, this);
this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); if (!this->socket_xfrm_events)
if (this->socket_xfrm_events <= 0)
{ {
DBG1(DBG_KNL, "unable to create XFRM event socket: %s (%d)",
strerror(errno), errno);
destroy(this); destroy(this);
return NULL; return NULL;
} }
addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) |
XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING);
if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr)))
{
DBG1(DBG_KNL, "unable to bind XFRM event socket: %s (%d)",
strerror(errno), errno);
destroy(this);
return NULL;
}
lib->watcher->add(lib->watcher, this->socket_xfrm_events, WATCHER_READ,
(watcher_cb_t)receive_events, this);
netlink_find_offload_feature(lib->settings->get_str(lib->settings, netlink_find_offload_feature(lib->settings->get_str(lib->settings,
"%s.plugins.kernel-netlink.hw_offload_feature_interface", "%s.plugins.kernel-netlink.hw_offload_feature_interface",

View File

@ -340,9 +340,9 @@ struct private_kernel_netlink_net_t {
netlink_socket_t *socket; netlink_socket_t *socket;
/** /**
* Netlink rt socket to receive address change events * Netlink rt event socket
*/ */
int socket_events; netlink_event_socket_t *socket_events;
/** /**
* earliest time of the next roam event * earliest time of the next roam event
@ -1448,76 +1448,36 @@ static void process_rule(private_kernel_netlink_net_t *this,
#endif #endif
} }
/** CALLBACK(receive_events, void,
* Receives events from kernel private_kernel_netlink_net_t *this, struct nlmsghdr *hdr)
*/
static bool receive_events(private_kernel_netlink_net_t *this, int fd,
watcher_event_t event)
{ {
char response[netlink_get_buflen()]; switch (hdr->nlmsg_type)
struct nlmsghdr *hdr = (struct nlmsghdr*)response;
struct sockaddr_nl addr;
socklen_t addr_len = sizeof(addr);
int len;
len = recvfrom(this->socket_events, response, sizeof(response),
MSG_DONTWAIT, (struct sockaddr*)&addr, &addr_len);
if (len < 0)
{ {
switch (errno) case RTM_NEWADDR:
{ case RTM_DELADDR:
case EINTR: process_addr(this, hdr, TRUE);
/* interrupted, try again */ break;
return TRUE; case RTM_NEWLINK:
case EAGAIN: case RTM_DELLINK:
/* no data ready, select again */ process_link(this, hdr, TRUE);
return TRUE; break;
default: case RTM_NEWROUTE:
DBG1(DBG_KNL, "unable to receive from RT event socket %s (%d)", case RTM_DELROUTE:
strerror(errno), errno); if (this->process_route)
sleep(1); {
return TRUE; process_route(this, hdr);
} }
break;
case RTM_NEWRULE:
case RTM_DELRULE:
if (this->process_rules)
{
process_rule(this, hdr);
}
break;
default:
break;
} }
if (addr.nl_pid != 0)
{ /* not from kernel. not interested, try another one */
return TRUE;
}
while (NLMSG_OK(hdr, len))
{
/* looks good so far, dispatch netlink message */
switch (hdr->nlmsg_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
process_addr(this, hdr, TRUE);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
process_link(this, hdr, TRUE);
break;
case RTM_NEWROUTE:
case RTM_DELROUTE:
if (this->process_route)
{
process_route(this, hdr);
}
break;
case RTM_NEWRULE:
case RTM_DELRULE:
if (this->process_rules)
{
process_rule(this, hdr);
}
break;
default:
break;
}
hdr = NLMSG_NEXT(hdr, len);
}
return TRUE;
} }
/** enumerator over addresses */ /** enumerator over addresses */
@ -3053,11 +3013,7 @@ METHOD(kernel_net_t, destroy, void,
manage_rule(this, RTM_DELRULE, AF_INET6, this->routing_table, manage_rule(this, RTM_DELRULE, AF_INET6, this->routing_table,
this->routing_table_prio); this->routing_table_prio);
} }
if (this->socket_events > 0) DESTROY_IF(this->socket_events);
{
lib->watcher->remove(lib->watcher, this->socket_events);
close(this->socket_events);
}
enumerator = this->routes->ht.create_enumerator(&this->routes->ht); enumerator = this->routes->ht.create_enumerator(&this->routes->ht);
while (enumerator->enumerate(enumerator, NULL, (void**)&route)) while (enumerator->enumerate(enumerator, NULL, (void**)&route))
{ {
@ -3093,7 +3049,7 @@ kernel_netlink_net_t *kernel_netlink_net_create()
{ {
private_kernel_netlink_net_t *this; private_kernel_netlink_net_t *this;
enumerator_t *enumerator; enumerator_t *enumerator;
struct sockaddr_nl addr; uint32_t groups;
char *exclude; char *exclude;
INIT(this, INIT(this,
@ -3186,40 +3142,26 @@ kernel_netlink_net_t *kernel_netlink_net_create()
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
} }
memset(&addr, 0, sizeof(addr)); groups = nl_group(RTNLGRP_IPV4_IFADDR) |
addr.nl_family = AF_NETLINK; nl_group(RTNLGRP_IPV6_IFADDR) |
nl_group(RTNLGRP_LINK);
/* create and bind RT socket for events (address/interface/route changes) */
this->socket_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (this->socket_events < 0)
{
DBG1(DBG_KNL, "unable to create RT event socket: %s (%d)",
strerror(errno), errno);
destroy(this);
return NULL;
}
addr.nl_groups = nl_group(RTNLGRP_IPV4_IFADDR) |
nl_group(RTNLGRP_IPV6_IFADDR) |
nl_group(RTNLGRP_LINK);
if (this->process_route) if (this->process_route)
{ {
addr.nl_groups |= nl_group(RTNLGRP_IPV4_ROUTE) | groups |= nl_group(RTNLGRP_IPV4_ROUTE) |
nl_group(RTNLGRP_IPV6_ROUTE); nl_group(RTNLGRP_IPV6_ROUTE);
} }
if (this->process_rules) if (this->process_rules)
{ {
addr.nl_groups |= nl_group(RTNLGRP_IPV4_RULE) | groups |= nl_group(RTNLGRP_IPV4_RULE) |
nl_group(RTNLGRP_IPV6_RULE); nl_group(RTNLGRP_IPV6_RULE);
} }
if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) this->socket_events = netlink_event_socket_create(NETLINK_ROUTE, groups,
receive_events, this);
if (!this->socket_events)
{ {
DBG1(DBG_KNL, "unable to bind RT event socket: %s (%d)",
strerror(errno), errno);
destroy(this); destroy(this);
return NULL; return NULL;
} }
lib->watcher->add(lib->watcher, this->socket_events, WATCHER_READ,
(watcher_cb_t)receive_events, this);
if (init_address_list(this) != SUCCESS) if (init_address_list(this) != SUCCESS)
{ {