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