1 /* $NetBSD: netmgr.h,v 1.7 2022/09/23 12:15:33 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #pragma once 17 18 #include <unistd.h> 19 20 #include <isc/mem.h> 21 #include <isc/region.h> 22 #include <isc/result.h> 23 #include <isc/types.h> 24 25 #ifndef _WIN32 26 #include <sys/socket.h> 27 #include <sys/types.h> 28 #endif 29 30 #if defined(SO_REUSEPORT_LB) || (defined(SO_REUSEPORT) && defined(__linux__)) 31 #define HAVE_SO_REUSEPORT_LB 1 32 #endif 33 34 /* 35 * Replacement for isc_sockettype_t provided by socket.h. 36 */ 37 typedef enum { 38 isc_socktype_tcp = 1, 39 isc_socktype_udp = 2, 40 isc_socktype_unix = 3, 41 isc_socktype_raw = 4 42 } isc_socktype_t; 43 44 typedef void (*isc_nm_recv_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult, 45 isc_region_t *region, void *cbarg); 46 /*%< 47 * Callback function to be used when receiving a packet. 48 * 49 * 'handle' the handle that can be used to send back the answer. 50 * 'eresult' the result of the event. 51 * 'region' contains the received data, if any. It will be freed 52 * after return by caller. 53 * 'cbarg' the callback argument passed to isc_nm_listenudp(), 54 * isc_nm_listentcpdns(), or isc_nm_read(). 55 */ 56 typedef isc_result_t (*isc_nm_accept_cb_t)(isc_nmhandle_t *handle, 57 isc_result_t result, void *cbarg); 58 /*%< 59 * Callback function to be used when accepting a connection. (This differs 60 * from isc_nm_cb_t below in that it returns a result code.) 61 * 62 * 'handle' the handle that can be used to send back the answer. 63 * 'eresult' the result of the event. 64 * 'cbarg' the callback argument passed to isc_nm_listentcp() or 65 * isc_nm_listentcpdns(). 66 */ 67 68 typedef void (*isc_nm_cb_t)(isc_nmhandle_t *handle, isc_result_t result, 69 void *cbarg); 70 /*%< 71 * Callback function for other network completion events (send, connect). 72 * 73 * 'handle' the handle on which the event took place. 74 * 'eresult' the result of the event. 75 * 'cbarg' the callback argument passed to isc_nm_send(), 76 * isc_nm_tcp_connect(), or isc_nm_listentcp() 77 */ 78 79 typedef void (*isc_nm_opaquecb_t)(void *arg); 80 /*%< 81 * Opaque callback function, used for isc_nmhandle 'reset' and 'free' 82 * callbacks. 83 */ 84 85 typedef void (*isc_nm_workcb_t)(void *arg); 86 typedef void (*isc_nm_after_workcb_t)(void *arg, isc_result_t result); 87 /*%< 88 * Callback functions for libuv threadpool work (see uv_work_t) 89 */ 90 91 void 92 isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst); 93 void 94 isc_nm_detach(isc_nm_t **mgr0); 95 /*%< 96 * Attach/detach a network manager. When all references have been 97 * released, the network manager is shut down, freeing all resources. 98 * Destroy is working the same way as detach, but it actively waits 99 * for all other references to be gone. 100 */ 101 102 /* Return thread ID of current thread, or ISC_NETMGR_TID_UNKNOWN */ 103 int 104 isc_nm_tid(void); 105 106 void 107 isc_nmsocket_close(isc_nmsocket_t **sockp); 108 /*%< 109 * isc_nmsocket_close() detaches a listening socket that was 110 * created by isc_nm_listenudp(), isc_nm_listentcp(), or 111 * isc_nm_listentcpdns(). Once there are no remaining child 112 * sockets with active handles, the socket will be closed. 113 */ 114 115 #ifdef NETMGR_TRACE 116 #define isc_nmhandle_attach(handle, dest) \ 117 isc__nmhandle_attach(handle, dest, __FILE__, __LINE__, __func__) 118 #define isc_nmhandle_detach(handlep) \ 119 isc__nmhandle_detach(handlep, __FILE__, __LINE__, __func__) 120 #define FLARG , const char *file, unsigned int line, const char *func 121 #else 122 #define isc_nmhandle_attach(handle, dest) isc__nmhandle_attach(handle, dest) 123 #define isc_nmhandle_detach(handlep) isc__nmhandle_detach(handlep) 124 #define FLARG 125 #endif 126 127 void 128 isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **dest FLARG); 129 void 130 isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG); 131 /*%< 132 * Increment/decrement the reference counter in a netmgr handle, 133 * but (unlike the attach/detach functions) do not change the pointer 134 * value. If reference counters drop to zero, the handle can be 135 * marked inactive, possibly triggering deletion of its associated 136 * socket. 137 * 138 * (This will be used to prevent a client from being cleaned up when 139 * it's passed to an isc_task event handler. The libuv code would not 140 * otherwise know that the handle was in use and might free it, along 141 * with the client.) 142 */ 143 #undef FLARG 144 145 int 146 isc_nmhandle_getfd(isc_nmhandle_t *handle); 147 148 void * 149 isc_nmhandle_getdata(isc_nmhandle_t *handle); 150 151 void * 152 isc_nmhandle_getextra(isc_nmhandle_t *handle); 153 154 bool 155 isc_nmhandle_is_stream(isc_nmhandle_t *handle); 156 157 void 158 isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg, 159 isc_nm_opaquecb_t doreset, isc_nm_opaquecb_t dofree); 160 /*%< 161 * isc_nmhandle_t has a void* opaque field (for example, ns_client_t). 162 * We reuse handle and `opaque` can also be reused between calls. 163 * This function sets this field and two callbacks: 164 * - doreset resets the `opaque` to initial state 165 * - dofree frees everything associated with `opaque` 166 */ 167 168 void 169 isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout); 170 void 171 isc_nmhandle_cleartimeout(isc_nmhandle_t *handle); 172 /*%< 173 * Set/clear the read/recv timeout for the socket connected to 'handle' 174 * to 'timeout' (in milliseconds), and reset the timer. 175 * 176 * When this is called on a 'wrapper' socket handle (for example, 177 * a TCPDNS socket wrapping a TCP connection), the timer is set for 178 * both socket layers. 179 */ 180 181 void 182 isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value); 183 /*%< 184 * Enable/disable keepalive on this connection by setting it to 'value'. 185 * 186 * When keepalive is active, we switch to using the keepalive timeout 187 * to determine when to close a connection, rather than the idle timeout. 188 * 189 * This applies only to TCP-based DNS connections (i.e., TCPDNS). 190 * On other types of connection it has no effect. 191 */ 192 193 isc_sockaddr_t 194 isc_nmhandle_peeraddr(isc_nmhandle_t *handle); 195 /*%< 196 * Return the peer address for the given handle. 197 */ 198 isc_sockaddr_t 199 isc_nmhandle_localaddr(isc_nmhandle_t *handle); 200 /*%< 201 * Return the local address for the given handle. 202 */ 203 204 isc_nm_t * 205 isc_nmhandle_netmgr(isc_nmhandle_t *handle); 206 /*%< 207 * Return a pointer to the netmgr object for the given handle. 208 */ 209 210 isc_result_t 211 isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb, 212 void *cbarg, size_t extrasize, isc_nmsocket_t **sockp); 213 /*%< 214 * Start listening for UDP packets on interface 'iface' using net manager 215 * 'mgr'. 216 * 217 * On success, 'sockp' will be updated to contain a new listening UDP socket. 218 * 219 * When a packet is received on the socket, 'cb' will be called with 'cbarg' 220 * as its argument. 221 * 222 * When handles are allocated for the socket, 'extrasize' additional bytes 223 * can be allocated along with the handle for an associated object, which 224 * can then be freed automatically when the handle is destroyed. 225 */ 226 227 void 228 isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, 229 isc_nm_cb_t cb, void *cbarg, unsigned int timeout, 230 size_t extrahandlesize); 231 /*%< 232 * Open a UDP socket, bind to 'local' and connect to 'peer', and 233 * immediately call 'cb' with a handle so that the caller can begin 234 * sending packets over UDP. 235 * 236 * When handles are allocated for the socket, 'extrasize' additional bytes 237 * can be allocated along with the handle for an associated object, which 238 * can then be freed automatically when the handle is destroyed. 239 * 240 * 'timeout' specifies the timeout interval in milliseconds. 241 * 242 * The connected socket can only be accessed via the handle passed to 243 * 'cb'. 244 */ 245 246 void 247 isc_nm_stoplistening(isc_nmsocket_t *sock); 248 /*%< 249 * Stop listening on socket 'sock'. 250 */ 251 252 void 253 isc_nm_pause(isc_nm_t *mgr); 254 /*%< 255 * Pause all processing, equivalent to taskmgr exclusive tasks. 256 * It won't return until all workers have been paused. 257 */ 258 259 void 260 isc_nm_resume(isc_nm_t *mgr); 261 /*%< 262 * Resume paused processing. It will return immediately after signalling 263 * workers to resume. 264 */ 265 266 void 267 isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg); 268 /* 269 * Begin (or continue) reading on the socket associated with 'handle', and 270 * update its recv callback to 'cb', which will be called as soon as there 271 * is data to process. 272 */ 273 274 void 275 isc_nm_pauseread(isc_nmhandle_t *handle); 276 /*%< 277 * Pause reading on this handle's socket, but remember the callback. 278 * 279 * Requires: 280 * \li 'handle' is a valid netmgr handle. 281 */ 282 283 void 284 isc_nm_cancelread(isc_nmhandle_t *handle); 285 /*%< 286 * Cancel reading on a connected socket. Calls the read/recv callback on 287 * active handles with a result code of ISC_R_CANCELED. 288 * 289 * Requires: 290 * \li 'sock' is a valid netmgr socket 291 * \li ...for which a read/recv callback has been defined. 292 */ 293 294 void 295 isc_nm_resumeread(isc_nmhandle_t *handle); 296 /*%< 297 * Resume reading on the handle's socket. 298 * 299 * Requires: 300 * \li 'handle' is a valid netmgr handle. 301 * \li ...for a socket with a defined read/recv callback. 302 */ 303 304 void 305 isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb, 306 void *cbarg); 307 /*%< 308 * Send the data in 'region' via 'handle'. Afterward, the callback 'cb' is 309 * called with the argument 'cbarg'. 310 * 311 * 'region' is not copied; it has to be allocated beforehand and freed 312 * in 'cb'. 313 */ 314 315 isc_result_t 316 isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface, 317 isc_nm_accept_cb_t accept_cb, void *accept_cbarg, 318 size_t extrahandlesize, int backlog, isc_quota_t *quota, 319 isc_nmsocket_t **sockp); 320 /*%< 321 * Start listening for raw messages over the TCP interface 'iface', using 322 * net manager 'mgr'. 323 * 324 * On success, 'sockp' will be updated to contain a new listening TCP 325 * socket. 326 * 327 * When connection is accepted on the socket, 'accept_cb' will be called with 328 * 'accept_cbarg' as its argument. The callback is expected to start a read. 329 * 330 * When handles are allocated for the socket, 'extrasize' additional bytes 331 * will be allocated along with the handle for an associated object. 332 * 333 * If 'quota' is not NULL, then the socket is attached to the specified 334 * quota. This allows us to enforce TCP client quota limits. 335 * 336 */ 337 338 void 339 isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, 340 isc_nm_cb_t cb, void *cbarg, unsigned int timeout, 341 size_t extrahandlesize); 342 /*%< 343 * Create a socket using netmgr 'mgr', bind it to the address 'local', 344 * and connect it to the address 'peer'. 345 * 346 * When the connection is complete or has timed out, call 'cb' with 347 * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along 348 * with the handle to use for an associated object. 349 * 350 * 'timeout' specifies the timeout interval in milliseconds. 351 * 352 * The connected socket can only be accessed via the handle passed to 353 * 'cb'. 354 */ 355 356 isc_result_t 357 isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface, 358 isc_nm_recv_cb_t recv_cb, void *recv_cbarg, 359 isc_nm_accept_cb_t accept_cb, void *accept_cbarg, 360 size_t extrahandlesize, int backlog, isc_quota_t *quota, 361 isc_nmsocket_t **sockp); 362 /*%< 363 * Start listening for DNS messages over the TCP interface 'iface', using 364 * net manager 'mgr'. 365 * 366 * On success, 'sockp' will be updated to contain a new listening TCPDNS 367 * socket. This is a wrapper around a raw TCP socket, which sends and 368 * receives DNS messages via that socket. It handles message buffering 369 * and pipelining, and automatically prepends messages with a two-byte 370 * length field. 371 * 372 * When a complete DNS message is received on the socket, 'cb' will be 373 * called with 'cbarg' as its argument. 374 * 375 * When a new TCPDNS connection is accepted, 'accept_cb' will be called 376 * with 'accept_cbarg' as its argument. 377 * 378 * When handles are allocated for the socket, 'extrasize' additional bytes 379 * will be allocated along with the handle for an associated object 380 * (typically ns_client). 381 * 382 * 'quota' is passed to isc_nm_listentcp() when opening the raw TCP socket. 383 */ 384 385 void 386 isc_nm_tcpdns_sequential(isc_nmhandle_t *handle); 387 /*%< 388 * Disable pipelining on this connection. Each DNS packet will be only 389 * processed after the previous completes. 390 * 391 * The socket must be unpaused after the query is processed. This is done 392 * the response is sent, or if we're dropping the query, it will be done 393 * when a handle is fully dereferenced by calling the socket's 394 * closehandle_cb callback. 395 * 396 * Note: This can only be run while a message is being processed; if it is 397 * run before any messages are read, no messages will be read. 398 * 399 * Also note: once this has been set, it cannot be reversed for a given 400 * connection. 401 */ 402 403 void 404 isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle, bool value); 405 /*%< 406 * Enable/disable keepalive on this connection by setting it to 'value'. 407 * 408 * When keepalive is active, we switch to using the keepalive timeout 409 * to determine when to close a connection, rather than the idle timeout. 410 */ 411 412 void 413 isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle, 414 uint32_t keepalive, uint32_t advertised); 415 /*%< 416 * Sets the initial, idle, and keepalive timeout values (in milliseconds) to use 417 * for TCP connections, and the timeout value to advertise in responses using 418 * the EDNS TCP Keepalive option (which should ordinarily be the same 419 * as 'keepalive'), in milliseconds. 420 * 421 * Requires: 422 * \li 'mgr' is a valid netmgr. 423 */ 424 425 bool 426 isc_nm_getloadbalancesockets(isc_nm_t *mgr); 427 void 428 isc_nm_setloadbalancesockets(isc_nm_t *mgr, bool enabled); 429 /*%< 430 * Get and set value of load balancing of the sockets. 431 * 432 * Requires: 433 * \li 'mgr' is a valid netmgr. 434 */ 435 436 void 437 isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle, 438 uint32_t *keepalive, uint32_t *advertised); 439 /*%< 440 * Gets the initial, idle, keepalive, or advertised timeout values, 441 * in milliseconds. 442 * 443 * Any integer pointer parameter not set to NULL will be updated to 444 * contain the corresponding timeout value. 445 * 446 * Requires: 447 * \li 'mgr' is a valid netmgr. 448 */ 449 450 void 451 isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp); 452 /*%< 453 * Simulate a broken firewall that blocks UDP messages larger than a given 454 * size. 455 */ 456 457 void 458 isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats); 459 /*%< 460 * Set a socket statistics counter set 'stats' for 'mgr'. 461 * 462 * Requires: 463 *\li 'mgr' is valid and doesn't have stats already set. 464 * 465 *\li stats is a valid set of statistics counters supporting the 466 * full range of socket-related stats counter numbers. 467 */ 468 469 void 470 isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, 471 isc_nm_cb_t cb, void *cbarg, unsigned int timeout, 472 size_t extrahandlesize); 473 /*%< 474 * Establish a DNS client connection via a TCP connection, bound to 475 * the address 'local' and connected to the address 'peer'. 476 * 477 * When the connection is complete or has timed out, call 'cb' with 478 * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along 479 * with the handle to use for an associated object. 480 * 481 * 'timeout' specifies the timeout interval in milliseconds. 482 * 483 * The connected socket can only be accessed via the handle passed to 484 * 'cb'. 485 */ 486 487 void 488 isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid); 489 /*%< 490 * Enqueue the 'task' onto the netmgr ievents queue. 491 * 492 * Requires: 493 * \li 'mgr' is a valid netmgr object 494 * \li 'task' is a valid task 495 * \li 'threadid' is either the preferred netmgr tid or -1, in which case 496 * tid will be picked randomly. The threadid is capped (by modulo) to 497 * maximum number of 'workers' as specifed in isc_nm_start() 498 */ 499 500 void 501 isc_nm_work_offload(isc_nm_t *mgr, isc_nm_workcb_t work_cb, 502 isc_nm_after_workcb_t after_work_cb, void *data); 503 /*%< 504 * Schedules a job to be handled by the libuv thread pool (see uv_work_t). 505 * The function specified in `work_cb` will be run by a thread in the 506 * thread pool; when complete, the `after_work_cb` function will run. 507 * 508 * Requires: 509 * \li 'mgr' is a valid netmgr object. 510 * \li We are currently running in a network manager thread. 511 */ 512 513 void 514 isc__nm_force_tid(int tid); 515 /*%< 516 * Force the thread ID to 'tid'. This is STRICTLY for use in unit 517 * tests and should not be used in any production code. 518 */ 519 520 void 521 isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout); 522 523 /* 524 * Timer related functions 525 */ 526 527 typedef struct isc_nm_timer isc_nm_timer_t; 528 529 typedef void (*isc_nm_timer_cb)(void *, isc_result_t); 530 531 void 532 isc_nm_timer_create(isc_nmhandle_t *, isc_nm_timer_cb, void *, 533 isc_nm_timer_t **); 534 535 void 536 isc_nm_timer_attach(isc_nm_timer_t *, isc_nm_timer_t **); 537 538 void 539 isc_nm_timer_detach(isc_nm_timer_t **); 540 541 void 542 isc_nm_timer_start(isc_nm_timer_t *, uint64_t); 543 544 void 545 isc_nm_timer_stop(isc_nm_timer_t *); 546