1 /*
2 * Copyright (c) 2023 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 #include <sys/socket.h>
30
31 #include <net/if.h>
32
33 #include <netinet/in.h>
34
35 #include <stdbool.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include <arpa/inet.h>
41
42 #include <darwintest.h>
43
44 T_GLOBAL_META(
45 T_META_NAMESPACE("xnu.net"),
46 T_META_RADAR_COMPONENT_NAME("xnu"),
47 T_META_RADAR_COMPONENT_VERSION("networking"),
48 T_META_ASROOT(true)
49 );
50
51 static void
test_so_bindtodevice(int domain,int type,int proto)52 test_so_bindtodevice(int domain, int type, int proto)
53 {
54 #ifndef SO_BINDTODEVICE
55 T_SKIP("SO_BINDTODEVICE not defined")
56 #else
57 int fd;
58 int boundif_level = domain == PF_INET ? IPPROTO_IP : IPPROTO_IPV6;
59 int boundif_name = domain == PF_INET ? IP_BOUND_IF : IPV6_BOUND_IF;
60 const char *boundif_str = domain == PF_INET ? "IP_BOUND_IF" : "IPV6_BOUND_IF";
61 char ifname[IFNAMSIZ + 1] = { 0 };
62 int intval;
63 socklen_t len;
64 unsigned int ifindex = if_nametoindex("lo0");
65
66 T_LOG("test_so_bindtodevice(%d, %d, %d)", domain, type, proto);
67
68 T_ASSERT_POSIX_SUCCESS(fd = socket(domain, type, proto), NULL);
69
70 /*
71 * First test the default values
72 */
73 intval = -1;
74 len = sizeof(int);
75 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, boundif_level, boundif_name, &intval, &len),
76 "get default %s, intval %d", boundif_str, intval);
77
78 T_ASSERT_EQ_INT(intval, 0, "default interface index 0");
79
80 len = sizeof(ifname);
81 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, &len),
82 "get default SO_BINDTODEVICE: \"%s\"", ifname);
83
84 T_ASSERT_EQ_STR(ifname, "", "default interface name empty");
85
86 /*
87 * Verify that SO_BINDTODEVICE can get the interface set by xxx_BOUND_IF
88 */
89 intval = (int)ifindex;
90 T_ASSERT_POSIX_SUCCESS(setsockopt(fd, boundif_level, boundif_name, &intval, len),
91 "set lo0 %s, intval %d", boundif_str, intval);
92
93 intval = -1;
94 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, boundif_level, boundif_name, &intval, &len),
95 "get %s, intval %d", boundif_str, intval);
96
97 T_ASSERT_EQ_INT(intval, (int)ifindex, "loopback interface index");
98
99 len = sizeof(ifname);
100 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, &len),
101 "get SO_BINDTODEVICE: \"%s\", len %u", ifname, len);
102
103 T_ASSERT_EQ_STR(ifname, "lo0", "loopback interface name");
104
105 /*
106 * Verify that an empty string clears the bound interface
107 */
108 strlcpy(ifname, "", sizeof(ifname));
109 len = IFNAMSIZ;
110 T_ASSERT_POSIX_SUCCESS(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, len), NULL);
111 T_LOG("set SO_BINDTODEVICE `\"\"` %s: len %d", boundif_str, intval);
112
113 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, boundif_level, boundif_name, &intval, &len), NULL);
114 T_LOG("cleared %s, intval %d", boundif_str, intval);
115
116 T_ASSERT_EQ_INT(intval, 0, "default interface index 0");
117
118 /*
119 * Verify interface set by SO_BINDTODEVICE is gotten by xxx_BOUND_IF
120 */
121 strlcpy(ifname, "lo0", sizeof(ifname));
122 len = (socklen_t)strlen(ifname);
123 T_ASSERT_POSIX_SUCCESS(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, len),
124 "set SO_BINDTODEVICE: \"%s\", len %u", ifname, len);
125
126 len = sizeof(ifname);
127 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, &len),
128 "lo0 SO_BINDTODEVICE: \"%s\"", ifname);
129
130 T_ASSERT_EQ_STR(ifname, "lo0", "loopback interface name");
131
132 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, boundif_level, boundif_name, &intval, &len),
133 "lo0 %s, intval %d", boundif_str, intval);
134
135 T_ASSERT_EQ_INT(intval, (int)ifindex, "loopback interface index");
136
137 /*
138 * Verify that a NULL string clears the bound interface
139 */
140 T_ASSERT_POSIX_SUCCESS(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0),
141 "set SO_BINDTODEVICE: NULL, len 0");
142
143 T_ASSERT_POSIX_SUCCESS(getsockopt(fd, boundif_level, boundif_name, &intval, &len), NULL);
144 T_LOG("cleared \"%s\", intval %d", boundif_str, intval);
145
146 T_ASSERT_EQ_INT(intval, 0, "default interface index 0");
147
148 /*
149 * Verify bounds
150 */
151 strlcpy(ifname, "lo0", sizeof(ifname));
152
153 len = IFNAMSIZ;
154 T_ASSERT_POSIX_SUCCESS(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, len),
155 "set SO_BINDTODEVICE: \"%s\", len %u (IFNAMSIZ) OK", ifname, len);
156
157 len = IFNAMSIZ + 1;
158 T_ASSERT_POSIX_FAILURE(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, len), EINVAL,
159 "set SO_BINDTODEVICE: \"%s\", len %u (IFNAMSIZ + 1) expected EINVAL", ifname, len);
160 #endif
161 }
162
163 T_DECL(so_bindtodevice_tcp_ipv4, "SO_BINDTODEVICE TCP IPv4")
164 {
165 test_so_bindtodevice(PF_INET, SOCK_STREAM, 0);
166 }
167
168 T_DECL(so_bindtodevice_tcp_ipv6, "SO_BINDTODEVICE TCP IPv6")
169 {
170 test_so_bindtodevice(PF_INET6, SOCK_STREAM, 0);
171 }
172
173 T_DECL(so_bindtodevice_udp_ipv4, "SO_BINDTODEVICE UDP IPv4")
174 {
175 test_so_bindtodevice(PF_INET, SOCK_DGRAM, 0);
176 }
177
178 T_DECL(so_bindtodevice_udp_ipv6, "SO_BINDTODEVICE UDP IPv6")
179 {
180 test_so_bindtodevice(PF_INET6, SOCK_DGRAM, 0);
181 }
182
183 T_DECL(so_bindtodevice_icmp_ipv4, "SO_BINDTODEVICE ICMP IPv4")
184 {
185 test_so_bindtodevice(PF_INET, SOCK_DGRAM, IPPROTO_ICMP);
186 }
187
188 T_DECL(so_bindtodevice_icmp_ipv6, "SO_BINDTODEVICE ICMP IPv6")
189 {
190 test_so_bindtodevice(PF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
191 }
192
193 T_DECL(so_bindtodevice_raw_ipv4, "SO_BINDTODEVICE RAW IPv4")
194 {
195 test_so_bindtodevice(PF_INET, SOCK_RAW, 0);
196 }
197
198 T_DECL(so_bindtodevice_raw_ipv6, "SO_BINDTODEVICE RAW IPv6")
199 {
200 test_so_bindtodevice(PF_INET6, SOCK_RAW, 0);
201 }
202