diff --git a/net/respondd/Makefile b/net/respondd/Makefile index 57836bd..1941feb 100644 --- a/net/respondd/Makefile +++ b/net/respondd/Makefile @@ -25,6 +25,7 @@ define Build/Prepare endef define Package/respondd/install + $(CP) ./files/* $(1)/ $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/respondd $(1)/usr/bin/ endef diff --git a/net/respondd/files/etc/config/respondd b/net/respondd/files/etc/config/respondd new file mode 100644 index 0000000..87f90aa --- /dev/null +++ b/net/respondd/files/etc/config/respondd @@ -0,0 +1,5 @@ +config respondd + option port '1001' + option mcast_group 'ff02::2:1001' + option iface_list_file '/etc/respondd.ifaces' + option data_dir '/lib/respondd' diff --git a/net/respondd/files/etc/init.d/respondd b/net/respondd/files/etc/init.d/respondd new file mode 100755 index 0000000..dab23fd --- /dev/null +++ b/net/respondd/files/etc/init.d/respondd @@ -0,0 +1,45 @@ +#!/bin/sh /etc/rc.common + +USE_PROCD=1 +START=50 + +DAEMON=/usr/bin/respondd +PIDFILE=/var/run/respondd.pid + +validate_respondd_section() { + uci_validate_section respondd respondd "${1}" \ + 'port:uinteger' \ + 'mcast_group:string' \ + 'iface_list_file:file' \ + 'data_dir:directory' +} + +start_service() { + config_load respondd + config_foreach start_respondd respondd +} + +start_respondd() { + local port mcast_group iface_list_file data_dir + validate_respondd_section "$1" + + procd_open_instance + procd_set_param command $DAEMON \ + ${mcast_group:+-g "$mcast_group"} \ + ${iface_list_file:+-c "$iface_list_file"} \ + ${port:+-p $port} \ + ${data_dir:+-d "$data_dir"} + procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} + procd_set_param pidfile $PIDFILE + procd_set_param stderr 1 + procd_close_instance +} + +reload_service() { + kill -HUP $(cat $PIDFILE) +} + +service_triggers() { + procd_add_reload_trigger "respondd" + procd_add_validation "validate_respondd_section" +} diff --git a/net/respondd/src/respondd.c b/net/respondd/src/respondd.c index e3242da..eefbd66 100644 --- a/net/respondd/src/respondd.c +++ b/net/respondd/src/respondd.c @@ -33,8 +33,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -64,9 +67,18 @@ struct request_type { int64_t cache_timeout; }; +struct ifidx_list { + struct ifidx_list *next; + int idx; +}; + +static int sock; static int64_t now; +static struct in6_addr mgroup_addr = IN6ADDR_ANY_INIT; static struct hsearch_data htab; +static struct ifidx_list *ifaces = NULL; +static char *iface_list_path = NULL; static struct json_object * merge_json(struct json_object *a, struct json_object *b); @@ -75,31 +87,31 @@ static struct json_object * merge_json(struct json_object *a, struct json_object static void usage() { puts("Usage:"); puts(" respondd -h"); - puts(" respondd [-p ] [-g -i [-i ..]] [-d ]"); + puts(" respondd [-p ] [-g -c ] [-d ]"); puts(" -p port number to listen on"); puts(" -g multicast group, e.g. ff02::2:1001"); - puts(" -i interface on which the group is joined"); + puts(" -c file with one iface name per line, on all of which"); + puts(" the multicast group is joined"); puts(" -d data provider directory (default: current directory)"); puts(" -h this help\n"); + puts("The is reloaded on SIGHUP.\n"); } -static void join_mcast(const int sock, const struct in6_addr addr, const char *iface) { +static void mcast_membership(int iface, bool member) { struct ipv6_mreq mreq; - mreq.ipv6mr_multiaddr = addr; - mreq.ipv6mr_interface = if_nametoindex(iface); + mreq.ipv6mr_multiaddr = mgroup_addr; + mreq.ipv6mr_interface = iface; - if (mreq.ipv6mr_interface == 0) - goto error; - - if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1) + if (setsockopt(sock, IPPROTO_IPV6, member? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) goto error; return; error: - fprintf(stderr, "Could not join multicast group on %s: ", iface); - perror(NULL); + error(0, errno, "Could not %s multicast group on interface #%d", + member? "join" : "leave", + iface); } @@ -111,6 +123,75 @@ static void update_time(void) { } +static void read_iface_list() { + FILE *f; + struct ifidx_list *tmp_item, *iter_new, *iter_old = ifaces; + struct ifidx_list **prev_ptr; + char *line = NULL; + size_t size = 0; + int len; + + if (IN6_IS_ADDR_UNSPECIFIED(&mgroup_addr) || iface_list_path == NULL) + return; + + f = fopen(iface_list_path, "r"); + if (f == NULL) { + error(0, errno, "warning: could not open iface list %s", iface_list_path); + return; + } + ifaces = NULL; + while ((len = getline(&line, &size, f)) != -1) { + if (line[len-1] == '\n') + line[len-1] = '\0'; + tmp_item = malloc(sizeof(struct ifidx_list)); + tmp_item->idx = if_nametoindex(line); + if (tmp_item->idx == 0) { + error(0, errno, "warning: could not find index for interface %s", line); + free(tmp_item); + continue; + } + + // Insert into sorted list ifaces + prev_ptr = &ifaces; + for (iter_new = ifaces; iter_new != NULL; iter_new = iter_new->next) { + if (iter_new->idx > tmp_item->idx) + break; + prev_ptr = &iter_new->next; + } + tmp_item->next = iter_new; + *prev_ptr = tmp_item; + } + fclose(f); + + for (iter_new = ifaces; iter_old != NULL || iter_new != NULL; ) { + if (iter_new == NULL || (iter_old != NULL && iter_old->idx < iter_new->idx)) { + // interface iter_old->idx disappeared + mcast_membership(iter_old->idx, false); + tmp_item = iter_old; + iter_old = iter_old->next; + free(tmp_item); + } + else if (iter_old == NULL || iter_old->idx > iter_new->idx) { + // interface iter_new->idx was added + mcast_membership(iter_new->idx, true); + iter_new = iter_new->next; + } + else if (iter_old->idx == iter_new->idx) { + // interface didn't change + iter_new = iter_new->next; + tmp_item = iter_old; + iter_old = iter_old->next; + free(tmp_item); + } + } +} + +static void signal_handler(int signal) { + if (signal == SIGHUP) + read_iface_list(); +} + + /** * Merges two JSON objects * @@ -331,6 +412,9 @@ static void serve(int sock) { input_bytes = recvfrom(sock, input, sizeof(input)-1, 0, (struct sockaddr *)&addr, &addrlen); + if (input_bytes == EINTR) + return; + if (input_bytes < 0) { perror("recvfrom failed"); exit(EXIT_FAILURE); @@ -372,9 +456,12 @@ static void serve(int sock) { int main(int argc, char **argv) { const int one = 1; - int sock; + struct sigaction sa = { + .sa_handler = &signal_handler, + .sa_flags = SA_RESTART, + }; + struct sockaddr_in6 server_addr = {}; - struct in6_addr mgroup_addr; sock = socket(PF_INET6, SOCK_DGRAM, 0); @@ -393,30 +480,23 @@ int main(int argc, char **argv) { opterr = 0; - int group_set = 0; - int c; - while ((c = getopt(argc, argv, "p:g:i:d:h")) != -1) { + while ((c = getopt(argc, argv, "p:g:c:d:h")) != -1) { switch (c) { case 'p': server_addr.sin6_port = htons(atoi(optarg)); break; case 'g': - if (!inet_pton(AF_INET6, optarg, &mgroup_addr)) { + if (!inet_pton(AF_INET6, optarg, &mgroup_addr) || + !IN6_IS_ADDR_MULTICAST(&mgroup_addr)) { perror("Invalid multicast group. This message will probably confuse you"); exit(EXIT_FAILURE); } - - group_set = 1; break; - case 'i': - if (!group_set) { - fprintf(stderr, "Multicast group must be given before interface.\n"); - exit(EXIT_FAILURE); - } - join_mcast(sock, mgroup_addr, optarg); + case 'c': + iface_list_path = optarg; break; case 'd': @@ -436,12 +516,21 @@ int main(int argc, char **argv) { } } + if ((iface_list_path == NULL) != (IN6_IS_ADDR_UNSPECIFIED(&mgroup_addr))) { + fprintf(stderr, "Error: only one of -g and -c given!\n"); + usage(); + exit(EXIT_FAILURE); + } + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } load_providers(); + read_iface_list(); + + sigaction(SIGHUP, &sa, NULL); while (true) serve(sock);