From f46d59e0dc7b54a3f4a7ead2fd2d4258b3f56782 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 17 Aug 2016 08:41:52 +0200 Subject: llmnrd: Allow to bind to a specific network interface Add a command line option -i/--interface which allows to bind the llmnrd sockets to a specific interface. If used, requests are only answered on the specified interface. Example: llmnrd -i eth0 Closes #9 Signed-off-by: Tobias Klauser --- iface.c | 12 +++++++++++- iface.h | 2 +- llmnr.c | 8 +++++--- llmnr.h | 4 ++-- llmnrd.c | 12 +++++++++--- socket.c | 27 +++++++++++++++++++++++++-- socket.h | 4 ++-- 7 files changed, 55 insertions(+), 14 deletions(-) diff --git a/iface.c b/iface.c index 8cb88fc..333a142 100644 --- a/iface.c +++ b/iface.c @@ -42,6 +42,7 @@ 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; @@ -234,6 +235,13 @@ static void iface_nlmsg_change_addr(const struct nlmsghdr *nlh) if ((ifa->ifa_flags & (IFA_F_TEMPORARY | IFA_F_TENTATIVE)) != 0) return; + /* + * If bound to a specific interface, don't report addresses of any other + * interface. + */ + if (iface_ifindex > 0 && index != iface_ifindex) + return; + if_indextoname(index, ifname); rta = (struct rtattr *)((const uint8_t *)nlh + NLMSG_SPACE(sizeof(*ifa))); @@ -388,9 +396,11 @@ static void* iface_run_wrapper(void *data __unused) return ERR_PTR(iface_run()); } -int iface_start_thread(bool ipv6) +int iface_start_thread(bool ipv6, const char *iface) { iface_ipv6 = ipv6; + if (iface) + iface_ifindex = if_nametoindex(iface); if (pthread_create(&iface_thread, NULL, iface_run_wrapper, NULL) < 0) { log_err("Failed to start interface monitoring thread\n"); diff --git a/iface.h b/iface.h index 5032d87..11c49e7 100644 --- a/iface.h +++ b/iface.h @@ -31,7 +31,7 @@ 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); +int iface_start_thread(bool ipv6, const char *iface); void iface_stop(void); size_t iface_addr_lookup(unsigned int ifindex, unsigned char family, diff --git a/llmnr.c b/llmnr.c index a833ef1..206f165 100644 --- a/llmnr.c +++ b/llmnr.c @@ -61,19 +61,21 @@ static void llmnr_iface_event_handle(enum iface_event_type type, unsigned char a } } -int llmnr_init(const char *hostname, uint16_t port, bool ipv6) +int llmnr_init(const char *hostname, uint16_t port, bool ipv6, const char *iface) { 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); + llmnr_sock_ipv4 = socket_open_ipv4(port, iface); if (llmnr_sock_ipv4 < 0) return -1; if (ipv6) { - llmnr_sock_ipv6 = socket_open_ipv6(port); + llmnr_sock_ipv6 = socket_open_ipv6(port, iface); if (llmnr_sock_ipv6 < 0) return -1; } diff --git a/llmnr.h b/llmnr.h index da86440..620e539 100644 --- a/llmnr.h +++ b/llmnr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Tobias Klauser + * Copyright (C) 2015-2016 Tobias Klauser * * This file is part of llmnrd. * @@ -22,7 +22,7 @@ #include #include -int llmnr_init(const char *hostname, uint16_t port, bool ipv6); +int llmnr_init(const char *hostname, uint16_t port, bool ipv6, const char *iface); int llmnr_run(void); void llmnr_stop(void); diff --git a/llmnrd.c b/llmnrd.c index 866e919..2bc4a20 100644 --- a/llmnrd.c +++ b/llmnrd.c @@ -39,9 +39,10 @@ #include "llmnr.h" #include "llmnr-packet.h" -static const char *short_opts = "H:p:6dhV"; +static const char *short_opts = "H:i:p:6dhV"; static const struct option long_opts[] = { { "hostname", required_argument, NULL, 'H' }, + { "interface", required_argument, NULL, 'i' }, { "port", required_argument, NULL, 'p' }, { "ipv6", no_argument, NULL, '6' }, { "daemonize", no_argument, NULL, 'd' }, @@ -55,6 +56,7 @@ static void __noreturn usage_and_exit(int status) fprintf(stdout, "Usage: llmnrd [OPTIONS]\n" "Options:\n" " -H, --hostname NAME set hostname to respond with (default: system hostname)\n" + " -i, --interface DEV bind socket to a specific interface, e.g. eth0\n" " -p, --port NUM set port number to listen on (default: %d)\n" " -6, --ipv6 enable LLMNR name resolution over IPv6\n" " -d, --daemonize run as daemon in the background\n" @@ -113,6 +115,7 @@ int main(int argc, char **argv) long num_arg; bool daemonize = false, ipv6 = false; char *hostname = NULL; + char *iface = NULL; uint16_t port = LLMNR_UDP_PORT; while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { @@ -123,6 +126,9 @@ int main(int argc, char **argv) case 'H': hostname = xstrdup(optarg); break; + case 'i': + iface = xstrdup(optarg); + break; case 'p': num_arg = strtol(optarg, NULL, 0); if (num_arg < 0 || num_arg > UINT16_MAX) { @@ -164,10 +170,10 @@ int main(int argc, char **argv) } } - if (llmnr_init(hostname, port, ipv6) < 0) + if (llmnr_init(hostname, port, ipv6, iface) < 0) goto out; - if (iface_start_thread(ipv6) < 0) + if (iface_start_thread(ipv6, iface) < 0) goto out; ret = llmnr_run(); diff --git a/socket.c b/socket.c index 21e715a..1103779 100644 --- a/socket.c +++ b/socket.c @@ -36,7 +36,24 @@ static const int YES = 1; static const int TTL = 255; -int socket_open_ipv4(uint16_t port) +static int socket_bind_to_device(int sock, const char *iface) { +#ifdef SO_BINDTODEVICE + /* bind socket to specific interface */ + if (iface) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1); + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) { + log_err("Failed to bind socket to device %s: %s\n", iface, strerror(errno)); + return -1; + } + } +#endif + return 0; +} + +int socket_open_ipv4(uint16_t port, const char *iface) { int sock; struct sockaddr_in sa; @@ -64,6 +81,9 @@ int socket_open_ipv4(uint16_t port) goto err; } + if (socket_bind_to_device(sock, iface) < 0) + goto err; + /* bind the socket */ memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; @@ -81,7 +101,7 @@ err: return -1; } -int socket_open_ipv6(uint16_t port) +int socket_open_ipv6(uint16_t port, const char *iface) { int sock, opt_pktinfo; struct sockaddr_in6 sa; @@ -120,6 +140,9 @@ int socket_open_ipv6(uint16_t port) goto err; } + if (socket_bind_to_device(sock, iface) < 0) + goto err; + /* bind the socket */ memset(&sa, 0, sizeof(sa)); sa.sin6_family = AF_INET6; diff --git a/socket.h b/socket.h index 93c41a0..49d999b 100644 --- a/socket.h +++ b/socket.h @@ -22,8 +22,8 @@ #include #include -int socket_open_ipv4(uint16_t port); -int socket_open_ipv6(uint16_t port); +int socket_open_ipv4(uint16_t port, const char *iface); +int socket_open_ipv6(uint16_t port, const char *iface); int socket_open_rtnl(bool ipv6); int socket_mcast_group_ipv4(int sock, unsigned int ifindex, bool join); -- cgit v1.2.3-54-g00ecf