xref: /freebsd/tests/sys/netinet/broadcast.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
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
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
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
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_WITHOUT_HEAD(INADDR_BROADCAST);
94 ATF_TC_BODY(INADDR_BROADCAST, tc)
95 {
96 	struct sockaddr_in sin = {
97 		.sin_family = AF_INET,
98 		.sin_len = sizeof(struct sockaddr_in),
99 	};
100 	socklen_t slen = sizeof(sin);
101 	int l, s;
102 
103 	l = bcastsock();
104 	ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
105 	ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
106 	sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
107 
108 	s = bcastsock();
109 	bcastecho(s, &sin, l);
110 
111 	close(s);
112 	close(l);
113 }
114 
115 /*
116  * Application sends on broadcast address of an interface, INADDR_BROADCAST
117  * goes on the wire of the selected interface.
118  */
119 ATF_TC_WITHOUT_HEAD(IP_ONESBCAST);
120 ATF_TC_BODY(IP_ONESBCAST, tc)
121 {
122 	struct ifaddrs *ifa0, *ifa;
123 	struct sockaddr_in sin = {
124 		.sin_family = AF_INET,
125 		.sin_len = sizeof(struct sockaddr_in),
126 	};
127 	socklen_t slen = sizeof(sin);
128 	int s, l;
129 	in_port_t port;
130 	bool skip = true;
131 
132 	s = bcastsock();
133 	ATF_REQUIRE(setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &(int){1},
134 	    sizeof(int)) == 0);
135 
136 	l = bcastsock();
137 	ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
138 	ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
139 	port = sin.sin_port;
140 
141 	ATF_REQUIRE(getifaddrs(&ifa0) == 0);
142 	for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
143 		if (ifa->ifa_addr->sa_family != AF_INET)
144 			continue;
145 		if (!(ifa->ifa_flags & IFF_BROADCAST))
146 			continue;
147 		skip = false;
148 		memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
149 		sin.sin_port = port;
150 		bcastecho(s, &sin, l);
151 	}
152 	freeifaddrs(ifa0);
153 	close(s);
154 	close(l);
155 	if (skip)
156 		atf_tc_skip("No broadcast address found");
157 }
158 
159 /*
160  * Application sends on broadcast address of an interface, and this is what
161  * goes out the wire.
162  */
163 ATF_TC_WITHOUT_HEAD(local_broadcast);
164 ATF_TC_BODY(local_broadcast, tc)
165 {
166 	struct sockaddr_in sin = {
167 		.sin_family = AF_INET,
168 		.sin_len = sizeof(struct sockaddr_in),
169 	};
170 	socklen_t slen = sizeof(sin);
171 	int s, l;
172 
173 	s = bcastsock();
174 	l = bcastsock();
175 	ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
176 	ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
177 	firstbcast(&sin.sin_addr);
178 
179 	bcastecho(s, &sin, l);
180 
181 	close(s);
182 	close(l);
183 }
184 
185 ATF_TP_ADD_TCS(tp)
186 {
187 	ATF_TP_ADD_TC(tp, INADDR_BROADCAST);
188 	ATF_TP_ADD_TC(tp, IP_ONESBCAST);
189 	ATF_TP_ADD_TC(tp, local_broadcast);
190 
191 	return (atf_no_error());
192 }
193