diff options
author | Tobias Klauser <tklauser@distanz.ch> | 2017-01-10 15:54:52 +0100 |
---|---|---|
committer | Tobias Klauser <tklauser@distanz.ch> | 2017-01-10 15:54:52 +0100 |
commit | e9cd5a6826f198029ee466ae63d56dca4dfa4ad7 (patch) | |
tree | cb259a1fc5cbb2b8bef965ea0f968b056fefbd5d | |
parent | 6281d3c3633fb1de98ff6010212325e45c226f3c (diff) |
llmnrd: merge rtnl interface event loop into main select() loops
Instead of spawming a thread for the sole purpose of watching the rtnl
for (presumably) seldom events, just merge the select() for all sockets
together in one single main loop.
This reduces unnecessary complexity and makes llmnrd no longer require
any locking.
It also allows us to handle signals in a thread-safe manner (as there
aren't any ;) and thus the race condition on exit reported in #20
Closes #20
Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | iface.c | 94 | ||||
-rw-r--r-- | iface.h | 8 | ||||
-rw-r--r-- | llmnr.c | 89 | ||||
-rw-r--r-- | llmnr.h | 8 | ||||
-rw-r--r-- | llmnrd.c | 93 |
6 files changed, 116 insertions, 178 deletions
@@ -7,7 +7,7 @@ VERSION = 0.2.1 # llmnrd binary D_P = llmnrd D_OBJS = llmnr.o iface.o socket.o util.o llmnrd.o -D_LIBS = -lpthread +D_LIBS = # llmnr-query binary Q_P = llmnr-query @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 Tobias Klauser <tklauser@distanz.ch> + * Copyright (C) 2015-2017 Tobias Klauser <tklauser@distanz.ch> * * This file is part of llmnrd. * @@ -19,7 +19,6 @@ #include <assert.h> #include <errno.h> #include <netdb.h> -#include <pthread.h> #include <stdint.h> #include <string.h> #include <unistd.h> @@ -40,10 +39,7 @@ #include "iface.h" -static bool iface_running = true; -static bool iface_ipv6 = false; static unsigned int iface_ifindex = 0; -static pthread_t iface_thread; static iface_event_handler_t iface_event_handler; struct iface_record { @@ -54,7 +50,6 @@ struct iface_record { }; static struct list_head iface_list_head; -static pthread_mutex_t iface_list_mutex; size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, struct sockaddr_storage *addrs, size_t addrs_size) @@ -65,8 +60,6 @@ size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, if (!addrs) return 0; - pthread_mutex_lock(&iface_list_mutex); - list_for_each_entry(rec, &iface_list_head, list) { if (rec->index == ifindex) { size_t i; @@ -81,8 +74,6 @@ size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, } } - pthread_mutex_unlock(&iface_list_mutex); - return n; } @@ -182,8 +173,6 @@ static void iface_addr_add(unsigned int index, unsigned char family, const void fill_sockaddr_storage(&sst, family, addr); - pthread_mutex_lock(&iface_list_mutex); - list_for_each_entry(rec, &iface_list_head, list) if (rec->index == index) goto add; @@ -195,7 +184,6 @@ static void iface_addr_add(unsigned int index, unsigned char family, const void list_add_tail(&rec->list, &iface_list_head); add: iface_record_addr_add(rec, &sst); - pthread_mutex_unlock(&iface_list_mutex); } static void iface_addr_del(unsigned int index, unsigned char family, const void *addr) @@ -205,16 +193,12 @@ static void iface_addr_del(unsigned int index, unsigned char family, const void fill_sockaddr_storage(&sst, family, addr); - pthread_mutex_lock(&iface_list_mutex); - list_for_each_entry(rec, &iface_list_head, list) { if (rec->index == index) { iface_record_addr_del(rec, &sst); break; } } - - pthread_mutex_unlock(&iface_list_mutex); } static void iface_nlmsg_change_link(const struct nlmsghdr *nlh __unused) @@ -342,75 +326,33 @@ static int iface_rtnl_enumerate(int sock, uint16_t type, unsigned char family) return iface_nlmsg_process((const struct nlmsghdr *)pktbuf, recvlen); } -void iface_register_event_handler(iface_event_handler_t event_handler) +void iface_init(int sock, const char *iface, bool ipv6, + iface_event_handler_t event_handler) { - iface_event_handler = event_handler; -} - -int iface_run(void) -{ - int ret = -1; - int sock; - INIT_LIST_HEAD(&iface_list_head); - if (pthread_mutex_init(&iface_list_mutex, NULL) != 0) { - log_err("Failed to initialize interface list mutex\n"); - return -1; - } - - sock = socket_open_rtnl(iface_ipv6); - if (sock < 0) - return -1; + iface_event_handler = event_handler; + if (iface) + iface_ifindex = if_nametoindex(iface); /* send RTM_GETADDR request to initially populate the interface list */ if (iface_rtnl_enumerate(sock, RTM_GETADDR, AF_INET) < 0) - goto out; - if (iface_ipv6) { + log_err("Failed to enumerate rtnl interfaces: %s\n", strerror(errno)); + if (ipv6) if (iface_rtnl_enumerate(sock, RTM_GETADDR, AF_INET6) < 0) - goto out; - } - - while (iface_running) { - ssize_t recvlen; - uint8_t pktbuf[8192]; - - if ((recvlen = recv(sock, pktbuf, sizeof(pktbuf), 0)) < 0) { - if (errno != EINTR) - log_err("Failed to receive netlink message: %s\n", strerror(errno)); - goto out; - } - - if (iface_nlmsg_process((const struct nlmsghdr *)pktbuf, recvlen) < 0) - log_warn("Error processing netlink message\n"); - } - - pthread_mutex_destroy(&iface_list_mutex); - ret = 0; -out: - close(sock); - return ret; -} - -static void* iface_run_wrapper(void *data __unused) -{ - return ERR_PTR(iface_run()); + log_err("Failed to enumerate rtnl interfaces: %s\n", strerror(errno)); } -int iface_start_thread(bool ipv6, const char *iface) +void iface_recv(int sock) { - iface_ipv6 = ipv6; - if (iface) - iface_ifindex = if_nametoindex(iface); + ssize_t recvlen; + uint8_t pktbuf[8192]; - if (pthread_create(&iface_thread, NULL, iface_run_wrapper, NULL) < 0) { - log_err("Failed to start interface monitoring thread\n"); - return -1; + if ((recvlen = recv(sock, pktbuf, sizeof(pktbuf), 0)) < 0) { + if (errno != EINTR) + log_err("Failed to receive netlink message: %s\n", strerror(errno)); + return; } - return 0; -} - -void iface_stop(void) -{ - iface_running = false; + if (iface_nlmsg_process((const struct nlmsghdr *)pktbuf, recvlen) < 0) + log_warn("Error processing netlink message\n"); } @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 Tobias Klauser <tklauser@distanz.ch> + * Copyright (C) 2015-2017 Tobias Klauser <tklauser@distanz.ch> * * This file is part of llmnrd. * @@ -30,9 +30,9 @@ enum iface_event_type { typedef void (*iface_event_handler_t)(enum iface_event_type, unsigned char af, unsigned int ifindex); -void iface_register_event_handler(iface_event_handler_t event_handler); -int iface_start_thread(bool ipv6, const char *iface); -void iface_stop(void); +void iface_init(int sock, const char *iface, bool ipv6, + iface_event_handler_t event_handler); +void iface_recv(int sock); size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, struct sockaddr_storage *addrs, size_t addrs_size); @@ -37,52 +37,17 @@ #include "llmnr-packet.h" #include "llmnr.h" -static int llmnr_sock_ipv4 = -1; -static int llmnr_sock_ipv6 = -1; -static bool llmnr_running = true; -/* - * Host name in DNS name format (length octet + name + 0 byte) - */ +static bool llmnr_ipv6 = false; +/* Host name in DNS name format (length octet + name + 0 byte) */ static char llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 2]; -static void llmnr_iface_event_handle(enum iface_event_type type, unsigned char af, - unsigned int ifindex) -{ - switch (af) { - case AF_INET: - socket_mcast_group_ipv4(llmnr_sock_ipv4, ifindex, type == IFACE_ADD); - break; - case AF_INET6: - socket_mcast_group_ipv6(llmnr_sock_ipv6, ifindex, type == IFACE_ADD); - break; - default: - /* ignore */ - break; - } -} - -int llmnr_init(const char *hostname, uint16_t port, bool ipv6, const char *iface) +void llmnr_init(const char *hostname, bool ipv6) { llmnr_hostname[0] = strlen(hostname); strncpy(&llmnr_hostname[1], hostname, LLMNR_LABEL_MAX_SIZE); llmnr_hostname[LLMNR_LABEL_MAX_SIZE + 1] = '\0'; - log_info("Starting llmnrd on port %u, hostname %s\n", port, hostname); - if (iface) - log_info("Binding to interface %s\n", iface); - - llmnr_sock_ipv4 = socket_open_ipv4(port, iface); - if (llmnr_sock_ipv4 < 0) - return -1; - - if (ipv6) { - llmnr_sock_ipv6 = socket_open_ipv6(port, iface); - if (llmnr_sock_ipv6 < 0) - return -1; - } - - iface_register_event_handler(&llmnr_iface_event_handle); - return 0; + llmnr_ipv6 = ipv6; } static bool llmnr_name_matches(const uint8_t *query) @@ -134,7 +99,7 @@ static void llmnr_respond(unsigned int ifindex, const struct llmnr_hdr *hdr, return; /* No AAAA responses if IPv6 is disabled */ - if (llmnr_sock_ipv6 < 0 && qtype == LLMNR_QTYPE_AAAA) + if (llmnr_ipv6 < 0 && qtype == LLMNR_QTYPE_AAAA) return; switch (qtype) { @@ -254,7 +219,7 @@ static void llmnr_packet_process(unsigned int ifindex, const uint8_t *pktbuf, si llmnr_respond(ifindex, hdr, query, query_len, sock, sst); } -static void llmnr_recv(int sock) +void llmnr_recv(int sock) { uint8_t pktbuf[2048], aux[128]; struct msghdr msg; @@ -297,45 +262,3 @@ static void llmnr_recv(int sock) else log_warn("Could not get interface of incoming packet\n"); } - -int llmnr_run(void) -{ - int ret = -1; - - while (llmnr_running) { - fd_set rfds; - int nfds, ret; - - FD_ZERO(&rfds); - FD_SET(llmnr_sock_ipv4, &rfds); - if (llmnr_sock_ipv6 >= 0) { - FD_SET(llmnr_sock_ipv6, &rfds); - nfds = max(llmnr_sock_ipv4, llmnr_sock_ipv6) + 1; - } else - nfds = llmnr_sock_ipv4 + 1; - - ret = select(nfds, &rfds, NULL, NULL, NULL); - if (ret < 0) { - if (errno != EINTR) - log_err("Failed to select() on socket: %s\n", strerror(errno)); - goto out; - } else if (ret) { - if (FD_ISSET(llmnr_sock_ipv4, &rfds)) - llmnr_recv(llmnr_sock_ipv4); - if (llmnr_sock_ipv6 >= 0 && FD_ISSET(llmnr_sock_ipv6, &rfds)) - llmnr_recv(llmnr_sock_ipv6); - } - } - - ret = 0; -out: - close(llmnr_sock_ipv4); - if (llmnr_sock_ipv6 >= 0) - close(llmnr_sock_ipv6); - return ret; -} - -void llmnr_stop(void) -{ - llmnr_running = false; -} @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 Tobias Klauser <tklauser@distanz.ch> + * Copyright (C) 2015-2017 Tobias Klauser <tklauser@distanz.ch> * * This file is part of llmnrd. * @@ -20,10 +20,8 @@ #define LLMNR_H #include <stdbool.h> -#include <stdint.h> -int llmnr_init(const char *hostname, uint16_t port, bool ipv6, const char *iface); -int llmnr_run(void); -void llmnr_stop(void); +void llmnr_init(const char *hostname, bool ipv6); +void llmnr_recv(int sock); #endif /* LLMNR_H */ @@ -1,7 +1,7 @@ /* * llmnrd -- LLMNR (RFC 4705) responder daemon. * - * Copyright (C) 2014-2016 Tobias Klauser <tklauser@distanz.ch> + * Copyright (C) 2014-2017 Tobias Klauser <tklauser@distanz.ch> * * This file is part of llmnrd. * @@ -38,6 +38,11 @@ #include "iface.h" #include "llmnr.h" #include "llmnr-packet.h" +#include "socket.h" + +static bool llmnrd_running = true; +static int llmnrd_sock_ipv4 = -1; +static int llmnrd_sock_ipv6 = -1; static const char *short_opts = "H:i:p:6dhV"; static const struct option long_opts[] = { @@ -82,8 +87,7 @@ static void signal_handler(int sig) case SIGQUIT: case SIGTERM: log_info("Interrupt received. Stopping llmnrd.\n"); - iface_stop(); - llmnr_stop(); + llmnrd_running = false; break; case SIGHUP: default: @@ -109,14 +113,32 @@ static void register_signal(int sig, void (*handler)(int)) } } +static void iface_event_handle(enum iface_event_type type, unsigned char af, + unsigned int ifindex) +{ + switch (af) { + case AF_INET: + socket_mcast_group_ipv4(llmnrd_sock_ipv4, ifindex, type == IFACE_ADD); + break; + case AF_INET6: + socket_mcast_group_ipv6(llmnrd_sock_ipv6, ifindex, type == IFACE_ADD); + break; + default: + /* ignore */ + break; + } +} + int main(int argc, char **argv) { - int c, ret = EXIT_FAILURE; + int c, ret = -1; long num_arg; bool daemonize = false, ipv6 = false; char *hostname = NULL; char *iface = NULL; uint16_t port = LLMNR_UDP_PORT; + int llmnrd_sock_rtnl = -1; + int nfds; while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (c) { @@ -166,18 +188,71 @@ int main(int argc, char **argv) if (daemonize) { if (daemon(0, 0) != 0) { log_err("Failed to daemonize process: %s\n", strerror(errno)); - return EXIT_FAILURE; + goto out; } } - if (llmnr_init(hostname, port, ipv6, iface) < 0) + log_info("Starting llmnrd on port %u, hostname %s\n", port, hostname); + if (iface) + log_info("Binding to interface %s\n", iface); + + llmnrd_sock_ipv4 = socket_open_ipv4(port, iface); + if (llmnrd_sock_ipv4 < 0) goto out; - if (iface_start_thread(ipv6, iface) < 0) + if (ipv6) { + llmnrd_sock_ipv6 = socket_open_ipv6(port, iface); + if (llmnrd_sock_ipv6 < 0) + goto out; + } + + llmnrd_sock_rtnl = socket_open_rtnl(ipv6); + if (llmnrd_sock_rtnl < 0) goto out; - ret = llmnr_run(); + llmnr_init(hostname, ipv6); + iface_init(llmnrd_sock_rtnl, iface, ipv6, &iface_event_handle); + + nfds = max(llmnrd_sock_ipv4, llmnrd_sock_rtnl); + if (llmnrd_sock_ipv6 >= 0) + nfds = max(nfds, llmnrd_sock_ipv6); + nfds += 1; + + while (llmnrd_running) { + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(llmnrd_sock_ipv4, &rfds); + FD_SET(llmnrd_sock_rtnl, &rfds); + if (llmnrd_sock_ipv6 >= 0) + FD_SET(llmnrd_sock_ipv6, &rfds); + + ret = select(nfds, &rfds, NULL, NULL, NULL); + if (ret < 0) { + if (errno != EINTR) + log_err("Failed to select() on socket: %s\n", strerror(errno)); + goto out; + } else if (ret) { + /* handle RTNL messages first so we can respond with + * up-to-date information. + */ + if (FD_ISSET(llmnrd_sock_rtnl, &rfds)) + iface_recv(llmnrd_sock_rtnl); + if (FD_ISSET(llmnrd_sock_ipv4, &rfds)) + llmnr_recv(llmnrd_sock_ipv4); + if (llmnrd_sock_ipv6 >= 0 && FD_ISSET(llmnrd_sock_ipv6, &rfds)) + llmnr_recv(llmnrd_sock_ipv6); + } + } + + ret = 0; out: + if (llmnrd_sock_rtnl >= 0) + close(llmnrd_sock_rtnl); + if (llmnrd_sock_ipv6 >= 0) + close(llmnrd_sock_ipv6); + if (llmnrd_sock_ipv4 >= 0) + close(llmnrd_sock_ipv4); free(hostname); - return ret; + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } |