xref: /xnu-11417.121.6/tests/ip_tos_35768492.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1 //
2 //  ip_tos_35768492.c
3 //  tests
4 //
5 //  Test that setting the TOS byte via IP_TOS/IPV6_TCLASS works equally
6 //  between setsockopt and cmsghdr.
7 //
8 //  Copyright (c) 2019-2024 Apple Inc. All rights reserved.
9 //
10 
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <stdbool.h>
15 #include <errno.h>
16 #include <darwintest.h>
17 
18 #define IPV6_USE_MIN_MTU        42 /* bool; send packets at the minimum MTU */
19 
20 T_GLOBAL_META(T_META_NAMESPACE("xnu.net"));
21 
22 typedef enum _tos_method_t {
23 	use_none,
24 	use_header_socket,
25 	use_setsockopt,
26 	use_header
27 } tos_method_t;
28 
29 static void
my_sendmsg(int sock,int level,int type,char * data,uint8_t tos_byte,tos_method_t method,const char * description)30 my_sendmsg(int sock, int level, int type, char *data, uint8_t tos_byte,
31     tos_method_t method, const char *description)
32 {
33 	struct msghdr msgvec = {0};
34 	struct iovec msg = {0};
35 	uint8_t ctrl[CMSG_SPACE(sizeof(int))] = {0};
36 	msg.iov_base = (void *)data;
37 	msg.iov_len = strlen(data);
38 	msgvec.msg_name = 0;
39 	msgvec.msg_namelen = 0;
40 	msgvec.msg_iov = &msg;
41 	msgvec.msg_iovlen = 1;
42 
43 	switch (method) {
44 	case use_header_socket: {
45 		int off = 0;
46 		msgvec.msg_control = &ctrl;
47 		msgvec.msg_controllen = sizeof(ctrl);
48 		struct cmsghdr * const cmsg = CMSG_FIRSTHDR(&msgvec);
49 		cmsg->cmsg_level = level;
50 		cmsg->cmsg_type = IPV6_USE_MIN_MTU;
51 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
52 		*(int *)CMSG_DATA(cmsg) = off;
53 		// fallthrough to also set socket opt
54 	}
55 	case use_setsockopt: {
56 		int val = tos_byte;
57 		int err = setsockopt(sock, level, type, &val, sizeof(val));
58 		T_ASSERT_TRUE(err == 0, "Set TOS field using setsockopt()");
59 		break;
60 	}
61 
62 	case use_header: {
63 		msgvec.msg_control = &ctrl;
64 		msgvec.msg_controllen = sizeof(ctrl);
65 		struct cmsghdr * const cmsg = CMSG_FIRSTHDR(&msgvec);
66 		cmsg->cmsg_level = level;
67 		cmsg->cmsg_type = type;
68 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
69 		*(int *)CMSG_DATA(cmsg) = tos_byte;
70 		break;
71 	}
72 
73 	default:
74 		break;
75 	}
76 
77 	ssize_t num = sendmsg(sock, &msgvec, 0);
78 	T_ASSERT_TRUE(num > 0, description);
79 }
80 
81 static void
my_recvmsg(int sock,int level,int type,const char * expected_data,uint8_t expected_tos_byte,const char * description)82 my_recvmsg(int sock, int level, int type, const char *expected_data,
83     uint8_t expected_tos_byte, const char *description)
84 {
85 #define BUF_SIZE 1024
86 	struct msghdr msgvec = {0};
87 	struct sockaddr_in peer = {0};
88 	struct iovec msg = {0};
89 	uint32_t ctrl_buf[256 / sizeof(uint32_t)] = {0};
90 	uint8_t buf[BUF_SIZE] = {0};
91 	msg.iov_base = buf;
92 	msg.iov_len = BUF_SIZE;
93 	msgvec.msg_name = &peer;
94 	msgvec.msg_namelen = sizeof(peer);
95 	msgvec.msg_iov = &msg;
96 	msgvec.msg_iovlen = 1;
97 	msgvec.msg_control = (struct cmsghdr *)ctrl_buf;
98 	msgvec.msg_controllen = sizeof(ctrl_buf);
99 
100 	ssize_t num = recvmsg(sock, &msgvec, 0);
101 	T_ASSERT_GT_INT(num, 0, NULL);
102 	int cmp = memcmp(buf, expected_data, strlen(expected_data));
103 	T_ASSERT_EQ_INT(cmp, 0, NULL);
104 
105 	uint8_t tos_byte = 0;
106 	for (struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msgvec); cmsg; cmsg = CMSG_NXTHDR(&msgvec, cmsg)) {
107 		if (cmsg->cmsg_level == level &&
108 		    cmsg->cmsg_len &&
109 		    cmsg->cmsg_type == type) {
110 			tos_byte = (uint8_t)*(int *)CMSG_DATA(cmsg);
111 			break;
112 		}
113 	}
114 
115 	T_ASSERT_EQ_INT(tos_byte, expected_tos_byte, description);
116 }
117 
118 T_DECL(ip_tos, "IPv4 TOS")
119 {
120 	int server4 = socket(AF_INET, SOCK_DGRAM, 0);
121 	T_ASSERT_TRUE(server4 >= 0, "Create server socket");
122 
123 	int val = 1;
124 	int err = setsockopt(server4, IPPROTO_IP, IP_RECVTOS, &val, sizeof(val));
125 	T_ASSERT_TRUE(err == 0, "setsockopt(IPPROTO_IP, IP_RECVTOS)");
126 
127 	int client4 = socket(AF_INET, SOCK_DGRAM, 0);
128 	T_ASSERT_TRUE(client4 >= 0, "Create client socket");
129 
130 	struct sockaddr_in addr;
131 	memset(&addr, 0, sizeof(addr));
132 	addr.sin_family = AF_INET;
133 	addr.sin_len = sizeof(addr);
134 	addr.sin_port = htons(8008);
135 	err = inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
136 	T_ASSERT_TRUE(err == 1, NULL);
137 	err = bind(server4, (struct sockaddr *)&addr, sizeof(addr));
138 	T_ASSERT_TRUE(err == 0, NULL);
139 	err = connect(client4, (struct sockaddr *)&addr, sizeof(addr));
140 	T_ASSERT_TRUE(err == 0, "connect client socket");
141 
142 	my_sendmsg(client4, IPPROTO_IP, IP_TOS, "hello", 0xff, use_setsockopt, "IPv4 setsockopt 0xff");
143 	my_recvmsg(server4, IPPROTO_IP, IP_RECVTOS, "hello", 0xff, "IPv4 setsockopt 0xff");
144 	my_sendmsg(client4, IPPROTO_IP, IP_TOS, "hello", 0xd0, use_setsockopt, "IPv4 setsocktop 0xd0");
145 	my_recvmsg(server4, IPPROTO_IP, IP_RECVTOS, "hello", 0xd0, "IPv4 setsockopt 0xd0");
146 	my_sendmsg(client4, IPPROTO_IP, IP_TOS, "hello", 0xff, use_header, "IPv4 header 0xff");
147 	my_recvmsg(server4, IPPROTO_IP, IP_RECVTOS, "hello", 0xff, "IPv4 header 0xff");
148 	my_sendmsg(client4, IPPROTO_IP, IP_TOS, "hello", 0xd0, use_header, "IPv4 header 0xd0");
149 	my_recvmsg(server4, IPPROTO_IP, IP_RECVTOS, "hello", 0xd0, "IPv4 header 0xd0");
150 }
151 
152 T_DECL(ip6_tclass, "IPv6 TCLASS")
153 {
154 	int err;
155 	int val = 1;
156 	int server6;
157 
158 	server6 = socket(AF_INET6, SOCK_DGRAM, 0);
159 	T_ASSERT_TRUE(server6 >= 0, "create server socket");
160 
161 	err = setsockopt(server6, IPPROTO_IPV6, IPV6_RECVTCLASS, &val, sizeof(val));
162 	T_ASSERT_TRUE(err == 0, "setsockopt(IPPROTO_IPV6, IPV6_RECVTCLASS) failed");
163 
164 	int client6 = socket(AF_INET6, SOCK_DGRAM, 0);
165 	T_ASSERT_TRUE(client6 >= 0, "create client socket");
166 
167 	struct sockaddr_in6 addr6;
168 	memset(&addr6, 0, sizeof(addr6));
169 	addr6.sin6_family = AF_INET6;
170 	addr6.sin6_len = sizeof(addr6);
171 	addr6.sin6_port = htons(8009);
172 
173 	err = inet_pton(AF_INET6, "::1", &(addr6.sin6_addr));
174 	T_ASSERT_TRUE(err == 1, "convert address");
175 	err = bind(server6, (struct sockaddr *)&addr6, sizeof(addr6));
176 	T_ASSERT_TRUE(err == 0, "bind server socket");
177 	err = connect(client6, (struct sockaddr *)&addr6, sizeof(addr6));
178 	T_ASSERT_TRUE(err == 0, "connect client socket");
179 
180 	my_sendmsg(client6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xff, use_setsockopt, "IPv6 setsockopt 0xff");
181 	my_recvmsg(server6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xff, "IPv6 setsockopt 0xff");
182 	my_sendmsg(client6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xd0, use_setsockopt, "IPv6 setsockopt 0xd0");
183 	my_recvmsg(server6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xd0, "IPv6 setsockopt 0xd0");
184 	my_sendmsg(client6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xfe, use_header, "IPv6 header 0xff");
185 	my_recvmsg(server6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xfe, "IPv6 header 0xff");
186 	my_sendmsg(client6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xa0, use_header, "IPv6 header 0xd0");
187 	my_recvmsg(server6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xa0, "IPv6 header 0xd0");
188 
189 	my_sendmsg(client6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0x60, use_header_socket, "IPv6 header & socket 0x60");
190 	my_recvmsg(server6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0x60, "IPv6 header 0x60");
191 
192 	int dscp = 0x59;
193 	setsockopt(client6, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof(dscp));
194 
195 	my_sendmsg(client6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xa0, use_header, "IPv6 header 0xd0");
196 	my_recvmsg(server6, IPPROTO_IPV6, IPV6_TCLASS, "hello", 0xa0, "IPv6 header 0xd0");
197 }
198