xref: /freebsd/tests/sys/netinet/broadcast.c (revision 09c9cab2c2bcbe50cc9653ad3f33497f45085d8e)
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