From: Felix Fietkau Date: Mon, 9 Jun 2014 18:38:27 +0000 (+0200) Subject: initial code refactoring to prepare for adding dynamic interface support X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=f13f07d145515b80b6f7cc3e1b06dc262ded174d;p=oweals%2Fmdnsd.git initial code refactoring to prepare for adding dynamic interface support Signed-off-by: Felix Fietkau --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 9807fe9..f191fa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") -SET(SOURCES main.c dns.c announce.c cache.c service.c util.c ubus.c) +SET(SOURCES main.c dns.c announce.c cache.c service.c util.c ubus.c interface.c) SET(LIBS ubox ubus resolv blobmsg_json) diff --git a/announce.c b/announce.c index a8a9540..7b52794 100644 --- a/announce.c +++ b/announce.c @@ -22,6 +22,7 @@ #include "util.h" #include "service.h" #include "announce.h" +#include "interface.h" #define TTL_TIMEOUT 75 @@ -50,7 +51,7 @@ announce_timer(struct uloop_timeout *timeout) case STATE_PROBE1: case STATE_PROBE2: case STATE_PROBE3: - dns_send_question(announce_fd, host, TYPE_ANY); + dns_send_question(cur_iface, host, TYPE_ANY); uloop_timeout_set(timeout, 250); announce_state++; break; diff --git a/cache.c b/cache.c index 403ddb8..4182016 100644 --- a/cache.c +++ b/cache.c @@ -39,6 +39,7 @@ #include "cache.h" #include "util.h" #include "dns.h" +#include "interface.h" static struct uloop_timeout cache_gc; struct avl_tree records, entries, hosts; @@ -118,7 +119,7 @@ cache_scan(void) struct cache_entry *s; avl_for_each_element(&entries, s, avl) - dns_send_question(&listener, s->entry, TYPE_PTR); + dns_send_question(cur_iface, s->entry, TYPE_PTR); } static struct cache_entry* @@ -152,7 +153,7 @@ cache_entry(struct uloop_fd *u, char *entry, int hlen, int ttl) avl_insert(&entries, &s->avl); if (!hlen) - dns_send_question(u, entry, TYPE_PTR); + dns_send_question(cur_iface, entry, TYPE_PTR); return s; } diff --git a/dns.c b/dns.c index 5390fe6..a646024 100644 --- a/dns.c +++ b/dns.c @@ -35,6 +35,7 @@ #include "announce.h" #include "util.h" #include "dns.h" +#include "interface.h" char rdata_buffer[MAX_DATA_LEN + 1]; static char name_buffer[MAX_NAME_LEN + 1]; @@ -65,42 +66,8 @@ dns_type_string(uint16_t type) return "N/A"; } -static int -dns_send_packet(int fd, struct iovec *iov, int iov_len) -{ - static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; - static struct sockaddr_in a = { - .sin_family = AF_INET, - .sin_port = htons(MCAST_PORT), - }; - static struct msghdr m = { - .msg_name = (struct sockaddr *) &a, - .msg_namelen = sizeof(a), - .msg_control = cmsg_data, - .msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)), - }; - struct in_pktinfo *pkti; - struct cmsghdr *cmsg; - - m.msg_iov = iov; - m.msg_iovlen = iov_len; - - memset(cmsg_data, 0, sizeof(cmsg_data)); - cmsg = CMSG_FIRSTHDR(&m); - cmsg->cmsg_len = m.msg_controllen; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - - pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); - pkti->ipi_ifindex = iface_index; - - a.sin_addr.s_addr = inet_addr(MCAST_ADDR); - - return sendmsg(fd, &m, 0); -} - void -dns_send_question(struct uloop_fd *u, const char *question, int type) +dns_send_question(struct interface *iface, const char *question, int type) { static struct dns_header h = { .questions = cpu_to_be16(1), @@ -131,7 +98,7 @@ dns_send_question(struct uloop_fd *u, const char *question, int type) iov[1].iov_len = len; - if (dns_send_packet(u->fd, iov, ARRAY_SIZE(iov)) < 0) + if (interface_send_packet(iface, iov, ARRAY_SIZE(iov)) < 0) fprintf(stderr, "failed to send question\n"); else DBG(1, "Q <- %s %s\n", dns_type_string(type), question); @@ -212,7 +179,7 @@ dns_send_answer(struct uloop_fd *u, const char *answer) DBG(1, "A <- %s %s\n", dns_type_string(dns_reply[i].type), answer); } - if (dns_send_packet(u->fd, iov, (dns_answer_cnt * 3) + 1) < 0) + if (interface_send_packet(cur_iface, iov, (dns_answer_cnt * 3) + 1) < 0) fprintf(stderr, "failed to send question\n"); for (i = 0; i < dns_answer_cnt; i++) { diff --git a/dns.h b/dns.h index c52d842..b39608e 100644 --- a/dns.h +++ b/dns.h @@ -64,9 +64,11 @@ struct dns_question { uint16_t class; } __attribute__((packed, aligned(2))); +struct interface; + extern char rdata_buffer[MAX_DATA_LEN + 1]; -extern void dns_send_question(struct uloop_fd *u, const char *question, int type); +extern void dns_send_question(struct interface *iface, const char *question, int type); extern void dns_init_answer(void); extern void dns_add_answer(int type, const uint8_t *rdata, uint16_t rdlength); extern void dns_send_answer(struct uloop_fd *u, const char *answer); diff --git a/interface.c b/interface.c new file mode 100644 index 0000000..866bd05 --- /dev/null +++ b/interface.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2014 John Crispin + * Copyright (C) 2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include "interface.h" +#include "util.h" +#include "dns.h" + +struct interface *cur_iface = NULL; + +int +interface_send_packet(struct interface *iface, struct iovec *iov, int iov_len) +{ + static size_t cmsg_data[( CMSG_SPACE(sizeof(struct in_pktinfo)) / sizeof(size_t)) + 1]; + static struct sockaddr_in a = { + .sin_family = AF_INET, + .sin_port = htons(MCAST_PORT), + }; + static struct msghdr m = { + .msg_name = (struct sockaddr *) &a, + .msg_namelen = sizeof(a), + .msg_control = cmsg_data, + .msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)), + }; + struct in_pktinfo *pkti; + struct cmsghdr *cmsg; + int fd = iface->fd.fd; + + m.msg_iov = iov; + m.msg_iovlen = iov_len; + + memset(cmsg_data, 0, sizeof(cmsg_data)); + cmsg = CMSG_FIRSTHDR(&m); + cmsg->cmsg_len = m.msg_controllen; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pkti = (struct in_pktinfo*) CMSG_DATA(cmsg); + pkti->ipi_ifindex = iface->ifindex; + + a.sin_addr.s_addr = inet_addr(MCAST_ADDR); + + return sendmsg(fd, &m, 0); +} + +static void interface_free(struct interface *iface) +{ + if (cur_iface == iface) + cur_iface = NULL; + + if (iface->fd.fd >= 0) { + uloop_fd_delete(&iface->fd); + close(iface->fd.fd); + } + free(iface); +} + +static void interface_start(struct interface *iface) +{ + cur_iface = iface; +} + +static void +iface_update_cb(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ + struct interface *iface; + + if (node_old) { + iface = container_of(node_old, struct interface, node); + interface_free(iface); + } + + if (node_new) { + iface = container_of(node_new, struct interface, node); + interface_start(iface); + } +} + +int interface_socket_setup(struct interface *iface) +{ + struct ip_mreqn mreq; + uint8_t ttl = 255; + int yes = 1; + int no = 0; + struct sockaddr_in sa = { 0 }; + struct in_addr in; + int fd = iface->fd.fd; + + inet_aton(iface->ip, &in); + + sa.sin_family = AF_INET; + sa.sin_port = htons(MCAST_PORT); + inet_pton(AF_INET, MCAST_ADDR, &sa.sin_addr); + + memset(&mreq, 0, sizeof(mreq)); + mreq.imr_address.s_addr = in.s_addr; + mreq.imr_multiaddr = sa.sin_addr; + mreq.imr_ifindex = iface->ifindex; + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + fprintf(stderr, "ioctl failed: IP_MULTICAST_TTL\n"); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + fprintf(stderr, "ioctl failed: SO_REUSEADDR\n"); + + /* Some network drivers have issues with dropping membership of + * mcast groups when the iface is down, but don't allow rejoining + * when it comes back up. This is an ugly workaround + * -- this was copied from avahi -- + */ + setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + + if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + fprintf(stderr, "failed to join multicast group: %s\n", strerror(errno)); + close(fd); + fd = -1; + return -1; + } + + if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) + fprintf(stderr, "ioctl failed: IP_RECVTTL\n"); + + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) + fprintf(stderr, "ioctl failed: IP_PKTINFO\n"); + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no)) < 0) + fprintf(stderr, "ioctl failed: IP_MULTICAST_LOOP\n"); + + return 0; +} + +static const char* +get_iface_ipv4(const char *ifname) +{ + static char buffer[INET_ADDRSTRLEN]; + struct ifreq ir; + const char *ret; + int sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + return NULL; + + memset(&ir, 0, sizeof(struct ifreq)); + strncpy(ir.ifr_name, ifname, sizeof(ir.ifr_name)); + + if (ioctl(sock, SIOCGIFADDR, &ir) < 0) + return NULL; + + ret = inet_ntop(AF_INET, &((struct sockaddr_in *) &ir.ifr_addr)->sin_addr, buffer, sizeof(buffer)); + close(sock); + + return ret; +} + +int interface_add(const char *name) +{ + struct interface *iface; + const char *ip_str; + char *name_buf, *ip_buf; + + ip_str = get_iface_ipv4(name); + if (!ip_str) + return -1; + + iface = calloc_a(sizeof(*iface), + &name_buf, strlen(name) + 1, + &ip_buf, strlen(ip_str) + 1); + + iface->name = strcpy(name_buf, name); + iface->ip = strcpy(ip_buf, ip_str); + iface->ifindex = if_nametoindex(name); + iface->fd.fd = -1; + + if (iface->ifindex <= 0) + goto error; + + vlist_add(&interfaces, &iface->node, name); + return 0; + +error: + free(iface); + return -1; +} + +VLIST_TREE(interfaces, avl_strcmp, iface_update_cb, false, false); diff --git a/interface.h b/interface.h new file mode 100644 index 0000000..fe709aa --- /dev/null +++ b/interface.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 John Crispin + * Copyright (C) 2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MDNS_INTERFACE_H +#define __MDNS_INTERFACE_H + +#include +#include + +#include +#include + +extern struct vlist_tree interfaces; +extern struct interface *cur_iface; + +struct interface { + struct vlist_node node; + + const char *name; + struct uloop_fd fd; + + int ifindex; + const char *ip; +}; + +int interface_add(const char *name); +int interface_send_packet(struct interface *iface, struct iovec *iov, int iov_len); +int interface_socket_setup(struct interface *iface); + +#endif diff --git a/main.c b/main.c index 39b8320..6624d36 100644 --- a/main.c +++ b/main.c @@ -38,11 +38,10 @@ #include "cache.h" #include "service.h" #include "announce.h" +#include "interface.h" static struct uloop_timeout reconnect; char *iface_name = "eth0"; -const char *iface_ip; -int iface_index; static int parse_answer(struct uloop_fd *u, uint8_t *buffer, int len, uint8_t **b, int *rlen, int cache) @@ -170,24 +169,21 @@ read_socket(struct uloop_fd *u, unsigned int events) static void reconnect_socket(struct uloop_timeout *timeout) { - - if (iface_ip) - listener.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, MCAST_ADDR, "5353"); - - if (!iface_ip || listener.fd < 0) { + cur_iface->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, MCAST_ADDR, "5353"); + if (cur_iface->fd.fd < 0) { fprintf(stderr, "failed to add listener: %s\n", strerror(errno)); uloop_timeout_set(&reconnect, 1000); } else { - if (socket_setup(listener.fd, iface_ip)) { + if (interface_socket_setup(cur_iface)) { uloop_timeout_set(&reconnect, 1000); - listener.fd = -1; + cur_iface->fd.fd = -1; return; } - uloop_fd_add(&listener, ULOOP_READ); + uloop_fd_add(&cur_iface->fd, ULOOP_READ); sleep(5); - dns_send_question(&listener, "_services._dns-sd._udp.local", TYPE_PTR); - announce_init(&listener); + dns_send_question(cur_iface, "_services._dns-sd._udp.local", TYPE_PTR); + announce_init(&cur_iface->fd); } } @@ -220,21 +216,16 @@ main(int argc, char **argv) if (!iface_name) return -1; - iface_ip = get_iface_ipv4(iface_name); + uloop_init(); - if (!iface_ip) { - fprintf(stderr, "failed to read ip for %s\n", iface_name); + if (interface_add(iface_name)) { + fprintf(stderr, "Failed to add interface %s\n", iface_name); return -1; } - iface_index = get_iface_index(iface_name); - - if (!iface_index) { - fprintf(stderr, "failed to read index for %s\n", iface_name); + if (!cur_iface) return -1; - } - fprintf(stderr, "interface %s has ip %s and index %d\n", iface_name, iface_ip, iface_index); signal_setup(); if (cache_init()) @@ -242,10 +233,9 @@ main(int argc, char **argv) service_init(); - listener.cb = read_socket; + cur_iface->fd.cb = read_socket; reconnect.cb = reconnect_socket; - uloop_init(); uloop_timeout_set(&reconnect, 100); ubus_startup(); uloop_run(); diff --git a/service.c b/service.c index 55cd2bb..cf1d336 100644 --- a/service.c +++ b/service.c @@ -34,6 +34,7 @@ #include "dns.h" #include "service.h" #include "util.h" +#include "interface.h" enum { SERVICE_PORT, @@ -117,8 +118,8 @@ service_send_a(struct uloop_fd *u) int len = dn_comp(host, buffer, MAX_NAME_LEN, NULL, NULL); struct in_addr in; - if (!inet_aton(iface_ip, &in)) { - fprintf(stderr, "%s is not valid\n", iface_ip); + if (!inet_aton(cur_iface->ip, &in)) { + fprintf(stderr, "%s is not valid\n", cur_iface->ip); return; } diff --git a/util.c b/util.c index 4684b70..0c4a4ee 100644 --- a/util.c +++ b/util.c @@ -34,7 +34,6 @@ #include "util.h" int debug = 0; -struct uloop_fd listener; static void signal_shutdown(int signal) @@ -73,53 +72,6 @@ rand_time_delta(uint32_t t) return val; } -const char* -get_iface_ipv4(const char *ifname) -{ - static char buffer[INET_ADDRSTRLEN]; - struct ifreq ir; - const char *ret; - int sock; - - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) - return NULL; - - memset(&ir, 0, sizeof(struct ifreq)); - - strncpy(ir.ifr_name, ifname, sizeof(ir.ifr_name)); - - if (ioctl(sock, SIOCGIFADDR, &ir) < 0) - return NULL; - - ret = inet_ntop(AF_INET, &((struct sockaddr_in *) &ir.ifr_addr)->sin_addr, buffer, sizeof(buffer)); - close(sock); - - return ret; -} - -int -get_iface_index(const char *ifname) -{ - struct ifreq ir; - int sock; - - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) - return 0; - - memset(&ir, 0, sizeof(struct ifreq)); - - strncpy(ir.ifr_name, ifname, sizeof(ir.ifr_name)); - - if (ioctl(sock, SIOCGIFINDEX, &ir) < 0) - return 0; - - close(sock); - - return ir.ifr_ifindex; -} - char* get_hostname(void) { @@ -131,59 +83,6 @@ get_hostname(void) return utsname.nodename; } -int -socket_setup(int fd, const char *ip) -{ - struct ip_mreqn mreq; - uint8_t ttl = 255; - int yes = 1; - int no = 0; - struct sockaddr_in sa = { 0 }; - struct in_addr in; - - inet_aton(iface_ip, &in); - - sa.sin_family = AF_INET; - sa.sin_port = htons(MCAST_PORT); - inet_pton(AF_INET, MCAST_ADDR, &sa.sin_addr); - - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_address.s_addr = in.s_addr; - mreq.imr_multiaddr = sa.sin_addr; - mreq.imr_ifindex = iface_index; - - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) - fprintf(stderr, "ioctl failed: IP_MULTICAST_TTL\n"); - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) - fprintf(stderr, "ioctl failed: SO_REUSEADDR\n"); - - /* Some network drivers have issues with dropping membership of - * mcast groups when the iface is down, but don't allow rejoining - * when it comes back up. This is an ugly workaround - * -- this was copied from avahi -- - */ - setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); - - if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - fprintf(stderr, "failed to join multicast group: %s\n", strerror(errno)); - close(fd); - fd = -1; - return -1; - } - - if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) - fprintf(stderr, "ioctl failed: IP_RECVTTL\n"); - - if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) - fprintf(stderr, "ioctl failed: IP_PKTINFO\n"); - - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no)) < 0) - fprintf(stderr, "ioctl failed: IP_MULTICAST_LOOP\n"); - - return 0; -} - void* memdup(const void *d, int l) { diff --git a/util.h b/util.h index 689e3fd..947f851 100644 --- a/util.h +++ b/util.h @@ -20,17 +20,11 @@ } while (0) extern int debug; -extern struct uloop_fd listener; -extern const char *iface_ip; -extern int iface_index; void *memdup(const void *d, int l); extern void signal_setup(void); -extern int socket_setup(int fd, const char *ip); extern char* get_hostname(void); -extern const char* get_iface_ipv4(const char *ifname); -extern int get_iface_index(const char *ifname); extern uint32_t rand_time_delta(uint32_t t); #endif