From 6b8ce9e956059dd60f2cd9a161955dc2e6ab5c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 13:24:50 +0100 Subject: [PATCH 01/43] create_send_sock() - improve error handling Check the return value from all functions, make sure to close the socket on error and make sure the sizeof() calls are consistent (sometime the type was used, sometimes the structs themselves). --- mdns-repeater.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index fec62da..b2a737b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -120,6 +120,7 @@ static int create_recv_sock() { 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)); + return r; } // enable loopback in case someone else needs the data @@ -139,17 +140,17 @@ static int create_recv_sock() { } static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock *sockdata) { + int r = -1; int sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { log_message(LOG_ERR, "send socket(): %s", strerror(errno)); - return sd; + r = sd; + goto out; } sockdata->ifname = ifname; sockdata->sockfd = sd; - int r = -1; - struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); @@ -158,19 +159,23 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock #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; + goto out; } #endif // get netmask - if (ioctl(sd, SIOCGIFNETMASK, &ifr) == 0) { - memcpy(&sockdata->mask, if_addr, sizeof(struct in_addr)); + if (ioctl(sd, SIOCGIFNETMASK, &ifr) < 0) { + log_message(LOG_ERR, "ioctl(SIOCGIFNETMASK): %s", strerror(errno)); + goto out; } + memcpy(&sockdata->mask, if_addr, sizeof(*if_addr)); // .. and interface address - if (ioctl(sd, SIOCGIFADDR, &ifr) == 0) { - memcpy(&sockdata->addr, if_addr, sizeof(struct in_addr)); + if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) { + log_message(LOG_ERR, "ioctl(SIOCGIFADDR): %s", strerror(errno)); + goto out; } + memcpy(&sockdata->addr, if_addr, sizeof(*if_addr)); // compute network (address & mask) sockdata->net.s_addr = sockdata->addr.s_addr & sockdata->mask.s_addr; @@ -178,7 +183,7 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock 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; + goto out; } // bind to an address @@ -189,34 +194,36 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock 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)); + goto out; } #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)); + 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(): %s", strerror(errno)); + goto out; } #endif // add membership to receiving socket struct ip_mreq mreq; - memset(&mreq, 0, sizeof(struct ip_mreq)); + memset(&mreq, 0, sizeof(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; + goto out; } // 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; + goto out; } int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) < 0) { log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_TTL): %s", strerror(errno)); - return r; + goto out; } char *addr_str = strdup(inet_ntoa(sockdata->addr)); @@ -228,6 +235,10 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock free(net_str); return sd; + +out: + close(sd); + return r; } static ssize_t send_packet(int fd, const void *data, size_t len) { From 2ca8a2376d106ad9591270c8e34c181ff356a281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 13:28:39 +0100 Subject: [PATCH 02/43] create_send_sock() - reorganise function a bit This is in preparation for later patches. --- mdns-repeater.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index b2a737b..e54c101 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -141,7 +141,18 @@ static int create_recv_sock() { static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock *sockdata) { int r = -1; - int sd = socket(AF_INET, SOCK_DGRAM, 0); + int sd; + struct ifreq ifr; + struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; + int on = 1; + struct sockaddr_in serveraddr; + struct ip_mreq mreq; + int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 + char *addr_str; + char *mask_str; + char *net_str; + + sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { log_message(LOG_ERR, "send socket(): %s", strerror(errno)); r = sd; @@ -151,10 +162,8 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock sockdata->ifname = ifname; sockdata->sockfd = sd; - 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) { @@ -180,14 +189,12 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock // 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)); goto out; } // bind to an address - struct sockaddr_in serveraddr; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(MDNS_PORT); @@ -205,7 +212,6 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock #endif // add membership to receiving socket - struct ip_mreq mreq; memset(&mreq, 0, sizeof(mreq)); mreq.imr_interface.s_addr = if_addr->s_addr; mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR); @@ -220,15 +226,14 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock goto out; } - int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) < 0) { log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_TTL): %s", strerror(errno)); goto out; } - char *addr_str = strdup(inet_ntoa(sockdata->addr)); - char *mask_str = strdup(inet_ntoa(sockdata->mask)); - char *net_str = strdup(inet_ntoa(sockdata->net)); + addr_str = strdup(inet_ntoa(sockdata->addr)); + mask_str = strdup(inet_ntoa(sockdata->mask)); + 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); From 5cf15229aa30fda86e97934b4a1ac977242b8bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 13:40:39 +0100 Subject: [PATCH 03/43] create_send_sock() - dynamically allocate sockdata --- mdns-repeater.c | 55 ++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index e54c101..8125a59 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -63,7 +63,7 @@ struct subnet { int server_sockfd = -1; int num_socks = 0; -struct if_sock socks[MAX_SOCKS]; +struct if_sock *socks[MAX_SOCKS]; int num_blacklisted_subnets = 0; struct subnet blacklisted_subnets[MAX_SUBNETS]; @@ -139,9 +139,10 @@ static int create_recv_sock() { return sd; } -static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock *sockdata) { - int r = -1; - int sd; +static struct if_sock * +create_send_sock(int recv_sockfd, const char *ifname) { + struct if_sock *sockdata; + int sd = -1; struct ifreq ifr; struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; int on = 1; @@ -152,10 +153,15 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock char *mask_str; char *net_str; + sockdata = malloc(sizeof(*sockdata)); + if (!sockdata) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { log_message(LOG_ERR, "send socket(): %s", strerror(errno)); - r = sd; goto out; } @@ -166,7 +172,7 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock strncpy(ifr.ifr_name, ifname, IFNAMSIZ); #ifdef SO_BINDTODEVICE - if ((r = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(struct ifreq))) < 0) { + if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(struct ifreq)) < 0) { log_message(LOG_ERR, "send setsockopt(SO_BINDTODEVICE): %s", strerror(errno)); goto out; } @@ -189,7 +195,7 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock // compute network (address & mask) sockdata->net.s_addr = sockdata->addr.s_addr & sockdata->mask.s_addr; - if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) { + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "send setsockopt(SO_REUSEADDR): %s", strerror(errno)); goto out; } @@ -199,13 +205,13 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock 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) { + if (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { log_message(LOG_ERR, "send bind(): %s", strerror(errno)); goto out; } #if __FreeBSD__ - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &serveraddr.sin_addr, sizeof(serveraddr.sin_addr))) < 0) { + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &serveraddr.sin_addr, sizeof(serveraddr.sin_addr)) < 0) { log_message(LOG_ERR, "send ip_multicast_if(): %s", strerror(errno)); goto out; } @@ -215,18 +221,18 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock memset(&mreq, 0, sizeof(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) { + if (setsockopt(recv_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); goto out; } // enable loopback in case someone else needs the data - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on))) < 0) { + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_LOOP): %s", strerror(errno)); goto out; } - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) < 0) { + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_TTL): %s", strerror(errno)); goto out; } @@ -239,11 +245,12 @@ static int create_send_sock(int recv_sockfd, const char *ifname, struct if_sock free(mask_str); free(net_str); - return sd; + return sockdata; out: + free(sockdata); close(sd); - return r; + return NULL; } static ssize_t send_packet(int fd, const void *data, size_t len) { @@ -562,8 +569,8 @@ int main(int argc, char *argv[]) { exit(2); } - int sockfd = create_send_sock(server_sockfd, argv[i], &socks[num_socks]); - if (sockfd < 0) { + socks[num_socks] = create_send_sock(server_sockfd, argv[i]); + if (!socks[num_socks]) { log_message(LOG_ERR, "unable to create socket for interface %s", argv[i]); r = 1; goto end_main; @@ -620,12 +627,12 @@ int main(int argc, char *argv[]) { char our_net = 0; for (j = 0; j < num_socks; j++) { // make sure packet originated from specified networks - if ((fromaddr.sin_addr.s_addr & socks[j].mask.s_addr) == socks[j].net.s_addr) { + if ((fromaddr.sin_addr.s_addr & socks[j]->mask.s_addr) == socks[j]->net.s_addr) { our_net = 1; } // check for loopback - if (fromaddr.sin_addr.s_addr == socks[j].addr.s_addr) { + if (fromaddr.sin_addr.s_addr == socks[j]->addr.s_addr) { discard = 1; break; } @@ -671,14 +678,14 @@ int main(int argc, char *argv[]) { 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) + if ((fromaddr.sin_addr.s_addr & socks[j]->mask.s_addr) == socks[j]->net.s_addr) continue; if (foreground) - printf("repeating data to %s\n", socks[j].ifname); + printf("repeating data to %s\n", socks[j]->ifname); // repeat data - ssize_t sentsize = send_packet(socks[j].sockfd, pkt_data, (size_t) recvsize); + 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)); @@ -700,8 +707,10 @@ int main(int argc, char *argv[]) { if (server_sockfd >= 0) close(server_sockfd); - for (i = 0; i < num_socks; i++) - close(socks[i].sockfd); + for (i = 0; i < num_socks; i++) { + close(socks[i]->sockfd); + free(socks[i]); + } // remove pid file if it belongs to us if (already_running() == getpid()) From 05c6c075905a470533f1cd4801be444d0362cce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 14:27:57 +0100 Subject: [PATCH 04/43] Use stdbool.h to define booleans --- mdns-repeater.c | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 8125a59..b765714 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -34,6 +34,7 @@ #include #include #include +#include #define PACKAGE "mdns-repeater" #define MDNS_ADDR "224.0.0.251" @@ -74,8 +75,8 @@ struct subnet whitelisted_subnets[MAX_SUBNETS]; #define PACKET_SIZE 65536 void *pkt_data = NULL; -int foreground = 0; -int shutdown_flag = 0; +bool foreground = false; +bool shutdown_flag = false; char *pid_file = PIDFILE; @@ -267,7 +268,7 @@ static ssize_t send_packet(int fd, const void *data, size_t len) { static void mdns_repeater_shutdown(int sig) { (void)sig; - shutdown_flag = 1; + shutdown_flag = true; } static pid_t already_running() { @@ -431,13 +432,20 @@ int tostring(struct subnet *s, char* buf, int len) { static int parse_opts(int argc, char *argv[]) { int c, res; - int help = 0; + bool help = false; struct subnet *ss; char *msg; + while ((c = getopt(argc, argv, "hfp:b:w:u:")) != -1) { switch (c) { - case 'h': help = 1; break; - case 'f': foreground = 1; break; + case 'h': + help = true; + break; + + case 'f': + foreground = true; + break; + case 'p': if (optarg[0] != '/') log_message(LOG_ERR, "pid file path must be absolute"); @@ -582,7 +590,7 @@ int main(int argc, char *argv[]) { switch_user(); } - if (! foreground) + if (!foreground) daemonize(); else { // check for pid file when running in foreground @@ -600,7 +608,7 @@ int main(int argc, char *argv[]) { goto end_main; } - while (! shutdown_flag) { + while (!shutdown_flag) { struct timeval tv = { .tv_sec = 10, .tv_usec = 0, @@ -615,25 +623,26 @@ int main(int argc, char *argv[]) { if (FD_ISSET(server_sockfd, &sockfd_set)) { struct sockaddr_in fromaddr; socklen_t sockaddr_size = sizeof(struct sockaddr_in); + ssize_t recvsize; + bool discard = false; + bool our_net = false; - ssize_t recvsize = recvfrom(server_sockfd, pkt_data, PACKET_SIZE, 0, - (struct sockaddr *) &fromaddr, &sockaddr_size); + 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 discard = 0; - char our_net = 0; for (j = 0; j < num_socks; j++) { // make sure packet originated from specified networks if ((fromaddr.sin_addr.s_addr & socks[j]->mask.s_addr) == socks[j]->net.s_addr) { - our_net = 1; + our_net = true; } // check for loopback if (fromaddr.sin_addr.s_addr == socks[j]->addr.s_addr) { - discard = 1; + discard = true; break; } } @@ -642,11 +651,11 @@ int main(int argc, char *argv[]) { continue; if (num_whitelisted_subnets != 0) { - char whitelisted_packet = 0; + bool whitelisted_packet = false; 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; + whitelisted_packet = true; break; } } @@ -657,11 +666,11 @@ int main(int argc, char *argv[]) { continue; } } else { - char blacklisted_packet = 0; + bool blacklisted_packet = false; 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; + blacklisted_packet = true; break; } } From afedb2603cde4e822bc1a8e3e665f9628891d3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 14:29:13 +0100 Subject: [PATCH 05/43] Use a linked list to keep track of send socks --- mdns-repeater.c | 53 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index b765714..a60c4ec 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -36,6 +36,8 @@ #include #include +#include "list.h" + #define PACKAGE "mdns-repeater" #define MDNS_ADDR "224.0.0.251" #define MDNS_PORT 5353 @@ -44,7 +46,6 @@ #define PIDFILE "/var/run/" PACKAGE ".pid" #endif -#define MAX_SOCKS 16 #define MAX_SUBNETS 16 struct if_sock { @@ -53,7 +54,9 @@ struct if_sock { struct in_addr addr; /* interface addr */ struct in_addr mask; /* interface mask */ struct in_addr net; /* interface network (computed) */ + struct list_head list; /* socket list */ }; +LIST_HEAD(send_socks); struct subnet { struct in_addr addr; /* subnet addr */ @@ -63,9 +66,6 @@ struct subnet { 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]; @@ -550,6 +550,7 @@ int main(int argc, char *argv[]) { pid_t running_pid; fd_set sockfd_set; int r = 0; + struct if_sock *sock, *tmp_sock; parse_opts(argc, argv); @@ -570,20 +571,15 @@ int main(int argc, char *argv[]) { } // 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); - } - - socks[num_socks] = create_send_sock(server_sockfd, argv[i]); - if (!socks[num_socks]) { + for (int i = optind; i < argc; i++) { + sock = create_send_sock(server_sockfd, argv[i]); + if (!sock) { log_message(LOG_ERR, "unable to create socket for interface %s", argv[i]); r = 1; goto end_main; } - num_socks++; + + list_add(&sock->list, &send_socks); } if (user) { @@ -633,15 +629,14 @@ int main(int argc, char *argv[]) { log_message(LOG_ERR, "recv(): %s", strerror(errno)); } - int j; - for (j = 0; j < num_socks; j++) { + list_for_each_entry(sock, &send_socks, list) { // make sure packet originated from specified networks - if ((fromaddr.sin_addr.s_addr & socks[j]->mask.s_addr) == socks[j]->net.s_addr) { + if ((fromaddr.sin_addr.s_addr & sock->mask.s_addr) == sock->net.s_addr) { our_net = true; } // check for loopback - if (fromaddr.sin_addr.s_addr == socks[j]->addr.s_addr) { + if (fromaddr.sin_addr.s_addr == sock->addr.s_addr) { discard = true; break; } @@ -652,7 +647,7 @@ int main(int argc, char *argv[]) { if (num_whitelisted_subnets != 0) { bool whitelisted_packet = false; - for (j = 0; j < num_whitelisted_subnets; j++) { + for (int 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 = true; @@ -667,7 +662,7 @@ int main(int argc, char *argv[]) { } } else { bool blacklisted_packet = false; - for (j = 0; j < num_blacklisted_subnets; j++) { + for (int 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 = true; @@ -685,16 +680,19 @@ int main(int argc, char *argv[]) { if (foreground) printf("data from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - for (j = 0; j < num_socks; j++) { + + list_for_each_entry(sock, &send_socks, list) { + ssize_t sentsize; + // 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) + if ((fromaddr.sin_addr.s_addr & sock->mask.s_addr) == sock->net.s_addr) continue; if (foreground) - printf("repeating data to %s\n", socks[j]->ifname); + printf("repeating data to %s\n", sock->ifname); // repeat data - ssize_t sentsize = send_packet(socks[j]->sockfd, pkt_data, (size_t) recvsize); + sentsize = send_packet(sock->sockfd, pkt_data, (size_t)recvsize); if (sentsize != recvsize) { if (sentsize < 0) log_message(LOG_ERR, "send(): %s", strerror(errno)); @@ -716,9 +714,10 @@ int main(int argc, char *argv[]) { if (server_sockfd >= 0) close(server_sockfd); - for (i = 0; i < num_socks; i++) { - close(socks[i]->sockfd); - free(socks[i]); + list_for_each_entry_safe(sock, tmp_sock, &send_socks, list) { + list_del(&sock->list); + close(sock->sockfd); + free(sock); } // remove pid file if it belongs to us From 0552d4d9041f3b38c8b5f444a07301651bb604d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 14:30:11 +0100 Subject: [PATCH 06/43] Add list.h This adds a double-linked list implementation taken from the Linux kernel. --- Makefile | 2 +- list.h | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 list.h diff --git a/Makefile b/Makefile index 90342d4..000d7d9 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CFLAGS+= -DHGVERSION="\"${HGVERSION}\"" all: mdns-repeater -mdns-repeater.o: _hgversion +mdns-repeater.o: _hgversion list.h mdns-repeater: mdns-repeater.o diff --git a/list.h b/list.h new file mode 100644 index 0000000..051ae05 --- /dev/null +++ b/list.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This is the doubly linked list implementation taken from the Linux + * kernel (26 June 2020). + * + * The only modifications are the ones necessary to make it compile + * outside the kernel tree, and to remove some functions which are not + * necessary (like hlist*). + */ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +struct list_head { + struct list_head *next, *prev; +}; + +#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER) + +#define container_of(ptr, type, member) \ + __extension__({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define LIST_HEAD_INIT(name) \ + { \ + &(name), &(name) \ + } + +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); pos = n, n = list_next_entry(n, member)) + +#endif From ccfd7929c5484da45aa835fdbadc81d0c2adcf5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 14:55:44 +0100 Subject: [PATCH 07/43] Rename parse() as parse_subnet() and simplify error handling --- mdns-repeater.c | 68 +++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index a60c4ec..91d570f 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -380,42 +380,54 @@ static void show_help(const char *progname) { ); } -int parse(char *input, struct subnet *s) { +static bool +parse_subnet(const char *input, struct subnet *s) { + bool r = false; int delim = 0; int end = 0; - while (input[end] != 0) { - if (input[end] == '/') { + char *addr = NULL; + int mask; + + while (input[end] != '\0') { + if (input[end] == '/') delim = end; - } end++; } if (end == 0 || delim == 0 || end == delim) { - return -1; + log_message(LOG_ERR, "invalid blacklist/whitelist argument: %s", input); + goto out; } - char *addr = (char*) malloc(end); + addr = malloc(end); + if (!addr) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } memset(addr, 0, end); strncpy(addr, input, delim); if (inet_pton(AF_INET, addr, &s->addr) != 1) { - free(addr); - return -2; + log_message(LOG_ERR, "could not parse blacklist/whitelist netmask: %s", input); + goto out; } memset(addr, 0, end); strncpy(addr, input+delim+1, end-delim-1); - int mask = atoi(addr); - free(addr); + mask = atoi(addr); if (mask < 0 || mask > 32) { - return -3; + log_message(LOG_ERR, "invalid blacklist/whitelist netmask: %s", input); + goto out; } s->mask.s_addr = ntohl((uint32_t)0xFFFFFFFF << (32 - mask)); s->net.s_addr = s->addr.s_addr & s->mask.s_addr; + r = true; - return 0; +out: + free(addr); + return r; } int tostring(struct subnet *s, char* buf, int len) { @@ -431,7 +443,7 @@ int tostring(struct subnet *s, char* buf, int len) { } static int parse_opts(int argc, char *argv[]) { - int c, res; + int c; bool help = false; struct subnet *ss; char *msg; @@ -465,19 +477,8 @@ static int parse_opts(int argc, char *argv[]) { } 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); - } - + if (!parse_subnet(optarg, ss)) + exit(2); num_blacklisted_subnets++; msg = malloc(128); @@ -498,19 +499,8 @@ static int parse_opts(int argc, char *argv[]) { } 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); - } - + if (!parse_subnet(optarg, ss)) + exit(2); num_whitelisted_subnets++; msg = malloc(128); From 5b7e84088255b246308e43f868daf44d1193c93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 15:03:51 +0100 Subject: [PATCH 08/43] Simplify parse_subnet() --- mdns-repeater.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 91d570f..f4c2698 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -383,39 +383,30 @@ static void show_help(const char *progname) { static bool parse_subnet(const char *input, struct subnet *s) { bool r = false; - int delim = 0; - int end = 0; char *addr = NULL; + char *delim; int mask; - while (input[end] != '\0') { - if (input[end] == '/') - delim = end; - end++; - } - - if (end == 0 || delim == 0 || end == delim) { - log_message(LOG_ERR, "invalid blacklist/whitelist argument: %s", input); + addr = strdup(input); + if (!addr) { + log_message(LOG_ERR, "strdup(): %s", strerror(errno)); goto out; } - addr = malloc(end); - if (!addr) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + delim = strchr(addr, '/'); + if (!delim) { + log_message(LOG_ERR, "invalid blacklist/whitelist argument: %s", input); goto out; } + *delim = '\0'; - memset(addr, 0, end); - strncpy(addr, input, delim); if (inet_pton(AF_INET, addr, &s->addr) != 1) { log_message(LOG_ERR, "could not parse blacklist/whitelist netmask: %s", input); goto out; } - memset(addr, 0, end); - strncpy(addr, input+delim+1, end-delim-1); - mask = atoi(addr); - + delim++; + mask = atoi(delim); if (mask < 0 || mask > 32) { log_message(LOG_ERR, "invalid blacklist/whitelist netmask: %s", input); goto out; From c24631add34146337352a61a1de590cf311c0e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 15:36:19 +0100 Subject: [PATCH 09/43] Convert the subnet black/whitelists to linked lists --- mdns-repeater.c | 102 ++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index f4c2698..b1478ca 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -46,8 +46,6 @@ #define PIDFILE "/var/run/" PACKAGE ".pid" #endif -#define MAX_SUBNETS 16 - struct if_sock { const char *ifname; /* interface name */ int sockfd; /* socket filedesc */ @@ -62,16 +60,13 @@ struct subnet { struct in_addr addr; /* subnet addr */ struct in_addr mask; /* subnet mask */ struct in_addr net; /* subnet net (computed) */ + struct list_head list; /* subnet list */ }; +LIST_HEAD(blacklisted_subnets); +LIST_HEAD(whitelisted_subnets); int server_sockfd = -1; -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; @@ -380,13 +375,20 @@ static void show_help(const char *progname) { ); } -static bool -parse_subnet(const char *input, struct subnet *s) { - bool r = false; +static struct subnet * +parse_subnet(const char *input) { + struct subnet *subnet; char *addr = NULL; char *delim; int mask; + subnet = malloc(sizeof(*subnet)); + if (!subnet) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + memset(subnet, 0, sizeof(*subnet)); + addr = strdup(input); if (!addr) { log_message(LOG_ERR, "strdup(): %s", strerror(errno)); @@ -400,7 +402,7 @@ parse_subnet(const char *input, struct subnet *s) { } *delim = '\0'; - if (inet_pton(AF_INET, addr, &s->addr) != 1) { + if (inet_pton(AF_INET, addr, &subnet->addr) != 1) { log_message(LOG_ERR, "could not parse blacklist/whitelist netmask: %s", input); goto out; } @@ -411,14 +413,16 @@ parse_subnet(const char *input, struct subnet *s) { log_message(LOG_ERR, "invalid blacklist/whitelist netmask: %s", input); goto out; } + free(addr); - s->mask.s_addr = ntohl((uint32_t)0xFFFFFFFF << (32 - mask)); - s->net.s_addr = s->addr.s_addr & s->mask.s_addr; - r = true; + subnet->mask.s_addr = ntohl((uint32_t)0xFFFFFFFF << (32 - mask)); + subnet->net.s_addr = subnet->addr.s_addr & subnet->mask.s_addr; + return subnet; out: free(addr); - return r; + free(subnet); + return NULL; } int tostring(struct subnet *s, char* buf, int len) { @@ -436,7 +440,7 @@ int tostring(struct subnet *s, char* buf, int len) { static int parse_opts(int argc, char *argv[]) { int c; bool help = false; - struct subnet *ss; + struct subnet *subnet; char *msg; while ((c = getopt(argc, argv, "hfp:b:w:u:")) != -1) { @@ -457,49 +461,31 @@ static int parse_opts(int argc, char *argv[]) { 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]; - if (!parse_subnet(optarg, ss)) + subnet = parse_subnet(optarg); + if (!subnet) exit(2); - num_blacklisted_subnets++; + list_add(&subnet->list, &blacklisted_subnets); msg = malloc(128); memset(msg, 0, 128); - tostring(ss, msg, 128); + tostring(subnet, 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]; - if (!parse_subnet(optarg, ss)) + case 'w': + subnet = parse_subnet(optarg); + if (!subnet) exit(2); - num_whitelisted_subnets++; + list_add(&subnet->list, &whitelisted_subnets); msg = malloc(128); memset(msg, 0, 128); - tostring(ss, msg, 128); + tostring(subnet, msg, 128); log_message(LOG_INFO, "whitelist %s", msg); free(msg); break; + case '?': case ':': fputs("\n", stderr); @@ -519,6 +505,11 @@ static int parse_opts(int argc, char *argv[]) { } } + if (!list_empty(&whitelisted_subnets) && !list_empty(&blacklisted_subnets)) { + log_message(LOG_ERR, "simultaneous whitelisting and blacklisting does not make sense"); + exit(2); + } + if (help) { show_help(argv[0]); exit(0); @@ -532,6 +523,7 @@ int main(int argc, char *argv[]) { fd_set sockfd_set; int r = 0; struct if_sock *sock, *tmp_sock; + struct subnet *subnet, *tmp_subnet; parse_opts(argc, argv); @@ -626,11 +618,11 @@ int main(int argc, char *argv[]) { if (discard || !our_net) continue; - if (num_whitelisted_subnets != 0) { + if (!list_empty(&whitelisted_subnets)) { bool whitelisted_packet = false; - for (int j = 0; j < num_whitelisted_subnets; j++) { + list_for_each_entry(subnet, &whitelisted_subnets, list) { // check for whitelist - if ((fromaddr.sin_addr.s_addr & whitelisted_subnets[j].mask.s_addr) == whitelisted_subnets[j].net.s_addr) { + if ((fromaddr.sin_addr.s_addr & subnet->mask.s_addr) == subnet->net.s_addr) { whitelisted_packet = true; break; } @@ -643,9 +635,9 @@ int main(int argc, char *argv[]) { } } else { bool blacklisted_packet = false; - for (int j = 0; j < num_blacklisted_subnets; j++) { + list_for_each_entry(subnet, &blacklisted_subnets, list) { // check for blacklist - if ((fromaddr.sin_addr.s_addr & blacklisted_subnets[j].mask.s_addr) == blacklisted_subnets[j].net.s_addr) { + if ((fromaddr.sin_addr.s_addr & subnet->mask.s_addr) == subnet->net.s_addr) { blacklisted_packet = true; break; } @@ -701,6 +693,16 @@ int main(int argc, char *argv[]) { free(sock); } + list_for_each_entry_safe(subnet, tmp_subnet, &blacklisted_subnets, list) { + list_del(&subnet->list); + free(subnet); + } + + list_for_each_entry_safe(subnet, tmp_subnet, &whitelisted_subnets, list) { + list_del(&subnet->list); + free(subnet); + } + // remove pid file if it belongs to us if (already_running() == getpid()) unlink(pid_file); From 7c725758171c7f5b889ad6430b42aa1cc35e651c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 15:50:06 +0100 Subject: [PATCH 10/43] Add subnet_match() helper function --- mdns-repeater.c | 51 +++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index b1478ca..2db7f09 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -425,6 +425,18 @@ parse_subnet(const char *input) { return NULL; } +static bool +subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) +{ + struct subnet *subnet; + + list_for_each_entry(subnet, subnets, list) + if ((fromaddr->sin_addr.s_addr & subnet->mask.s_addr) == subnet->net.s_addr) + return true; + + return false; +} + 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)); @@ -618,36 +630,17 @@ int main(int argc, char *argv[]) { if (discard || !our_net) continue; - if (!list_empty(&whitelisted_subnets)) { - bool whitelisted_packet = false; - list_for_each_entry(subnet, &whitelisted_subnets, list) { - // check for whitelist - if ((fromaddr.sin_addr.s_addr & subnet->mask.s_addr) == subnet->net.s_addr) { - whitelisted_packet = true; - break; - } - } - - if (!whitelisted_packet) { - if (foreground) - printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - continue; - } - } else { - bool blacklisted_packet = false; - list_for_each_entry(subnet, &blacklisted_subnets, list) { - // check for blacklist - if ((fromaddr.sin_addr.s_addr & subnet->mask.s_addr) == subnet->net.s_addr) { - blacklisted_packet = true; - break; - } - } + if (!list_empty(&whitelisted_subnets) && + !subnet_match(&fromaddr, &whitelisted_subnets)) { + if (foreground) + printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); + continue; + } - if (blacklisted_packet) { - if (foreground) - printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - continue; - } + if (subnet_match(&fromaddr, &blacklisted_subnets)) { + if (foreground) + printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); + continue; } if (foreground) From 6bdef4600b389f92dbefdb41176291243c8bb500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 16:41:12 +0100 Subject: [PATCH 11/43] Create some socket/subnet print helpers --- mdns-repeater.c | 71 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 2db7f09..dd4deb0 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -93,6 +93,38 @@ void log_message(int loglevel, char *fmt_str, ...) { } } +static char * +addr_mask_net_to_string(struct in_addr *addr, struct in_addr *mask, + struct in_addr *net) { + const char *fmt = "addr %s mask %s net %s"; + /* sizeof(fmt) = some extra bytes, and it's compile-time constant */ + static char msg[sizeof(fmt) + 3 * INET6_ADDRSTRLEN]; + char addrbuf[INET6_ADDRSTRLEN]; + char maskbuf[INET6_ADDRSTRLEN]; + char netbuf[INET6_ADDRSTRLEN]; + + snprintf(msg, sizeof(msg), fmt, + inet_ntop(AF_INET, addr, addrbuf, sizeof(addrbuf)), + inet_ntop(AF_INET, mask, maskbuf, sizeof(maskbuf)), + inet_ntop(AF_INET, net, netbuf, sizeof(netbuf))); + + return msg; +} + +static char * +if_sock_to_string(struct if_sock *sock) { + return addr_mask_net_to_string(&sock->addr, + &sock->mask, + &sock->net); +} + +static char * +subnet_to_string(struct subnet *subnet) { + return addr_mask_net_to_string(&subnet->addr, + &subnet->mask, + &subnet->net); +} + static int create_recv_sock() { int sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { @@ -145,9 +177,6 @@ create_send_sock(int recv_sockfd, const char *ifname) { struct sockaddr_in serveraddr; struct ip_mreq mreq; int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 - char *addr_str; - char *mask_str; - char *net_str; sockdata = malloc(sizeof(*sockdata)); if (!sockdata) { @@ -233,14 +262,7 @@ create_send_sock(int recv_sockfd, const char *ifname) { goto out; } - addr_str = strdup(inet_ntoa(sockdata->addr)); - mask_str = strdup(inet_ntoa(sockdata->mask)); - 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); - + log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, if_sock_to_string(sockdata)); return sockdata; out: @@ -437,23 +459,10 @@ subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) return false; } -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; bool help = false; struct subnet *subnet; - char *msg; while ((c = getopt(argc, argv, "hfp:b:w:u:")) != -1) { switch (c) { @@ -477,12 +486,7 @@ static int parse_opts(int argc, char *argv[]) { if (!subnet) exit(2); list_add(&subnet->list, &blacklisted_subnets); - - msg = malloc(128); - memset(msg, 0, 128); - tostring(subnet, msg, 128); - log_message(LOG_INFO, "blacklist %s", msg); - free(msg); + log_message(LOG_INFO, "blacklist %s", subnet_to_string(subnet)); break; case 'w': @@ -490,12 +494,7 @@ static int parse_opts(int argc, char *argv[]) { if (!subnet) exit(2); list_add(&subnet->list, &whitelisted_subnets); - - msg = malloc(128); - memset(msg, 0, 128); - tostring(subnet, msg, 128); - log_message(LOG_INFO, "whitelist %s", msg); - free(msg); + log_message(LOG_INFO, "whitelist %s", subnet_to_string(subnet)); break; case '?': From 5a737a1bdc3b42a1d69acc34acb0159b24f18bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 17:17:53 +0100 Subject: [PATCH 12/43] Add a signal pipe This is in preparation for the next patch which switches to a poll()-based event loop. --- mdns-repeater.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index dd4deb0..5fe07c2 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -71,7 +71,10 @@ int server_sockfd = -1; void *pkt_data = NULL; bool foreground = false; -bool shutdown_flag = false; + +int signal_pipe_fds[2]; +#define PIPE_RD 0 +#define PIPE_WR 1 char *pid_file = PIDFILE; @@ -283,9 +286,9 @@ static ssize_t send_packet(int fd, const void *data, size_t len) { return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in)); } -static void mdns_repeater_shutdown(int sig) { - (void)sig; - shutdown_flag = true; +static void +signal_shutdown(int sig) { + write(signal_pipe_fds[PIPE_WR], &sig, 1); } static pid_t already_running() { @@ -335,7 +338,7 @@ static void daemonize() { // signals signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); - signal(SIGTERM, mdns_repeater_shutdown); + signal(SIGTERM, signal_shutdown); setsid(); umask(0027); @@ -546,6 +549,12 @@ int main(int argc, char *argv[]) { openlog(PACKAGE, LOG_PID | LOG_CONS, LOG_DAEMON); + // create signal pipe pair + if (pipe(signal_pipe_fds) < 0) { + log_message(LOG_ERR, "pipe(): %s", strerror(errno)); + goto end_main; + } + // create receiving socket server_sockfd = create_recv_sock(); if (server_sockfd < 0) { @@ -588,7 +597,7 @@ int main(int argc, char *argv[]) { goto end_main; } - while (!shutdown_flag) { + while (true) { struct timeval tv = { .tv_sec = 10, .tv_usec = 0, @@ -596,10 +605,15 @@ int main(int argc, char *argv[]) { FD_ZERO(&sockfd_set); FD_SET(server_sockfd, &sockfd_set); + FD_SET(signal_pipe_fds[PIPE_RD], &sockfd_set); + int numfd = select(server_sockfd + 1, &sockfd_set, NULL, NULL, &tv); if (numfd <= 0) continue; + if (FD_ISSET(signal_pipe_fds[PIPE_RD], &sockfd_set)) + break; + if (FD_ISSET(server_sockfd, &sockfd_set)) { struct sockaddr_in fromaddr; socklen_t sockaddr_size = sizeof(struct sockaddr_in); From c1cc11a605d5a2e4e8a45271a2aeb517287c68a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 19:44:19 +0100 Subject: [PATCH 13/43] Switch from select() to poll() This is a preparatory step to simplify the following changes. --- mdns-repeater.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 5fe07c2..a2b643a 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "list.h" @@ -534,7 +535,6 @@ static int parse_opts(int argc, char *argv[]) { int main(int argc, char *argv[]) { pid_t running_pid; - fd_set sockfd_set; int r = 0; struct if_sock *sock, *tmp_sock; struct subnet *subnet, *tmp_subnet; @@ -597,24 +597,26 @@ int main(int argc, char *argv[]) { goto end_main; } - while (true) { - struct timeval tv = { - .tv_sec = 10, - .tv_usec = 0, - }; - - FD_ZERO(&sockfd_set); - FD_SET(server_sockfd, &sockfd_set); - FD_SET(signal_pipe_fds[PIPE_RD], &sockfd_set); + int pfd_count = 2; + struct pollfd pfds[2] = { + { + .fd = signal_pipe_fds[PIPE_RD], + .events = POLLIN, + }, { + .fd = server_sockfd, + .events = POLLIN, + } + }; - int numfd = select(server_sockfd + 1, &sockfd_set, NULL, NULL, &tv); - if (numfd <= 0) + while (true) { + r = poll(pfds, pfd_count, -1); + if (r <= 0) continue; - if (FD_ISSET(signal_pipe_fds[PIPE_RD], &sockfd_set)) + if (pfds[0].revents & POLLIN) break; - if (FD_ISSET(server_sockfd, &sockfd_set)) { + if (pfds[1].revents & POLLIN) { struct sockaddr_in fromaddr; socklen_t sockaddr_size = sizeof(struct sockaddr_in); ssize_t recvsize; From f4d788f98b81bf84a9d08e5558aaed1be95b406f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 20:46:45 +0100 Subject: [PATCH 14/43] Turn receiving sockets into a list as well --- mdns-repeater.c | 152 +++++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 54 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index a2b643a..7835abd 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -57,6 +57,15 @@ struct if_sock { }; LIST_HEAD(send_socks); +#define PACKET_SIZE 65536 +struct recv_sock { + const char *name; /* name of this socket */ + int sockfd; /* socket fd */ + char pkt_data[PACKET_SIZE]; /* incoming packet data */ + struct list_head list; /* socket list */ +}; +LIST_HEAD(recv_socks); + struct subnet { struct in_addr addr; /* subnet addr */ struct in_addr mask; /* subnet mask */ @@ -66,11 +75,6 @@ struct subnet { LIST_HEAD(blacklisted_subnets); LIST_HEAD(whitelisted_subnets); -int server_sockfd = -1; - -#define PACKET_SIZE 65536 -void *pkt_data = NULL; - bool foreground = false; int signal_pipe_fds[2]; @@ -129,56 +133,70 @@ subnet_to_string(struct subnet *subnet) { &subnet->net); } -static int create_recv_sock() { - int sd = socket(AF_INET, SOCK_DGRAM, 0); +static struct recv_sock * +create_recv_sock() { + struct recv_sock *sock; + int sd; + int on = 1; + struct sockaddr_in serveraddr; + + sock = malloc(sizeof(*sock)); + if (!sock) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + + sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { log_message(LOG_ERR, "recv socket(): %s", strerror(errno)); - return sd; + goto out; } + sock->sockfd = sd; - int r = -1; - - int on = 1; - if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) { + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %s", strerror(errno)); - return r; + goto out; } /* 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) { + if (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { log_message(LOG_ERR, "recv bind(): %s", strerror(errno)); - return r; + goto out; } // enable loopback in case someone else needs the data - if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on))) < 0) { + if (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; + goto out; } #ifdef IP_PKTINFO - if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, &on, sizeof(on))) < 0) { + if (setsockopt(sd, SOL_IP, IP_PKTINFO, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %s", strerror(errno)); - return r; + goto out; } #endif - return sd; + return sock; + +out: + free(sock); + return NULL; } static struct if_sock * -create_send_sock(int recv_sockfd, const char *ifname) { +create_send_sock(const char *ifname, struct list_head *recv_socks) { struct if_sock *sockdata; int sd = -1; struct ifreq ifr; struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; int on = 1; struct sockaddr_in serveraddr; + struct recv_sock *recv_sock; struct ip_mreq mreq; int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 @@ -246,13 +264,16 @@ create_send_sock(int recv_sockfd, const char *ifname) { } #endif - // add membership to receiving socket + // add membership to receiving sockets memset(&mreq, 0, sizeof(mreq)); mreq.imr_interface.s_addr = if_addr->s_addr; mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR); - if (setsockopt(recv_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); - goto out; + list_for_each_entry(recv_sock, recv_socks, list) { + if (setsockopt(recv_sock->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); + goto out; + } } // enable loopback in case someone else needs the data @@ -537,7 +558,11 @@ int main(int argc, char *argv[]) { pid_t running_pid; int r = 0; struct if_sock *sock, *tmp_sock; + struct recv_sock *recv_sock, *tmp_recv_sock; struct subnet *subnet, *tmp_subnet; + int pfds_count = 0; + int pfds_used = 0; + struct pollfd *pfds; parse_opts(argc, argv); @@ -554,18 +579,21 @@ int main(int argc, char *argv[]) { log_message(LOG_ERR, "pipe(): %s", strerror(errno)); goto end_main; } + pfds_count++; - // create receiving socket - server_sockfd = create_recv_sock(); - if (server_sockfd < 0) { + // create receiving sockets + recv_sock = create_recv_sock(); + if (!recv_sock) { log_message(LOG_ERR, "unable to create server socket"); r = 1; goto end_main; } + list_add(&recv_sock->list, &recv_socks); + pfds_count++; // create sending sockets for (int i = optind; i < argc; i++) { - sock = create_send_sock(server_sockfd, argv[i]); + sock = create_send_sock(argv[i], &recv_socks); if (!sock) { log_message(LOG_ERR, "unable to create socket for interface %s", argv[i]); r = 1; @@ -590,43 +618,60 @@ int main(int argc, char *argv[]) { } } - pkt_data = malloc(PACKET_SIZE); - if (pkt_data == NULL) { - log_message(LOG_ERR, "cannot malloc() packet buffer: %s", strerror(errno)); + pfds = calloc(pfds_count, sizeof(struct pollfd)); + if (!pfds) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); r = 1; goto end_main; } - int pfd_count = 2; - struct pollfd pfds[2] = { - { - .fd = signal_pipe_fds[PIPE_RD], - .events = POLLIN, - }, { - .fd = server_sockfd, - .events = POLLIN, - } - }; + pfds[pfds_used].fd = signal_pipe_fds[PIPE_RD]; + pfds[pfds_used].events = POLLIN; + pfds_used++; + + list_for_each_entry(recv_sock, &recv_socks, list) { + pfds[pfds_used].fd = recv_sock->sockfd; + pfds[pfds_used].events = POLLIN; + pfds_used++; + } while (true) { - r = poll(pfds, pfd_count, -1); + r = poll(pfds, pfds_used, -1); if (r <= 0) continue; if (pfds[0].revents & POLLIN) break; - if (pfds[1].revents & POLLIN) { + for (int i = 1; i < pfds_used; i++) { struct sockaddr_in fromaddr; socklen_t sockaddr_size = sizeof(struct sockaddr_in); ssize_t recvsize; bool discard = false; bool our_net = false; - recvsize = recvfrom(server_sockfd, pkt_data, PACKET_SIZE, 0, - (struct sockaddr *) &fromaddr, &sockaddr_size); + if (!(pfds[i].revents & POLLIN)) + continue; + + recv_sock = NULL; + list_for_each_entry(tmp_recv_sock, &recv_socks, list) { + if (tmp_recv_sock->sockfd == pfds[i].fd) { + recv_sock = tmp_recv_sock; + break; + } + } + + if (!recv_sock) + continue; + + recvsize = recvfrom(recv_sock->sockfd, + recv_sock->pkt_data, + sizeof(recv_sock->pkt_data), 0, + (struct sockaddr *)&fromaddr, + &sockaddr_size); if (recvsize < 0) { log_message(LOG_ERR, "recv(): %s", strerror(errno)); + continue; } list_for_each_entry(sock, &send_socks, list) { @@ -673,7 +718,7 @@ int main(int argc, char *argv[]) { printf("repeating data to %s\n", sock->ifname); // repeat data - sentsize = send_packet(sock->sockfd, pkt_data, (size_t)recvsize); + sentsize = send_packet(sock->sockfd, recv_sock->pkt_data, recvsize); if (sentsize != recvsize) { if (sentsize < 0) log_message(LOG_ERR, "send(): %s", strerror(errno)); @@ -688,12 +733,11 @@ int main(int argc, char *argv[]) { log_message(LOG_INFO, "shutting down..."); end_main: - - if (pkt_data != NULL) - free(pkt_data); - - if (server_sockfd >= 0) - close(server_sockfd); + list_for_each_entry_safe(recv_sock, tmp_recv_sock, &recv_socks, list) { + list_del(&recv_sock->list); + close(recv_sock->sockfd); + free(recv_sock); + } list_for_each_entry_safe(sock, tmp_sock, &send_socks, list) { list_del(&sock->list); From e551477e417da288f4f39183ed099b5c82790de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 20:51:20 +0100 Subject: [PATCH 15/43] Rename struct if_sock to send_sock For consistency with recv_sock and to make it clear that these structs have nothing to do with e.g. struct if_addr or struct in_addr. --- mdns-repeater.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 7835abd..c369749 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -47,7 +47,7 @@ #define PIDFILE "/var/run/" PACKAGE ".pid" #endif -struct if_sock { +struct send_sock { const char *ifname; /* interface name */ int sockfd; /* socket filedesc */ struct in_addr addr; /* interface addr */ @@ -120,7 +120,7 @@ addr_mask_net_to_string(struct in_addr *addr, struct in_addr *mask, } static char * -if_sock_to_string(struct if_sock *sock) { +send_sock_to_string(struct send_sock *sock) { return addr_mask_net_to_string(&sock->addr, &sock->mask, &sock->net); @@ -188,9 +188,9 @@ create_recv_sock() { return NULL; } -static struct if_sock * +static struct send_sock * create_send_sock(const char *ifname, struct list_head *recv_socks) { - struct if_sock *sockdata; + struct send_sock *sockdata; int sd = -1; struct ifreq ifr; struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; @@ -287,7 +287,7 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { goto out; } - log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, if_sock_to_string(sockdata)); + log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, send_sock_to_string(sockdata)); return sockdata; out: @@ -557,7 +557,7 @@ static int parse_opts(int argc, char *argv[]) { int main(int argc, char *argv[]) { pid_t running_pid; int r = 0; - struct if_sock *sock, *tmp_sock; + struct send_sock *send_sock, *tmp_send_sock; struct recv_sock *recv_sock, *tmp_recv_sock; struct subnet *subnet, *tmp_subnet; int pfds_count = 0; @@ -593,14 +593,14 @@ int main(int argc, char *argv[]) { // create sending sockets for (int i = optind; i < argc; i++) { - sock = create_send_sock(argv[i], &recv_socks); - if (!sock) { + send_sock = create_send_sock(argv[i], &recv_socks); + if (!send_sock) { log_message(LOG_ERR, "unable to create socket for interface %s", argv[i]); r = 1; goto end_main; } - list_add(&sock->list, &send_socks); + list_add(&send_sock->list, &send_socks); } if (user) { @@ -674,14 +674,14 @@ int main(int argc, char *argv[]) { continue; } - list_for_each_entry(sock, &send_socks, list) { + list_for_each_entry(send_sock, &send_socks, list) { // make sure packet originated from specified networks - if ((fromaddr.sin_addr.s_addr & sock->mask.s_addr) == sock->net.s_addr) { + if ((fromaddr.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) { our_net = true; } // check for loopback - if (fromaddr.sin_addr.s_addr == sock->addr.s_addr) { + if (fromaddr.sin_addr.s_addr == send_sock->addr.s_addr) { discard = true; break; } @@ -707,18 +707,18 @@ int main(int argc, char *argv[]) { printf("data from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - list_for_each_entry(sock, &send_socks, list) { + list_for_each_entry(send_sock, &send_socks, list) { ssize_t sentsize; // do not repeat packet back to the same network from which it originated - if ((fromaddr.sin_addr.s_addr & sock->mask.s_addr) == sock->net.s_addr) + if ((fromaddr.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) continue; if (foreground) - printf("repeating data to %s\n", sock->ifname); + printf("repeating data to %s\n", send_sock->ifname); // repeat data - sentsize = send_packet(sock->sockfd, recv_sock->pkt_data, recvsize); + sentsize = send_packet(send_sock->sockfd, recv_sock->pkt_data, recvsize); if (sentsize != recvsize) { if (sentsize < 0) log_message(LOG_ERR, "send(): %s", strerror(errno)); @@ -739,10 +739,10 @@ int main(int argc, char *argv[]) { free(recv_sock); } - list_for_each_entry_safe(sock, tmp_sock, &send_socks, list) { - list_del(&sock->list); - close(sock->sockfd); - free(sock); + list_for_each_entry_safe(send_sock, tmp_send_sock, &send_socks, list) { + list_del(&send_sock->list); + close(send_sock->sockfd); + free(send_sock); } list_for_each_entry_safe(subnet, tmp_subnet, &blacklisted_subnets, list) { From 9754065adce46a90e67d3379ebd4462f2439a669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Sun, 4 Feb 2024 23:49:59 +0100 Subject: [PATCH 16/43] Support IPv6 in recvfrom This is just in preparation for further changes to send_sock and recv_sock. --- mdns-repeater.c | 60 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index c369749..3c08b5c 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -59,10 +59,16 @@ LIST_HEAD(send_socks); #define PACKET_SIZE 65536 struct recv_sock { - const char *name; /* name of this socket */ - int sockfd; /* socket fd */ - char pkt_data[PACKET_SIZE]; /* incoming packet data */ - struct list_head list; /* socket list */ + const char *name; /* name of this socket */ + int sockfd; /* socket fd */ + char pkt_data[PACKET_SIZE]; /* incoming packet data */ + union { + struct sockaddr_storage from; /* sender addr */ + struct sockaddr_in6 from_in6; /* sender addr (IPv6) */ + struct sockaddr_in from_in; /* sender addr (IPv4) */ + }; + char from_str[INET6_ADDRSTRLEN]; /* sender addr (str) */ + struct list_head list; /* socket list */ }; LIST_HEAD(recv_socks); @@ -644,8 +650,7 @@ int main(int argc, char *argv[]) { break; for (int i = 1; i < pfds_used; i++) { - struct sockaddr_in fromaddr; - socklen_t sockaddr_size = sizeof(struct sockaddr_in); + socklen_t sockaddr_size; ssize_t recvsize; bool discard = false; bool our_net = false; @@ -664,24 +669,45 @@ int main(int argc, char *argv[]) { if (!recv_sock) continue; + sockaddr_size = sizeof(recv_sock->from); recvsize = recvfrom(recv_sock->sockfd, recv_sock->pkt_data, sizeof(recv_sock->pkt_data), 0, - (struct sockaddr *)&fromaddr, + (struct sockaddr *)&recv_sock->from, &sockaddr_size); if (recvsize < 0) { log_message(LOG_ERR, "recv(): %s", strerror(errno)); continue; } + switch (recv_sock->from.ss_family) { + case AF_INET: + if (!inet_ntop(AF_INET, + &recv_sock->from_in.sin_addr, + recv_sock->from_str, + sizeof(recv_sock->from_str))) + recv_sock->from_str[0] = '\0'; + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, + &recv_sock->from_in6.sin6_addr, + recv_sock->from_str, + sizeof(recv_sock->from_str))) + recv_sock->from_str[0] = '\0'; + /* Not supported yet */ + continue; + default: + continue; + } + list_for_each_entry(send_sock, &send_socks, list) { // make sure packet originated from specified networks - if ((fromaddr.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) { + if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) { our_net = true; } // check for loopback - if (fromaddr.sin_addr.s_addr == send_sock->addr.s_addr) { + if (recv_sock->from_in.sin_addr.s_addr == send_sock->addr.s_addr) { discard = true; break; } @@ -691,27 +717,29 @@ int main(int argc, char *argv[]) { continue; if (!list_empty(&whitelisted_subnets) && - !subnet_match(&fromaddr, &whitelisted_subnets)) { + !subnet_match(&recv_sock->from_in, &whitelisted_subnets)) { if (foreground) - printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); + printf("skipping packet from=%s size=%zd\n", + recv_sock->from_str, recvsize); continue; } - if (subnet_match(&fromaddr, &blacklisted_subnets)) { + if (subnet_match(&recv_sock->from_in, &blacklisted_subnets)) { if (foreground) - printf("skipping packet from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); + printf("skipping packet from=%s size=%zd\n", + recv_sock->from_str, recvsize); continue; } if (foreground) - printf("data from=%s size=%zd\n", inet_ntoa(fromaddr.sin_addr), recvsize); - + printf("data from=%s size=%zd\n", + recv_sock->from_str, recvsize); list_for_each_entry(send_sock, &send_socks, list) { ssize_t sentsize; // do not repeat packet back to the same network from which it originated - if ((fromaddr.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) + if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) continue; if (foreground) From c830507c2f9f07192528acaaf85529bc7f576aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Mon, 5 Feb 2024 00:43:29 +0100 Subject: [PATCH 17/43] Support IPv6 in subnets (blacklist/whitelist) There's still no support in the actual sending/receiving though. --- mdns-repeater.c | 148 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 117 insertions(+), 31 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 3c08b5c..1b82c5c 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -47,6 +47,8 @@ #define PIDFILE "/var/run/" PACKAGE ".pid" #endif +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + struct send_sock { const char *ifname; /* interface name */ int sockfd; /* socket filedesc */ @@ -73,10 +75,20 @@ struct recv_sock { LIST_HEAD(recv_socks); struct subnet { - struct in_addr addr; /* subnet addr */ - struct in_addr mask; /* subnet mask */ - struct in_addr net; /* subnet net (computed) */ - struct list_head list; /* subnet list */ + union { + struct sockaddr_storage addr; /* subnet addr */ + struct sockaddr_in6 addr_in6; /* subnet addr (IPv6) */ + struct sockaddr_in addr_in; /* subnet addr (IPv4) */ + }; + union { + struct in6_addr mask_in6; /* subnet mask (IPv6) */ + struct in_addr mask_in; /* subnet mask (IPv4) */ + }; + union { + struct in6_addr net_in6; /* subnet net (IPv6) */ + struct in_addr net_in; /* subnet net (IPv4) */ + }; + struct list_head list; /* subnet list */ }; LIST_HEAD(blacklisted_subnets); LIST_HEAD(whitelisted_subnets); @@ -108,8 +120,9 @@ void log_message(int loglevel, char *fmt_str, ...) { } static char * -addr_mask_net_to_string(struct in_addr *addr, struct in_addr *mask, - struct in_addr *net) { +addr6_mask_net_to_string(struct sockaddr_in6 *addr, + struct in6_addr *mask, + struct in6_addr *net) { const char *fmt = "addr %s mask %s net %s"; /* sizeof(fmt) = some extra bytes, and it's compile-time constant */ static char msg[sizeof(fmt) + 3 * INET6_ADDRSTRLEN]; @@ -118,7 +131,28 @@ addr_mask_net_to_string(struct in_addr *addr, struct in_addr *mask, char netbuf[INET6_ADDRSTRLEN]; snprintf(msg, sizeof(msg), fmt, - inet_ntop(AF_INET, addr, addrbuf, sizeof(addrbuf)), + inet_ntop(AF_INET6, &addr->sin6_addr, + addrbuf, sizeof(addrbuf)), + inet_ntop(AF_INET6, mask, maskbuf, sizeof(maskbuf)), + inet_ntop(AF_INET6, net, netbuf, sizeof(netbuf))); + + return msg; +} + +static char * +addr4_mask_net_to_string(struct sockaddr_in *addr, + struct in_addr *mask, + struct in_addr *net) { + const char *fmt = "addr %s mask %s net %s"; + /* sizeof(fmt) = some extra bytes, and it's compile-time constant */ + static char msg[sizeof(fmt) + 3 * INET_ADDRSTRLEN]; + char addrbuf[INET_ADDRSTRLEN]; + char maskbuf[INET_ADDRSTRLEN]; + char netbuf[INET_ADDRSTRLEN]; + + snprintf(msg, sizeof(msg), fmt, + inet_ntop(AF_INET, &addr->sin_addr, + addrbuf, sizeof(addrbuf)), inet_ntop(AF_INET, mask, maskbuf, sizeof(maskbuf)), inet_ntop(AF_INET, net, netbuf, sizeof(netbuf))); @@ -127,16 +161,28 @@ addr_mask_net_to_string(struct in_addr *addr, struct in_addr *mask, static char * send_sock_to_string(struct send_sock *sock) { - return addr_mask_net_to_string(&sock->addr, - &sock->mask, - &sock->net); + struct sockaddr_storage addr; + struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; + + addr.ss_family = AF_INET; + memcpy(&addr_in->sin_addr, &sock->addr, sizeof(addr_in->sin_addr)); + return addr4_mask_net_to_string(addr_in, &sock->mask, &sock->net); } static char * subnet_to_string(struct subnet *subnet) { - return addr_mask_net_to_string(&subnet->addr, - &subnet->mask, - &subnet->net); + switch (subnet->addr.ss_family) { + case AF_INET6: + return addr6_mask_net_to_string(&subnet->addr_in6, + &subnet->mask_in6, + &subnet->net_in6); + case AF_INET: + return addr4_mask_net_to_string(&subnet->addr_in, + &subnet->mask_in, + &subnet->net_in); + default: + return "ERROR"; + } } static struct recv_sock * @@ -428,12 +474,19 @@ static void show_help(const char *progname) { ); } +/* + * Expected input, strings of the form: + * 192.168.0.12/24 + * 2001:db8::/32 + */ static struct subnet * parse_subnet(const char *input) { struct subnet *subnet; - char *addr = NULL; + char *addr_str = NULL; char *delim; - int mask; + struct in6_addr *addr_in6; + struct in_addr *addr_in; + int prefix_len; subnet = malloc(sizeof(*subnet)); if (!subnet) { @@ -442,38 +495,67 @@ parse_subnet(const char *input) { } memset(subnet, 0, sizeof(*subnet)); - addr = strdup(input); - if (!addr) { + addr_str = strdup(input); + if (!addr_str) { log_message(LOG_ERR, "strdup(): %s", strerror(errno)); goto out; } - delim = strchr(addr, '/'); + delim = strchr(addr_str, '/'); if (!delim) { log_message(LOG_ERR, "invalid blacklist/whitelist argument: %s", input); goto out; } - *delim = '\0'; - if (inet_pton(AF_INET, addr, &subnet->addr) != 1) { - log_message(LOG_ERR, "could not parse blacklist/whitelist netmask: %s", input); + *delim = '\0'; + delim++; + prefix_len = atoi(delim); + if (prefix_len < 0) { + log_message(LOG_ERR, "invalid blacklist/whitelist prefix length: %s", input); goto out; } - delim++; - mask = atoi(delim); - if (mask < 0 || mask > 32) { - log_message(LOG_ERR, "invalid blacklist/whitelist netmask: %s", input); + addr_in6 = &subnet->addr_in6.sin6_addr; + addr_in = &subnet->addr_in.sin_addr; + + // First, try parsing an IPv6 address + if (inet_pton(AF_INET6, addr_str, addr_in6) == 1) { + if (prefix_len > 128) { + log_message(LOG_ERR, "blacklist/whitelist prefix length > 128: %s", input); + goto out; + } + + for (int i = 0; i < sizeof(addr_in6->s6_addr); i++) { + uint8_t mask = 0xff << (8 - MIN(prefix_len, 8)); + prefix_len -= MIN(prefix_len, 8); + subnet->mask_in6.s6_addr[i] = mask; + subnet->net_in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; + } + + subnet->addr.ss_family = AF_INET6; + + // Second, try parsing an IPv4 address + } else if (inet_pton(AF_INET, addr_str, addr_in) == 1) { + if (prefix_len > 32) { + log_message(LOG_ERR, "blacklist/whitelist prefix length > 32: %s", input); + goto out; + } + + subnet->mask_in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); + subnet->net_in.s_addr = addr_in->s_addr & subnet->mask_in.s_addr; + subnet->addr.ss_family = AF_INET; + + // Give up + } else { + log_message(LOG_ERR, "could not parse blacklist/whitelist netmask: %s", input); goto out; } - free(addr); - subnet->mask.s_addr = ntohl((uint32_t)0xFFFFFFFF << (32 - mask)); - subnet->net.s_addr = subnet->addr.s_addr & subnet->mask.s_addr; + free(addr_str); return subnet; out: - free(addr); + free(addr_str); free(subnet); return NULL; } @@ -483,9 +565,13 @@ subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) { struct subnet *subnet; - list_for_each_entry(subnet, subnets, list) - if ((fromaddr->sin_addr.s_addr & subnet->mask.s_addr) == subnet->net.s_addr) + list_for_each_entry(subnet, subnets, list) { + if (subnet->addr.ss_family != AF_INET) + continue; + + if ((fromaddr->sin_addr.s_addr & subnet->mask_in.s_addr) == subnet->net_in.s_addr) return true; + } return false; } From b842c61971daa6fd19cc22221432ee3fe49b366d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Mon, 5 Feb 2024 13:10:44 +0100 Subject: [PATCH 18/43] Support IPv6 in recv_sock() After this change, we can actually receive IPv6 multicast mDNS packets, but just log and discard them (for now). --- mdns-repeater.c | 111 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 1b82c5c..f67b3cb 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -40,7 +40,8 @@ #include "list.h" #define PACKAGE "mdns-repeater" -#define MDNS_ADDR "224.0.0.251" +#define MDNS_ADDR4 "224.0.0.251" +#define MDNS_ADDR6 "FF02::FB" #define MDNS_PORT 5353 #ifndef PIDFILE @@ -64,6 +65,11 @@ struct recv_sock { const char *name; /* name of this socket */ int sockfd; /* socket fd */ char pkt_data[PACKET_SIZE]; /* incoming packet data */ + union { + struct sockaddr_storage addr; /* socket addr */ + struct sockaddr_in6 addr_in6; /* socket addr (IPv6) */ + struct sockaddr_in addr_in; /* socket addr (IPv4) */ + }; union { struct sockaddr_storage from; /* sender addr */ struct sockaddr_in6 from_in6; /* sender addr (IPv6) */ @@ -186,11 +192,57 @@ subnet_to_string(struct subnet *subnet) { } static struct recv_sock * -create_recv_sock() { +create_recv_sock6() { + struct recv_sock *sock; + int sd; + int on = 1; + + sock = malloc(sizeof(*sock)); + if (!sock) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + + sd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sd < 0) { + log_message(LOG_ERR, "recv socket6(): %s", strerror(errno)); + goto out; + } + sock->sockfd = sd; + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "recv setsockopt6(SO_REUSEADDR): %s", strerror(errno)); + goto out; + } + + // enable loopback in case someone else needs the data + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "recv setsockopt6(IP_MULTICAST_LOOP): %s", strerror(errno)); + goto out; + } + + /* bind to an address */ + memset(&sock->addr, 0, sizeof(sock->addr)); + sock->addr_in6.sin6_family = AF_INET6; + sock->addr_in6.sin6_port = htons(MDNS_PORT); + sock->addr_in6.sin6_addr = in6addr_any; + if (bind(sd, (struct sockaddr *)&sock->addr_in6, sizeof(sock->addr_in6)) < 0) { + log_message(LOG_ERR, "recv bind6(): %s", strerror(errno)); + goto out; + } + + return sock; + +out: + free(sock); + return NULL; +} + +static struct recv_sock * +create_recv_sock4() { struct recv_sock *sock; int sd; int on = 1; - struct sockaddr_in serveraddr; sock = malloc(sizeof(*sock)); if (!sock) { @@ -211,11 +263,11 @@ create_recv_sock() { } /* bind to an address */ - 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 (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { + memset(&sock->addr, 0, sizeof(sock->addr)); + sock->addr_in.sin_family = AF_INET; + sock->addr_in.sin_port = htons(MDNS_PORT); + sock->addr_in.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(sd, (struct sockaddr *)&sock->addr_in, sizeof(sock->addr_in)) < 0) { log_message(LOG_ERR, "recv bind(): %s", strerror(errno)); goto out; } @@ -249,6 +301,7 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { int on = 1; struct sockaddr_in serveraddr; struct recv_sock *recv_sock; + struct ipv6_mreq mreq6; struct ip_mreq mreq; int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 @@ -319,12 +372,27 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { // add membership to receiving sockets memset(&mreq, 0, sizeof(mreq)); mreq.imr_interface.s_addr = if_addr->s_addr; - mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR); + mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR4); + memset(&mreq6, 0, sizeof(mreq6)); + inet_pton(AF_INET6, MDNS_ADDR6, &mreq6.ipv6mr_multiaddr.s6_addr); + mreq6.ipv6mr_interface = if_nametoindex(ifname); + list_for_each_entry(recv_sock, recv_socks, list) { - if (setsockopt(recv_sock->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); - goto out; + switch (recv_sock->addr.ss_family) { + case AF_INET6: + if (setsockopt(recv_sock->sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + &mreq6, sizeof(mreq6)) < 0) { + log_message(LOG_ERR, "recv setsockopt6(IPV6_ADD_MEMBERSHIP): %s", strerror(errno)); + goto out; + } + break; + case AF_INET: + if (setsockopt(recv_sock->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + log_message(LOG_ERR, "recv setsockopt4(IP_ADD_MEMBERSHIP): %s", strerror(errno)); + goto out; + } + break; } } @@ -354,7 +422,7 @@ static ssize_t send_packet(int fd, const void *data, size_t len) { 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); + toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR4); } return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in)); @@ -674,9 +742,18 @@ int main(int argc, char *argv[]) { pfds_count++; // create receiving sockets - recv_sock = create_recv_sock(); + recv_sock = create_recv_sock6(); + if (!recv_sock) { + log_message(LOG_ERR, "unable to create server IPv6 socket"); + r = 1; + goto end_main; + } + list_add(&recv_sock->list, &recv_socks); + pfds_count++; + + recv_sock = create_recv_sock4(); if (!recv_sock) { - log_message(LOG_ERR, "unable to create server socket"); + log_message(LOG_ERR, "unable to create server IPv4 socket"); r = 1; goto end_main; } @@ -780,6 +857,8 @@ int main(int argc, char *argv[]) { recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; + printf("skipping v6 packet from=%s size=%zd\n", + recv_sock->from_str, recvsize); /* Not supported yet */ continue; default: From 5cd357993b64c15e8341e2052bffb41c65e740b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 12:30:14 +0100 Subject: [PATCH 19/43] Use IP_MULTICAST_IF instead of SO_BINDTODEVICE IP_MULTICAST_IF isn't FreeBSD specific and SO_BINDDEVICE is Linux-specific, so use IP_MULTICAST_IF for all platforms. --- mdns-repeater.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index f67b3cb..fb56fd4 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -303,7 +303,7 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { struct recv_sock *recv_sock; struct ipv6_mreq mreq6; struct ip_mreq mreq; - int ttl = 255; // IP TTL should be 255: https://datatracker.ietf.org/doc/html/rfc6762#section-11 + int ttl = 255; // https://datatracker.ietf.org/doc/html/rfc6762#section-11 sockdata = malloc(sizeof(*sockdata)); if (!sockdata) { @@ -323,13 +323,6 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); -#ifdef SO_BINDTODEVICE - if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(struct ifreq)) < 0) { - log_message(LOG_ERR, "send setsockopt(SO_BINDTODEVICE): %s", strerror(errno)); - goto out; - } -#endif - // get netmask if (ioctl(sd, SIOCGIFNETMASK, &ifr) < 0) { log_message(LOG_ERR, "ioctl(SIOCGIFNETMASK): %s", strerror(errno)); @@ -352,22 +345,35 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { goto out; } - // bind to an address + // record the address to use 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; + + // bind to an address if (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { log_message(LOG_ERR, "send bind(): %s", strerror(errno)); goto out; } -#if __FreeBSD__ + // bind to a device if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &serveraddr.sin_addr, sizeof(serveraddr.sin_addr)) < 0) { log_message(LOG_ERR, "send ip_multicast_if(): %s", strerror(errno)); goto out; } -#endif + + // enable loopback in case someone else needs the data + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_LOOP): %s", strerror(errno)); + goto out; + } + + // set the TTL per RFC6762 + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { + log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_TTL): %s", strerror(errno)); + goto out; + } // add membership to receiving sockets memset(&mreq, 0, sizeof(mreq)); @@ -396,17 +402,6 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { } } - // enable loopback in case someone else needs the data - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on)) < 0) { - log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_LOOP): %s", strerror(errno)); - goto out; - } - - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { - log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_TTL): %s", strerror(errno)); - goto out; - } - log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, send_sock_to_string(sockdata)); return sockdata; From 6652c4d2710d21083c5df936f547d7ab8449e0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 12:55:33 +0100 Subject: [PATCH 20/43] Use IPv4/IPv6 agnostic storage for send_sock addresses --- mdns-repeater.c | 99 +++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index fb56fd4..9f1e90e 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -51,12 +51,22 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) struct send_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 list_head list; /* socket list */ + const char *ifname; /* interface name */ + int sockfd; /* socket fd */ + union { + struct sockaddr_storage addr; /* socket addr */ + struct sockaddr_in6 addr_in6; /* socket addr (IPv6) */ + struct sockaddr_in addr_in; /* socket addr (IPv4) */ + }; + union { + struct in6_addr mask_in6; /* socket mask (IPv6) */ + struct in_addr mask_in; /* socket mask (IPv4) */ + }; + union { + struct in6_addr net_in6; /* socket net (IPv6) */ + struct in_addr net_in; /* socket net (IPv4) */ + }; + struct list_head list; /* socket list */ }; LIST_HEAD(send_socks); @@ -167,12 +177,18 @@ addr4_mask_net_to_string(struct sockaddr_in *addr, static char * send_sock_to_string(struct send_sock *sock) { - struct sockaddr_storage addr; - struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; - - addr.ss_family = AF_INET; - memcpy(&addr_in->sin_addr, &sock->addr, sizeof(addr_in->sin_addr)); - return addr4_mask_net_to_string(addr_in, &sock->mask, &sock->net); + switch (sock->addr.ss_family) { + case AF_INET6: + return addr6_mask_net_to_string(&sock->addr_in6, + &sock->mask_in6, + &sock->net_in6); + case AF_INET: + return addr4_mask_net_to_string(&sock->addr_in, + &sock->mask_in, + &sock->net_in); + default: + return "ERROR"; + } } static char * @@ -294,71 +310,66 @@ create_recv_sock4() { static struct send_sock * create_send_sock(const char *ifname, struct list_head *recv_socks) { - struct send_sock *sockdata; + struct send_sock *sock; int sd = -1; struct ifreq ifr; struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; int on = 1; - struct sockaddr_in serveraddr; - struct recv_sock *recv_sock; + int ttl = 255; // https://datatracker.ietf.org/doc/html/rfc6762#section-11 struct ipv6_mreq mreq6; struct ip_mreq mreq; - int ttl = 255; // https://datatracker.ietf.org/doc/html/rfc6762#section-11 + struct recv_sock *recv_sock; - sockdata = malloc(sizeof(*sockdata)); - if (!sockdata) { + sock = malloc(sizeof(*sock)); + if (!sock) { log_message(LOG_ERR, "malloc(): %s", strerror(errno)); goto out; } + memset(sock, 0, sizeof(*sock)); + sock->ifname = ifname; sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { log_message(LOG_ERR, "send socket(): %s", strerror(errno)); goto out; } + sock->sockfd = sd; - sockdata->ifname = ifname; - sockdata->sockfd = sd; - + // get netmask memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - - // get netmask if (ioctl(sd, SIOCGIFNETMASK, &ifr) < 0) { log_message(LOG_ERR, "ioctl(SIOCGIFNETMASK): %s", strerror(errno)); goto out; } - memcpy(&sockdata->mask, if_addr, sizeof(*if_addr)); + memcpy(&sock->mask_in, if_addr, sizeof(*if_addr)); - // .. and interface address + // ...and interface address if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) { log_message(LOG_ERR, "ioctl(SIOCGIFADDR): %s", strerror(errno)); goto out; } - memcpy(&sockdata->addr, if_addr, sizeof(*if_addr)); + memcpy(&sock->addr_in.sin_addr, if_addr, sizeof(*if_addr)); + sock->addr.ss_family = AF_INET; + sock->addr_in.sin_port = htons(MDNS_PORT); - // compute network (address & mask) - sockdata->net.s_addr = sockdata->addr.s_addr & sockdata->mask.s_addr; + // ...then compute the network + sock->net_in.s_addr = sock->addr_in.sin_addr.s_addr & sock->mask_in.s_addr; + // make sure that the address can be used by other applications if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "send setsockopt(SO_REUSEADDR): %s", strerror(errno)); goto out; } - // record the address to use - 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; - - // bind to an address - if (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { + // bind to the address + if (bind(sd, (struct sockaddr *)&sock->addr_in, sizeof(sock->addr_in)) < 0) { log_message(LOG_ERR, "send bind(): %s", strerror(errno)); goto out; } - // bind to a device - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &serveraddr.sin_addr, sizeof(serveraddr.sin_addr)) < 0) { + // bind to the device + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &sock->addr_in.sin_addr, sizeof(sock->addr_in)) < 0) { log_message(LOG_ERR, "send ip_multicast_if(): %s", strerror(errno)); goto out; } @@ -402,11 +413,11 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { } } - log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, send_sock_to_string(sockdata)); - return sockdata; + log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, send_sock_to_string(sock)); + return sock; out: - free(sockdata); + free(sock); close(sd); return NULL; } @@ -862,12 +873,12 @@ int main(int argc, char *argv[]) { list_for_each_entry(send_sock, &send_socks, list) { // make sure packet originated from specified networks - if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) { + if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) { our_net = true; } // check for loopback - if (recv_sock->from_in.sin_addr.s_addr == send_sock->addr.s_addr) { + if (recv_sock->from_in.sin_addr.s_addr == send_sock->addr_in.sin_addr.s_addr) { discard = true; break; } @@ -899,7 +910,7 @@ int main(int argc, char *argv[]) { ssize_t sentsize; // do not repeat packet back to the same network from which it originated - if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask.s_addr) == send_sock->net.s_addr) + if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) continue; if (foreground) From e49c9c61596facfb4829a908923f8663bb313e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 13:38:13 +0100 Subject: [PATCH 21/43] Add a separate create_send_sock6() function The function is incomplete (missing address/routes), but the sockets are not used yet. --- mdns-repeater.c | 154 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 125 insertions(+), 29 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 9f1e90e..3599161 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -309,14 +309,110 @@ create_recv_sock4() { } static struct send_sock * -create_send_sock(const char *ifname, struct list_head *recv_socks) { +create_send_sock6(const char *ifname, struct list_head *recv_socks) { + struct send_sock *sock = NULL; + int sd = -1; + int ifindex; + int on = 1; + int ttl = 255; // https://datatracker.ietf.org/doc/html/rfc6762#section-11 + struct ipv6_mreq mreq6; + struct recv_sock *recv_sock; + + ifindex = if_nametoindex (ifname); + if (ifindex < 1) { + log_message(LOG_ERR, "if_nametoindex(%s): %s", ifname, strerror(errno)); + goto out; + } + + sock = malloc(sizeof(*sock)); + if (!sock) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + memset(sock, 0, sizeof(*sock)); + sock->ifname = ifname; + + sd = socket(AF_INET6, SOCK_DGRAM, 0); + if (sd < 0) { + log_message(LOG_ERR, "send socket6(): %s", strerror(errno)); + goto out; + } + sock->sockfd = sd; + + // FIXME: get a list of addresses and routes for the interface + + sock->addr.ss_family = AF_INET6; + sock->addr_in.sin_port = htons(MDNS_PORT); + + // make sure that the socket uses only IPv6 + if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "send setsockopt(IPV6_V6ONLY): %s", strerror(errno)); + goto out; + } + + // make sure that the address can be used by other applications + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "send setsockopt6(SO_REUSEADDR): %s", strerror(errno)); + goto out; + } + + // bind to the address + if (bind(sd, (struct sockaddr *)&sock->addr_in6, sizeof(sock->addr_in6)) < 0) { + log_message(LOG_ERR, "send bind6(): %s", strerror(errno)); + goto out; + } + + // bind to the device + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0) { + log_message(LOG_ERR, "send setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno)); + goto out; + } + + // enable loopback in case someone else needs the data + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "send setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); + goto out; + } + + // set the TTL per RFC6762 + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { + log_message(LOG_ERR, "send setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno)); + goto out; + } + + // add membership to receiving sockets + memset(&mreq6, 0, sizeof(mreq6)); + inet_pton(AF_INET6, MDNS_ADDR6, &mreq6.ipv6mr_multiaddr.s6_addr); + mreq6.ipv6mr_interface = ifindex; + + list_for_each_entry(recv_sock, recv_socks, list) { + if (recv_sock->addr.ss_family != AF_INET6) + continue; + + if (setsockopt(recv_sock->sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + &mreq6, sizeof(mreq6)) < 0) { + log_message(LOG_ERR, "recv setsockopt(IPV6_ADD_MEMBERSHIP): %s", strerror(errno)); + goto out; + } + } + + log_message(LOG_INFO, "dev %s %s", sock->ifname, send_sock_to_string(sock)); + return sock; + +out: + free(sock); + close(sd); + return NULL; +} + +static struct send_sock * +create_send_sock4(const char *ifname, struct list_head *recv_socks) { struct send_sock *sock; int sd = -1; struct ifreq ifr; struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; int on = 1; int ttl = 255; // https://datatracker.ietf.org/doc/html/rfc6762#section-11 - struct ipv6_mreq mreq6; struct ip_mreq mreq; struct recv_sock *recv_sock; @@ -330,7 +426,7 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { - log_message(LOG_ERR, "send socket(): %s", strerror(errno)); + log_message(LOG_ERR, "send socket4(): %s", strerror(errno)); goto out; } sock->sockfd = sd; @@ -358,19 +454,19 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { // make sure that the address can be used by other applications if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { - log_message(LOG_ERR, "send setsockopt(SO_REUSEADDR): %s", strerror(errno)); + log_message(LOG_ERR, "send setsockopt4(SO_REUSEADDR): %s", strerror(errno)); goto out; } // bind to the address if (bind(sd, (struct sockaddr *)&sock->addr_in, sizeof(sock->addr_in)) < 0) { - log_message(LOG_ERR, "send bind(): %s", strerror(errno)); + log_message(LOG_ERR, "send bind4(): %s", strerror(errno)); goto out; } // bind to the device if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &sock->addr_in.sin_addr, sizeof(sock->addr_in)) < 0) { - log_message(LOG_ERR, "send ip_multicast_if(): %s", strerror(errno)); + log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_IF): %s", strerror(errno)); goto out; } @@ -390,30 +486,19 @@ create_send_sock(const char *ifname, struct list_head *recv_socks) { memset(&mreq, 0, sizeof(mreq)); mreq.imr_interface.s_addr = if_addr->s_addr; mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR4); - memset(&mreq6, 0, sizeof(mreq6)); - inet_pton(AF_INET6, MDNS_ADDR6, &mreq6.ipv6mr_multiaddr.s6_addr); - mreq6.ipv6mr_interface = if_nametoindex(ifname); list_for_each_entry(recv_sock, recv_socks, list) { - switch (recv_sock->addr.ss_family) { - case AF_INET6: - if (setsockopt(recv_sock->sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, - &mreq6, sizeof(mreq6)) < 0) { - log_message(LOG_ERR, "recv setsockopt6(IPV6_ADD_MEMBERSHIP): %s", strerror(errno)); - goto out; - } - break; - case AF_INET: - if (setsockopt(recv_sock->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - log_message(LOG_ERR, "recv setsockopt4(IP_ADD_MEMBERSHIP): %s", strerror(errno)); - goto out; - } - break; + if (recv_sock->addr.ss_family != AF_INET) + continue; + + if (setsockopt(recv_sock->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s", strerror(errno)); + goto out; } } - log_message(LOG_INFO, "dev %s %s", ifr.ifr_name, send_sock_to_string(sock)); + log_message(LOG_INFO, "dev %s %s", sock->ifname, send_sock_to_string(sock)); return sock; out: @@ -747,7 +832,7 @@ int main(int argc, char *argv[]) { } pfds_count++; - // create receiving sockets + // create receiving IPv6 sockets recv_sock = create_recv_sock6(); if (!recv_sock) { log_message(LOG_ERR, "unable to create server IPv6 socket"); @@ -757,6 +842,7 @@ int main(int argc, char *argv[]) { list_add(&recv_sock->list, &recv_socks); pfds_count++; + // create receiving IPv4 sockets recv_sock = create_recv_sock4(); if (!recv_sock) { log_message(LOG_ERR, "unable to create server IPv4 socket"); @@ -766,15 +852,25 @@ int main(int argc, char *argv[]) { list_add(&recv_sock->list, &recv_socks); pfds_count++; - // create sending sockets + // create sending IPv6 sockets for (int i = optind; i < argc; i++) { - send_sock = create_send_sock(argv[i], &recv_socks); + send_sock = create_send_sock6(argv[i], &recv_socks); if (!send_sock) { - log_message(LOG_ERR, "unable to create socket for interface %s", argv[i]); + log_message(LOG_ERR, "unable to create IPv6 socket for interface %s", argv[i]); r = 1; goto end_main; } + list_add(&send_sock->list, &send_socks); + } + // create sending IPv4 sockets + for (int i = optind; i < argc; i++) { + send_sock = create_send_sock4(argv[i], &recv_socks); + if (!send_sock) { + log_message(LOG_ERR, "unable to create IPv4 socket for interface %s", argv[i]); + r = 1; + goto end_main; + } list_add(&send_sock->list, &send_socks); } From 2e27ccea46fa0e0c1701186ee016fbe7e4398949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 13:42:09 +0100 Subject: [PATCH 22/43] Add IPV6_V6ONLY to create_recv_sock6() --- mdns-repeater.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 3599161..3ccd076 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -226,6 +226,13 @@ create_recv_sock6() { } sock->sockfd = sd; + // make sure that the socket uses only IPv6 + if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "send setsockopt(IPV6_V6ONLY): %s", strerror(errno)); + goto out; + } + + // make sure that the address can be used by other applications if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "recv setsockopt6(SO_REUSEADDR): %s", strerror(errno)); goto out; @@ -233,11 +240,11 @@ create_recv_sock6() { // enable loopback in case someone else needs the data if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)) < 0) { - log_message(LOG_ERR, "recv setsockopt6(IP_MULTICAST_LOOP): %s", strerror(errno)); + log_message(LOG_ERR, "recv setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno)); goto out; } - /* bind to an address */ + // bind to an address memset(&sock->addr, 0, sizeof(sock->addr)); sock->addr_in6.sin6_family = AF_INET6; sock->addr_in6.sin6_port = htons(MDNS_PORT); @@ -273,12 +280,13 @@ create_recv_sock4() { } sock->sockfd = sd; + // make sure that the address can be used by other applications if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %s", strerror(errno)); goto out; } - /* bind to an address */ + // bind to an address memset(&sock->addr, 0, sizeof(sock->addr)); sock->addr_in.sin_family = AF_INET; sock->addr_in.sin_port = htons(MDNS_PORT); From 9d65bcc4cd8dc7264f7fad55a6229037d5ca9f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 13:53:11 +0100 Subject: [PATCH 23/43] Split send_socks into separate v4/v6 lists --- mdns-repeater.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 3ccd076..747d685 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -68,7 +68,8 @@ struct send_sock { }; struct list_head list; /* socket list */ }; -LIST_HEAD(send_socks); +LIST_HEAD(send_socks6); +LIST_HEAD(send_socks4); #define PACKET_SIZE 65536 struct recv_sock { @@ -868,7 +869,7 @@ int main(int argc, char *argv[]) { r = 1; goto end_main; } - list_add(&send_sock->list, &send_socks); + list_add(&send_sock->list, &send_socks6); } // create sending IPv4 sockets @@ -879,7 +880,7 @@ int main(int argc, char *argv[]) { r = 1; goto end_main; } - list_add(&send_sock->list, &send_socks); + list_add(&send_sock->list, &send_socks4); } if (user) { @@ -949,7 +950,6 @@ int main(int argc, char *argv[]) { (struct sockaddr *)&recv_sock->from, &sockaddr_size); if (recvsize < 0) { - log_message(LOG_ERR, "recv(): %s", strerror(errno)); continue; } @@ -960,6 +960,8 @@ int main(int argc, char *argv[]) { recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; + printf("got v4 packet from=%s size=%zd\n", + recv_sock->from_str, recvsize); break; case AF_INET6: if (!inet_ntop(AF_INET6, @@ -975,7 +977,7 @@ int main(int argc, char *argv[]) { continue; } - list_for_each_entry(send_sock, &send_socks, list) { + list_for_each_entry(send_sock, &send_socks4, list) { // make sure packet originated from specified networks if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) { our_net = true; @@ -1010,7 +1012,7 @@ int main(int argc, char *argv[]) { printf("data from=%s size=%zd\n", recv_sock->from_str, recvsize); - list_for_each_entry(send_sock, &send_socks, list) { + list_for_each_entry(send_sock, &send_socks4, list) { ssize_t sentsize; // do not repeat packet back to the same network from which it originated @@ -1042,7 +1044,13 @@ int main(int argc, char *argv[]) { free(recv_sock); } - list_for_each_entry_safe(send_sock, tmp_send_sock, &send_socks, list) { + list_for_each_entry_safe(send_sock, tmp_send_sock, &send_socks6, list) { + list_del(&send_sock->list); + close(send_sock->sockfd); + free(send_sock); + } + + list_for_each_entry_safe(send_sock, tmp_send_sock, &send_socks4, list) { list_del(&send_sock->list); close(send_sock->sockfd); free(send_sock); From 933653075ef5ea2953593e9608ea29fe0d7ff218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 14:10:08 +0100 Subject: [PATCH 24/43] Move packet repetition logic to separate functions --- mdns-repeater.c | 142 ++++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 70 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 747d685..97cef0e 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -76,6 +76,7 @@ struct recv_sock { const char *name; /* name of this socket */ int sockfd; /* socket fd */ char pkt_data[PACKET_SIZE]; /* incoming packet data */ + ssize_t pkt_size; /* incoming packet len */ union { struct sockaddr_storage addr; /* socket addr */ struct sockaddr_in6 addr_in6; /* socket addr (IPv6) */ @@ -814,6 +815,69 @@ static int parse_opts(int argc, char *argv[]) { return optind; } +static void +repeat_packet4(struct recv_sock *recv_sock) { + struct send_sock *send_sock; + bool our_net = false; + ssize_t sentsize; + + list_for_each_entry(send_sock, &send_socks4, list) { + // make sure packet originated from specified networks + if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) { + our_net = true; + } + + // check for loopback + if (recv_sock->from_in.sin_addr.s_addr == send_sock->addr_in.sin_addr.s_addr) + return; + } + + if (!our_net) + return; + + if (!list_empty(&whitelisted_subnets) && + !subnet_match(&recv_sock->from_in, &whitelisted_subnets)) { + if (foreground) + printf("skipping packet from=%s size=%zd (not whitelisted)\n", + recv_sock->from_str, recv_sock->pkt_size); + return; + } + + if (subnet_match(&recv_sock->from_in, &blacklisted_subnets)) { + if (foreground) + printf("skipping packet from=%s size=%zd (blacklisted)\n", + recv_sock->from_str, recv_sock->pkt_size); + return; + } + + if (foreground) + printf("got v4 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); + + list_for_each_entry(send_sock, &send_socks4, list) { + // do not repeat packet back to the same network from which it originated + if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) + continue; + + if (foreground) + printf("repeating data to %s\n", send_sock->ifname); + + // repeat data + sentsize = send_packet(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); + if (sentsize < 0) + log_message(LOG_ERR, "send(): %s", strerror(errno)); + else if (sentsize != recv_sock->pkt_size) + log_message(LOG_ERR, "send_packet size differs: sent=%zd actual=%zd", + recv_sock->pkt_size, sentsize); + } +} + +static void +repeat_packet6(struct recv_sock *recv_sock) +{ + // Not implemented yet + printf("skipping v6 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); +} + int main(int argc, char *argv[]) { pid_t running_pid; int r = 0; @@ -925,9 +989,6 @@ int main(int argc, char *argv[]) { for (int i = 1; i < pfds_used; i++) { socklen_t sockaddr_size; - ssize_t recvsize; - bool discard = false; - bool our_net = false; if (!(pfds[i].revents & POLLIN)) continue; @@ -944,14 +1005,13 @@ int main(int argc, char *argv[]) { continue; sockaddr_size = sizeof(recv_sock->from); - recvsize = recvfrom(recv_sock->sockfd, - recv_sock->pkt_data, - sizeof(recv_sock->pkt_data), 0, - (struct sockaddr *)&recv_sock->from, - &sockaddr_size); - if (recvsize < 0) { + recv_sock->pkt_size = recvfrom(recv_sock->sockfd, + recv_sock->pkt_data, + sizeof(recv_sock->pkt_data), 0, + (struct sockaddr *)&recv_sock->from, + &sockaddr_size); + if (recv_sock->pkt_size < 0) continue; - } switch (recv_sock->from.ss_family) { case AF_INET: @@ -960,8 +1020,7 @@ int main(int argc, char *argv[]) { recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; - printf("got v4 packet from=%s size=%zd\n", - recv_sock->from_str, recvsize); + repeat_packet4(recv_sock); break; case AF_INET6: if (!inet_ntop(AF_INET6, @@ -969,69 +1028,12 @@ int main(int argc, char *argv[]) { recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; - printf("skipping v6 packet from=%s size=%zd\n", - recv_sock->from_str, recvsize); - /* Not supported yet */ + repeat_packet6(recv_sock); continue; default: continue; } - list_for_each_entry(send_sock, &send_socks4, list) { - // make sure packet originated from specified networks - if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) { - our_net = true; - } - - // check for loopback - if (recv_sock->from_in.sin_addr.s_addr == send_sock->addr_in.sin_addr.s_addr) { - discard = true; - break; - } - } - - if (discard || !our_net) - continue; - - if (!list_empty(&whitelisted_subnets) && - !subnet_match(&recv_sock->from_in, &whitelisted_subnets)) { - if (foreground) - printf("skipping packet from=%s size=%zd\n", - recv_sock->from_str, recvsize); - continue; - } - - if (subnet_match(&recv_sock->from_in, &blacklisted_subnets)) { - if (foreground) - printf("skipping packet from=%s size=%zd\n", - recv_sock->from_str, recvsize); - continue; - } - - if (foreground) - printf("data from=%s size=%zd\n", - recv_sock->from_str, recvsize); - - list_for_each_entry(send_sock, &send_socks4, list) { - ssize_t sentsize; - - // do not repeat packet back to the same network from which it originated - if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) - continue; - - if (foreground) - printf("repeating data to %s\n", send_sock->ifname); - - // repeat data - sentsize = send_packet(send_sock->sockfd, recv_sock->pkt_data, 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); - } - } } } From d88f61c317d2d5c068b440a58d8b37bc0c0e148c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 14:25:03 +0100 Subject: [PATCH 25/43] Move packet reception to a separate function --- mdns-repeater.c | 76 +++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 97cef0e..8331e4b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -878,6 +878,39 @@ repeat_packet6(struct recv_sock *recv_sock) printf("skipping v6 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); } +static void +recv_packet(struct recv_sock *recv_sock) +{ + socklen_t sockaddr_size = sizeof(recv_sock->from); + + recv_sock->pkt_size = recvfrom(recv_sock->sockfd, + recv_sock->pkt_data, + sizeof(recv_sock->pkt_data), 0, + (struct sockaddr *)&recv_sock->from, + &sockaddr_size); + if (recv_sock->pkt_size < 0) + return; + + switch (recv_sock->from.ss_family) { + case AF_INET: + if (!inet_ntop(AF_INET, + &recv_sock->from_in.sin_addr, + recv_sock->from_str, + sizeof(recv_sock->from_str))) + recv_sock->from_str[0] = '\0'; + repeat_packet4(recv_sock); + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, + &recv_sock->from_in6.sin6_addr, + recv_sock->from_str, + sizeof(recv_sock->from_str))) + recv_sock->from_str[0] = '\0'; + repeat_packet6(recv_sock); + break; + } +} + int main(int argc, char *argv[]) { pid_t running_pid; int r = 0; @@ -988,52 +1021,15 @@ int main(int argc, char *argv[]) { break; for (int i = 1; i < pfds_used; i++) { - socklen_t sockaddr_size; - if (!(pfds[i].revents & POLLIN)) continue; - recv_sock = NULL; - list_for_each_entry(tmp_recv_sock, &recv_socks, list) { - if (tmp_recv_sock->sockfd == pfds[i].fd) { - recv_sock = tmp_recv_sock; + list_for_each_entry(recv_sock, &recv_socks, list) { + if (recv_sock->sockfd == pfds[i].fd) { + recv_packet(recv_sock); break; } } - - if (!recv_sock) - continue; - - sockaddr_size = sizeof(recv_sock->from); - recv_sock->pkt_size = recvfrom(recv_sock->sockfd, - recv_sock->pkt_data, - sizeof(recv_sock->pkt_data), 0, - (struct sockaddr *)&recv_sock->from, - &sockaddr_size); - if (recv_sock->pkt_size < 0) - continue; - - switch (recv_sock->from.ss_family) { - case AF_INET: - if (!inet_ntop(AF_INET, - &recv_sock->from_in.sin_addr, - recv_sock->from_str, - sizeof(recv_sock->from_str))) - recv_sock->from_str[0] = '\0'; - repeat_packet4(recv_sock); - break; - case AF_INET6: - if (!inet_ntop(AF_INET6, - &recv_sock->from_in6.sin6_addr, - recv_sock->from_str, - sizeof(recv_sock->from_str))) - recv_sock->from_str[0] = '\0'; - repeat_packet6(recv_sock); - continue; - default: - continue; - } - } } From 14282d8da2e1ad4d9d97e764c691437b3a8dd873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 15:49:08 +0100 Subject: [PATCH 26/43] Reorganize structs a bit This is in preparation for later patches adding multiple net/mask/addr triplets to send_sock() for IPv6. --- mdns-repeater.c | 159 +++++++++++++++++++++--------------------------- 1 file changed, 71 insertions(+), 88 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 8331e4b..33eb738 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -50,22 +50,23 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) +union sockaddr_u { + struct sockaddr_storage ss; /* socket addr */ + struct sockaddr_in6 sin6; /* socket addr (IPv6) */ + struct sockaddr_in sin; /* socket addr (IPv4) */ +}; + +union in_addr_u { + struct in6_addr in6; /* address (IPv6) */ + struct in_addr in; /* address (IPv4) */ +}; + struct send_sock { const char *ifname; /* interface name */ int sockfd; /* socket fd */ - union { - struct sockaddr_storage addr; /* socket addr */ - struct sockaddr_in6 addr_in6; /* socket addr (IPv6) */ - struct sockaddr_in addr_in; /* socket addr (IPv4) */ - }; - union { - struct in6_addr mask_in6; /* socket mask (IPv6) */ - struct in_addr mask_in; /* socket mask (IPv4) */ - }; - union { - struct in6_addr net_in6; /* socket net (IPv6) */ - struct in_addr net_in; /* socket net (IPv4) */ - }; + union sockaddr_u addr; /* socket addr */ + union in_addr_u mask; /* socket mask */ + union in_addr_u net; /* socket net */ struct list_head list; /* socket list */ }; LIST_HEAD(send_socks6); @@ -77,35 +78,17 @@ struct recv_sock { int sockfd; /* socket fd */ char pkt_data[PACKET_SIZE]; /* incoming packet data */ ssize_t pkt_size; /* incoming packet len */ - union { - struct sockaddr_storage addr; /* socket addr */ - struct sockaddr_in6 addr_in6; /* socket addr (IPv6) */ - struct sockaddr_in addr_in; /* socket addr (IPv4) */ - }; - union { - struct sockaddr_storage from; /* sender addr */ - struct sockaddr_in6 from_in6; /* sender addr (IPv6) */ - struct sockaddr_in from_in; /* sender addr (IPv4) */ - }; + union sockaddr_u addr; /* socket addr */ + union sockaddr_u from; /* sender addr */ char from_str[INET6_ADDRSTRLEN]; /* sender addr (str) */ struct list_head list; /* socket list */ }; LIST_HEAD(recv_socks); struct subnet { - union { - struct sockaddr_storage addr; /* subnet addr */ - struct sockaddr_in6 addr_in6; /* subnet addr (IPv6) */ - struct sockaddr_in addr_in; /* subnet addr (IPv4) */ - }; - union { - struct in6_addr mask_in6; /* subnet mask (IPv6) */ - struct in_addr mask_in; /* subnet mask (IPv4) */ - }; - union { - struct in6_addr net_in6; /* subnet net (IPv6) */ - struct in_addr net_in; /* subnet net (IPv4) */ - }; + union sockaddr_u addr; /* subnet addr */ + union in_addr_u mask; /* subnet mask */ + union in_addr_u net; /* subnet net */ struct list_head list; /* subnet list */ }; LIST_HEAD(blacklisted_subnets); @@ -179,15 +162,15 @@ addr4_mask_net_to_string(struct sockaddr_in *addr, static char * send_sock_to_string(struct send_sock *sock) { - switch (sock->addr.ss_family) { + switch (sock->addr.ss.ss_family) { case AF_INET6: - return addr6_mask_net_to_string(&sock->addr_in6, - &sock->mask_in6, - &sock->net_in6); + return addr6_mask_net_to_string(&sock->addr.sin6, + &sock->mask.in6, + &sock->net.in6); case AF_INET: - return addr4_mask_net_to_string(&sock->addr_in, - &sock->mask_in, - &sock->net_in); + return addr4_mask_net_to_string(&sock->addr.sin, + &sock->mask.in, + &sock->net.in); default: return "ERROR"; } @@ -195,15 +178,15 @@ send_sock_to_string(struct send_sock *sock) { static char * subnet_to_string(struct subnet *subnet) { - switch (subnet->addr.ss_family) { + switch (subnet->addr.ss.ss_family) { case AF_INET6: - return addr6_mask_net_to_string(&subnet->addr_in6, - &subnet->mask_in6, - &subnet->net_in6); + return addr6_mask_net_to_string(&subnet->addr.sin6, + &subnet->mask.in6, + &subnet->net.in6); case AF_INET: - return addr4_mask_net_to_string(&subnet->addr_in, - &subnet->mask_in, - &subnet->net_in); + return addr4_mask_net_to_string(&subnet->addr.sin, + &subnet->mask.in, + &subnet->net.in); default: return "ERROR"; } @@ -248,10 +231,10 @@ create_recv_sock6() { // bind to an address memset(&sock->addr, 0, sizeof(sock->addr)); - sock->addr_in6.sin6_family = AF_INET6; - sock->addr_in6.sin6_port = htons(MDNS_PORT); - sock->addr_in6.sin6_addr = in6addr_any; - if (bind(sd, (struct sockaddr *)&sock->addr_in6, sizeof(sock->addr_in6)) < 0) { + sock->addr.sin6.sin6_family = AF_INET6; + sock->addr.sin6.sin6_port = htons(MDNS_PORT); + sock->addr.sin6.sin6_addr = in6addr_any; + if (bind(sd, (struct sockaddr *)&sock->addr.sin6, sizeof(sock->addr.sin6)) < 0) { log_message(LOG_ERR, "recv bind6(): %s", strerror(errno)); goto out; } @@ -290,10 +273,10 @@ create_recv_sock4() { // bind to an address memset(&sock->addr, 0, sizeof(sock->addr)); - sock->addr_in.sin_family = AF_INET; - sock->addr_in.sin_port = htons(MDNS_PORT); - sock->addr_in.sin_addr.s_addr = htonl(INADDR_ANY); - if (bind(sd, (struct sockaddr *)&sock->addr_in, sizeof(sock->addr_in)) < 0) { + sock->addr.sin.sin_family = AF_INET; + sock->addr.sin.sin_port = htons(MDNS_PORT); + sock->addr.sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(sd, (struct sockaddr *)&sock->addr.sin, sizeof(sock->addr.sin)) < 0) { log_message(LOG_ERR, "recv bind(): %s", strerror(errno)); goto out; } @@ -351,8 +334,8 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { // FIXME: get a list of addresses and routes for the interface - sock->addr.ss_family = AF_INET6; - sock->addr_in.sin_port = htons(MDNS_PORT); + sock->addr.ss.ss_family = AF_INET6; + sock->addr.sin.sin_port = htons(MDNS_PORT); // make sure that the socket uses only IPv6 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { @@ -367,7 +350,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } // bind to the address - if (bind(sd, (struct sockaddr *)&sock->addr_in6, sizeof(sock->addr_in6)) < 0) { + if (bind(sd, (struct sockaddr *)&sock->addr.sin6, sizeof(sock->addr.sin6)) < 0) { log_message(LOG_ERR, "send bind6(): %s", strerror(errno)); goto out; } @@ -396,7 +379,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { mreq6.ipv6mr_interface = ifindex; list_for_each_entry(recv_sock, recv_socks, list) { - if (recv_sock->addr.ss_family != AF_INET6) + if (recv_sock->addr.ss.ss_family != AF_INET6) continue; if (setsockopt(recv_sock->sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, @@ -448,19 +431,19 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { log_message(LOG_ERR, "ioctl(SIOCGIFNETMASK): %s", strerror(errno)); goto out; } - memcpy(&sock->mask_in, if_addr, sizeof(*if_addr)); + memcpy(&sock->mask.in, if_addr, sizeof(*if_addr)); // ...and interface address if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) { log_message(LOG_ERR, "ioctl(SIOCGIFADDR): %s", strerror(errno)); goto out; } - memcpy(&sock->addr_in.sin_addr, if_addr, sizeof(*if_addr)); - sock->addr.ss_family = AF_INET; - sock->addr_in.sin_port = htons(MDNS_PORT); + memcpy(&sock->addr.sin.sin_addr, if_addr, sizeof(*if_addr)); + sock->addr.ss.ss_family = AF_INET; + sock->addr.sin.sin_port = htons(MDNS_PORT); // ...then compute the network - sock->net_in.s_addr = sock->addr_in.sin_addr.s_addr & sock->mask_in.s_addr; + sock->net.in.s_addr = sock->addr.sin.sin_addr.s_addr & sock->mask.in.s_addr; // make sure that the address can be used by other applications if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { @@ -469,13 +452,13 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { } // bind to the address - if (bind(sd, (struct sockaddr *)&sock->addr_in, sizeof(sock->addr_in)) < 0) { + if (bind(sd, (struct sockaddr *)&sock->addr.sin, sizeof(sock->addr.sin)) < 0) { log_message(LOG_ERR, "send bind4(): %s", strerror(errno)); goto out; } // bind to the device - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &sock->addr_in.sin_addr, sizeof(sock->addr_in)) < 0) { + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &sock->addr.sin.sin_addr, sizeof(sock->addr.sin)) < 0) { log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_IF): %s", strerror(errno)); goto out; } @@ -498,7 +481,7 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR4); list_for_each_entry(recv_sock, recv_socks, list) { - if (recv_sock->addr.ss_family != AF_INET) + if (recv_sock->addr.ss.ss_family != AF_INET) continue; if (setsockopt(recv_sock->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, @@ -684,8 +667,8 @@ parse_subnet(const char *input) { goto out; } - addr_in6 = &subnet->addr_in6.sin6_addr; - addr_in = &subnet->addr_in.sin_addr; + addr_in6 = &subnet->addr.sin6.sin6_addr; + addr_in = &subnet->addr.sin.sin_addr; // First, try parsing an IPv6 address if (inet_pton(AF_INET6, addr_str, addr_in6) == 1) { @@ -697,11 +680,11 @@ parse_subnet(const char *input) { for (int i = 0; i < sizeof(addr_in6->s6_addr); i++) { uint8_t mask = 0xff << (8 - MIN(prefix_len, 8)); prefix_len -= MIN(prefix_len, 8); - subnet->mask_in6.s6_addr[i] = mask; - subnet->net_in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; + subnet->mask.in6.s6_addr[i] = mask; + subnet->net.in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; } - subnet->addr.ss_family = AF_INET6; + subnet->addr.ss.ss_family = AF_INET6; // Second, try parsing an IPv4 address } else if (inet_pton(AF_INET, addr_str, addr_in) == 1) { @@ -710,9 +693,9 @@ parse_subnet(const char *input) { goto out; } - subnet->mask_in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); - subnet->net_in.s_addr = addr_in->s_addr & subnet->mask_in.s_addr; - subnet->addr.ss_family = AF_INET; + subnet->mask.in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); + subnet->net.in.s_addr = addr_in->s_addr & subnet->mask.in.s_addr; + subnet->addr.ss.ss_family = AF_INET; // Give up } else { @@ -735,10 +718,10 @@ subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) struct subnet *subnet; list_for_each_entry(subnet, subnets, list) { - if (subnet->addr.ss_family != AF_INET) + if (subnet->addr.ss.ss_family != AF_INET) continue; - if ((fromaddr->sin_addr.s_addr & subnet->mask_in.s_addr) == subnet->net_in.s_addr) + if ((fromaddr->sin_addr.s_addr & subnet->mask.in.s_addr) == subnet->net.in.s_addr) return true; } @@ -823,12 +806,12 @@ repeat_packet4(struct recv_sock *recv_sock) { list_for_each_entry(send_sock, &send_socks4, list) { // make sure packet originated from specified networks - if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) { + if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->mask.in.s_addr) == send_sock->net.in.s_addr) { our_net = true; } // check for loopback - if (recv_sock->from_in.sin_addr.s_addr == send_sock->addr_in.sin_addr.s_addr) + if (recv_sock->from.sin.sin_addr.s_addr == send_sock->addr.sin.sin_addr.s_addr) return; } @@ -836,14 +819,14 @@ repeat_packet4(struct recv_sock *recv_sock) { return; if (!list_empty(&whitelisted_subnets) && - !subnet_match(&recv_sock->from_in, &whitelisted_subnets)) { + !subnet_match(&recv_sock->from.sin, &whitelisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (not whitelisted)\n", recv_sock->from_str, recv_sock->pkt_size); return; } - if (subnet_match(&recv_sock->from_in, &blacklisted_subnets)) { + if (subnet_match(&recv_sock->from.sin, &blacklisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (blacklisted)\n", recv_sock->from_str, recv_sock->pkt_size); @@ -855,7 +838,7 @@ repeat_packet4(struct recv_sock *recv_sock) { list_for_each_entry(send_sock, &send_socks4, list) { // do not repeat packet back to the same network from which it originated - if ((recv_sock->from_in.sin_addr.s_addr & send_sock->mask_in.s_addr) == send_sock->net_in.s_addr) + if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->mask.in.s_addr) == send_sock->net.in.s_addr) continue; if (foreground) @@ -891,10 +874,10 @@ recv_packet(struct recv_sock *recv_sock) if (recv_sock->pkt_size < 0) return; - switch (recv_sock->from.ss_family) { + switch (recv_sock->from.ss.ss_family) { case AF_INET: if (!inet_ntop(AF_INET, - &recv_sock->from_in.sin_addr, + &recv_sock->from.sin.sin_addr, recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; @@ -902,7 +885,7 @@ recv_packet(struct recv_sock *recv_sock) break; case AF_INET6: if (!inet_ntop(AF_INET6, - &recv_sock->from_in6.sin6_addr, + &recv_sock->from.sin6.sin6_addr, recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; From 32b08bca9493792802b5e6355291220354942828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 16:12:14 +0100 Subject: [PATCH 27/43] Create a struct for addr/mask/net tuples --- mdns-repeater.c | 113 +++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 63 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 33eb738..933c16b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -61,12 +61,16 @@ union in_addr_u { struct in_addr in; /* address (IPv4) */ }; -struct send_sock { - const char *ifname; /* interface name */ - int sockfd; /* socket fd */ +struct addr_mask { union sockaddr_u addr; /* socket addr */ union in_addr_u mask; /* socket mask */ union in_addr_u net; /* socket net */ +}; + +struct send_sock { + const char *ifname; /* interface name */ + int sockfd; /* socket fd */ + struct addr_mask am; /* socket addr/mask/net */ struct list_head list; /* socket list */ }; LIST_HEAD(send_socks6); @@ -86,9 +90,7 @@ struct recv_sock { LIST_HEAD(recv_socks); struct subnet { - union sockaddr_u addr; /* subnet addr */ - union in_addr_u mask; /* subnet mask */ - union in_addr_u net; /* subnet net */ + struct addr_mask am; /* subnet addr/mask/net */ struct list_head list; /* subnet list */ }; LIST_HEAD(blacklisted_subnets); @@ -121,9 +123,9 @@ void log_message(int loglevel, char *fmt_str, ...) { } static char * -addr6_mask_net_to_string(struct sockaddr_in6 *addr, - struct in6_addr *mask, - struct in6_addr *net) { +addr6_mask_to_string(struct sockaddr_in6 *addr, + struct in6_addr *mask, + struct in6_addr *net) { const char *fmt = "addr %s mask %s net %s"; /* sizeof(fmt) = some extra bytes, and it's compile-time constant */ static char msg[sizeof(fmt) + 3 * INET6_ADDRSTRLEN]; @@ -141,9 +143,9 @@ addr6_mask_net_to_string(struct sockaddr_in6 *addr, } static char * -addr4_mask_net_to_string(struct sockaddr_in *addr, - struct in_addr *mask, - struct in_addr *net) { +addr4_mask_to_string(struct sockaddr_in *addr, + struct in_addr *mask, + struct in_addr *net) { const char *fmt = "addr %s mask %s net %s"; /* sizeof(fmt) = some extra bytes, and it's compile-time constant */ static char msg[sizeof(fmt) + 3 * INET_ADDRSTRLEN]; @@ -161,32 +163,17 @@ addr4_mask_net_to_string(struct sockaddr_in *addr, } static char * -send_sock_to_string(struct send_sock *sock) { - switch (sock->addr.ss.ss_family) { - case AF_INET6: - return addr6_mask_net_to_string(&sock->addr.sin6, - &sock->mask.in6, - &sock->net.in6); - case AF_INET: - return addr4_mask_net_to_string(&sock->addr.sin, - &sock->mask.in, - &sock->net.in); - default: - return "ERROR"; - } -} - -static char * -subnet_to_string(struct subnet *subnet) { - switch (subnet->addr.ss.ss_family) { +addr_mask_to_string(struct addr_mask *nm) +{ + switch (nm->addr.ss.ss_family) { case AF_INET6: - return addr6_mask_net_to_string(&subnet->addr.sin6, - &subnet->mask.in6, - &subnet->net.in6); + return addr6_mask_to_string(&nm->addr.sin6, + &nm->mask.in6, + &nm->net.in6); case AF_INET: - return addr4_mask_net_to_string(&subnet->addr.sin, - &subnet->mask.in, - &subnet->net.in); + return addr4_mask_to_string(&nm->addr.sin, + &nm->mask.in, + &nm->net.in); default: return "ERROR"; } @@ -334,8 +321,8 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { // FIXME: get a list of addresses and routes for the interface - sock->addr.ss.ss_family = AF_INET6; - sock->addr.sin.sin_port = htons(MDNS_PORT); + sock->am.addr.ss.ss_family = AF_INET6; + sock->am.addr.sin.sin_port = htons(MDNS_PORT); // make sure that the socket uses only IPv6 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { @@ -350,7 +337,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } // bind to the address - if (bind(sd, (struct sockaddr *)&sock->addr.sin6, sizeof(sock->addr.sin6)) < 0) { + if (bind(sd, (struct sockaddr *)&sock->am.addr.sin6, sizeof(sock->am.addr.sin6)) < 0) { log_message(LOG_ERR, "send bind6(): %s", strerror(errno)); goto out; } @@ -389,7 +376,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } } - log_message(LOG_INFO, "dev %s %s", sock->ifname, send_sock_to_string(sock)); + log_message(LOG_INFO, "dev %s %s", sock->ifname, addr_mask_to_string(&sock->am)); return sock; out: @@ -431,19 +418,19 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { log_message(LOG_ERR, "ioctl(SIOCGIFNETMASK): %s", strerror(errno)); goto out; } - memcpy(&sock->mask.in, if_addr, sizeof(*if_addr)); + memcpy(&sock->am.mask.in, if_addr, sizeof(*if_addr)); // ...and interface address if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) { log_message(LOG_ERR, "ioctl(SIOCGIFADDR): %s", strerror(errno)); goto out; } - memcpy(&sock->addr.sin.sin_addr, if_addr, sizeof(*if_addr)); - sock->addr.ss.ss_family = AF_INET; - sock->addr.sin.sin_port = htons(MDNS_PORT); + memcpy(&sock->am.addr.sin.sin_addr, if_addr, sizeof(*if_addr)); + sock->am.addr.ss.ss_family = AF_INET; + sock->am.addr.sin.sin_port = htons(MDNS_PORT); // ...then compute the network - sock->net.in.s_addr = sock->addr.sin.sin_addr.s_addr & sock->mask.in.s_addr; + sock->am.net.in.s_addr = sock->am.addr.sin.sin_addr.s_addr & sock->am.mask.in.s_addr; // make sure that the address can be used by other applications if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { @@ -452,13 +439,13 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { } // bind to the address - if (bind(sd, (struct sockaddr *)&sock->addr.sin, sizeof(sock->addr.sin)) < 0) { + if (bind(sd, (struct sockaddr *)&sock->am.addr.sin, sizeof(sock->am.addr.sin)) < 0) { log_message(LOG_ERR, "send bind4(): %s", strerror(errno)); goto out; } // bind to the device - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &sock->addr.sin.sin_addr, sizeof(sock->addr.sin)) < 0) { + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &sock->am.addr.sin.sin_addr, sizeof(sock->am.addr.sin)) < 0) { log_message(LOG_ERR, "send setsockopt(IP_MULTICAST_IF): %s", strerror(errno)); goto out; } @@ -491,7 +478,7 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { } } - log_message(LOG_INFO, "dev %s %s", sock->ifname, send_sock_to_string(sock)); + log_message(LOG_INFO, "dev %s %s", sock->ifname, addr_mask_to_string(&sock->am)); return sock; out: @@ -667,8 +654,8 @@ parse_subnet(const char *input) { goto out; } - addr_in6 = &subnet->addr.sin6.sin6_addr; - addr_in = &subnet->addr.sin.sin_addr; + addr_in6 = &subnet->am.addr.sin6.sin6_addr; + addr_in = &subnet->am.addr.sin.sin_addr; // First, try parsing an IPv6 address if (inet_pton(AF_INET6, addr_str, addr_in6) == 1) { @@ -680,11 +667,11 @@ parse_subnet(const char *input) { for (int i = 0; i < sizeof(addr_in6->s6_addr); i++) { uint8_t mask = 0xff << (8 - MIN(prefix_len, 8)); prefix_len -= MIN(prefix_len, 8); - subnet->mask.in6.s6_addr[i] = mask; - subnet->net.in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; + subnet->am.mask.in6.s6_addr[i] = mask; + subnet->am.net.in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; } - subnet->addr.ss.ss_family = AF_INET6; + subnet->am.addr.ss.ss_family = AF_INET6; // Second, try parsing an IPv4 address } else if (inet_pton(AF_INET, addr_str, addr_in) == 1) { @@ -693,9 +680,9 @@ parse_subnet(const char *input) { goto out; } - subnet->mask.in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); - subnet->net.in.s_addr = addr_in->s_addr & subnet->mask.in.s_addr; - subnet->addr.ss.ss_family = AF_INET; + subnet->am.mask.in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); + subnet->am.net.in.s_addr = addr_in->s_addr & subnet->am.mask.in.s_addr; + subnet->am.addr.ss.ss_family = AF_INET; // Give up } else { @@ -718,10 +705,10 @@ subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) struct subnet *subnet; list_for_each_entry(subnet, subnets, list) { - if (subnet->addr.ss.ss_family != AF_INET) + if (subnet->am.addr.ss.ss_family != AF_INET) continue; - if ((fromaddr->sin_addr.s_addr & subnet->mask.in.s_addr) == subnet->net.in.s_addr) + if ((fromaddr->sin_addr.s_addr & subnet->am.mask.in.s_addr) == subnet->am.net.in.s_addr) return true; } @@ -755,7 +742,7 @@ static int parse_opts(int argc, char *argv[]) { if (!subnet) exit(2); list_add(&subnet->list, &blacklisted_subnets); - log_message(LOG_INFO, "blacklist %s", subnet_to_string(subnet)); + log_message(LOG_INFO, "blacklist %s", addr_mask_to_string(&subnet->am)); break; case 'w': @@ -763,7 +750,7 @@ static int parse_opts(int argc, char *argv[]) { if (!subnet) exit(2); list_add(&subnet->list, &whitelisted_subnets); - log_message(LOG_INFO, "whitelist %s", subnet_to_string(subnet)); + log_message(LOG_INFO, "whitelist %s", addr_mask_to_string(&subnet->am)); break; case '?': @@ -806,12 +793,12 @@ repeat_packet4(struct recv_sock *recv_sock) { list_for_each_entry(send_sock, &send_socks4, list) { // make sure packet originated from specified networks - if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->mask.in.s_addr) == send_sock->net.in.s_addr) { + if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->am.mask.in.s_addr) == send_sock->am.net.in.s_addr) { our_net = true; } // check for loopback - if (recv_sock->from.sin.sin_addr.s_addr == send_sock->addr.sin.sin_addr.s_addr) + if (recv_sock->from.sin.sin_addr.s_addr == send_sock->am.addr.sin.sin_addr.s_addr) return; } @@ -838,7 +825,7 @@ repeat_packet4(struct recv_sock *recv_sock) { list_for_each_entry(send_sock, &send_socks4, list) { // do not repeat packet back to the same network from which it originated - if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->mask.in.s_addr) == send_sock->net.in.s_addr) + if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->am.mask.in.s_addr) == send_sock->am.net.in.s_addr) continue; if (foreground) From 8bcbb1c23af8ee7c7c0930fb0b034404dc39bc5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 16:23:10 +0100 Subject: [PATCH 28/43] Remove struct subnet, use struct addr_mask instead --- mdns-repeater.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 933c16b..1bfa446 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -65,7 +65,10 @@ struct addr_mask { union sockaddr_u addr; /* socket addr */ union in_addr_u mask; /* socket mask */ union in_addr_u net; /* socket net */ + struct list_head list; /* addr_mask list */ }; +LIST_HEAD(blacklisted_subnets); +LIST_HEAD(whitelisted_subnets); struct send_sock { const char *ifname; /* interface name */ @@ -89,13 +92,6 @@ struct recv_sock { }; LIST_HEAD(recv_socks); -struct subnet { - struct addr_mask am; /* subnet addr/mask/net */ - struct list_head list; /* subnet list */ -}; -LIST_HEAD(blacklisted_subnets); -LIST_HEAD(whitelisted_subnets); - bool foreground = false; int signal_pipe_fds[2]; @@ -618,9 +614,9 @@ static void show_help(const char *progname) { * 192.168.0.12/24 * 2001:db8::/32 */ -static struct subnet * +static struct addr_mask * parse_subnet(const char *input) { - struct subnet *subnet; + struct addr_mask *subnet; char *addr_str = NULL; char *delim; struct in6_addr *addr_in6; @@ -654,8 +650,8 @@ parse_subnet(const char *input) { goto out; } - addr_in6 = &subnet->am.addr.sin6.sin6_addr; - addr_in = &subnet->am.addr.sin.sin_addr; + addr_in6 = &subnet->addr.sin6.sin6_addr; + addr_in = &subnet->addr.sin.sin_addr; // First, try parsing an IPv6 address if (inet_pton(AF_INET6, addr_str, addr_in6) == 1) { @@ -667,11 +663,11 @@ parse_subnet(const char *input) { for (int i = 0; i < sizeof(addr_in6->s6_addr); i++) { uint8_t mask = 0xff << (8 - MIN(prefix_len, 8)); prefix_len -= MIN(prefix_len, 8); - subnet->am.mask.in6.s6_addr[i] = mask; - subnet->am.net.in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; + subnet->mask.in6.s6_addr[i] = mask; + subnet->net.in6.s6_addr[i] = addr_in6->s6_addr[i] & mask; } - subnet->am.addr.ss.ss_family = AF_INET6; + subnet->addr.ss.ss_family = AF_INET6; // Second, try parsing an IPv4 address } else if (inet_pton(AF_INET, addr_str, addr_in) == 1) { @@ -680,9 +676,9 @@ parse_subnet(const char *input) { goto out; } - subnet->am.mask.in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); - subnet->am.net.in.s_addr = addr_in->s_addr & subnet->am.mask.in.s_addr; - subnet->am.addr.ss.ss_family = AF_INET; + subnet->mask.in.s_addr = ntohl(0xFFFFFFFF << (32 - prefix_len)); + subnet->net.in.s_addr = addr_in->s_addr & subnet->mask.in.s_addr; + subnet->addr.ss.ss_family = AF_INET; // Give up } else { @@ -702,13 +698,13 @@ parse_subnet(const char *input) { static bool subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) { - struct subnet *subnet; + struct addr_mask *subnet; list_for_each_entry(subnet, subnets, list) { - if (subnet->am.addr.ss.ss_family != AF_INET) + if (subnet->addr.ss.ss_family != AF_INET) continue; - if ((fromaddr->sin_addr.s_addr & subnet->am.mask.in.s_addr) == subnet->am.net.in.s_addr) + if ((fromaddr->sin_addr.s_addr & subnet->mask.in.s_addr) == subnet->net.in.s_addr) return true; } @@ -718,7 +714,7 @@ subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) static int parse_opts(int argc, char *argv[]) { int c; bool help = false; - struct subnet *subnet; + struct addr_mask *subnet; while ((c = getopt(argc, argv, "hfp:b:w:u:")) != -1) { switch (c) { @@ -742,7 +738,7 @@ static int parse_opts(int argc, char *argv[]) { if (!subnet) exit(2); list_add(&subnet->list, &blacklisted_subnets); - log_message(LOG_INFO, "blacklist %s", addr_mask_to_string(&subnet->am)); + log_message(LOG_INFO, "blacklist %s", addr_mask_to_string(subnet)); break; case 'w': @@ -750,7 +746,7 @@ static int parse_opts(int argc, char *argv[]) { if (!subnet) exit(2); list_add(&subnet->list, &whitelisted_subnets); - log_message(LOG_INFO, "whitelist %s", addr_mask_to_string(&subnet->am)); + log_message(LOG_INFO, "whitelist %s", addr_mask_to_string(subnet)); break; case '?': @@ -886,7 +882,7 @@ int main(int argc, char *argv[]) { int r = 0; struct send_sock *send_sock, *tmp_send_sock; struct recv_sock *recv_sock, *tmp_recv_sock; - struct subnet *subnet, *tmp_subnet; + struct addr_mask *subnet, *tmp_subnet; int pfds_count = 0; int pfds_used = 0; struct pollfd *pfds; From d410c0f4a9ed38842773a20c581960150d05546c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 16:43:22 +0100 Subject: [PATCH 29/43] Create separate send_sock structs for IPv4 and IPv6 --- mdns-repeater.c | 50 ++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 1bfa446..c1995f4 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -70,13 +70,20 @@ struct addr_mask { LIST_HEAD(blacklisted_subnets); LIST_HEAD(whitelisted_subnets); -struct send_sock { +struct send_sock6 { const char *ifname; /* interface name */ int sockfd; /* socket fd */ struct addr_mask am; /* socket addr/mask/net */ struct list_head list; /* socket list */ }; LIST_HEAD(send_socks6); + +struct send_sock4 { + const char *ifname; /* interface name */ + int sockfd; /* socket fd */ + struct addr_mask am; /* socket addr/mask/net */ + struct list_head list; /* socket list */ +}; LIST_HEAD(send_socks4); #define PACKET_SIZE 65536 @@ -284,9 +291,9 @@ create_recv_sock4() { return NULL; } -static struct send_sock * +static struct send_sock6 * create_send_sock6(const char *ifname, struct list_head *recv_socks) { - struct send_sock *sock = NULL; + struct send_sock6 *sock = NULL; int sd = -1; int ifindex; int on = 1; @@ -381,9 +388,9 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { return NULL; } -static struct send_sock * +static struct send_sock4 * create_send_sock4(const char *ifname, struct list_head *recv_socks) { - struct send_sock *sock; + struct send_sock4 *sock; int sd = -1; struct ifreq ifr; struct in_addr *if_addr = &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; @@ -783,7 +790,7 @@ static int parse_opts(int argc, char *argv[]) { static void repeat_packet4(struct recv_sock *recv_sock) { - struct send_sock *send_sock; + struct send_sock4 *send_sock; bool our_net = false; ssize_t sentsize; @@ -880,7 +887,8 @@ recv_packet(struct recv_sock *recv_sock) int main(int argc, char *argv[]) { pid_t running_pid; int r = 0; - struct send_sock *send_sock, *tmp_send_sock; + struct send_sock6 *send_sock6, *tmp_send_sock6; + struct send_sock4 *send_sock4, *tmp_send_sock4; struct recv_sock *recv_sock, *tmp_recv_sock; struct addr_mask *subnet, *tmp_subnet; int pfds_count = 0; @@ -926,24 +934,24 @@ int main(int argc, char *argv[]) { // create sending IPv6 sockets for (int i = optind; i < argc; i++) { - send_sock = create_send_sock6(argv[i], &recv_socks); - if (!send_sock) { + send_sock6 = create_send_sock6(argv[i], &recv_socks); + if (!send_sock6) { log_message(LOG_ERR, "unable to create IPv6 socket for interface %s", argv[i]); r = 1; goto end_main; } - list_add(&send_sock->list, &send_socks6); + list_add(&send_sock6->list, &send_socks6); } // create sending IPv4 sockets for (int i = optind; i < argc; i++) { - send_sock = create_send_sock4(argv[i], &recv_socks); - if (!send_sock) { + send_sock4 = create_send_sock4(argv[i], &recv_socks); + if (!send_sock4) { log_message(LOG_ERR, "unable to create IPv4 socket for interface %s", argv[i]); r = 1; goto end_main; } - list_add(&send_sock->list, &send_socks4); + list_add(&send_sock4->list, &send_socks4); } if (user) { @@ -1008,16 +1016,16 @@ int main(int argc, char *argv[]) { free(recv_sock); } - list_for_each_entry_safe(send_sock, tmp_send_sock, &send_socks6, list) { - list_del(&send_sock->list); - close(send_sock->sockfd); - free(send_sock); + list_for_each_entry_safe(send_sock6, tmp_send_sock6, &send_socks6, list) { + list_del(&send_sock6->list); + close(send_sock6->sockfd); + free(send_sock6); } - list_for_each_entry_safe(send_sock, tmp_send_sock, &send_socks4, list) { - list_del(&send_sock->list); - close(send_sock->sockfd); - free(send_sock); + list_for_each_entry_safe(send_sock4, tmp_send_sock4, &send_socks4, list) { + list_del(&send_sock4->list); + close(send_sock4->sockfd); + free(send_sock4); } list_for_each_entry_safe(subnet, tmp_subnet, &blacklisted_subnets, list) { From 2d413115151e331270d2fc8fa9549096e4043c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 16:52:25 +0100 Subject: [PATCH 30/43] Change struct send_sock6 to include a list of nets --- mdns-repeater.c | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index c1995f4..97130f8 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -73,7 +73,7 @@ LIST_HEAD(whitelisted_subnets); struct send_sock6 { const char *ifname; /* interface name */ int sockfd; /* socket fd */ - struct addr_mask am; /* socket addr/mask/net */ + struct list_head ams; /* socket addr/mask/nets*/ struct list_head list; /* socket list */ }; LIST_HEAD(send_socks6); @@ -294,6 +294,7 @@ create_recv_sock4() { static struct send_sock6 * create_send_sock6(const char *ifname, struct list_head *recv_socks) { struct send_sock6 *sock = NULL; + struct addr_mask *am, *tmp_am; int sd = -1; int ifindex; int on = 1; @@ -313,8 +314,18 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { goto out; } memset(sock, 0, sizeof(*sock)); + INIT_LIST_HEAD(&sock->ams); sock->ifname = ifname; + // FIXME: get a list of addresses and routes for the interface + am = malloc(sizeof(*am)); + if (!am) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + am->addr.ss.ss_family = AF_INET6; + am->addr.sin.sin_port = htons(MDNS_PORT); + sd = socket(AF_INET6, SOCK_DGRAM, 0); if (sd < 0) { log_message(LOG_ERR, "send socket6(): %s", strerror(errno)); @@ -322,11 +333,6 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } sock->sockfd = sd; - // FIXME: get a list of addresses and routes for the interface - - sock->am.addr.ss.ss_family = AF_INET6; - sock->am.addr.sin.sin_port = htons(MDNS_PORT); - // make sure that the socket uses only IPv6 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { log_message(LOG_ERR, "send setsockopt(IPV6_V6ONLY): %s", strerror(errno)); @@ -340,7 +346,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } // bind to the address - if (bind(sd, (struct sockaddr *)&sock->am.addr.sin6, sizeof(sock->am.addr.sin6)) < 0) { + if (bind(sd, (struct sockaddr *)&am->addr.sin6, sizeof(am->addr.sin6)) < 0) { log_message(LOG_ERR, "send bind6(): %s", strerror(errno)); goto out; } @@ -379,11 +385,17 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } } - log_message(LOG_INFO, "dev %s %s", sock->ifname, addr_mask_to_string(&sock->am)); + log_message(LOG_INFO, "dev %s %s", sock->ifname, addr_mask_to_string(am)); return sock; out: - free(sock); + if (sock) { + list_for_each_entry_safe(am, tmp_am, &sock->ams, list) { + list_del(&am->list); + free(am); + } + free(sock); + } close(sd); return NULL; } @@ -890,7 +902,7 @@ int main(int argc, char *argv[]) { struct send_sock6 *send_sock6, *tmp_send_sock6; struct send_sock4 *send_sock4, *tmp_send_sock4; struct recv_sock *recv_sock, *tmp_recv_sock; - struct addr_mask *subnet, *tmp_subnet; + struct addr_mask *am, *tmp_am; int pfds_count = 0; int pfds_used = 0; struct pollfd *pfds; @@ -1017,6 +1029,10 @@ int main(int argc, char *argv[]) { } list_for_each_entry_safe(send_sock6, tmp_send_sock6, &send_socks6, list) { + list_for_each_entry_safe(am, tmp_am, &send_sock6->ams, list) { + list_del(&am->list); + free(am); + } list_del(&send_sock6->list); close(send_sock6->sockfd); free(send_sock6); @@ -1028,14 +1044,14 @@ int main(int argc, char *argv[]) { free(send_sock4); } - list_for_each_entry_safe(subnet, tmp_subnet, &blacklisted_subnets, list) { - list_del(&subnet->list); - free(subnet); + list_for_each_entry_safe(am, tmp_am, &blacklisted_subnets, list) { + list_del(&am->list); + free(am); } - list_for_each_entry_safe(subnet, tmp_subnet, &whitelisted_subnets, list) { - list_del(&subnet->list); - free(subnet); + list_for_each_entry_safe(am, tmp_am, &whitelisted_subnets, list) { + list_del(&am->list); + free(am); } // remove pid file if it belongs to us From 1e1d59c7e666cc8c9ceacbd49cde0c1af1073391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 18:08:44 +0100 Subject: [PATCH 31/43] Add proper address determination to create_send_sock6() --- mdns-repeater.c | 57 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 97130f8..1e7868b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -301,6 +302,8 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { int ttl = 255; // https://datatracker.ietf.org/doc/html/rfc6762#section-11 struct ipv6_mreq mreq6; struct recv_sock *recv_sock; + struct ifaddrs *ifa, *ifap = NULL; + struct sockaddr_in6 *bindaddr = NULL; ifindex = if_nametoindex (ifname); if (ifindex < 1) { @@ -317,14 +320,46 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { INIT_LIST_HEAD(&sock->ams); sock->ifname = ifname; - // FIXME: get a list of addresses and routes for the interface - am = malloc(sizeof(*am)); - if (!am) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + if (getifaddrs(&ifap) < 0) { + log_message(LOG_ERR, "getifaddrs(): %s", strerror(errno)); + goto out; + } + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + else if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + else if (!ifa->ifa_netmask) + continue; + else if (strcmp(ifa->ifa_name, ifname)) + continue; + + am = malloc(sizeof(*am)); + if (!am) { + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + goto out; + } + memset(am, 0, sizeof(*am)); + + am->addr.ss.ss_family = AF_INET6; + am->addr.sin6.sin6_port = htons(MDNS_PORT); + am->addr.sin6.sin6_addr = ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + am->addr.sin6.sin6_scope_id = ifindex; + am->mask.in6 = ((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; + for (int i = 0; i < sizeof(am->net.in6.s6_addr); i++) + am->net.in6.s6_addr[i] = am->addr.sin6.sin6_addr.s6_addr[i] & + am->mask.in6.s6_addr[i]; + list_add(&am->list, &sock->ams); + + if (IN6_IS_ADDR_LINKLOCAL(&am->addr.sin6.sin6_addr)) + bindaddr = &am->addr.sin6; + } + + if (!bindaddr) { + log_message(LOG_ERR, "no IPv6 link-local address for dev %s", ifname); goto out; } - am->addr.ss.ss_family = AF_INET6; - am->addr.sin.sin_port = htons(MDNS_PORT); sd = socket(AF_INET6, SOCK_DGRAM, 0); if (sd < 0) { @@ -346,8 +381,8 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } // bind to the address - if (bind(sd, (struct sockaddr *)&am->addr.sin6, sizeof(am->addr.sin6)) < 0) { - log_message(LOG_ERR, "send bind6(): %s", strerror(errno)); + if (bind(sd, (struct sockaddr *)bindaddr, sizeof(*bindaddr)) < 0) { + log_message(LOG_ERR, "send bind6(): %s %i %i", strerror(errno), errno, EINVAL); goto out; } @@ -385,7 +420,10 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { } } - log_message(LOG_INFO, "dev %s %s", sock->ifname, addr_mask_to_string(am)); + list_for_each_entry(am, &sock->ams, list) + log_message(LOG_INFO, "dev %s %s", sock->ifname, addr_mask_to_string(am)); + + freeifaddrs(ifap); return sock; out: @@ -397,6 +435,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { free(sock); } close(sd); + freeifaddrs(ifap); return NULL; } From 1a39f02ff417e0038ecf0985b1a3a952d779c822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 20:37:44 +0100 Subject: [PATCH 32/43] Split recv_packet() into v4/v6 versions --- mdns-repeater.c | 50 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 1e7868b..ecb8d4b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -903,7 +903,28 @@ repeat_packet6(struct recv_sock *recv_sock) } static void -recv_packet(struct recv_sock *recv_sock) +recv_packet6(struct recv_sock *recv_sock) +{ + socklen_t sockaddr_size = sizeof(recv_sock->from); + + recv_sock->pkt_size = recvfrom(recv_sock->sockfd, + recv_sock->pkt_data, + sizeof(recv_sock->pkt_data), 0, + (struct sockaddr *)&recv_sock->from, + &sockaddr_size); + if (recv_sock->pkt_size < 0) + return; + + if (!inet_ntop(AF_INET6, + &recv_sock->from.sin6.sin6_addr, + recv_sock->from_str, + sizeof(recv_sock->from_str))) + recv_sock->from_str[0] = '\0'; + repeat_packet6(recv_sock); +} + +static void +recv_packet4(struct recv_sock *recv_sock) { socklen_t sockaddr_size = sizeof(recv_sock->from); @@ -915,22 +936,23 @@ recv_packet(struct recv_sock *recv_sock) if (recv_sock->pkt_size < 0) return; - switch (recv_sock->from.ss.ss_family) { + if (!inet_ntop(AF_INET, + &recv_sock->from.sin.sin_addr, + recv_sock->from_str, + sizeof(recv_sock->from_str))) + recv_sock->from_str[0] = '\0'; + repeat_packet4(recv_sock); +} + +static void +recv_packet(struct recv_sock *recv_sock) +{ + switch (recv_sock->addr.ss.ss_family) { case AF_INET: - if (!inet_ntop(AF_INET, - &recv_sock->from.sin.sin_addr, - recv_sock->from_str, - sizeof(recv_sock->from_str))) - recv_sock->from_str[0] = '\0'; - repeat_packet4(recv_sock); + recv_packet4(recv_sock); break; case AF_INET6: - if (!inet_ntop(AF_INET6, - &recv_sock->from.sin6.sin6_addr, - recv_sock->from_str, - sizeof(recv_sock->from_str))) - recv_sock->from_str[0] = '\0'; - repeat_packet6(recv_sock); + recv_packet6(recv_sock); break; } } From cad376314e393be715b0cf0f8a3f5a3fc68db7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Tue, 6 Feb 2024 21:36:02 +0100 Subject: [PATCH 33/43] Improve recv_packet6() to get ifindex --- mdns-repeater.c | 66 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index ecb8d4b..1e0da07 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -51,6 +51,11 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) +struct _in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; + union sockaddr_u { struct sockaddr_storage ss; /* socket addr */ struct sockaddr_in6 sin6; /* socket addr (IPv6) */ @@ -220,6 +225,12 @@ create_recv_sock6() { goto out; } + // provides info on which interface a packet arrived via + if (setsockopt(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { + log_message(LOG_ERR, "recv setsockopt(IPV6_RECVPKTINFO): %s", strerror(errno)); + goto out; + } + // bind to an address memset(&sock->addr, 0, sizeof(sock->addr)); sock->addr.sin6.sin6_family = AF_INET6; @@ -896,31 +907,66 @@ repeat_packet4(struct recv_sock *recv_sock) { } static void -repeat_packet6(struct recv_sock *recv_sock) +repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) { // Not implemented yet - printf("skipping v6 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); + printf("skipping v6 packet from=%s ifindex=%u size=%zd\n", + recv_sock->from_str, ifindex, recv_sock->pkt_size); } static void recv_packet6(struct recv_sock *recv_sock) { - socklen_t sockaddr_size = sizeof(recv_sock->from); - - recv_sock->pkt_size = recvfrom(recv_sock->sockfd, - recv_sock->pkt_data, - sizeof(recv_sock->pkt_data), 0, - (struct sockaddr *)&recv_sock->from, - &sockaddr_size); + uint8_t cmsgbuf[1024]; + struct iovec iov[] = { + { + .iov_base = &recv_sock->pkt_data, + .iov_len = sizeof(recv_sock->pkt_data), + } + }; + struct msghdr msg = { + .msg_name = &recv_sock->from, + .msg_namelen = sizeof(recv_sock->from), + .msg_iov = iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + .msg_flags = 0, + }; + struct cmsghdr *chdr; + struct _in6_pktinfo *pktinfo = NULL; + + recv_sock->pkt_size = recvmsg(recv_sock->sockfd, &msg, 0); if (recv_sock->pkt_size < 0) return; + else if (msg.msg_flags & MSG_TRUNC) + return; + else if (msg.msg_flags & MSG_CTRUNC) + return; + + for (chdr = CMSG_FIRSTHDR(&msg); chdr; chdr = CMSG_NXTHDR(&msg, chdr)) { + printf("Got a hdr: lvl %i and type %i\n", + chdr->cmsg_level, chdr->cmsg_type); + + if (chdr->cmsg_level != IPPROTO_IPV6) + continue; + + if (chdr->cmsg_type != IPV6_PKTINFO) + continue; + + pktinfo = (struct _in6_pktinfo *)CMSG_DATA(chdr); + break; + } + + if (!pktinfo) + return; if (!inet_ntop(AF_INET6, &recv_sock->from.sin6.sin6_addr, recv_sock->from_str, sizeof(recv_sock->from_str))) recv_sock->from_str[0] = '\0'; - repeat_packet6(recv_sock); + repeat_packet6(recv_sock, pktinfo->ipi6_ifindex); } static void From f37d382e46380b4ad0daaf4864ec31c628e86b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 09:04:47 +0100 Subject: [PATCH 34/43] Split subnet_match into v4/v6 versions --- mdns-repeater.c | 105 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 1e0da07..ddfde00 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -43,6 +43,10 @@ #define PACKAGE "mdns-repeater" #define MDNS_ADDR4 "224.0.0.251" #define MDNS_ADDR6 "FF02::FB" +static const struct in6_addr mdns_addr6_in = { .s6_addr = { + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, +}}; #define MDNS_PORT 5353 #ifndef PIDFILE @@ -78,6 +82,7 @@ LIST_HEAD(whitelisted_subnets); struct send_sock6 { const char *ifname; /* interface name */ + unsigned ifindex; /* interface index */ int sockfd; /* socket fd */ struct list_head ams; /* socket addr/mask/nets*/ struct list_head list; /* socket list */ @@ -330,6 +335,7 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { memset(sock, 0, sizeof(*sock)); INIT_LIST_HEAD(&sock->ams); sock->ifname = ifname; + sock->ifindex = ifindex; if (getifaddrs(&ifap) < 0) { log_message(LOG_ERR, "getifaddrs(): %s", strerror(errno)); @@ -765,7 +771,26 @@ parse_subnet(const char *input) { } static bool -subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) +subnet_match6(struct sockaddr_in6 *from, struct list_head *subnets) +{ + struct addr_mask *subnet; + + list_for_each_entry(subnet, subnets, list) { + if (subnet->addr.ss.ss_family != AF_INET6) + continue; + + for (int i = 0; i < sizeof(from->sin6_addr); i++) + if ((from->sin6_addr.s6_addr[i] & subnet->mask.in6.s6_addr[i]) != subnet->net.in6.s6_addr[i]) + continue; + + return true; + } + + return false; +} + +static bool +subnet_match4(struct sockaddr_in *from, struct list_head *subnets) { struct addr_mask *subnet; @@ -773,7 +798,7 @@ subnet_match(struct sockaddr_in *fromaddr, struct list_head *subnets) if (subnet->addr.ss.ss_family != AF_INET) continue; - if ((fromaddr->sin_addr.s_addr & subnet->mask.in.s_addr) == subnet->net.in.s_addr) + if ((from->sin_addr.s_addr & subnet->mask.in.s_addr) == subnet->net.in.s_addr) return true; } @@ -850,6 +875,64 @@ static int parse_opts(int argc, char *argv[]) { return optind; } +static void +repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) +{ + struct send_sock6 *send_sock; + struct addr_mask *am; + //bool our_net = false; + //ssize_t sentsize; + + list_for_each_entry(send_sock, &send_socks6, list) { + list_for_each_entry(am, &send_sock->ams, list) { + if (IN6_ARE_ADDR_EQUAL(&recv_sock->from.sin6.sin6_addr, + &am->addr.sin6.sin6_addr)) { + if (foreground) + printf("skipping packet from=%s size=%zd (ourself)\n", + recv_sock->from_str, recv_sock->pkt_size); + return; + } + } + } + + if (!list_empty(&whitelisted_subnets) && + !subnet_match6(&recv_sock->from.sin6, &whitelisted_subnets)) { + if (foreground) + printf("skipping packet from=%s size=%zd (not whitelisted)\n", + recv_sock->from_str, recv_sock->pkt_size); + return; + } + + if (subnet_match6(&recv_sock->from.sin6, &blacklisted_subnets)) { + if (foreground) + printf("skipping packet from=%s size=%zd (blacklisted)\n", + recv_sock->from_str, recv_sock->pkt_size); + return; + } + + if (foreground) + printf("got v6 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); + + list_for_each_entry(send_sock, &send_socks6, list) { + // do not repeat packet back to the same interface from which it originated + if (send_sock->ifindex == ifindex) + continue; + + if (foreground) + printf("repeating data to %s\n", send_sock->ifname); + +#if 0 + // repeat data + sentsize = send_packet(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); + if (sentsize < 0) + log_message(LOG_ERR, "send(): %s", strerror(errno)); + else if (sentsize != recv_sock->pkt_size) + log_message(LOG_ERR, "send_packet size differs: sent=%zd actual=%zd", + recv_sock->pkt_size, sentsize); +#endif + } +} + static void repeat_packet4(struct recv_sock *recv_sock) { struct send_sock4 *send_sock; @@ -871,14 +954,14 @@ repeat_packet4(struct recv_sock *recv_sock) { return; if (!list_empty(&whitelisted_subnets) && - !subnet_match(&recv_sock->from.sin, &whitelisted_subnets)) { + !subnet_match4(&recv_sock->from.sin, &whitelisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (not whitelisted)\n", recv_sock->from_str, recv_sock->pkt_size); return; } - if (subnet_match(&recv_sock->from.sin, &blacklisted_subnets)) { + if (subnet_match4(&recv_sock->from.sin, &blacklisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (blacklisted)\n", recv_sock->from_str, recv_sock->pkt_size); @@ -906,14 +989,6 @@ repeat_packet4(struct recv_sock *recv_sock) { } } -static void -repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) -{ - // Not implemented yet - printf("skipping v6 packet from=%s ifindex=%u size=%zd\n", - recv_sock->from_str, ifindex, recv_sock->pkt_size); -} - static void recv_packet6(struct recv_sock *recv_sock) { @@ -945,9 +1020,6 @@ recv_packet6(struct recv_sock *recv_sock) return; for (chdr = CMSG_FIRSTHDR(&msg); chdr; chdr = CMSG_NXTHDR(&msg, chdr)) { - printf("Got a hdr: lvl %i and type %i\n", - chdr->cmsg_level, chdr->cmsg_type); - if (chdr->cmsg_level != IPPROTO_IPV6) continue; @@ -955,6 +1027,9 @@ recv_packet6(struct recv_sock *recv_sock) continue; pktinfo = (struct _in6_pktinfo *)CMSG_DATA(chdr); + if (!IN6_ARE_ADDR_EQUAL(&pktinfo->ipi6_addr, &mdns_addr6_in)) + pktinfo = NULL; + break; } From cf08782c4d126a87137d389e50ed646a77c19e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 10:18:31 +0100 Subject: [PATCH 35/43] Split send_packet() into v4/v6 functions IPv6 mDNS repetition actually works now :D --- mdns-repeater.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index ddfde00..5b68487 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -43,7 +43,7 @@ #define PACKAGE "mdns-repeater" #define MDNS_ADDR4 "224.0.0.251" #define MDNS_ADDR6 "FF02::FB" -static const struct in6_addr mdns_addr6_in = { .s6_addr = { +static const struct in6_addr mdns_addr_in6 = { .s6_addr = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, }}; @@ -558,16 +558,28 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { return NULL; } -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_ADDR4); +static ssize_t +send_packet6(int fd, const char *data, size_t len) { + static struct sockaddr_in6 toaddr6; + + if (toaddr6.sin6_family != AF_INET6) { + toaddr6.sin6_family = AF_INET6; + toaddr6.sin6_port = htons(MDNS_PORT); + toaddr6.sin6_addr = mdns_addr_in6; } + return sendto(fd, data, len, 0, (struct sockaddr *)&toaddr6, sizeof(toaddr6)); +} + +static ssize_t +send_packet4(int fd, const char *data, size_t len) { + static struct sockaddr_in toaddr4; - return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in)); + if (toaddr4.sin_family != AF_INET) { + toaddr4.sin_family = AF_INET; + toaddr4.sin_port = htons(MDNS_PORT); + toaddr4.sin_addr.s_addr = inet_addr(MDNS_ADDR4); + } + return sendto(fd, data, len, 0, (struct sockaddr *)&toaddr4, sizeof(toaddr4)); } static void @@ -880,8 +892,7 @@ repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) { struct send_sock6 *send_sock; struct addr_mask *am; - //bool our_net = false; - //ssize_t sentsize; + ssize_t sentsize; list_for_each_entry(send_sock, &send_socks6, list) { list_for_each_entry(am, &send_sock->ams, list) { @@ -921,15 +932,13 @@ repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) if (foreground) printf("repeating data to %s\n", send_sock->ifname); -#if 0 // repeat data - sentsize = send_packet(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); + sentsize = send_packet6(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); if (sentsize < 0) - log_message(LOG_ERR, "send(): %s", strerror(errno)); + log_message(LOG_ERR, "send6(): %s", strerror(errno)); else if (sentsize != recv_sock->pkt_size) - log_message(LOG_ERR, "send_packet size differs: sent=%zd actual=%zd", + log_message(LOG_ERR, "send_packet6 size differs: sent=%zd actual=%zd", recv_sock->pkt_size, sentsize); -#endif } } @@ -980,11 +989,11 @@ repeat_packet4(struct recv_sock *recv_sock) { printf("repeating data to %s\n", send_sock->ifname); // repeat data - sentsize = send_packet(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); + sentsize = send_packet4(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); if (sentsize < 0) - log_message(LOG_ERR, "send(): %s", strerror(errno)); + log_message(LOG_ERR, "send4(): %s", strerror(errno)); else if (sentsize != recv_sock->pkt_size) - log_message(LOG_ERR, "send_packet size differs: sent=%zd actual=%zd", + log_message(LOG_ERR, "send_packet4 size differs: sent=%zd actual=%zd", recv_sock->pkt_size, sentsize); } } @@ -1027,7 +1036,7 @@ recv_packet6(struct recv_sock *recv_sock) continue; pktinfo = (struct _in6_pktinfo *)CMSG_DATA(chdr); - if (!IN6_ARE_ADDR_EQUAL(&pktinfo->ipi6_addr, &mdns_addr6_in)) + if (!IN6_ARE_ADDR_EQUAL(&pktinfo->ipi6_addr, &mdns_addr_in6)) pktinfo = NULL; break; From 1ec0df1d4dff265f5f334e55e9ff85f397cad376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 11:39:06 +0100 Subject: [PATCH 36/43] Fix a bug in the IPv6 subnet matching function And introduce a helper function to match addresses to net/mask. --- mdns-repeater.c | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 5b68487..23a805b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -56,8 +56,8 @@ static const struct in6_addr mdns_addr_in6 = { .s6_addr = { #define MIN(a, b) ((a) < (b) ? (a) : (b)) struct _in6_pktinfo { - struct in6_addr ipi6_addr; /* src/dst IPv6 address */ - unsigned int ipi6_ifindex; /* send/recv interface index */ + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface */ }; union sockaddr_u { @@ -84,7 +84,7 @@ struct send_sock6 { const char *ifname; /* interface name */ unsigned ifindex; /* interface index */ int sockfd; /* socket fd */ - struct list_head ams; /* socket addr/mask/nets*/ + struct list_head ams; /* addr/mask/net list */ struct list_head list; /* socket list */ }; LIST_HEAD(send_socks6); @@ -783,36 +783,36 @@ parse_subnet(const char *input) { } static bool -subnet_match6(struct sockaddr_in6 *from, struct list_head *subnets) +same_network(union sockaddr_u *addr, struct addr_mask *addr_mask) { - struct addr_mask *subnet; - - list_for_each_entry(subnet, subnets, list) { - if (subnet->addr.ss.ss_family != AF_INET6) - continue; - - for (int i = 0; i < sizeof(from->sin6_addr); i++) - if ((from->sin6_addr.s6_addr[i] & subnet->mask.in6.s6_addr[i]) != subnet->net.in6.s6_addr[i]) - continue; + if (addr->ss.ss_family != addr_mask->addr.ss.ss_family) + return false; + switch (addr->ss.ss_family) { + case AF_INET6: + for (int i = 0; i < sizeof(addr->sin6.sin6_addr.s6_addr); i++) + if ((addr->sin6.sin6_addr.s6_addr[i] & + addr_mask->mask.in6.s6_addr[i]) + != addr_mask->net.in6.s6_addr[i]) + return false; return true; + case AF_INET: + return ((addr->sin.sin_addr.s_addr & + addr_mask->mask.in.s_addr) + == addr_mask->net.in.s_addr); + default: + return false; } - - return false; } static bool -subnet_match4(struct sockaddr_in *from, struct list_head *subnets) +subnet_match(union sockaddr_u *from, struct list_head *subnets) { struct addr_mask *subnet; - list_for_each_entry(subnet, subnets, list) { - if (subnet->addr.ss.ss_family != AF_INET) - continue; - - if ((from->sin_addr.s_addr & subnet->mask.in.s_addr) == subnet->net.in.s_addr) + list_for_each_entry(subnet, subnets, list) + if (same_network(from, subnet)) return true; - } return false; } @@ -907,14 +907,14 @@ repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) } if (!list_empty(&whitelisted_subnets) && - !subnet_match6(&recv_sock->from.sin6, &whitelisted_subnets)) { + !subnet_match(&recv_sock->from, &whitelisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (not whitelisted)\n", recv_sock->from_str, recv_sock->pkt_size); return; } - if (subnet_match6(&recv_sock->from.sin6, &blacklisted_subnets)) { + if (subnet_match(&recv_sock->from, &blacklisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (blacklisted)\n", recv_sock->from_str, recv_sock->pkt_size); @@ -963,14 +963,14 @@ repeat_packet4(struct recv_sock *recv_sock) { return; if (!list_empty(&whitelisted_subnets) && - !subnet_match4(&recv_sock->from.sin, &whitelisted_subnets)) { + !subnet_match(&recv_sock->from, &whitelisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (not whitelisted)\n", recv_sock->from_str, recv_sock->pkt_size); return; } - if (subnet_match4(&recv_sock->from.sin, &blacklisted_subnets)) { + if (subnet_match(&recv_sock->from, &blacklisted_subnets)) { if (foreground) printf("skipping packet from=%s size=%zd (blacklisted)\n", recv_sock->from_str, recv_sock->pkt_size); From bf48652c248ab46c563ff36736f3540f98a9b8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 11:48:16 +0100 Subject: [PATCH 37/43] Use the new same_network() function in other places as well --- mdns-repeater.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 23a805b..400e90b 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -950,9 +950,8 @@ repeat_packet4(struct recv_sock *recv_sock) { list_for_each_entry(send_sock, &send_socks4, list) { // make sure packet originated from specified networks - if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->am.mask.in.s_addr) == send_sock->am.net.in.s_addr) { + if (same_network(&recv_sock->from, &send_sock->am)) our_net = true; - } // check for loopback if (recv_sock->from.sin.sin_addr.s_addr == send_sock->am.addr.sin.sin_addr.s_addr) @@ -982,7 +981,7 @@ repeat_packet4(struct recv_sock *recv_sock) { list_for_each_entry(send_sock, &send_socks4, list) { // do not repeat packet back to the same network from which it originated - if ((recv_sock->from.sin.sin_addr.s_addr & send_sock->am.mask.in.s_addr) == send_sock->am.net.in.s_addr) + if (same_network(&recv_sock->from, &send_sock->am)) continue; if (foreground) From d8873765f1358ffb78fabd222750e64ff93875b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 12:09:22 +0100 Subject: [PATCH 38/43] Add a same_address() helper function This is a bit more readable... --- mdns-repeater.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 400e90b..5f40887 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -782,6 +782,24 @@ parse_subnet(const char *input) { return NULL; } +static bool +same_address(union sockaddr_u *a, union sockaddr_u *b) +{ + if (a->ss.ss_family != b->ss.ss_family) + return false; + + switch (a->ss.ss_family) { + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a->sin6.sin6_addr, + &b->sin6.sin6_addr); + case AF_INET: + return (a->sin.sin_addr.s_addr == + b->sin.sin_addr.s_addr); + default: + return false; + } +} + static bool same_network(union sockaddr_u *addr, struct addr_mask *addr_mask) { @@ -896,8 +914,7 @@ repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) list_for_each_entry(send_sock, &send_socks6, list) { list_for_each_entry(am, &send_sock->ams, list) { - if (IN6_ARE_ADDR_EQUAL(&recv_sock->from.sin6.sin6_addr, - &am->addr.sin6.sin6_addr)) { + if (same_address(&recv_sock->from, &am->addr)) { if (foreground) printf("skipping packet from=%s size=%zd (ourself)\n", recv_sock->from_str, recv_sock->pkt_size); @@ -954,7 +971,7 @@ repeat_packet4(struct recv_sock *recv_sock) { our_net = true; // check for loopback - if (recv_sock->from.sin.sin_addr.s_addr == send_sock->am.addr.sin.sin_addr.s_addr) + if (same_address(&recv_sock->from, &send_sock->am.addr)) return; } From 46813579a3115ec82aa58983e7494d6615e3e817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 12:29:26 +0100 Subject: [PATCH 39/43] Make more use of the log_message() function --- mdns-repeater.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 5f40887..67c8269 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -124,6 +124,9 @@ void log_message(int loglevel, char *fmt_str, ...) { va_list ap; char buf[2048]; + if (loglevel == LOG_DEBUG && !foreground) + return; + va_start(ap, fmt_str); vsnprintf(buf, 2047, fmt_str, ap); va_end(ap); @@ -915,9 +918,9 @@ repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) list_for_each_entry(send_sock, &send_socks6, list) { list_for_each_entry(am, &send_sock->ams, list) { if (same_address(&recv_sock->from, &am->addr)) { - if (foreground) - printf("skipping packet from=%s size=%zd (ourself)\n", - recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "skipping packet from=%s size=%zd (ourself)", + recv_sock->from_str, recv_sock->pkt_size); return; } } @@ -925,29 +928,29 @@ repeat_packet6(struct recv_sock *recv_sock, unsigned ifindex) if (!list_empty(&whitelisted_subnets) && !subnet_match(&recv_sock->from, &whitelisted_subnets)) { - if (foreground) - printf("skipping packet from=%s size=%zd (not whitelisted)\n", - recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "skipping packet from=%s size=%zd (not whitelisted)", + recv_sock->from_str, recv_sock->pkt_size); return; } if (subnet_match(&recv_sock->from, &blacklisted_subnets)) { - if (foreground) - printf("skipping packet from=%s size=%zd (blacklisted)\n", - recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "skipping packet from=%s size=%zd (blacklisted)", + recv_sock->from_str, recv_sock->pkt_size); return; } - if (foreground) - printf("got v6 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "got v6 packet from=%s size=%zd", + recv_sock->from_str, recv_sock->pkt_size); list_for_each_entry(send_sock, &send_socks6, list) { // do not repeat packet back to the same interface from which it originated if (send_sock->ifindex == ifindex) continue; - if (foreground) - printf("repeating data to %s\n", send_sock->ifname); + log_message(LOG_DEBUG, "repeating data to %s", send_sock->ifname); // repeat data sentsize = send_packet6(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); @@ -980,29 +983,29 @@ repeat_packet4(struct recv_sock *recv_sock) { if (!list_empty(&whitelisted_subnets) && !subnet_match(&recv_sock->from, &whitelisted_subnets)) { - if (foreground) - printf("skipping packet from=%s size=%zd (not whitelisted)\n", - recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "skipping packet from=%s size=%zd (not whitelisted)", + recv_sock->from_str, recv_sock->pkt_size); return; } if (subnet_match(&recv_sock->from, &blacklisted_subnets)) { - if (foreground) - printf("skipping packet from=%s size=%zd (blacklisted)\n", - recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "skipping packet from=%s size=%zd (blacklisted)", + recv_sock->from_str, recv_sock->pkt_size); return; } - if (foreground) - printf("got v4 packet from=%s size=%zd\n", recv_sock->from_str, recv_sock->pkt_size); + log_message(LOG_DEBUG, + "got v4 packet from=%s size=%zd", + recv_sock->from_str, recv_sock->pkt_size); list_for_each_entry(send_sock, &send_socks4, list) { // do not repeat packet back to the same network from which it originated if (same_network(&recv_sock->from, &send_sock->am)) continue; - if (foreground) - printf("repeating data to %s\n", send_sock->ifname); + log_message(LOG_DEBUG, "repeating data to %s", send_sock->ifname); // repeat data sentsize = send_packet4(send_sock->sockfd, recv_sock->pkt_data, recv_sock->pkt_size); From 7cc7e2c63f72b3263915b1b5488df7bbb84f40fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 12:37:17 +0100 Subject: [PATCH 40/43] Add a malloc() wrapper --- mdns-repeater.c | 60 ++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 67c8269..580df45 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -139,6 +139,17 @@ void log_message(int loglevel, char *fmt_str, ...) { } } +static void * +xmalloc(size_t size) +{ + void *tmp; + + tmp = calloc(1, size); + if (!tmp) + log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + return tmp; +} + static char * addr6_mask_to_string(struct sockaddr_in6 *addr, struct in6_addr *mask, @@ -202,11 +213,9 @@ create_recv_sock6() { int sd; int on = 1; - sock = malloc(sizeof(*sock)); - if (!sock) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + sock = xmalloc(sizeof(*sock)); + if (!sock) goto out; - } sd = socket(AF_INET6, SOCK_DGRAM, 0); if (sd < 0) { @@ -240,7 +249,6 @@ create_recv_sock6() { } // bind to an address - memset(&sock->addr, 0, sizeof(sock->addr)); sock->addr.sin6.sin6_family = AF_INET6; sock->addr.sin6.sin6_port = htons(MDNS_PORT); sock->addr.sin6.sin6_addr = in6addr_any; @@ -262,11 +270,9 @@ create_recv_sock4() { int sd; int on = 1; - sock = malloc(sizeof(*sock)); - if (!sock) { + sock = xmalloc(sizeof(*sock)); + if (!sock) log_message(LOG_ERR, "malloc(): %s", strerror(errno)); - goto out; - } sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { @@ -282,7 +288,6 @@ create_recv_sock4() { } // bind to an address - memset(&sock->addr, 0, sizeof(sock->addr)); sock->addr.sin.sin_family = AF_INET; sock->addr.sin.sin_port = htons(MDNS_PORT); sock->addr.sin.sin_addr.s_addr = htonl(INADDR_ANY); @@ -330,12 +335,10 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { goto out; } - sock = malloc(sizeof(*sock)); - if (!sock) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + sock = xmalloc(sizeof(*sock)); + if (!sock) goto out; - } - memset(sock, 0, sizeof(*sock)); + INIT_LIST_HEAD(&sock->ams); sock->ifname = ifname; sock->ifindex = ifindex; @@ -355,12 +358,9 @@ create_send_sock6(const char *ifname, struct list_head *recv_socks) { else if (strcmp(ifa->ifa_name, ifname)) continue; - am = malloc(sizeof(*am)); - if (!am) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + am = xmalloc(sizeof(*am)); + if (!am) goto out; - } - memset(am, 0, sizeof(*am)); am->addr.ss.ss_family = AF_INET6; am->addr.sin6.sin6_port = htons(MDNS_PORT); @@ -470,12 +470,10 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { struct ip_mreq mreq; struct recv_sock *recv_sock; - sock = malloc(sizeof(*sock)); - if (!sock) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + sock = xmalloc(sizeof(*sock)); + if (!sock) goto out; - } - memset(sock, 0, sizeof(*sock)); + sock->ifname = ifname; sd = socket(AF_INET, SOCK_DGRAM, 0); @@ -492,7 +490,7 @@ create_send_sock4(const char *ifname, struct list_head *recv_socks) { log_message(LOG_ERR, "ioctl(SIOCGIFNETMASK): %s", strerror(errno)); goto out; } - memcpy(&sock->am.mask.in, if_addr, sizeof(*if_addr)); + sock->am.mask.in = *if_addr; // ...and interface address if (ioctl(sd, SIOCGIFADDR, &ifr) < 0) { @@ -713,12 +711,9 @@ parse_subnet(const char *input) { struct in_addr *addr_in; int prefix_len; - subnet = malloc(sizeof(*subnet)); - if (!subnet) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); + subnet = xmalloc(sizeof(*subnet)); + if (!subnet) goto out; - } - memset(subnet, 0, sizeof(*subnet)); addr_str = strdup(input); if (!addr_str) { @@ -1191,9 +1186,8 @@ int main(int argc, char *argv[]) { } } - pfds = calloc(pfds_count, sizeof(struct pollfd)); + pfds = xmalloc(pfds_count * sizeof(struct pollfd)); if (!pfds) { - log_message(LOG_ERR, "malloc(): %s", strerror(errno)); r = 1; goto end_main; } From d39410b35d44ea4de607741ecfe1ff4dc5f0fcc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 12:57:45 +0100 Subject: [PATCH 41/43] Add -4 and -6 command line options and update help text --- mdns-repeater.c | 124 +++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/mdns-repeater.c b/mdns-repeater.c index 580df45..5d80098 100644 --- a/mdns-repeater.c +++ b/mdns-repeater.c @@ -1,6 +1,7 @@ /* * mdns-repeater.c - mDNS repeater daemon * Copyright (C) 2011 Darell Tan + * Copyright (C) 2024 David Härdeman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -111,6 +112,8 @@ struct recv_sock { LIST_HEAD(recv_socks); bool foreground = false; +bool ipv4_only = false; +bool ipv6_only = false; int signal_pipe_fds[2]; #define PIPE_RD 0 @@ -676,25 +679,28 @@ static void switch_user() { } } -static void show_help(const char *progname) { +static void +show_help(const char *progname) +{ fprintf(stderr, "mDNS repeater (version " HGVERSION ")\n"); - fprintf(stderr, "Copyright (C) 2011 Darell Tan\n\n"); + fprintf(stderr, "Copyright (C) 2011-2024 Darell Tan\n\n"); - fprintf(stderr, "usage: %s [ -f ] ...\n", progname); + fprintf(stderr, "Usage: %s [options] ...\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 for debugging\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" - " -u run as this user (by name)\n" - " -h shows this help\n" - "\n" - ); + " specifies an interface like \"eth0\"\n" + "Packets received on an interface is repeated across all other specified interfaces\n" + "\n" + "Options:\n" + " -f runs in foreground for debugging\n" + " -b blacklist subnet (eg. 192.168.1.1/24 or fc00::/7)\n" + " -w whitelist subnet (eg. 192.168.1.1/24 or fc00::/7)\n" + " -p specifies the pid file path (default: " PIDFILE ")\n" + " -u run as this user (by name)\n" + " -4 use IPv4 only\n" + " -6 use IPv6 only\n" + " -h shows this help\n" + "\n" + ); } /* @@ -833,12 +839,14 @@ subnet_match(union sockaddr_u *from, struct list_head *subnets) return false; } -static int parse_opts(int argc, char *argv[]) { +static int +parse_opts(int argc, char *argv[]) +{ int c; bool help = false; struct addr_mask *subnet; - while ((c = getopt(argc, argv, "hfp:b:w:u:")) != -1) { + while ((c = getopt(argc, argv, "hf46p:b:w:u:")) != -1) { switch (c) { case 'h': help = true; @@ -848,6 +856,14 @@ static int parse_opts(int argc, char *argv[]) { foreground = true; break; + case '4': + ipv4_only = true; + break; + + case '6': + ipv6_only = true; + break; + case 'p': if (optarg[0] != '/') log_message(LOG_ERR, "pid file path must be absolute"); @@ -876,13 +892,12 @@ static int parse_opts(int argc, char *argv[]) { fputs("\n", stderr); break; - case 'u': { + case 'u': if ((user = getpwnam(optarg)) == NULL) { log_message(LOG_ERR, "No such user '%s'", optarg); exit(2); } break; - } default: log_message(LOG_ERR, "unknown option %c", optopt); @@ -890,6 +905,11 @@ static int parse_opts(int argc, char *argv[]) { } } + if (ipv4_only && ipv6_only) { + log_message(LOG_ERR, "simultaneous IPv4 and IPv6-only mode does not make sense"); + exit(2); + } + if (!list_empty(&whitelisted_subnets) && !list_empty(&blacklisted_subnets)) { log_message(LOG_ERR, "simultaneous whitelisting and blacklisting does not make sense"); exit(2); @@ -1130,45 +1150,53 @@ int main(int argc, char *argv[]) { pfds_count++; // create receiving IPv6 sockets - recv_sock = create_recv_sock6(); - if (!recv_sock) { - log_message(LOG_ERR, "unable to create server IPv6 socket"); - r = 1; - goto end_main; + if (!ipv4_only) { + recv_sock = create_recv_sock6(); + if (!recv_sock) { + log_message(LOG_ERR, "unable to create server IPv6 socket"); + r = 1; + goto end_main; + } + list_add(&recv_sock->list, &recv_socks); + pfds_count++; } - list_add(&recv_sock->list, &recv_socks); - pfds_count++; // create receiving IPv4 sockets - recv_sock = create_recv_sock4(); - if (!recv_sock) { - log_message(LOG_ERR, "unable to create server IPv4 socket"); - r = 1; - goto end_main; + if (!ipv6_only) { + recv_sock = create_recv_sock4(); + if (!recv_sock) { + log_message(LOG_ERR, "unable to create server IPv4 socket"); + r = 1; + goto end_main; + } + list_add(&recv_sock->list, &recv_socks); + pfds_count++; } - list_add(&recv_sock->list, &recv_socks); - pfds_count++; // create sending IPv6 sockets - for (int i = optind; i < argc; i++) { - send_sock6 = create_send_sock6(argv[i], &recv_socks); - if (!send_sock6) { - log_message(LOG_ERR, "unable to create IPv6 socket for interface %s", argv[i]); - r = 1; - goto end_main; + if (!ipv4_only) { + for (int i = optind; i < argc; i++) { + send_sock6 = create_send_sock6(argv[i], &recv_socks); + if (!send_sock6) { + log_message(LOG_ERR, "unable to create IPv6 socket for interface %s", argv[i]); + r = 1; + goto end_main; + } + list_add(&send_sock6->list, &send_socks6); } - list_add(&send_sock6->list, &send_socks6); } // create sending IPv4 sockets - for (int i = optind; i < argc; i++) { - send_sock4 = create_send_sock4(argv[i], &recv_socks); - if (!send_sock4) { - log_message(LOG_ERR, "unable to create IPv4 socket for interface %s", argv[i]); - r = 1; - goto end_main; + if (!ipv6_only) { + for (int i = optind; i < argc; i++) { + send_sock4 = create_send_sock4(argv[i], &recv_socks); + if (!send_sock4) { + log_message(LOG_ERR, "unable to create IPv4 socket for interface %s", argv[i]); + r = 1; + goto end_main; + } + list_add(&send_sock4->list, &send_socks4); } - list_add(&send_sock4->list, &send_socks4); } if (user) { From 756b8548cd100744eaf1766a6b6a7f9f6fa1896b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 13:01:52 +0100 Subject: [PATCH 42/43] Update README --- README.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.txt b/README.txt index e41c73d..5694857 100644 --- a/README.txt +++ b/README.txt @@ -1,18 +1,22 @@ mdns-repeater ============== mdns-repeater is a Multicast DNS repeater for Linux. Multicast DNS uses the -224.0.0.251 address, which is "administratively scoped" and does not -leave the subnet. +224.0.0.251 (IPv4) and ff02::fb (IPv6) addresses, which are "administratively +scoped" and do not leave the subnet. This program re-broadcast mDNS packets from one interface to other interfaces. It was written primarily to be run on my Linksys WRT54G which runs dd-wrt, since my wireless network is on a different subnet from my wired network and I would like my zeroconf devices to work properly across the two subnets. -Since the mDNS protocol sends the AA records in the packet itself, the +Since the mDNS protocol sends the A records in the packet itself, the repeater does not need to forge the source address. Instead, the source address is of the interface that repeats the packet. +For IPv6, some devices send AAAA records containing a link-local address. +The repeater does currently not change such records, meaning that hosts +might be advertised with unreachable addresses. + USAGE ----- @@ -32,6 +36,7 @@ long as you abide by the software license. LICENSE -------- Copyright (C) 2011 Darell Tan +Copyright (C) 2024 David Härdeman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License From 75966e248ad71b0685daf767c0ce599b62c2b2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Wed, 7 Feb 2024 13:12:50 +0100 Subject: [PATCH 43/43] Add a .gitignore file --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6dc6f16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +_hgversion +mdns-repeater +mdns-repeater-*.zip