aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Dockerfile20
-rw-r--r--README.md80
-rw-r--r--mdns-repeater.c652
-rw-r--r--mdns.tarbin0 -> 9226752 bytes
-rw-r--r--run.sh31
5 files changed, 783 insertions, 0 deletions
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7e6de36
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+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
+
+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/README.md b/README.md
index 4739dec..6fc9d91 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,82 @@
# docker-mdns-repeater-mikrotik
mdns-repeater in mikrotik container
+
+Based on:
+https://github.com/geekman/mdns-repeater
+https://github.com/monstrenyatko/docker-mdns-repeater
+
+This is work in progress, but you can base your config on that.
+
+## 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`.
+
+```
+ /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
+
+
+/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
+```
+
+## Build & pack container
+```
+docker buildx build --no-cache --platform linux/arm/v6 -t mdns .
+docker save mdns > mdns.tar
+8.8M mdns.tar # size after pack
+```
+
+## Logs from running container
+```
+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
+``` \ No newline at end of file
diff --git a/mdns-repeater.c b/mdns-repeater.c
new file mode 100644
index 0000000..2e743c3
--- /dev/null
+++ b/mdns-repeater.c
@@ -0,0 +1,652 @@
+/*
+ * 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 <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <errno.h>
+
+#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 ] <ifdev> ...\n", progname);
+ fprintf(stderr, "\n"
+ "<ifdev> 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/mdns.tar b/mdns.tar
new file mode 100644
index 0000000..83757d8
--- /dev/null
+++ b/mdns.tar
Binary files differ
diff --git a/run.sh b/run.sh
new file mode 100644
index 0000000..a758f62
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# Exit on error
+set -e
+
+HOSTNAME="mDns"
+INTERFACE="eth0"
+VLANS="20 100"
+
+MTU=$(ip link show "$INTERFACE" | awk '{print $5}')
+
+for VLAN in $VLANS; 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"
+ }
+ echo "bring up ${IFNAME} interface"
+ ip link set "${IFNAME}" up
+
+ # DHCP
+ [ -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"
+done
+
+exec "$@"