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 +#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/mdns.tar b/mdns.tar new file mode 100644 index 0000000..83757d8 Binary files /dev/null and b/mdns.tar 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 "$@"