mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-03 08:05:21 +00:00
netmgr: add isc_nm_routeconnect()
isc_nm_routeconnect() opens a route/netlink socket, then calls a connect callback, much like isc_nm_udpconnect(), with a handle that can then be monitored for network changes. Internally the socket is treated as a UDP socket, since route/netlink sockets follow the datagram contract.
This commit is contained in:
@@ -232,6 +232,18 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
|
||||
* 'cb'.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_nm_routeconnect(isc_nm_t *mgr, isc_nm_cb_t cb, void *cbarg,
|
||||
size_t extrahandlesize);
|
||||
/*%<
|
||||
* Open a route/netlink socket and call 'cb', so the caller can be
|
||||
* begin listening for interface changes. This behaves similarly to
|
||||
* isc_nm_udpconnect().
|
||||
*
|
||||
* Returns ISC_R_NOTIMPLEMENTED on systems where route/netlink sockets
|
||||
* are not supported.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_nm_stoplistening(isc_nmsocket_t *sock);
|
||||
/*%<
|
||||
|
@@ -240,6 +240,8 @@ typedef enum isc__netievent_type {
|
||||
netievent_udpread,
|
||||
netievent_udpcancel,
|
||||
|
||||
netievent_routeconnect,
|
||||
|
||||
netievent_tcpconnect,
|
||||
netievent_tcpclose,
|
||||
netievent_tcpsend,
|
||||
@@ -967,6 +969,8 @@ struct isc_nmsocket {
|
||||
atomic_bool active;
|
||||
atomic_bool destroying;
|
||||
|
||||
bool route_sock;
|
||||
|
||||
/*%
|
||||
* Socket is closed if it's not active and all the possible
|
||||
* callbacks were fired, there are no active handles, etc.
|
||||
@@ -1351,6 +1355,12 @@ isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0);
|
||||
* Callback handlers for asynchronous UDP events (listen, stoplisten, send).
|
||||
*/
|
||||
|
||||
void
|
||||
isc__nm_async_routeconnect(isc__networker_t *worker, isc__netievent_t *ev0);
|
||||
/*%<
|
||||
* Callback handler for route socket events.
|
||||
*/
|
||||
|
||||
void
|
||||
isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
|
||||
isc_nm_cb_t cb, void *cbarg);
|
||||
@@ -1880,6 +1890,8 @@ NETIEVENT_SOCKET_TYPE(tcpstartread);
|
||||
NETIEVENT_SOCKET_REQ_TYPE(tlssend);
|
||||
NETIEVENT_SOCKET_REQ_TYPE(udpconnect);
|
||||
|
||||
NETIEVENT_SOCKET_REQ_TYPE(routeconnect);
|
||||
|
||||
NETIEVENT_SOCKET_REQ_RESULT_TYPE(connectcb);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_TYPE(readcb);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_TYPE(sendcb);
|
||||
@@ -1946,6 +1958,8 @@ NETIEVENT_SOCKET_REQ_DECL(tcpsend);
|
||||
NETIEVENT_SOCKET_REQ_DECL(tlssend);
|
||||
NETIEVENT_SOCKET_REQ_DECL(udpconnect);
|
||||
|
||||
NETIEVENT_SOCKET_REQ_DECL(routeconnect);
|
||||
|
||||
NETIEVENT_SOCKET_REQ_RESULT_DECL(connectcb);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_DECL(readcb);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_DECL(sendcb);
|
||||
|
@@ -913,6 +913,8 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) {
|
||||
NETIEVENT_CASE(udpcancel);
|
||||
NETIEVENT_CASE(udpclose);
|
||||
|
||||
NETIEVENT_CASE(routeconnect);
|
||||
|
||||
NETIEVENT_CASE(tcpaccept);
|
||||
NETIEVENT_CASE(tcpconnect);
|
||||
NETIEVENT_CASE(tcplisten);
|
||||
@@ -1072,6 +1074,7 @@ NETIEVENT_SOCKET_REQ_DEF(tcpconnect);
|
||||
NETIEVENT_SOCKET_REQ_DEF(tcpsend);
|
||||
NETIEVENT_SOCKET_REQ_DEF(tlssend);
|
||||
NETIEVENT_SOCKET_REQ_DEF(udpconnect);
|
||||
NETIEVENT_SOCKET_REQ_DEF(routeconnect);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_DEF(connectcb);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_DEF(readcb);
|
||||
NETIEVENT_SOCKET_REQ_RESULT_DEF(sendcb);
|
||||
@@ -1447,12 +1450,8 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
|
||||
|
||||
REQUIRE(sock != NULL);
|
||||
REQUIRE(mgr != NULL);
|
||||
REQUIRE(iface != NULL);
|
||||
|
||||
family = iface->type.sa.sa_family;
|
||||
|
||||
*sock = (isc_nmsocket_t){ .type = type,
|
||||
.iface = *iface,
|
||||
.fd = -1,
|
||||
.ah_size = 32,
|
||||
.inactivehandles = isc_astack_new(
|
||||
@@ -1460,6 +1459,13 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
|
||||
.inactivereqs = isc_astack_new(
|
||||
mgr->mctx, ISC_NM_REQS_STACK_SIZE) };
|
||||
|
||||
if (iface != NULL) {
|
||||
family = iface->type.sa.sa_family;
|
||||
sock->iface = *iface;
|
||||
} else {
|
||||
family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
#if NETMGR_TRACE
|
||||
sock->backtrace_size = isc_backtrace(sock->backtrace, TRACE_SIZE);
|
||||
ISC_LINK_INIT(sock, active_link);
|
||||
@@ -1492,6 +1498,12 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
|
||||
case AF_INET6:
|
||||
sock->statsindex = udp6statsindex;
|
||||
break;
|
||||
case AF_UNSPEC:
|
||||
/*
|
||||
* Route sockets are AF_UNSPEC, and don't
|
||||
* have stats counters.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
INSIST(0);
|
||||
ISC_UNREACHABLE();
|
||||
@@ -1521,6 +1533,10 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
|
||||
break;
|
||||
}
|
||||
|
||||
if (sock->statsindex != NULL) {
|
||||
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_ACTIVE]);
|
||||
}
|
||||
|
||||
isc_mutex_init(&sock->lock);
|
||||
isc_condition_init(&sock->cond);
|
||||
isc_condition_init(&sock->scond);
|
||||
|
@@ -31,6 +31,32 @@
|
||||
#include "netmgr-int.h"
|
||||
#include "uv-compat.h"
|
||||
|
||||
#ifdef HAVE_NET_ROUTE_H
|
||||
#include <net/route.h>
|
||||
#if defined(RTM_VERSION) && defined(RTM_NEWADDR) && defined(RTM_DELADDR)
|
||||
#define USE_ROUTE_SOCKET 1
|
||||
#define ROUTE_SOCKET_PF PF_ROUTE
|
||||
#define ROUTE_SOCKET_PROTOCOL 0
|
||||
#define MSGHDR rt_msghdr
|
||||
#define MSGTYPE rtm_type
|
||||
#endif /* if defined(RTM_VERSION) && defined(RTM_NEWADDR) && \
|
||||
* defined(RTM_DELADDR) */
|
||||
#endif /* ifdef HAVE_NET_ROUTE_H */
|
||||
|
||||
#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#if defined(RTM_NEWADDR) && defined(RTM_DELADDR)
|
||||
#define USE_ROUTE_SOCKET 1
|
||||
#define USE_NETLINK 1
|
||||
#define ROUTE_SOCKET_PF PF_NETLINK
|
||||
#define ROUTE_SOCKET_PROTOCOL NETLINK_ROUTE
|
||||
#define MSGHDR nlmsghdr
|
||||
#define MSGTYPE nlmsg_type
|
||||
#endif /* if defined(RTM_NEWADDR) && defined(RTM_DELADDR) */
|
||||
#endif /* if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H) \
|
||||
*/
|
||||
|
||||
static isc_result_t
|
||||
udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
|
||||
isc_sockaddr_t *peer);
|
||||
@@ -189,6 +215,197 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
|
||||
return (result);
|
||||
}
|
||||
|
||||
#ifdef USE_ROUTE_SOCKET
|
||||
static isc_result_t
|
||||
route_socket(uv_os_sock_t *fdp) {
|
||||
isc_result_t result;
|
||||
uv_os_sock_t fd;
|
||||
#ifdef USE_NETLINK
|
||||
struct sockaddr_nl sa;
|
||||
int r;
|
||||
#endif
|
||||
|
||||
result = isc__nm_socket(ROUTE_SOCKET_PF, SOCK_RAW,
|
||||
ROUTE_SOCKET_PROTOCOL, &fd);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
#ifdef USE_NETLINK
|
||||
sa.nl_family = PF_NETLINK;
|
||||
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
|
||||
r = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (r < 0) {
|
||||
isc__nm_closesocket(fd);
|
||||
return (isc_errno_toresult(r));
|
||||
}
|
||||
#endif
|
||||
|
||||
*fdp = fd;
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
route_connect_direct(isc_nmsocket_t *sock) {
|
||||
isc__networker_t *worker = NULL;
|
||||
isc_result_t result = ISC_R_UNSET;
|
||||
int r;
|
||||
|
||||
REQUIRE(isc__nm_in_netthread());
|
||||
REQUIRE(sock->tid == isc_nm_tid());
|
||||
|
||||
worker = &sock->mgr->workers[isc_nm_tid()];
|
||||
|
||||
atomic_store(&sock->connecting, true);
|
||||
|
||||
r = uv_udp_init(&worker->loop, &sock->uv_handle.udp);
|
||||
RUNTIME_CHECK(r == 0);
|
||||
uv_handle_set_data(&sock->uv_handle.handle, sock);
|
||||
|
||||
r = uv_timer_init(&worker->loop, &sock->timer);
|
||||
RUNTIME_CHECK(r == 0);
|
||||
uv_handle_set_data((uv_handle_t *)&sock->timer, sock);
|
||||
|
||||
if (isc__nm_closing(sock)) {
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = uv_udp_open(&sock->uv_handle.udp, sock->fd);
|
||||
if (r != 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
|
||||
|
||||
atomic_store(&sock->connecting, false);
|
||||
atomic_store(&sock->connected, true);
|
||||
|
||||
done:
|
||||
result = isc__nm_uverr2result(r);
|
||||
error:
|
||||
|
||||
LOCK(&sock->lock);
|
||||
sock->result = result;
|
||||
SIGNAL(&sock->cond);
|
||||
if (!atomic_load(&sock->active)) {
|
||||
WAIT(&sock->scond, &sock->lock);
|
||||
}
|
||||
INSIST(atomic_load(&sock->active));
|
||||
UNLOCK(&sock->lock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Asynchronous 'udpconnect' call handler: open a new UDP socket and
|
||||
* call the 'open' callback with a handle.
|
||||
*/
|
||||
void
|
||||
isc__nm_async_routeconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
|
||||
isc__netievent_routeconnect_t *ievent =
|
||||
(isc__netievent_routeconnect_t *)ev0;
|
||||
isc_nmsocket_t *sock = ievent->sock;
|
||||
isc__nm_uvreq_t *req = ievent->req;
|
||||
isc_result_t result;
|
||||
|
||||
UNUSED(worker);
|
||||
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->type == isc_nm_udpsocket);
|
||||
REQUIRE(sock->parent == NULL);
|
||||
REQUIRE(sock->tid == isc_nm_tid());
|
||||
|
||||
result = route_connect_direct(sock);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
atomic_store(&sock->active, false);
|
||||
isc__nm_udp_close(sock);
|
||||
isc__nm_connectcb(sock, req, result, true);
|
||||
} else {
|
||||
/*
|
||||
* The callback has to be called after the socket has been
|
||||
* initialized
|
||||
*/
|
||||
isc__nm_connectcb(sock, req, ISC_R_SUCCESS, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* The sock is now attached to the handle.
|
||||
*/
|
||||
isc__nmsocket_detach(&sock);
|
||||
}
|
||||
#endif /* USE_ROUTE_SOCKET */
|
||||
|
||||
isc_result_t
|
||||
isc_nm_routeconnect(isc_nm_t *mgr, isc_nm_cb_t cb, void *cbarg,
|
||||
size_t extrahandlesize) {
|
||||
#ifdef USE_ROUTE_SOCKET
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
isc_nmsocket_t *sock = NULL;
|
||||
isc__netievent_udpconnect_t *event = NULL;
|
||||
isc__nm_uvreq_t *req = NULL;
|
||||
|
||||
REQUIRE(VALID_NM(mgr));
|
||||
|
||||
sock = isc_mem_get(mgr->mctx, sizeof(*sock));
|
||||
isc__nmsocket_init(sock, mgr, isc_nm_udpsocket, NULL);
|
||||
|
||||
sock->connect_cb = cb;
|
||||
sock->connect_cbarg = cbarg;
|
||||
sock->extrahandlesize = extrahandlesize;
|
||||
sock->result = ISC_R_UNSET;
|
||||
atomic_init(&sock->client, true);
|
||||
sock->route_sock = true;
|
||||
|
||||
req = isc__nm_uvreq_get(mgr, sock);
|
||||
req->cb.connect = cb;
|
||||
req->cbarg = cbarg;
|
||||
req->handle = isc__nmhandle_get(sock, NULL, NULL);
|
||||
|
||||
result = route_socket(&sock->fd);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (isc__nm_in_netthread()) {
|
||||
sock->tid = isc_nm_tid();
|
||||
}
|
||||
isc__nmsocket_clearcb(sock);
|
||||
isc__nm_connectcb(sock, req, result, true);
|
||||
atomic_store(&sock->closed, true);
|
||||
isc__nmsocket_detach(&sock);
|
||||
return (result);
|
||||
}
|
||||
|
||||
event = isc__nm_get_netievent_routeconnect(mgr, sock, req);
|
||||
|
||||
if (isc__nm_in_netthread()) {
|
||||
atomic_store(&sock->active, true);
|
||||
sock->tid = isc_nm_tid();
|
||||
isc__nm_async_routeconnect(&mgr->workers[sock->tid],
|
||||
(isc__netievent_t *)event);
|
||||
isc__nm_put_netievent_routeconnect(mgr, event);
|
||||
} else {
|
||||
atomic_init(&sock->active, false);
|
||||
sock->tid = 0;
|
||||
isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
|
||||
(isc__netievent_t *)event);
|
||||
}
|
||||
LOCK(&sock->lock);
|
||||
while (sock->result == ISC_R_UNSET) {
|
||||
WAIT(&sock->cond, &sock->lock);
|
||||
}
|
||||
atomic_store(&sock->active, true);
|
||||
BROADCAST(&sock->scond);
|
||||
UNLOCK(&sock->lock);
|
||||
|
||||
return (sock->result);
|
||||
#else /* USE_ROUTE_SOCKET */
|
||||
UNUSED(mgr);
|
||||
UNUSED(cb);
|
||||
UNUSED(cbarg);
|
||||
UNUSED(extrahandlesize);
|
||||
return (ISC_R_NOTIMPLEMENTED);
|
||||
#endif /* USE_ROUTE_SOCKET */
|
||||
}
|
||||
|
||||
/*
|
||||
* Asynchronous 'udplisten' call handler: start listening on a UDP socket.
|
||||
*/
|
||||
@@ -338,8 +555,8 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
|
||||
isc__nm_uvreq_t *req = NULL;
|
||||
uint32_t maxudp;
|
||||
bool free_buf;
|
||||
isc_sockaddr_t sockaddr;
|
||||
isc_result_t result;
|
||||
isc_sockaddr_t sockaddr, *sa = NULL;
|
||||
|
||||
REQUIRE(VALID_NMSOCK(sock));
|
||||
REQUIRE(sock->tid == isc_nm_tid());
|
||||
@@ -398,10 +615,13 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
|
||||
goto free;
|
||||
}
|
||||
|
||||
result = isc_sockaddr_fromsockaddr(&sockaddr, addr);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
if (!sock->route_sock) {
|
||||
result = isc_sockaddr_fromsockaddr(&sockaddr, addr);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
sa = &sockaddr;
|
||||
}
|
||||
|
||||
req = isc__nm_get_read_req(sock, &sockaddr);
|
||||
req = isc__nm_get_read_req(sock, sa);
|
||||
|
||||
/*
|
||||
* The callback will be called synchronously, because result is
|
||||
|
Reference in New Issue
Block a user