NETIEVENT_CASE(udpcancel);
NETIEVENT_CASE(udpclose);
+ NETIEVENT_CASE(routeconnect);
+
NETIEVENT_CASE(tcpaccept);
NETIEVENT_CASE(tcpconnect);
NETIEVENT_CASE(tcplisten);
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);
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(
.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);
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();
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);
#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);
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.
*/
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());
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