xref: /xnu-12377.61.12/tests/ip_pktinfo.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 /*
2  * Copyright (c) 2025 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #define __APPLE_USE_RFC_3542 1
30 
31 #include <darwintest.h>
32 
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <unistd.h>
37 
38 #include "net_test_lib.h"
39 
40 #define MAX_IPv4_STR_LEN        16
41 #define MAX_IPv6_STR_LEN        64
42 
43 T_GLOBAL_META(
44 	T_META_NAMESPACE("xnu.net"),
45 	T_META_ASROOT(true),
46 	T_META_RADAR_COMPONENT_NAME("xnu"),
47 	T_META_RADAR_COMPONENT_VERSION("networking"),
48 	T_META_CHECK_LEAKS(false));
49 
50 static char *ifname1;
51 static char *ifname2;
52 
53 #define IPV4_MULTICAST_ADDR_STR "239.1.2.3"
54 #define IPV6_MULTICAST_ADDR_STR "FF12:0:0:0:0:0:0:FC"
55 
56 #define TEN_NET                 0x0a000000
57 #define TEN_1_NET               (TEN_NET | 0x010000)
58 #define TEN_1_BROADCAST         (TEN_1_NET | 0xff)
59 
60 static network_interface_pair_list_t    S_feth_pairs;
61 
62 
63 static char *data = "hello\n";
64 
65 static bool success = false;
66 
67 static void
get_ipv4_address(u_int unit,u_int addr_index,struct in_addr * ip)68 get_ipv4_address(u_int unit, u_int addr_index, struct in_addr *ip)
69 {
70 	/* up to 255 units, 255 addresses */
71 	ip->s_addr = htonl(TEN_1_NET | (unit << 8) | addr_index);
72 	return;
73 }
74 
75 static void
network_interface_assign_address(network_interface_t netif,unsigned int unit,unsigned int address_index)76 network_interface_assign_address(network_interface_t netif,
77     unsigned int unit, unsigned int address_index)
78 {
79 	get_ipv4_address(unit, address_index, &netif->ip);
80 	ifnet_add_ip_address(netif->if_name, netif->ip,
81 	    inet_class_c_subnet_mask);
82 	route_add_inet_scoped_subnet(netif->if_name, netif->if_index,
83 	    netif->ip, inet_class_c_subnet_mask);
84 	ifnet_start_ipv6(netif->if_name);
85 	T_ASSERT_EQ(inet6_get_linklocal_address(netif->if_index, &netif->ip6), 1, NULL);
86 }
87 
88 static void
initialize_feth_pairs(u_int n,bool need_address)89 initialize_feth_pairs(u_int n, bool need_address)
90 {
91 	network_interface_pair_t        scan;
92 
93 	S_feth_pairs = network_interface_pair_list_alloc(n);
94 	scan = S_feth_pairs->list;
95 	for (unsigned int i = 0; i < n; i++, scan++) {
96 		network_interface_create(&scan->one, FETH_NAME);
97 		network_interface_create(&scan->two, FETH_NAME);
98 		if (need_address) {
99 			network_interface_assign_address(&scan->one, i, 1);
100 			network_interface_assign_address(&scan->two, i, 2);
101 		}
102 		fake_set_peer(scan->one.if_name, scan->two.if_name);
103 	}
104 
105 	ifname1 = S_feth_pairs->list->one.if_name;
106 	ifname2 = S_feth_pairs->list->two.if_name;
107 }
108 
109 static void
cleanup(void)110 cleanup(void)
111 {
112 	network_interface_pair_list_destroy(S_feth_pairs);
113 	/* allow for the detach to be final before the next test */
114 	usleep(100000);
115 }
116 
117 static void
init(void)118 init(void)
119 {
120 	T_ATEND(cleanup);
121 
122 	success = false;
123 	initialize_feth_pairs(1, true);
124 }
125 
126 static int
setup_receiver(char * bind_to_ifname,bool bind_to_port,in_addr_t bind_to_addr,in_port_t * bound_port)127 setup_receiver(char *bind_to_ifname, bool bind_to_port, in_addr_t bind_to_addr, in_port_t *bound_port)
128 {
129 	int receiver_fd;
130 	socklen_t solen;
131 	struct sockaddr_in sin = {};
132 	char ifname[IFNAMSIZ];
133 	char laddr_str[MAX_IPv4_STR_LEN];
134 	struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
135 	int optval;
136 
137 	/*
138 	 * Setup receiver bound to ifname1
139 	 */
140 	T_ASSERT_POSIX_SUCCESS(receiver_fd = socket(AF_INET, SOCK_DGRAM, 0), NULL);
141 
142 	T_ASSERT_POSIX_SUCCESS(setsockopt(receiver_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)), NULL);
143 
144 	optval = 1;
145 	T_ASSERT_POSIX_SUCCESS(setsockopt(receiver_fd, SOL_SOCKET, SO_DEBUG, &optval, sizeof(int)), NULL);
146 
147 	optval = 1;
148 	T_ASSERT_POSIX_SUCCESS(setsockopt(receiver_fd, IPPROTO_IP, IP_RECVPKTINFO, &optval, sizeof(int)), NULL);
149 
150 	optval = 1;
151 	T_ASSERT_POSIX_SUCCESS(setsockopt(receiver_fd, IPPROTO_UDP, UDP_NOCKSUM, &optval, sizeof(int)), NULL);
152 
153 	if (bind_to_ifname != NULL) {
154 		solen = strlen(bind_to_ifname);
155 		T_ASSERT_POSIX_SUCCESS(setsockopt(receiver_fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_ifname, solen), NULL);
156 	}
157 
158 	if (bind_to_port || bind_to_addr != INADDR_ANY) {
159 		sin.sin_family = AF_INET;
160 		sin.sin_len = sizeof(struct sockaddr_in);
161 		sin.sin_addr.s_addr = bind_to_addr;
162 		T_ASSERT_POSIX_SUCCESS(bind(receiver_fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)), NULL);
163 	}
164 	solen = sizeof(struct sockaddr_in);
165 	T_ASSERT_POSIX_SUCCESS(getsockname(receiver_fd, (struct sockaddr *)&sin, &solen), NULL);
166 	inet_ntop(AF_INET, &sin.sin_addr, laddr_str, sizeof(laddr_str));
167 
168 	solen = sizeof(ifname);
169 	T_ASSERT_POSIX_SUCCESS(getsockopt(receiver_fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, &solen), NULL);
170 
171 	T_LOG("receiver bound to %s:%u over '%s'", laddr_str, ntohs(sin.sin_port), ifname);
172 
173 	*bound_port = sin.sin_port;
174 	return receiver_fd;
175 }
176 
177 int
setup_sender(char * bind_to_ifname,in_addr_t connect_to_addr,in_port_t connect_to_port)178 setup_sender(char *bind_to_ifname, in_addr_t connect_to_addr, in_port_t connect_to_port)
179 {
180 	int sender_fd;
181 	struct sockaddr_in connect_to_sin = {};
182 	struct sockaddr_in sin = {};
183 	socklen_t solen;
184 	char laddr_str[MAX_IPv4_STR_LEN];
185 	char faddr_str[MAX_IPv4_STR_LEN];
186 	char ifname[IFNAMSIZ];
187 	struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
188 	int optval;
189 
190 	T_ASSERT_POSIX_SUCCESS(sender_fd = socket(AF_INET, SOCK_DGRAM, 0), NULL);
191 
192 	T_ASSERT_POSIX_SUCCESS(setsockopt(sender_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)), NULL);
193 
194 	optval = 1;
195 	T_ASSERT_POSIX_SUCCESS(setsockopt(sender_fd, SOL_SOCKET, SO_DEBUG, &optval, sizeof(int)), NULL);
196 
197 	optval = 1;
198 	T_ASSERT_POSIX_SUCCESS(setsockopt(sender_fd, IPPROTO_IP, IP_RECVPKTINFO, &optval, sizeof(int)), NULL);
199 
200 	optval = 1;
201 	T_ASSERT_POSIX_SUCCESS(setsockopt(sender_fd, IPPROTO_UDP, UDP_NOCKSUM, &optval, sizeof(int)), NULL);
202 
203 	if (bind_to_ifname != NULL) {
204 		solen = strlen(bind_to_ifname);
205 		T_ASSERT_POSIX_SUCCESS(setsockopt(sender_fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_ifname, solen), NULL);
206 	}
207 
208 	connect_to_sin.sin_family = AF_INET;
209 	connect_to_sin.sin_len = sizeof(struct sockaddr_in);
210 	connect_to_sin.sin_port = connect_to_port;
211 	connect_to_sin.sin_addr.s_addr = connect_to_addr;
212 
213 	T_ASSERT_POSIX_SUCCESS(connect(sender_fd, (struct sockaddr *)&connect_to_sin, sizeof(struct sockaddr_in)), NULL);
214 
215 	solen = sizeof(struct sockaddr_in);
216 	T_ASSERT_POSIX_SUCCESS(getsockname(sender_fd, (struct sockaddr *)&sin, &solen), NULL);
217 	inet_ntop(AF_INET, &sin.sin_addr, laddr_str, sizeof(laddr_str));
218 	inet_ntop(AF_INET, &connect_to_sin.sin_addr, faddr_str, sizeof(faddr_str));
219 
220 	solen = sizeof(ifname);
221 	T_ASSERT_POSIX_SUCCESS(getsockopt(sender_fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, &solen), NULL);
222 
223 	T_LOG("sender_fd connected from %s:%u to %s:%u over '%s'",
224 	    laddr_str, ntohs(sin.sin_port), faddr_str, ntohs(connect_to_sin.sin_port),
225 	    ifname);
226 
227 	return sender_fd;
228 }
229 
230 
231 static void
echo(int receiver_fd,bool by_ip_addr)232 echo(int receiver_fd, bool by_ip_addr)
233 {
234 	struct msghdr recvmsghdr = {};
235 	char control_space[CMSG_SPACE(128)] = {};
236 	char packet_space[1500] = {};
237 	struct cmsghdr *cmsg;
238 	ssize_t retval;
239 	struct iovec recv_iov = {};
240 	struct sockaddr_in peer_addr;
241 	struct in_pktinfo recv_in_pktinfo = {};
242 	struct in_pktinfo send_in_pktinfo = {};
243 	char ifname[IFNAMSIZ] = {};
244 	struct msghdr reply_msg = {};
245 	struct iovec reply_iov = {};
246 	char reply_control_space[CMSG_SPACE(128)] = {};
247 	char spec_dst_str[MAX_IPv4_STR_LEN];
248 	char addr_str[MAX_IPv4_STR_LEN];
249 	char peer_addr_str[MAX_IPv4_STR_LEN];
250 
251 	T_LOG("%s(by_ip_addr: %s)", __func__, by_ip_addr ? "true" : "false");
252 
253 	recv_iov.iov_len = sizeof(packet_space);
254 	recv_iov.iov_base = &packet_space;
255 
256 	recvmsghdr.msg_name = &peer_addr;
257 	recvmsghdr.msg_namelen = sizeof(struct sockaddr_in);
258 	recvmsghdr.msg_iov = &recv_iov;
259 	recvmsghdr.msg_iovlen = 1;
260 	recvmsghdr.msg_control = &control_space;
261 	recvmsghdr.msg_controllen = sizeof(control_space);
262 	recvmsghdr.msg_flags = 0;
263 
264 	T_ASSERT_POSIX_SUCCESS(retval = recvmsg(receiver_fd, &recvmsghdr, 0), NULL);
265 
266 	for (cmsg = CMSG_FIRSTHDR(&recvmsghdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&recvmsghdr, cmsg)) {
267 		if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVPKTINFO) {
268 			T_ASSERT_EQ(CMSG_LEN(sizeof(struct in_pktinfo)), (size_t)cmsg->cmsg_len,
269 			    "CMSG_LEN(struct in_pktinfo), (size_t)cmsg->cmsg_len");
270 			memcpy(&recv_in_pktinfo, CMSG_DATA(cmsg), sizeof(struct in_pktinfo));
271 		}
272 	}
273 
274 	ifname[0] = 0;
275 	if_indextoname(recv_in_pktinfo.ipi_ifindex, ifname);
276 	inet_ntop(AF_INET, &recv_in_pktinfo.ipi_spec_dst, spec_dst_str, sizeof(spec_dst_str));
277 	inet_ntop(AF_INET, &recv_in_pktinfo.ipi_addr, addr_str, sizeof(addr_str));
278 	inet_ntop(AF_INET, &peer_addr.sin_addr, peer_addr_str, sizeof(peer_addr_str));
279 
280 	T_LOG("received %ld bytes from %s:%u with IP_RECVPKTINFO ipi_ifindex: %u (%s) ipi_spec_dst: %s ipi_addr: %s",
281 	    retval, peer_addr_str, ntohs(peer_addr.sin_port),
282 	    recv_in_pktinfo.ipi_ifindex, ifname, spec_dst_str, addr_str);
283 
284 	reply_iov.iov_base = packet_space;
285 	reply_iov.iov_len = retval;
286 
287 	reply_msg.msg_name = &peer_addr;
288 	reply_msg.msg_namelen = sizeof(struct sockaddr_in);
289 	reply_msg.msg_iov = &reply_iov;
290 	reply_msg.msg_iovlen = 1;
291 	reply_msg.msg_control = reply_control_space;
292 	reply_msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
293 
294 	send_in_pktinfo.ipi_addr.s_addr = 0;
295 	if (by_ip_addr) {
296 		send_in_pktinfo.ipi_ifindex = 0;
297 		send_in_pktinfo.ipi_spec_dst.s_addr = recv_in_pktinfo.ipi_addr.s_addr;
298 	} else {
299 		send_in_pktinfo.ipi_ifindex = recv_in_pktinfo.ipi_ifindex;
300 		send_in_pktinfo.ipi_spec_dst.s_addr = 0;
301 	}
302 	cmsg = CMSG_FIRSTHDR(&reply_msg);
303 	cmsg->cmsg_level = IPPROTO_IP;
304 	cmsg->cmsg_type = IP_PKTINFO;
305 	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
306 	memcpy(CMSG_DATA(cmsg), &send_in_pktinfo, sizeof(struct in_pktinfo));
307 
308 	ifname[0] = 0;
309 	if_indextoname(send_in_pktinfo.ipi_ifindex, ifname);
310 	inet_ntop(AF_INET, &send_in_pktinfo.ipi_spec_dst, spec_dst_str, sizeof(spec_dst_str));
311 	inet_ntop(AF_INET, &send_in_pktinfo.ipi_addr, addr_str, sizeof(addr_str));
312 
313 	T_LOG("sending %ld bytes to %s:%u with IP_PKTINFO ipi_ifindex: %u (%s) ipi_spec_dst: %s ipi_addr: %s",
314 	    retval, peer_addr_str, ntohs(peer_addr.sin_port),
315 	    send_in_pktinfo.ipi_ifindex, ifname, spec_dst_str, addr_str);
316 
317 	T_ASSERT_POSIX_SUCCESS(retval = sendmsg(receiver_fd, &reply_msg, 0), NULL);
318 }
319 
320 static void
echo_and_check(int receiver_fd,bool by_ip_addr)321 echo_and_check(int receiver_fd, bool by_ip_addr)
322 {
323 	socklen_t solen;
324 	struct sockaddr_in before_sin = {};
325 	char before_ifname[IFNAMSIZ];
326 	u_int before_ifindex;
327 	struct sockaddr_in after_sin = {};
328 	char after_ifname[IFNAMSIZ];
329 	u_int after_ifindex;
330 	char before_addr_str[MAX_IPv4_STR_LEN];
331 	char after_addr_str[MAX_IPv4_STR_LEN];
332 
333 	T_LOG("%s(by_ip_addr: %s)", __func__, by_ip_addr ? "true" : "false");
334 
335 	solen = sizeof(struct sockaddr_in);
336 	T_ASSERT_POSIX_SUCCESS(getsockname(receiver_fd, (struct sockaddr *)&before_sin, &solen), NULL);
337 	inet_ntop(AF_INET, &before_sin.sin_addr, before_addr_str, sizeof(before_addr_str));
338 
339 	solen = sizeof(before_ifname);
340 	T_ASSERT_POSIX_SUCCESS(getsockopt(receiver_fd, SOL_SOCKET, SO_BINDTODEVICE, before_ifname, &solen), NULL);
341 	before_ifindex = if_nametoindex(before_ifname);
342 
343 	echo(receiver_fd, by_ip_addr);
344 
345 	solen = sizeof(struct sockaddr_in);
346 	T_ASSERT_POSIX_SUCCESS(getsockname(receiver_fd, (struct sockaddr *)&after_sin, &solen), NULL);
347 	inet_ntop(AF_INET, &after_sin.sin_addr, after_addr_str, sizeof(after_addr_str));
348 
349 	solen = sizeof(after_ifname);
350 	T_ASSERT_POSIX_SUCCESS(getsockopt(receiver_fd, SOL_SOCKET, SO_BINDTODEVICE, after_ifname, &solen), NULL);
351 	after_ifindex = if_nametoindex(after_ifname);
352 
353 
354 	T_LOG("before bound to %s:%u over '%s'/%u", before_addr_str, ntohs(before_sin.sin_port), before_ifname, before_ifindex);
355 	T_LOG("after bound to %s:%u over '%s'/%u", after_addr_str, ntohs(after_sin.sin_port), after_ifname, after_ifindex);
356 
357 	T_ASSERT_EQ_USHORT(before_sin.sin_port, after_sin.sin_port, "same port");
358 	T_ASSERT_EQ_UINT(before_sin.sin_addr.s_addr, after_sin.sin_addr.s_addr, "same IP address");
359 	T_ASSERT_EQ_UINT(before_ifindex, after_ifindex, "same interface index");
360 }
361 
362 static void
do_test_ip_pktinfo(bool bind_to_device,bool bind_to_port,in_addr_t bind_to_addr)363 do_test_ip_pktinfo(bool bind_to_device, bool bind_to_port, in_addr_t bind_to_addr)
364 {
365 	int receiver_fd;
366 	in_port_t receiver_port = 0;
367 	int sender_fd;
368 	ssize_t retval;
369 
370 	init();
371 
372 	receiver_fd = setup_receiver(bind_to_device ? ifname1 : NULL,
373 	    bind_to_port,
374 	    bind_to_addr ? S_feth_pairs->list->one.ip.s_addr : INADDR_ANY,
375 	    &receiver_port);
376 	sender_fd = setup_sender(ifname2, S_feth_pairs->list->one.ip.s_addr, receiver_port);
377 
378 	T_ASSERT_POSIX_SUCCESS(retval = send(sender_fd, data, strlen(data) + 1, 0), NULL);
379 	echo_and_check(receiver_fd, true);
380 
381 	T_ASSERT_POSIX_SUCCESS(retval = send(sender_fd, data, strlen(data) + 1, 0), NULL);
382 	echo_and_check(receiver_fd, false);
383 
384 	close(sender_fd);
385 	close(receiver_fd);
386 
387 	success = true;
388 }
389 
390 
391 T_DECL(ip_pktinfo_010, "IP_PTKINFO bind_to_device=false bind_to_port=true bind_to_addr=false")
392 {
393 	do_test_ip_pktinfo(false, true, false);
394 }
395 
396 T_DECL(ip_pktinfo_011, "IP_PTKINFO bind_to_device=false bind_to_port=true bind_to_addr=true")
397 {
398 	do_test_ip_pktinfo(false, true, true);
399 }
400 
401 T_DECL(ip_pktinfo_110, "IP_PTKINFO bind_to_device=true bind_to_port=true bind_to_addr=false")
402 {
403 	do_test_ip_pktinfo(true, true, false);
404 }
405 
406 T_DECL(ip_pktinfo_111, "IP_PTKINFO bind_to_device=true bind_to_port=true bind_to_addr=true")
407 {
408 	do_test_ip_pktinfo(true, true, true);
409 }
410