diff --git a/.gitignore b/.gitignore index c6127b3..d874ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1 @@ -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf +*.tar diff --git a/Dockerfile b/Dockerfile index 7e6de36..f12a909 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,6 @@ -FROM alpine AS builder -ARG MDNS_REPEATER_VERSION=local -ADD mdns-repeater.c mdns-repeater.c -RUN set -ex && \ - apk add build-base && \ - gcc -o /bin/mdns-repeater mdns-repeater.c -DMDNS_REPEATER_VERSION=\"${MDNS_REPEATER_VERSION}\" - -FROM alpine - -RUN set -ex && \ - apk add vlan libcap bash -COPY --from=builder /bin/mdns-repeater /bin/mdns-repeater -RUN chmod +x /bin/mdns-repeater -RUN setcap cap_net_raw=+ep /bin/mdns-repeater +FROM monstrenyatko/mdns-repeater COPY run.sh /app/ RUN chmod +x /app/run.sh ENTRYPOINT ["/app/run.sh"] -CMD ["/bin/mdns-repeater", "-f", "eth0.20", "eth0.100"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4371ecc --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +container_name := mdns-repeater +repo_name := mag1024/mikrotik-docker-mdns-repeater + +default: container/arm64 +all: container/arm64 container/arm-v6 +clean: + rm -f *.tar + +container/%: + docker buildx build --load --platform linux/$(subst -,/,$*) -t $(container_name) . + docker save $(container_name) -o $(container_name)-$*.tar + +push: + docker buildx build --platform linux/arm64,linux/arm/v6 --push github.com/$(repo_name) -t $(repo_name):latest diff --git a/README.md b/README.md index dc7a8ac..60d0750 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,73 @@ +# DEPRECATED +As of RouterOS 7.16, Mikrotik now has a built-in mDNS repeater implementation: e.g. +`/ip/dns/set mdns-repeat-ifaces=bridge,vlan-iot` + # docker-mdns-repeater-mikrotik -mdns-repeater in mikrotik container +An mDNS repeater that can run as a container on Mikrotik routers. Based on: -https://github.com/geekman/mdns-repeater -https://github.com/monstrenyatko/docker-mdns-repeater +* [geekman/mdns-repeater](https://github.com/geekman/mdns-repeater) +* [monstrenyatko/docker-mdns-repeater](https://github.com/monstrenyatko/docker-mdns-repeater) +* [TheMickeyMike/docker-mdns-repeater-mikrotik](https://github.com/TheMickeyMike/docker-mdns-repeater-mikrotik) -This is work in progress, but you can base your config on that. +Images availabe on Dockerhub at [mag1024/mikrotik-docker-mdns-repeater](https://hub.docker.com/repository/docker/mag1024/mikrotik-docker-mdns-repeater). -## Mikrotik config -Based on official docs https://help.mikrotik.com/docs/display/ROS/Container -Instead of adding `veth2` to docker bridge i've added it to my home-lan bridge `BR1`. -Veth2 is added as tagged port with two vlans (20,100), so in container on `eth0` i will create two vlan interfaces `eth0.20` and `eth0.100` with active dhcp client for IP leese, please look at `run.sh`. +## How it works +As of Oct 2022, the Mikrotik container implementation is limited to exactly one +network interface. There is no option for an equivalent of 'host' mode +networking, and the interface must be of type veth, so we have to get creative +to get a functional repeater. The key is to attach the veth to a trunk bridge +that contains multiple vlans corresponding to the networks we want to repeat +across, and then create interfaces for each of the vlans inside the container, +using the veth as the parent. The set of vlans/interfaces to use is specified +via the _REPEATER_INTERFACES_ env variable, and the container runs a dhcp client +to obtain an IP for each of them. -``` - /interface/bridge/port/print -Flags: I - INACTIVE; H - HW-OFFLOAD -Columns: INTERFACE, BRIDGE, HW, PVID, PRIORITY, PATH-COST, INTERNAL-PATH-COST, HOR -IZON - # INTERFACE BRIDGE HW PVID PRIORITY PATH-COST IN HORIZON -10 veth2 BR1 1 0x80 10 10 none +## Setup +Begin by following the [Mikrotik container +documentation](https://help.mikrotik.com/docs/display/ROS/Container) to create +the veth interface. Instead of creating a separate docker bridge, assign the +new interface as a 'tagged' port to the bridge containing the interfaces you +wish to repeat across. These interfaces can be vlan interfaces, or physical +interfaces with pvid set -- depending on whether you use vlans for the rest of +your network setup. Refer to the [Mikrotik bridge +documentation](https://help.mikrotik.com/docs/display/ROS/Bridge+VLAN+Table) for +more details. + +The following example uses _veth-trunk_ veth interface and _br-trunk_ bridge, +configured with vlans 10, 11, 12. +Note: The address here does not matter, but it must have one to make the +interface 'active'. +``` +/interface/veth/print +Flags: X - disabled; R - running + 0 R name="veth-trunk" address=10.200.200.200/24 gateway=10.200.200.1 +``` -/interface/bridge/vlan/print -Flags: D - DYNAMIC -Columns: BRIDGE, VLAN-IDS, CURRENT-TAGGED, CURRENT-UNTAGGED -# BRIDGE VLAN-IDS CURRENT-TAGGED CURRENT-UNTAGGED -0 BR1 100 BR1 - veth2 -3 BR1 20 BR1 - veth2 -4 D BR1 1 BR1 - veth2 +Note: Again, pvid of the _veth_ itself does not matter. +``` +/interface/bridge/port/print +Flags: I - INACTIVE; H - HW-OFFLOAD +Columns: INTERFACE, BRIDGE, HW, PVID, PRIORITY, PATH-COST, INTERNAL-PATH-COST, HORIZON +# INTERFACE BRIDGE HW PVID PRIORITY PATH-COST INTERNAL-PATH-COST HORIZON +0 H ether2 br-trunk yes 10 0x80 10 10 none +1 H ether3 br-trunk yes 13 0x80 10 10 none +... +8 veth-trunk br-trunk 111 0x80 10 10 none ``` -## Build & pack container +Note: The name of the interface inside the container is always _eth0_. ``` -docker buildx build --no-cache --platform linux/arm/v6 -t mdns . -docker save mdns -o mdns.tar -8.8M mdns.tar # size after pack +/container/envs/print + 0 name="repeater_envs" key="REPEATER_INTERFACES" value="eth0.10 eth0.11 eth0.12" ``` -## Logs from running container +Note: you may have to set the registry first via `/container/config/set registry-url=https://registry-1.docker.io`. +Note: `start-on-boot` is only available on Mikrotik 7.6+ ``` -log print where topics~"container" - jun/29 22:01:28 container,info,debug create interface eth0.20 - jun/29 22:01:28 container,info,debug bring up eth0.20 interface - jun/29 22:01:28 container,info,debug /app/run.sh: line 25: kill: (19) - No such process - jun/29 22:01:28 container,info,debug starting dhcp client on eth0.20 - jun/29 22:01:28 container,info,debug udhcpc: started, v1.35.0 - jun/29 22:01:29 container,info,debug udhcpc: broadcasting discover - jun/29 22:01:29 container,info,debug udhcpc: broadcasting select for 10.0.20.27, server 10.0.20.1 - jun/29 22:01:29 container,info,debug udhcpc: lease of 10.0.20.27 obtained from 10.0.20.1, lease time 86400 - jun/29 22:01:29 container,info,debug create interface eth0.100 - jun/29 22:01:29 container,info,debug bring up eth0.100 interface - jun/29 22:01:29 container,info,debug /app/run.sh: line 25: kill: (34) - No such process - jun/29 22:01:29 container,info,debug starting dhcp client on eth0.100 - jun/29 22:01:29 container,info,debug udhcpc: started, v1.35.0 - jun/29 22:01:29 container,info,debug udhcpc: broadcasting discover - jun/29 22:01:30 container,info,debug udhcpc: broadcasting select for 10.0.100.244, server 10.0.100.1 - jun/29 22:01:30 container,info,debug udhcpc: lease of 10.0.100.244 obtained from 10.0.100.1, lease time 86400 - jun/29 22:01:30 container,info,debug + exec /bin/mdns-repeater -f eth0.20 eth0.100 - jun/29 22:01:30 container,info,debug mdns-repeater: dev eth0.20 addr 10.0.20.27 mask 255.255.255.0 net 10.0.20.0 - jun/29 22:01:30 container,info,debug mdns-repeater: dev eth0.100 addr 10.0.100.244 mask 255.255.255.0 net 10.0.100.0 - jul/01 21:49:34 container,info,debug bring up eth0.20 interface - jul/01 21:49:34 container,info,debug /app/run.sh: line 25: kill: (22) - No such process - jul/01 21:49:34 container,info,debug starting dhcp client on eth0.20 - jul/01 21:49:34 container,info,debug udhcpc: started, v1.35.0 - jul/01 21:49:34 container,info,debug udhcpc: broadcasting discover - jul/01 21:49:34 container,info,debug udhcpc: broadcasting select for 10.0.20.27, server 10.0.20.1 - jul/01 21:49:34 container,info,debug udhcpc: lease of 10.0.20.27 obtained from 10.0.20.1, lease time 86400 - jul/01 21:49:34 container,info,debug bring up eth0.100 interface - jul/01 21:49:34 container,info,debug /app/run.sh: line 25: kill: (40) - No such process - jul/01 21:49:34 container,info,debug starting dhcp client on eth0.100 - jul/01 21:49:34 container,info,debug udhcpc: started, v1.35.0 - jul/01 21:49:34 container,info,debug udhcpc: broadcasting discover - jul/01 21:49:35 container,info,debug udhcpc: broadcasting select for 10.0.100.244, server 10.0.100.1 - jul/01 21:49:35 container,info,debug udhcpc: lease of 10.0.100.244 obtained from 10.0.100.1, lease time 86400 - jul/01 21:49:35 container,info,debug + exec /bin/mdns-repeater -f eth0.20 eth0.100 - jul/01 21:49:35 container,info,debug mdns-repeater: dev eth0.20 addr 10.0.20.27 mask 255.255.255.0 net 10.0.20.0 - jul/01 21:49:35 container,info,debug mdns-repeater: dev eth0.100 addr 10.0.100.244 mask 255.255.255.0 net 10.0.100.0 +/container/print + 0 ... tag="mag1024/mikrotik-docker-mdns-repeater:latest" os="linux" + arch="arm64" interface=veth-trunk envlist="repeater_envs" mounts="" dns="" hostname="mdns-repeater" logging=yes + start-on-boot=yes status=running ``` diff --git a/mdns-repeater.c b/mdns-repeater.c deleted file mode 100644 index 2e743c3..0000000 --- a/mdns-repeater.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - * mdns-repeater.c - mDNS repeater daemon - * Copyright (C) 2011 Darell Tan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PACKAGE "mdns-repeater" -#define MDNS_ADDR "224.0.0.251" -#define MDNS_PORT 5353 - -#ifndef PIDFILE -#define PIDFILE "/var/run/" PACKAGE ".pid" -#endif - -#define MAX_SOCKS 16 -#define MAX_SUBNETS 16 - -struct if_sock { - const char *ifname; /* interface name */ - int sockfd; /* socket filedesc */ - struct in_addr addr; /* interface addr */ - struct in_addr mask; /* interface mask */ - struct in_addr net; /* interface network (computed) */ -}; - -struct subnet { - struct in_addr addr; /* subnet addr */ - struct in_addr mask; /* subnet mask */ - struct in_addr net; /* subnet net (computed) */ -}; - -int server_sockfd = -1; - -int num_socks = 0; -struct if_sock socks[MAX_SOCKS]; - -int num_blacklisted_subnets = 0; -struct subnet blacklisted_subnets[MAX_SUBNETS]; - -int num_whitelisted_subnets = 0; -struct subnet whitelisted_subnets[MAX_SUBNETS]; - -#define PACKET_SIZE 65536 -void *pkt_data = NULL; - -int foreground = 0; -int debug = 0; -int shutdown_flag = 0; - -char *pid_file = PIDFILE; - -void log_message(int loglevel, char *fmt_str, ...) { - va_list ap; - char buf[2048]; - - va_start(ap, fmt_str); - vsnprintf(buf, 2047, fmt_str, ap); - va_end(ap); - buf[2047] = 0; - - if (foreground) { - fprintf(stderr, "%s: %s\n", PACKAGE, buf); - } else { - syslog(loglevel, "%s", buf); - } -} - -static int create_recv_sock() { - int sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) { - log_message(LOG_ERR, "recv socket(): %s", strerror(errno)); - return sd; - } - - int r = -1; - - int on = 1; - if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %s", strerror(errno)); - return r; - } - - /* bind to an address */ - struct sockaddr_in serveraddr; - memset(&serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin_family = AF_INET; - serveraddr.sin_port = htons(MDNS_PORT); - serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */ - if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) { - log_message(LOG_ERR, "recv bind(): %s", strerror(errno)); - } - - // enable loopback in case someone else needs the data - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %s", strerror(errno)); - return r; - } - -#ifdef IP_PKTINFO - if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %s", strerror(errno)); - return r; - } -#endif - - return sd; -} - -static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock *sockdata) { - int sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) { - log_message(LOG_ERR, "send socket(): %s", strerror(errno)); - return sd; - } - - sockdata->ifname = ifname; - sockdata->sockfd = sd; - - int r = -1; - - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - struct in_addr *if_addr = &((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; - -#ifdef SO_BINDTODEVICE - if ((r = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(struct ifreq))) < 0) { - log_message(LOG_ERR, "send setsockopt(SO_BINDTODEVICE): %s", strerror(errno)); - return r; - } -#endif - - // get netmask - if (ioctl(sd, SIOCGIFNETMASK, &ifr) == 0) { - memcpy(&sockdata->mask, if_addr, sizeof(struct in_addr)); - } - - // .. and interface address - if (ioctl(sd, SIOCGIFADDR, &ifr) == 0) { - memcpy(&sockdata->addr, if_addr, sizeof(struct in_addr)); - } - - // compute network (address & mask) - sockdata->net.s_addr = sockdata->addr.s_addr & sockdata->mask.s_addr; - - int on = 1; - if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "send setsockopt(SO_REUSEADDR): %s", strerror(errno)); - return r; - } - - // bind to an address - struct sockaddr_in serveraddr; - memset(&serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin_family = AF_INET; - serveraddr.sin_port = htons(MDNS_PORT); - serveraddr.sin_addr.s_addr = if_addr->s_addr; - if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) { - log_message(LOG_ERR, "send bind(): %s", strerror(errno)); - } - -#if __FreeBSD__ - if((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &serveraddr.sin_addr, sizeof(serveraddr.sin_addr))) < 0) { - log_message(LOG_ERR, "send ip_multicast_if(): errno %d: %s", errno, strerror(errno)); - } -#endif - - // add membership to receiving socket - struct ip_mreq mreq; - memset(&mreq, 0, sizeof(struct ip_mreq)); - mreq.imr_interface.s_addr = if_addr->s_addr; - mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR); - if ((r = setsockopt(recv_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); - return r; - } - - // enable loopback in case someone else needs the data - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on))) < 0) { - log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_LOOP): %s", strerror(errno)); - return r; - } - - char *addr_str = strdup(inet_ntoa(sockdata->addr)); - char *mask_str = strdup(inet_ntoa(sockdata->mask)); - char *net_str = strdup(inet_ntoa(sockdata->net)); - log_message(LOG_INFO, "dev %s addr %s mask %s net %s", ifr.ifr_name, addr_str, mask_str, net_str); - free(addr_str); - free(mask_str); - free(net_str); - - return sd; -} - -static ssize_t send_packet(int fd, const void *data, size_t len) { - static struct sockaddr_in toaddr; - if (toaddr.sin_family != AF_INET) { - memset(&toaddr, 0, sizeof(struct sockaddr_in)); - toaddr.sin_family = AF_INET; - toaddr.sin_port = htons(MDNS_PORT); - toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR); - } - - return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in)); -} - -static void mdns_repeater_shutdown(int sig) { - shutdown_flag = 1; -} - -static pid_t already_running() { - FILE *f; - int count; - pid_t pid; - - f = fopen(pid_file, "r"); - if (f != NULL) { - count = fscanf(f, "%d", &pid); - fclose(f); - if (count == 1) { - if (kill(pid, 0) == 0) - return pid; - } - } - - return -1; -} - -static int write_pidfile() { - FILE *f; - int r; - - f = fopen(pid_file, "w"); - if (f != NULL) { - r = fprintf(f, "%d", getpid()); - fclose(f); - return (r > 0); - } - - return 0; -} - -static void daemonize() { - pid_t running_pid; - pid_t pid = fork(); - if (pid < 0) { - log_message(LOG_ERR, "fork(): %s", strerror(errno)); - exit(1); - } - - // exit parent process - if (pid > 0) - exit(0); - - // signals - signal(SIGCHLD, SIG_IGN); - signal(SIGHUP, SIG_IGN); - signal(SIGTERM, mdns_repeater_shutdown); - - setsid(); - umask(0027); - chdir("/"); - - // close all std fd and reopen /dev/null for them - int i; - for (i = 0; i < 3; i++) { - close(i); - if (open("/dev/null", O_RDWR) != i) { - log_message(LOG_ERR, "unable to open /dev/null for fd %d", i); - exit(1); - } - } - - // check for pid file - running_pid = already_running(); - if (running_pid != -1) { - log_message(LOG_ERR, "already running as pid %d", running_pid); - exit(1); - } else if (! write_pidfile()) { - log_message(LOG_ERR, "unable to write pid file %s", pid_file); - exit(1); - } -} - -static void show_help(const char *progname) { - fprintf(stderr, "mDNS repeater (version " MDNS_REPEATER_VERSION ")\n"); - fprintf(stderr, "Copyright (C) 2011 Darell Tan\n\n"); - fprintf(stderr, "usage: %s [ -f ] ...\n", progname); - fprintf(stderr, "\n" - " specifies an interface like \"eth0\"\n" - "packets received on an interface is repeated across all other specified interfaces\n" - "maximum number of interfaces is 5\n" - "\n" - " flags:\n" - " -f runs in foreground\n" - " -d log debug messages when runs in foreground\n" - " -b blacklist subnet (eg. 192.168.1.1/24)\n" - " -w whitelist subnet (eg. 192.168.1.1/24)\n" - " -p specifies the pid file path (default: " PIDFILE ")\n" - " -h shows this help\n" - "\n" - ); -} - -int parse(char *input, struct subnet *s) { - int delim = 0; - int end = 0; - while (input[end] != 0) { - if (input[end] == '/') { - delim = end; - } - end++; - } - - if (end == 0 || delim == 0 || end == delim) { - return -1; - } - - char *addr = (char*) malloc(end); - - memset(addr, 0, end); - strncpy(addr, input, delim); - if (inet_pton(AF_INET, addr, &s->addr) != 1) { - free(addr); - return -2; - } - - memset(addr, 0, end); - strncpy(addr, input+delim+1, end-delim-1); - int mask = atoi(addr); - free(addr); - - if (mask < 0 || mask > 32) { - return -3; - } - - s->mask.s_addr = ntohl((uint32_t)0xFFFFFFFF << (32 - mask)); - s->net.s_addr = s->addr.s_addr & s->mask.s_addr; - - return 0; -} - -int tostring(struct subnet *s, char* buf, int len) { - char *addr_str = strdup(inet_ntoa(s->addr)); - char *mask_str = strdup(inet_ntoa(s->mask)); - char *net_str = strdup(inet_ntoa(s->net)); - int l = snprintf(buf, len, "addr %s mask %s net %s", addr_str, mask_str, net_str); - free(addr_str); - free(mask_str); - free(net_str); - - return l; -} - -static int parse_opts(int argc, char *argv[]) { - int c, res; - int help = 0; - struct subnet *ss; - char *msg; - while ((c = getopt(argc, argv, "hfdp:b:w:")) != -1) { - switch (c) { - case 'h': help = 1; break; - case 'f': foreground = 1; break; - case 'd': debug = 1; break; - case 'p': - if (optarg[0] != '/') - log_message(LOG_ERR, "pid file path must be absolute"); - else - pid_file = optarg; - break; - - case 'b': - if (num_blacklisted_subnets >= MAX_SUBNETS) { - log_message(LOG_ERR, "too many blacklisted subnets (maximum is %d)", MAX_SUBNETS); - exit(2); - } - - if (num_whitelisted_subnets != 0) { - log_message(LOG_ERR, "simultaneous whitelisting and blacklisting does not make sense"); - exit(2); - } - - ss = &blacklisted_subnets[num_blacklisted_subnets]; - res = parse(optarg, ss); - switch (res) { - case -1: - log_message(LOG_ERR, "invalid blacklist argument"); - exit(2); - case -2: - log_message(LOG_ERR, "could not parse netmask"); - exit(2); - case -3: - log_message(LOG_ERR, "invalid netmask"); - exit(2); - } - - num_blacklisted_subnets++; - - msg = malloc(128); - memset(msg, 0, 128); - tostring(ss, msg, 128); - log_message(LOG_INFO, "blacklist %s", msg); - free(msg); - break; - case 'w': - if (num_whitelisted_subnets >= MAX_SUBNETS) { - log_message(LOG_ERR, "too many whitelisted subnets (maximum is %d)", MAX_SUBNETS); - exit(2); - } - - if (num_blacklisted_subnets != 0) { - log_message(LOG_ERR, "simultaneous whitelisting and blacklisting does not make sense"); - exit(2); - } - - ss = &whitelisted_subnets[num_whitelisted_subnets]; - res = parse(optarg, ss); - switch (res) { - case -1: - log_message(LOG_ERR, "invalid whitelist argument"); - exit(2); - case -2: - log_message(LOG_ERR, "could not parse netmask"); - exit(2); - case -3: - log_message(LOG_ERR, "invalid netmask"); - exit(2); - } - - num_whitelisted_subnets++; - - msg = malloc(128); - memset(msg, 0, 128); - tostring(ss, msg, 128); - log_message(LOG_INFO, "whitelist %s", msg); - free(msg); - break; - case '?': - case ':': - fputs("\n", stderr); - break; - - default: - log_message(LOG_ERR, "unknown option %c", optopt); - exit(2); - } - } - - if (help) { - show_help(argv[0]); - exit(0); - } - - return optind; -} - -int main(int argc, char *argv[]) { - pid_t running_pid; - fd_set sockfd_set; - int r = 0; - - parse_opts(argc, argv); - - if ((argc - optind) <= 1) { - show_help(argv[0]); - log_message(LOG_ERR, "error: at least 2 interfaces must be specified"); - exit(2); - } - - openlog(PACKAGE, LOG_PID | LOG_CONS, LOG_DAEMON); - if (! foreground) - daemonize(); - else { - // check for pid file when running in foreground - running_pid = already_running(); - if (running_pid != -1) { - log_message(LOG_ERR, "already running as pid %d", running_pid); - exit(1); - } - } - - // create receiving socket - server_sockfd = create_recv_sock(); - if (server_sockfd < 0) { - log_message(LOG_ERR, "unable to create server socket"); - r = 1; - goto end_main; - } - - // create sending sockets - int i; - for (i = optind; i < argc; i++) { - if (num_socks >= MAX_SOCKS) { - log_message(LOG_ERR, "too many sockets (maximum is %d)", MAX_SOCKS); - exit(2); - } - - int sockfd = create_send_sock(server_sockfd, argv[i], &socks[num_socks]); - if (sockfd < 0) { - log_message(LOG_ERR, "unable to create socket for interface %s", argv[i]); - r = 1; - goto end_main; - } - num_socks++; - } - - pkt_data = malloc(PACKET_SIZE); - if (pkt_data == NULL) { - log_message(LOG_ERR, "cannot malloc() packet buffer: %s", strerror(errno)); - r = 1; - goto end_main; - } - - while (! shutdown_flag) { - struct timeval tv = { - .tv_sec = 10, - .tv_usec = 0, - }; - - FD_ZERO(&sockfd_set); - FD_SET(server_sockfd, &sockfd_set); - int numfd = select(server_sockfd + 1, &sockfd_set, NULL, NULL, &tv); - if (numfd <= 0) - continue; - - if (FD_ISSET(server_sockfd, &sockfd_set)) { - struct sockaddr_in fromaddr; - socklen_t sockaddr_size = sizeof(struct sockaddr_in); - - ssize_t recvsize = recvfrom(server_sockfd, pkt_data, PACKET_SIZE, 0, - (struct sockaddr *) &fromaddr, &sockaddr_size); - if (recvsize < 0) { - log_message(LOG_ERR, "recv(): %s", strerror(errno)); - } - - int j; - char self_generated_packet = 0; - for (j = 0; j < num_socks; j++) { - // check for loopback - if (fromaddr.sin_addr.s_addr == socks[j].addr.s_addr) { - self_generated_packet = 1; - break; - } - } - - if (self_generated_packet) - continue; - - if (num_whitelisted_subnets != 0) { - char whitelisted_packet = 0; - for (j = 0; j < num_whitelisted_subnets; j++) { - // check for whitelist - if ((fromaddr.sin_addr.s_addr & whitelisted_subnets[j].mask.s_addr) == whitelisted_subnets[j].net.s_addr) { - whitelisted_packet = 1; - break; - } - } - - if (!whitelisted_packet) { - if (foreground && debug) - printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - continue; - } - } else { - char blacklisted_packet = 0; - for (j = 0; j < num_blacklisted_subnets; j++) { - // check for blacklist - if ((fromaddr.sin_addr.s_addr & blacklisted_subnets[j].mask.s_addr) == blacklisted_subnets[j].net.s_addr) { - blacklisted_packet = 1; - break; - } - } - - if (blacklisted_packet) { - if (foreground && debug) - printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - continue; - } - } - - for (j = 0; j < num_socks; j++) { - // do not repeat packet back to the same network from which it originated - if ((fromaddr.sin_addr.s_addr & socks[j].mask.s_addr) == socks[j].net.s_addr) - continue; - - if (foreground && debug) - printf("%s (%zd bytes) -> %s\n", inet_ntoa(fromaddr.sin_addr), recvsize, socks[j].ifname); - - // repeat data - ssize_t sentsize = send_packet(socks[j].sockfd, pkt_data, (size_t) recvsize); - if (sentsize != recvsize) { - if (sentsize < 0) - log_message(LOG_ERR, "send(): %s", strerror(errno)); - else - log_message(LOG_ERR, "send_packet size differs: sent=%zd actual=%zd", - recvsize, sentsize); - } - } - } - } - - log_message(LOG_INFO, "shutting down..."); - -end_main: - - if (pkt_data != NULL) - free(pkt_data); - - if (server_sockfd >= 0) - close(server_sockfd); - - for (i = 0; i < num_socks; i++) - close(socks[i].sockfd); - - // remove pid file if it belongs to us - if (already_running() == getpid()) - unlink(pid_file); - - log_message(LOG_INFO, "exit."); - - return r; -} diff --git a/run.sh b/run.sh index a758f62..16ef9ca 100644 --- a/run.sh +++ b/run.sh @@ -3,29 +3,25 @@ # Exit on error set -e -HOSTNAME="mDns" -INTERFACE="eth0" -VLANS="20 100" +MTU=$(ip link show "${REPEATER_INTERFACES%%[ .]*}" | awk '{print $5}') -MTU=$(ip link show "$INTERFACE" | awk '{print $5}') - -for VLAN in $VLANS; do +for IFNAME in $REPEATER_INTERFACES; do # INTERFACE PROVISION - IFNAME="${INTERFACE}.${VLAN}" - [ ! -d "/sys/class/net/${IFNAME}" ] && { - echo "create interface ${IFNAME}" - ip link add link "$INTERFACE" name "$IFNAME" mtu "$MTU" type vlan id "$VLAN" + [ ! -d "/sys/class/net/$IFNAME" ] && { + echo "create interface $IFNAME" + ip link add link "${IFNAME%%.*}" \ + name "$IFNAME" mtu "$MTU" type vlan id "${IFNAME##*.}" } - echo "bring up ${IFNAME} interface" - ip link set "${IFNAME}" up + echo "bring up $IFNAME interface" + ip link set "$IFNAME" up # DHCP - [ -f "/var/run/udhcpc.${IFNAME}.pid" ] && { + [ -f "/var/run/udhcpc.$IFNAME.pid" ] && { kill "$(cat "/var/run/udhcpc.$IFNAME.pid")" || true rm "/var/run/udhcpc.$IFNAME.pid" } - echo "starting dhcp client on ${IFNAME}" - udhcpc -b -i "$IFNAME" -x hostname:"$HOSTNAME" -p "/var/run/udhcpc.${IFNAME}.pid" + echo "starting dhcp client on $IFNAME" + udhcpc -b -i "$IFNAME" -x hostname:"$(hostname)" -p "/var/run/udhcpc.$IFNAME.pid" done -exec "$@" +exec /bin/mdns-repeater -f $REPEATER_INTERFACES