~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/net/reuseport_addr_any.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 
  3 /* Test that sockets listening on a specific address are preferred
  4  * over sockets listening on addr_any.
  5  */
  6 
  7 #define _GNU_SOURCE
  8 
  9 #include <arpa/inet.h>
 10 #include <errno.h>
 11 #include <error.h>
 12 #include <linux/dccp.h>
 13 #include <linux/in.h>
 14 #include <linux/unistd.h>
 15 #include <stdbool.h>
 16 #include <stdio.h>
 17 #include <stdlib.h>
 18 #include <string.h>
 19 #include <sys/epoll.h>
 20 #include <sys/types.h>
 21 #include <sys/socket.h>
 22 #include <unistd.h>
 23 
 24 #ifndef SOL_DCCP
 25 #define SOL_DCCP 269
 26 #endif
 27 
 28 static const char *IP4_ADDR = "127.0.0.1";
 29 static const char *IP6_ADDR = "::1";
 30 static const char *IP4_MAPPED6 = "::ffff:127.0.0.1";
 31 
 32 static const int PORT = 8888;
 33 
 34 static void build_rcv_fd(int family, int proto, int *rcv_fds, int count,
 35                          const char *addr_str)
 36 {
 37         struct sockaddr_in  addr4 = {0};
 38         struct sockaddr_in6 addr6 = {0};
 39         struct sockaddr *addr;
 40         int opt, i, sz;
 41 
 42         memset(&addr, 0, sizeof(addr));
 43 
 44         switch (family) {
 45         case AF_INET:
 46                 addr4.sin_family = family;
 47                 if (!addr_str)
 48                         addr4.sin_addr.s_addr = htonl(INADDR_ANY);
 49                 else if (!inet_pton(family, addr_str, &addr4.sin_addr.s_addr))
 50                         error(1, errno, "inet_pton failed: %s", addr_str);
 51                 addr4.sin_port = htons(PORT);
 52                 sz = sizeof(addr4);
 53                 addr = (struct sockaddr *)&addr4;
 54                 break;
 55         case AF_INET6:
 56                 addr6.sin6_family = AF_INET6;
 57                 if (!addr_str)
 58                         addr6.sin6_addr = in6addr_any;
 59                 else if (!inet_pton(family, addr_str, &addr6.sin6_addr))
 60                         error(1, errno, "inet_pton failed: %s", addr_str);
 61                 addr6.sin6_port = htons(PORT);
 62                 sz = sizeof(addr6);
 63                 addr = (struct sockaddr *)&addr6;
 64                 break;
 65         default:
 66                 error(1, 0, "Unsupported family %d", family);
 67                 /* clang does not recognize error() above as terminating
 68                  * the program, so it complains that saddr, sz are
 69                  * not initialized when this code path is taken. Silence it.
 70                  */
 71                 return;
 72         }
 73 
 74         for (i = 0; i < count; ++i) {
 75                 rcv_fds[i] = socket(family, proto, 0);
 76                 if (rcv_fds[i] < 0)
 77                         error(1, errno, "failed to create receive socket");
 78 
 79                 opt = 1;
 80                 if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt,
 81                                sizeof(opt)))
 82                         error(1, errno, "failed to set SO_REUSEPORT");
 83 
 84                 if (bind(rcv_fds[i], addr, sz))
 85                         error(1, errno, "failed to bind receive socket");
 86 
 87                 if (proto == SOCK_STREAM && listen(rcv_fds[i], 10))
 88                         error(1, errno, "tcp: failed to listen on receive port");
 89                 else if (proto == SOCK_DCCP) {
 90                         if (setsockopt(rcv_fds[i], SOL_DCCP,
 91                                         DCCP_SOCKOPT_SERVICE,
 92                                         &(int) {htonl(42)}, sizeof(int)))
 93                                 error(1, errno, "failed to setsockopt");
 94 
 95                         if (listen(rcv_fds[i], 10))
 96                                 error(1, errno, "dccp: failed to listen on receive port");
 97                 }
 98         }
 99 }
100 
101 static int connect_and_send(int family, int proto)
102 {
103         struct sockaddr_in  saddr4 = {0};
104         struct sockaddr_in  daddr4 = {0};
105         struct sockaddr_in6 saddr6 = {0};
106         struct sockaddr_in6 daddr6 = {0};
107         struct sockaddr *saddr, *daddr;
108         int fd, sz;
109 
110         switch (family) {
111         case AF_INET:
112                 saddr4.sin_family = AF_INET;
113                 saddr4.sin_addr.s_addr = htonl(INADDR_ANY);
114                 saddr4.sin_port = 0;
115 
116                 daddr4.sin_family = AF_INET;
117                 if (!inet_pton(family, IP4_ADDR, &daddr4.sin_addr.s_addr))
118                         error(1, errno, "inet_pton failed: %s", IP4_ADDR);
119                 daddr4.sin_port = htons(PORT);
120 
121                 sz = sizeof(saddr4);
122                 saddr = (struct sockaddr *)&saddr4;
123                 daddr = (struct sockaddr *)&daddr4;
124         break;
125         case AF_INET6:
126                 saddr6.sin6_family = AF_INET6;
127                 saddr6.sin6_addr = in6addr_any;
128 
129                 daddr6.sin6_family = AF_INET6;
130                 if (!inet_pton(family, IP6_ADDR, &daddr6.sin6_addr))
131                         error(1, errno, "inet_pton failed: %s", IP6_ADDR);
132                 daddr6.sin6_port = htons(PORT);
133 
134                 sz = sizeof(saddr6);
135                 saddr = (struct sockaddr *)&saddr6;
136                 daddr = (struct sockaddr *)&daddr6;
137         break;
138         default:
139                 error(1, 0, "Unsupported family %d", family);
140                 /* clang does not recognize error() above as terminating
141                  * the program, so it complains that saddr, daddr, sz are
142                  * not initialized when this code path is taken. Silence it.
143                  */
144                 return -1;
145         }
146 
147         fd = socket(family, proto, 0);
148         if (fd < 0)
149                 error(1, errno, "failed to create send socket");
150 
151         if (proto == SOCK_DCCP &&
152                 setsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE,
153                                 &(int){htonl(42)}, sizeof(int)))
154                 error(1, errno, "failed to setsockopt");
155 
156         if (bind(fd, saddr, sz))
157                 error(1, errno, "failed to bind send socket");
158 
159         if (connect(fd, daddr, sz))
160                 error(1, errno, "failed to connect send socket");
161 
162         if (send(fd, "a", 1, 0) < 0)
163                 error(1, errno, "failed to send message");
164 
165         return fd;
166 }
167 
168 static int receive_once(int epfd, int proto)
169 {
170         struct epoll_event ev;
171         int i, fd;
172         char buf[8];
173 
174         i = epoll_wait(epfd, &ev, 1, 3);
175         if (i < 0)
176                 error(1, errno, "epoll_wait failed");
177 
178         if (proto == SOCK_STREAM || proto == SOCK_DCCP) {
179                 fd = accept(ev.data.fd, NULL, NULL);
180                 if (fd < 0)
181                         error(1, errno, "failed to accept");
182                 i = recv(fd, buf, sizeof(buf), 0);
183                 close(fd);
184         } else {
185                 i = recv(ev.data.fd, buf, sizeof(buf), 0);
186         }
187 
188         if (i < 0)
189                 error(1, errno, "failed to recv");
190 
191         return ev.data.fd;
192 }
193 
194 static void test(int *rcv_fds, int count, int family, int proto, int fd)
195 {
196         struct epoll_event ev;
197         int epfd, i, send_fd, recv_fd;
198 
199         epfd = epoll_create(1);
200         if (epfd < 0)
201                 error(1, errno, "failed to create epoll");
202 
203         ev.events = EPOLLIN;
204         for (i = 0; i < count; ++i) {
205                 ev.data.fd = rcv_fds[i];
206                 if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev))
207                         error(1, errno, "failed to register sock epoll");
208         }
209 
210         send_fd = connect_and_send(family, proto);
211 
212         recv_fd = receive_once(epfd, proto);
213         if (recv_fd != fd)
214                 error(1, 0, "received on an unexpected socket");
215 
216         close(send_fd);
217         close(epfd);
218 }
219 
220 
221 static void run_one_test(int fam_send, int fam_rcv, int proto,
222                          const char *addr_str)
223 {
224         /* Below we test that a socket listening on a specific address
225          * is always selected in preference over a socket listening
226          * on addr_any. Bugs where this is not the case often result
227          * in sockets created first or last to get picked. So below
228          * we make sure that there are always addr_any sockets created
229          * before and after a specific socket is created.
230          */
231         int rcv_fds[10], i;
232 
233         build_rcv_fd(AF_INET, proto, rcv_fds, 2, NULL);
234         build_rcv_fd(AF_INET6, proto, rcv_fds + 2, 2, NULL);
235         build_rcv_fd(fam_rcv, proto, rcv_fds + 4, 1, addr_str);
236         build_rcv_fd(AF_INET, proto, rcv_fds + 5, 2, NULL);
237         build_rcv_fd(AF_INET6, proto, rcv_fds + 7, 2, NULL);
238         test(rcv_fds, 9, fam_send, proto, rcv_fds[4]);
239         for (i = 0; i < 9; ++i)
240                 close(rcv_fds[i]);
241         fprintf(stderr, "pass\n");
242 }
243 
244 static void test_proto(int proto, const char *proto_str)
245 {
246         if (proto == SOCK_DCCP) {
247                 int test_fd;
248 
249                 test_fd = socket(AF_INET, proto, 0);
250                 if (test_fd < 0) {
251                         if (errno == ESOCKTNOSUPPORT) {
252                                 fprintf(stderr, "DCCP not supported: skipping DCCP tests\n");
253                                 return;
254                         } else
255                                 error(1, errno, "failed to create a DCCP socket");
256                 }
257                 close(test_fd);
258         }
259 
260         fprintf(stderr, "%s IPv4 ... ", proto_str);
261         run_one_test(AF_INET, AF_INET, proto, IP4_ADDR);
262 
263         fprintf(stderr, "%s IPv6 ... ", proto_str);
264         run_one_test(AF_INET6, AF_INET6, proto, IP6_ADDR);
265 
266         fprintf(stderr, "%s IPv4 mapped to IPv6 ... ", proto_str);
267         run_one_test(AF_INET, AF_INET6, proto, IP4_MAPPED6);
268 }
269 
270 int main(void)
271 {
272         test_proto(SOCK_DGRAM, "UDP");
273         test_proto(SOCK_STREAM, "TCP");
274         test_proto(SOCK_DCCP, "DCCP");
275 
276         fprintf(stderr, "SUCCESS\n");
277         return 0;
278 }
279 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php