From d9c381ddc74f4889892f5fe2244227885287556c Mon Sep 17 00:00:00 2001 From: Florian Zschocke Date: Mon, 10 Oct 2022 14:55:07 +0200 Subject: [PATCH] Provide mdns_sock for IPv4 and IPv6 Refactor the code for creating the multicast socket to be able to create a socket for IPv4 and a socket for IPv6. The `mdns_socket` function is replaced by `mdns_ipv4_socket` and `mdns_ipv6_socket` functions. --- src/mcsock.c | 175 +++++++++++++++++++++++++++++++++++++-------------- src/mcsock.h | 14 ++++- src/mdnsd.c | 2 +- src/mquery.c | 4 +- 4 files changed, 142 insertions(+), 53 deletions(-) diff --git a/src/mcsock.c b/src/mcsock.c index fe54ec8..d8a55b8 100644 --- a/src/mcsock.c +++ b/src/mcsock.c @@ -64,7 +64,7 @@ static struct in_addr get_addr(const char *ifname) } -static int mc_socket(struct ifnfo *iface, unsigned char ttl) +static int mc_socket_setup_ipv4(int sd, struct ifnfo *iface, struct sockaddr_in *sin) { #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX struct ip_mreqn imr ={ 0 }; @@ -73,22 +73,109 @@ static int mc_socket(struct ifnfo *iface, unsigned char ttl) imr.imr_interface.s_addr = htonl(INADDR_ANY); #endif + + if (iface) { + if (iface->ifindex != 0 && iface->inaddr.s_addr == 0) { + iface->inaddr = get_addr(iface->ifname); + } + + /* Set interface for outbound multicast */ +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + imr.imr_ifindex = iface->ifindex; + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr))) + WARN("Failed setting IP_MULTICAST_IF to %d: %s", iface->ifindex, strerror(errno)); +#else + imr.imr_interface = iface->inaddr; + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &(iface->inaddr), sizeof(struct in_addr))) + WARN("Failed setting IP_MULTICAST_IF to %s: %s", inet_ntoa(iface->inaddr), strerror(errno)); +#endif + + /* Filter inbound traffic from anyone (ANY) to port 5353 on ifname */ + if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &iface->ifname, strlen(iface->ifname))) + INFO("Failed setting SO_BINDTODEVICE on %s: %s", iface->ifname, strerror(errno)); + } + + + /* Join link-local multicast group on the given interface. */ + imr.imr_multiaddr = sin->sin_addr; + if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr))) + WARN("Failed joining IPv4 multicast group %s: %s", inet_ntoa(imr.imr_multiaddr), strerror(errno)); + + + sin->sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(sd, (struct sockaddr *)sin, sizeof(struct sockaddr_in))) { + close(sd); + ERR("Failed binding socket to *:%d: %s", ntohs(sin->sin_port), strerror(errno)); + return -1; + } + INFO("Bound to *:%d%s%s", ntohs(sin->sin_port), iface?" on iface " : "", iface?iface->ifname:""); + + + return sd; +} + + +static int mc_socket_setup_ipv6(int sd, struct ifnfo *iface, struct sockaddr_in6 *sin6) +{ + struct ipv6_mreq i6mr = { 0 }; + + + if (iface) { + /* Set interface for outbound multicast */ + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &(iface->ifindex), sizeof(iface->ifindex))) + WARN("Failed setting IPV6_MULTICAST_IF to %d: %s", iface->ifindex, strerror(errno)); + + /* Filter inbound traffic from anyone (ANY) to port 5353 on ifname */ + if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &iface->ifname, strlen(iface->ifname))) + INFO("Failed setting SO_BINDTODEVICE on %s: %s", iface->ifname, strerror(errno)); + + i6mr.ipv6mr_interface = iface->ifindex; + } + + + /* Join link-local multicast group on the given interface. */ + i6mr.ipv6mr_multiaddr = sin6->sin6_addr; + if (setsockopt(sd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &i6mr, sizeof(i6mr))) + WARN("Failed joining IPv6 multicast group: %s", strerror(errno)); + + + sin6->sin6_addr = in6addr_any; + if (bind(sd, (struct sockaddr *)sin6, sizeof(struct sockaddr_in6))) { + close(sd); + ERR("Failed binding socket to *:%d: %s", ntohs(sin6->sin6_port), strerror(errno)); + return -1; + } + INFO("Bound to *:%d%s%s", ntohs(sin6->sin6_port), iface?" on iface " : "", iface?iface->ifname:""); + + + return sd; +} + + +static int mc_socket(struct ifnfo *iface, unsigned char ttl, struct sockaddr *saddr) +{ const int on = 1; const int off = 0; - struct sockaddr_in sin; + const char ipv6 = (saddr->sa_family == AF_INET6); + const int so_level = (saddr->sa_family == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP; + + int so_optname; socklen_t len; int unicast_ttl = 255; + int multicast_ttl = ttl; int bufsiz; int sd; - sd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + + sd = socket(saddr->sa_family, SOCK_DGRAM | SOCK_NONBLOCK, 0); if (sd < 0) { ERR("Failed creating UDP socket: %s", strerror(errno)); return -1; } + /* Set IP independent socket options */ #ifdef SO_REUSEPORT if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) WARN("Failed setting SO_REUSEPORT: %s", strerror(errno)); @@ -103,74 +190,66 @@ static int mc_socket(struct ifnfo *iface, unsigned char ttl) INFO("Failed doubling the size of the receive buffer: %s", strerror(errno)); } - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &on, sizeof(on))) - WARN("Failed enabling IP_MULTICAST_LOOP on %s: %s", iface->ifname, strerror(errno)); - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_ALL, &off, sizeof(off))) - WARN("Failed disabling IP_MULTICAST_ALL on %s: %s", iface->ifname, strerror(errno)); + + /* Set socket options on IP level */ + so_optname = (ipv6) ? IPV6_MULTICAST_LOOP : IP_MULTICAST_LOOP; + if (setsockopt(sd, so_level, so_optname, &on, sizeof(on))) + WARN("Failed enabling IP%s_MULTICAST_LOOP on %s: %s", (ipv6)?"V6":"", iface->ifname, strerror(errno)); + + if (! ipv6) { + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_ALL, &off, sizeof(off))) + WARN("Failed disabling IP_MULTICAST_ALL on %s: %s", iface->ifname, strerror(errno)); + } /* * All traffic on mDNS is link-local only, so the default * TTL is set to 1. Some users may however want to route mDNS. */ - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) - WARN("Failed setting IP_MULTICAST_TTL to %d: %s", ttl, strerror(errno)); + so_optname = (ipv6) ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL; + if (setsockopt(sd, so_level, so_optname, &multicast_ttl, sizeof(multicast_ttl))) + WARN("Failed setting %s to %d: %s", (ipv6)?"IPV6_MULTICAST_HOPS":"IP_MULTICAST_TTL", multicast_ttl, strerror(errno)); /* mDNS also supports unicast, so we need a relevant TTL there too */ - if (setsockopt(sd, IPPROTO_IP, IP_TTL, &unicast_ttl, sizeof(unicast_ttl))) - WARN("Failed setting IP_TTL to %d: %s", unicast_ttl, strerror(errno)); + so_optname = (ipv6) ? IPV6_UNICAST_HOPS : IP_TTL; + if (setsockopt(sd, so_level, so_optname, &unicast_ttl, sizeof(unicast_ttl))) + WARN("Failed setting %s to %d: %s", (ipv6)?"IPV6_UNICAST_HOPS":"IP_TTL", unicast_ttl, strerror(errno)); - if (iface) { - if (iface->ifindex != 0 && iface->inaddr.s_addr == 0) { - iface->inaddr = get_addr(iface->ifname); - } - /* Set interface for outbound multicast */ -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - imr.imr_ifindex = iface->ifindex; - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr))) - WARN("Failed setting IP_MULTICAST_IF to %d: %s", iface->ifindex, strerror(errno)); -#else - imr.imr_interface = iface->inaddr; - if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &(iface->inaddr), sizeof(struct in_addr))) - WARN("Failed setting IP_MULTICAST_IF to %s: %s", inet_ntoa(iface->inaddr), strerror(errno)); -#endif - /* Filter inbound traffic from anyone (ANY) to port 5353 on ifname */ - if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &iface->ifname, strlen(iface->ifname))) - INFO("Failed setting SO_BINDTODEVICE on %s: %s", iface->ifname, strerror(errno)); - } + if (saddr->sa_family == AF_INET6) + return mc_socket_setup_ipv6(sd, iface, (struct sockaddr_in6*)saddr); + + return mc_socket_setup_ipv4(sd, iface, (struct sockaddr_in*)saddr); +} - /* - * Join mDNS link-local group on the given interface, that way - * we can receive multicast without a proper net route (default - * route or a 224.0.0.0/24 net route). - */ - imr.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); - if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr))) - WARN("Failed joining mDNS group 224.0.0.251: %s", strerror(errno)); - memset(&sin, 0, sizeof(sin)); +int mdns_ipv4_socket(struct ifnfo *iface, unsigned char ttl) +{ + /* Default to TTL of 1 for mDNS as it is link-local */ + if (ttl == 0) + ttl = 1; + + struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(5353); - if (bind(sd, (struct sockaddr *)&sin, sizeof(sin))) { - close(sd); - ERR("Failed binding socket to *:5353: %s", strerror(errno)); - return -1; - } - INFO("Bound to *:5353 on iface %s", iface->ifname); + inet_pton(AF_INET, "224.0.0.251", &(sin.sin_addr)); - - return sd; + return mc_socket(iface, ttl, (struct sockaddr*)&sin); } -int mdns_socket(struct ifnfo *iface, unsigned char ttl) +int mdns_ipv6_socket(struct ifnfo *iface, unsigned char ttl) { /* Default to TTL of 1 for mDNS as it is link-local */ if (ttl == 0) ttl = 1; - return mc_socket(iface, ttl); + struct sockaddr_in6 sin6 = { 0 }; + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(5353); + inet_pton(AF_INET6, "ff02::fb", &(sin6.sin6_addr)); + + return mc_socket(iface, ttl, (struct sockaddr*)&sin6); } diff --git a/src/mcsock.h b/src/mcsock.h index e857c8c..53ae0e6 100644 --- a/src/mcsock.h +++ b/src/mcsock.h @@ -41,12 +41,22 @@ struct ifnfo { }; /** - * Create multicast socket for mDNS. + * Create IPv4 multicast socket for mDNS. * * @param iface Specify if socket should be bound to specific interface * @param ttl Multicast TTL, 0 for default (1) * @return socket file descriptor or <0 in case or error */ -int mdns_socket(struct ifnfo *iface, unsigned char ttl); +int mdns_ipv4_socket(struct ifnfo *iface, unsigned char ttl); + + +/** + * Create IPv6 multicast socket for mDNS. + * + * @param iface Specify if socket should be bound to specific interface + * @param ttl Multicast TTL, 0 for default (1) + * @return socket file descriptor or <0 in case or error + */ +int mdns_ipv6_socket(struct ifnfo *iface, unsigned char ttl); #endif /* MDNSD_MCSOCK_H_ */ diff --git a/src/mdnsd.c b/src/mdnsd.c index ad2fd64..4f2c33e 100644 --- a/src/mdnsd.c +++ b/src/mdnsd.c @@ -68,7 +68,7 @@ static int multicast_socket(struct iface *iface, unsigned char ttl) memcpy(ifa.ifname, iface->ifname, sizeof(ifa.ifname)); ifa.ifindex = iface->ifindex; ifa.inaddr = iface->inaddr; - return mdns_socket(&ifa, ttl); + return mdns_ipv4_socket(&ifa, ttl); } void mdnsd_conflict(char *name, int type, void *arg) diff --git a/src/mquery.c b/src/mquery.c index 9c9779f..37ddefc 100644 --- a/src/mquery.c +++ b/src/mquery.c @@ -188,10 +188,10 @@ static int msock(char *ifname) if (ifname) { memcpy(ifa.ifname, ifname, sizeof(ifa.ifname)); ifa.ifindex = if_nametoindex(ifname); - return mdns_socket(&ifa, 0); + return mdns_ipv4_socket(&ifa, 0); } - return mdns_socket(NULL, 0); + return mdns_ipv4_socket(NULL, 0); }