1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <net/if.h>
32 #include <arpa/inet.h>
33 #include <errno.h>
34 #include <ifaddrs.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include <atf-c.h>
41
42 static const char buf[] = "Hello";
43
44 /* Create a UDP socket with SO_BROADCAST set. */
45 static int
bcastsock(void)46 bcastsock(void)
47 {
48 int s;
49
50 ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
51 ATF_REQUIRE(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &(int){1},
52 sizeof(int)) == 0);
53 return (s);
54 }
55
56 /* Send on socket 's' with address 'to', confirm receive on 'r'. */
57 static void
bcastecho(int s,struct sockaddr_in * to,int r)58 bcastecho(int s, struct sockaddr_in *to, int r)
59 {
60 char rbuf[sizeof(buf)];
61
62 printf("Sending to %s\n", inet_ntoa(to->sin_addr));
63 ATF_REQUIRE_MSG(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)to,
64 sizeof(*to)) == sizeof(buf), "sending of broadcast failed: %d",
65 errno);
66 ATF_REQUIRE(recv(r, rbuf, sizeof(rbuf), 0) == sizeof(rbuf));
67 ATF_REQUIRE_MSG(memcmp(buf, rbuf, sizeof(buf)) == 0,
68 "failed to receive own broadcast");
69 }
70
71 /* Find a first broadcast capable interface and copy its broadcast address. */
72 static void
firstbcast(struct in_addr * out)73 firstbcast(struct in_addr *out)
74 {
75 struct ifaddrs *ifa0, *ifa;
76 struct sockaddr_in sin;
77
78 ATF_REQUIRE(getifaddrs(&ifa0) == 0);
79 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next)
80 if (ifa->ifa_addr->sa_family == AF_INET &&
81 (ifa->ifa_flags & IFF_BROADCAST))
82 break;
83 if (ifa == NULL) {
84 freeifaddrs(ifa0);
85 atf_tc_skip("No broadcast address found");
86 }
87 memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
88 *out = sin.sin_addr;
89 freeifaddrs(ifa0);
90 }
91
92 /* Application sends to INADDR_BROADCAST, and this goes on the wire. */
93 ATF_TC(INADDR_BROADCAST);
ATF_TC_HEAD(INADDR_BROADCAST,tc)94 ATF_TC_HEAD(INADDR_BROADCAST, tc)
95 {
96 atf_tc_set_md_var(tc, "require.config", "allow_network_access");
97 }
ATF_TC_BODY(INADDR_BROADCAST,tc)98 ATF_TC_BODY(INADDR_BROADCAST, tc)
99 {
100 struct sockaddr_in sin = {
101 .sin_family = AF_INET,
102 .sin_len = sizeof(struct sockaddr_in),
103 };
104 socklen_t slen = sizeof(sin);
105 int l, s;
106
107 l = bcastsock();
108 ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
109 ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
110 sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
111
112 s = bcastsock();
113 bcastecho(s, &sin, l);
114
115 close(s);
116 close(l);
117 }
118
119 /*
120 * Application sends on broadcast address of an interface, INADDR_BROADCAST
121 * goes on the wire of the selected interface.
122 */
123 ATF_TC_WITHOUT_HEAD(IP_ONESBCAST);
ATF_TC_BODY(IP_ONESBCAST,tc)124 ATF_TC_BODY(IP_ONESBCAST, tc)
125 {
126 struct ifaddrs *ifa0, *ifa;
127 struct sockaddr_in sin = {
128 .sin_family = AF_INET,
129 .sin_len = sizeof(struct sockaddr_in),
130 };
131 socklen_t slen = sizeof(sin);
132 int s, l;
133 in_port_t port;
134 bool skip = true;
135
136 s = bcastsock();
137 ATF_REQUIRE(setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &(int){1},
138 sizeof(int)) == 0);
139
140 l = bcastsock();
141 ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
142 ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
143 port = sin.sin_port;
144
145 ATF_REQUIRE(getifaddrs(&ifa0) == 0);
146 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
147 if (ifa->ifa_addr->sa_family != AF_INET)
148 continue;
149 if (!(ifa->ifa_flags & IFF_BROADCAST))
150 continue;
151 skip = false;
152 memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
153 sin.sin_port = port;
154 bcastecho(s, &sin, l);
155 }
156 freeifaddrs(ifa0);
157 close(s);
158 close(l);
159 if (skip)
160 atf_tc_skip("No broadcast address found");
161 }
162
163 /*
164 * Application sends on broadcast address of an interface, and this is what
165 * goes out the wire.
166 */
167 ATF_TC_WITHOUT_HEAD(local_broadcast);
ATF_TC_BODY(local_broadcast,tc)168 ATF_TC_BODY(local_broadcast, tc)
169 {
170 struct sockaddr_in sin = {
171 .sin_family = AF_INET,
172 .sin_len = sizeof(struct sockaddr_in),
173 };
174 socklen_t slen = sizeof(sin);
175 int s, l;
176
177 s = bcastsock();
178 l = bcastsock();
179 ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
180 ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
181 firstbcast(&sin.sin_addr);
182
183 bcastecho(s, &sin, l);
184
185 close(s);
186 close(l);
187 }
188
ATF_TP_ADD_TCS(tp)189 ATF_TP_ADD_TCS(tp)
190 {
191 ATF_TP_ADD_TC(tp, INADDR_BROADCAST);
192 ATF_TP_ADD_TC(tp, IP_ONESBCAST);
193 ATF_TP_ADD_TC(tp, local_broadcast);
194
195 return (atf_no_error());
196 }
197