xref: /xnu-11417.140.69/tests/aqm_qdelay_utun.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
1 /*
2  * Copyright (c) 2021 Apple Inc. All rights reserved.
3  */
4 
5 #include <sys/socket.h>
6 #include <sys/sockio.h>
7 #include <sys/ioctl.h>
8 #include <sys/kern_control.h>
9 #include <sys/sys_domain.h>
10 
11 #include <net/if.h>
12 #include <net/if_utun.h>
13 #include <netinet/in.h>
14 #include <netinet/in_var.h>
15 #include <netinet/ip6.h>
16 #include <arpa/inet.h>
17 
18 #include <err.h>
19 #include <errno.h>
20 #include <stdbool.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <net/pktsched/pktsched.h>
28 #include <net/classq/if_classq.h>
29 
30 #include <darwintest.h>
31 #include <darwintest_utils.h>
32 
33 static void
nsec_to_str(unsigned long long nsec,char * buf)34 nsec_to_str(unsigned long long nsec, char *buf)
35 {
36 	const char *u;
37 	long double n = nsec, t;
38 
39 	if (nsec >= NSEC_PER_SEC) {
40 		t = n / NSEC_PER_SEC;
41 		u = "sec ";
42 	} else if (n >= USEC_PER_SEC) {
43 		t = n / USEC_PER_SEC;
44 		u = "msec";
45 	} else if (n >= MSEC_PER_SEC) {
46 		t = n / MSEC_PER_SEC;
47 		u = "usec";
48 	} else {
49 		t = n;
50 		u = "nsec";
51 	}
52 
53 	snprintf(buf, 32, "%-5.2Lf %4s", t, u);
54 }
55 
56 static int
create_tun()57 create_tun()
58 {
59 	int tun_fd;
60 	struct ctl_info kernctl_info;
61 	struct sockaddr_ctl kernctl_addr;
62 
63 	T_QUIET; T_ASSERT_POSIX_SUCCESS(tun_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL), NULL);
64 
65 	memset(&kernctl_info, 0, sizeof(kernctl_info));
66 	strlcpy(kernctl_info.ctl_name, UTUN_CONTROL_NAME, sizeof(kernctl_info.ctl_name));
67 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ioctl(tun_fd, CTLIOCGINFO, &kernctl_info), NULL);
68 
69 	memset(&kernctl_addr, 0, sizeof(kernctl_addr));
70 	kernctl_addr.sc_len = sizeof(kernctl_addr);
71 	kernctl_addr.sc_family = AF_SYSTEM;
72 	kernctl_addr.ss_sysaddr = AF_SYS_CONTROL;
73 	kernctl_addr.sc_id = kernctl_info.ctl_id;
74 	kernctl_addr.sc_unit = 0;
75 
76 	T_QUIET; T_ASSERT_POSIX_SUCCESS(bind(tun_fd, (struct sockaddr *)&kernctl_addr, sizeof(kernctl_addr)), NULL);
77 
78 	const int enable = 1;
79 	T_QUIET; T_ASSERT_POSIX_SUCCESS(setsockopt(tun_fd, SYSPROTO_CONTROL, UTUN_OPT_ENABLE_NETIF,
80 	    &enable, sizeof(enable)), NULL);
81 
82 	T_QUIET; T_ASSERT_POSIX_FAILURE(setsockopt(tun_fd, SYSPROTO_CONTROL, UTUN_OPT_ENABLE_FLOWSWITCH,
83 	    &enable, sizeof(enable)), EINVAL, NULL);
84 
85 	T_QUIET; T_ASSERT_POSIX_SUCCESS(connect(tun_fd, (struct sockaddr *)&kernctl_addr, sizeof(kernctl_addr)), NULL);;
86 
87 	return tun_fd;
88 }
89 
90 static short
ifnet_get_flags(int s,const char ifname[IFNAMSIZ])91 ifnet_get_flags(int s, const char ifname[IFNAMSIZ])
92 {
93 	struct ifreq    ifr;
94 	memset(&ifr, 0, sizeof(ifr));
95 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
96 	T_QUIET; T_WITH_ERRNO; T_EXPECT_POSIX_ZERO(ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr), NULL);
97 	return ifr.ifr_flags;
98 }
99 
100 static void
ifnet_add_addr4(const char ifname[IFNAMSIZ],struct in_addr * addr,struct in_addr * mask,struct in_addr * broadaddr)101 ifnet_add_addr4(const char ifname[IFNAMSIZ], struct in_addr *addr, struct in_addr *mask, struct in_addr *broadaddr)
102 {
103 	struct sockaddr_in *sin;
104 	struct in_aliasreq ifra;
105 	int s;
106 
107 	T_QUIET; T_EXPECT_POSIX_SUCCESS(s = socket(AF_INET, SOCK_DGRAM, 0), NULL);
108 
109 	memset(&ifra, 0, sizeof(ifra));
110 	strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
111 
112 	if (addr != NULL) {
113 		sin = &ifra.ifra_addr;
114 		sin->sin_len = sizeof(*sin);
115 		sin->sin_family = AF_INET;
116 		sin->sin_addr = *addr;
117 	}
118 
119 	if (mask != NULL) {
120 		sin = &ifra.ifra_mask;
121 		sin->sin_len = sizeof(*sin);
122 		sin->sin_family = AF_INET;
123 		sin->sin_addr = *mask;
124 	}
125 
126 	if (broadaddr != NULL || (addr != NULL &&
127 	    (ifnet_get_flags(s, ifname) & IFF_POINTOPOINT) != 0)) {
128 		sin = &ifra.ifra_broadaddr;
129 		sin->sin_len = sizeof(*sin);
130 		sin->sin_family = AF_INET;
131 		sin->sin_addr = (broadaddr != NULL) ? *broadaddr : *addr;
132 	}
133 
134 	T_QUIET; T_WITH_ERRNO; T_EXPECT_POSIX_ZERO(ioctl(s, SIOCAIFADDR, &ifra), NULL);
135 
136 	T_QUIET; T_WITH_ERRNO; T_EXPECT_POSIX_ZERO(close(s), NULL);
137 }
138 
139 static struct if_qstatsreq ifqr;
140 static struct if_ifclassq_stats *ifcqs;
141 static uint32_t scheduler;
142 
143 #define FQ_IF_BE_INDEX  7
144 static int
aqmstats_setup(char * iface)145 aqmstats_setup(char *iface)
146 {
147 	unsigned int ifindex;
148 	int s;
149 
150 	ifindex = if_nametoindex(iface);
151 	T_QUIET; T_ASSERT_TRUE(ifindex != 0, "interface index for utun");
152 
153 	ifcqs = malloc(sizeof(*ifcqs));
154 	T_ASSERT_TRUE(ifcqs != 0, "Allocated ifcqs");
155 
156 	T_QUIET; T_ASSERT_POSIX_SUCCESS(s = socket(AF_INET, SOCK_DGRAM, 0), NULL);
157 
158 	bzero(&ifqr, sizeof(ifqr));
159 	strlcpy(ifqr.ifqr_name, iface, sizeof(ifqr.ifqr_name));
160 	ifqr.ifqr_buf = ifcqs;
161 	ifqr.ifqr_len = sizeof(*ifcqs);
162 
163 	// Get the scheduler
164 	ifqr.ifqr_slot = 0;
165 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ioctl(s, SIOCGIFQUEUESTATS, (char *)&ifqr), NULL);
166 	scheduler = ifcqs->ifqs_scheduler;
167 
168 	// Update the slot to BE
169 	ifqr.ifqr_slot = FQ_IF_BE_INDEX;
170 
171 	return s;
172 }
173 
174 static void
aqmstats_cleanup()175 aqmstats_cleanup()
176 {
177 	free(ifcqs);
178 }
179 
180 T_DECL(aqm_qdelay, "This test checks the min/max/avg AQM queuing delay", T_META_TAG_VM_PREFERRED)
181 {
182 	T_SETUPBEGIN;
183 
184 	// Create tun device with IPv4 address
185 	int tun_fd = create_tun();
186 
187 	char ifname[IFXNAMSIZ];
188 	socklen_t optlen = IFNAMSIZ;
189 	T_QUIET; T_ASSERT_POSIX_SUCCESS(getsockopt(tun_fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, ifname, &optlen), NULL);
190 	T_ASSERT_TRUE(ifname[optlen - 1] == '\0', NULL);
191 	T_LOG("Created interface %s", ifname);
192 
193 	uint32_t ifaddr = (10 << 24) | ((unsigned)getpid() & 0xffff) << 8 | 160;
194 	struct in_addr tun_addr1, tun_addr2, mask;
195 	tun_addr1.s_addr = htonl(ifaddr);
196 	tun_addr2.s_addr = htonl(ifaddr + 1);
197 	mask.s_addr = htonl(0xffffffff);
198 
199 	ifnet_add_addr4(ifname, &tun_addr1, &mask, &tun_addr2);
200 
201 	// Create UDP socket to send
202 	int sock_fd;
203 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sock_fd = socket(AF_INET, SOCK_DGRAM, 0), NULL);
204 	struct sockaddr_in sin;
205 	memset(&sin, 0, sizeof(sin));
206 	sin.sin_len = sizeof(sin);
207 	sin.sin_family = AF_INET;
208 	sin.sin_addr = tun_addr1;
209 
210 	T_QUIET; T_ASSERT_POSIX_SUCCESS(bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin)), NULL);
211 
212 	struct sockaddr_in dest;
213 	memset(&sin, 0, sizeof(dest));
214 	dest.sin_len = sizeof(dest);
215 	dest.sin_family = AF_INET;
216 	dest.sin_addr = tun_addr2;
217 	dest.sin_port = ntohs(12345);
218 
219 	// Setup the state for AQM stats
220 	int stats_fd = aqmstats_setup(ifname);
221 
222 	T_SETUPEND;
223 
224 	char min[32], max[32], avg[32];
225 	// Get the current value of min/max/avg qdelay
226 	if (scheduler == PKTSCHEDT_FQ_CODEL) {
227 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ioctl(stats_fd, SIOCGIFQUEUESTATS, (char *)&ifqr), NULL);
228 		nsec_to_str(ifcqs->ifqs_fq_codel_stats.fcls_min_qdelay, min);
229 		nsec_to_str(ifcqs->ifqs_fq_codel_stats.fcls_max_qdelay, max);
230 		nsec_to_str(ifcqs->ifqs_fq_codel_stats.fcls_avg_qdelay, avg);
231 
232 		T_LOG("min/max/avg qdelay %10s    %10s    %10s", min, max, avg);
233 	}
234 
235 	// Send data
236 	T_LOG("Sending 10 UDP packets...");
237 	uint8_t content[0x578] = {0};
238 	for (int i = 0; i < 5; i++) {
239 		sendto(sock_fd, content, sizeof(content), 0, (struct sockaddr *)&dest,
240 		    (socklen_t) sizeof(dest));
241 		usleep(1000);
242 	}
243 
244 	// Get the current value of min/max/avg qdelay
245 	if (scheduler == PKTSCHEDT_FQ_CODEL) {
246 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ioctl(stats_fd, SIOCGIFQUEUESTATS, (char *)&ifqr), NULL);
247 		nsec_to_str(ifcqs->ifqs_fq_codel_stats.fcls_min_qdelay, min);
248 		nsec_to_str(ifcqs->ifqs_fq_codel_stats.fcls_max_qdelay, max);
249 		nsec_to_str(ifcqs->ifqs_fq_codel_stats.fcls_avg_qdelay, avg);
250 
251 		T_LOG("min/max/avg qdelay %10s    %10s    %10s", min, max, avg);
252 		T_ASSERT_TRUE(ifcqs->ifqs_fq_codel_stats.fcls_min_qdelay <= ifcqs->ifqs_fq_codel_stats.fcls_avg_qdelay &&
253 		    ifcqs->ifqs_fq_codel_stats.fcls_min_qdelay <= ifcqs->ifqs_fq_codel_stats.fcls_max_qdelay, "min qdelay check");
254 		T_ASSERT_TRUE(ifcqs->ifqs_fq_codel_stats.fcls_avg_qdelay <= ifcqs->ifqs_fq_codel_stats.fcls_max_qdelay, "avg qdelay check");
255 	}
256 
257 	aqmstats_cleanup();
258 	// Close socket and utun device
259 	T_QUIET; T_ASSERT_POSIX_SUCCESS(close(sock_fd), NULL);
260 	T_QUIET; T_ASSERT_POSIX_SUCCESS(close(tun_fd), NULL);
261 }
262