Skip to content

Commit

Permalink
Provide mdns_sock for IPv4 and IPv6
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
fzs committed Oct 10, 2022
1 parent ba0524f commit d9c381d
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 53 deletions.
175 changes: 127 additions & 48 deletions src/mcsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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));
Expand All @@ -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);
}
14 changes: 12 additions & 2 deletions src/mcsock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
2 changes: 1 addition & 1 deletion src/mdnsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/mquery.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}


Expand Down

0 comments on commit d9c381d

Please sign in to comment.