#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <atf-c.h>
static const char buf[] = "Hello";
static int
bcastsock(void)
{
int s;
ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
ATF_REQUIRE(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &(int){1},
sizeof(int)) == 0);
return (s);
}
static void
bcastecho(int s, struct sockaddr_in *to, int r)
{
char rbuf[sizeof(buf)];
printf("Sending to %s\n", inet_ntoa(to->sin_addr));
ATF_REQUIRE_MSG(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)to,
sizeof(*to)) == sizeof(buf), "sending of broadcast failed: %d",
errno);
ATF_REQUIRE(recv(r, rbuf, sizeof(rbuf), 0) == sizeof(rbuf));
ATF_REQUIRE_MSG(memcmp(buf, rbuf, sizeof(buf)) == 0,
"failed to receive own broadcast");
}
static void
firstbcast(struct in_addr *out)
{
struct ifaddrs *ifa0, *ifa;
struct sockaddr_in sin;
ATF_REQUIRE(getifaddrs(&ifa0) == 0);
for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next)
if (ifa->ifa_addr->sa_family == AF_INET &&
(ifa->ifa_flags & IFF_BROADCAST))
break;
if (ifa == NULL) {
freeifaddrs(ifa0);
atf_tc_skip("No broadcast address found");
}
memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
*out = sin.sin_addr;
freeifaddrs(ifa0);
}
ATF_TC(INADDR_BROADCAST);
ATF_TC_HEAD(INADDR_BROADCAST, tc)
{
atf_tc_set_md_var(tc, "require.config", "allow_network_access");
}
ATF_TC_BODY(INADDR_BROADCAST, tc)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
};
socklen_t slen = sizeof(sin);
int l, s;
l = bcastsock();
ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
s = bcastsock();
bcastecho(s, &sin, l);
close(s);
close(l);
}
ATF_TC_WITHOUT_HEAD(IP_ONESBCAST);
ATF_TC_BODY(IP_ONESBCAST, tc)
{
struct ifaddrs *ifa0, *ifa;
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
};
socklen_t slen = sizeof(sin);
int s, l;
in_port_t port;
bool skip = true;
s = bcastsock();
ATF_REQUIRE(setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &(int){1},
sizeof(int)) == 0);
l = bcastsock();
ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
port = sin.sin_port;
ATF_REQUIRE(getifaddrs(&ifa0) == 0);
for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
if (!(ifa->ifa_flags & IFF_BROADCAST))
continue;
skip = false;
memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
sin.sin_port = port;
bcastecho(s, &sin, l);
}
freeifaddrs(ifa0);
close(s);
close(l);
if (skip)
atf_tc_skip("No broadcast address found");
}
ATF_TC_WITHOUT_HEAD(local_broadcast);
ATF_TC_BODY(local_broadcast, tc)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_len = sizeof(struct sockaddr_in),
};
socklen_t slen = sizeof(sin);
int s, l;
s = bcastsock();
l = bcastsock();
ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
firstbcast(&sin.sin_addr);
bcastecho(s, &sin, l);
close(s);
close(l);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, INADDR_BROADCAST);
ATF_TP_ADD_TC(tp, IP_ONESBCAST);
ATF_TP_ADD_TC(tp, local_broadcast);
return (atf_no_error());
}