Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * It is possible to use SO_REUSEPORT to open multiple sockets bound to
4 * equivalent local addresses using AF_INET and AF_INET6 at the same time. If
5 * the AF_INET6 socket has IPV6_V6ONLY set, it's clear which socket should
6 * receive a given incoming packet. However, when it is not set, incoming v4
7 * packets should prefer the AF_INET socket(s). This behavior was defined with
8 * the original SO_REUSEPORT implementation, but broke with
9 * e32ea7e74727 ("soreuseport: fast reuseport UDP socket selection")
10 * This test creates these mixed AF_INET/AF_INET6 sockets and asserts the
11 * AF_INET preference for v4 packets.
12 */
13
14#define _GNU_SOURCE
15
16#include <arpa/inet.h>
17#include <errno.h>
18#include <error.h>
19#include <linux/in.h>
20#include <linux/unistd.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/epoll.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <unistd.h>
28#include <sched.h>
29
30static const int PORT = 8888;
31
32static void build_rcv_fd(int family, int proto, int *rcv_fds, int count)
33{
34 struct sockaddr_storage addr;
35 struct sockaddr_in *addr4;
36 struct sockaddr_in6 *addr6;
37 int opt, i;
38
39 switch (family) {
40 case AF_INET:
41 addr4 = (struct sockaddr_in *)&addr;
42 addr4->sin_family = AF_INET;
43 addr4->sin_addr.s_addr = htonl(INADDR_ANY);
44 addr4->sin_port = htons(PORT);
45 break;
46 case AF_INET6:
47 addr6 = (struct sockaddr_in6 *)&addr;
48 addr6->sin6_family = AF_INET6;
49 addr6->sin6_addr = in6addr_any;
50 addr6->sin6_port = htons(PORT);
51 break;
52 default:
53 error(1, 0, "Unsupported family %d", family);
54 }
55
56 for (i = 0; i < count; ++i) {
57 rcv_fds[i] = socket(family, proto, 0);
58 if (rcv_fds[i] < 0)
59 error(1, errno, "failed to create receive socket");
60
61 opt = 1;
62 if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt,
63 sizeof(opt)))
64 error(1, errno, "failed to set SO_REUSEPORT");
65
66 if (bind(rcv_fds[i], (struct sockaddr *)&addr, sizeof(addr)))
67 error(1, errno, "failed to bind receive socket");
68
69 if (proto == SOCK_STREAM && listen(rcv_fds[i], 10))
70 error(1, errno, "failed to listen on receive port");
71 }
72}
73
74static void send_from_v4(int proto)
75{
76 struct sockaddr_in saddr, daddr;
77 int fd;
78
79 saddr.sin_family = AF_INET;
80 saddr.sin_addr.s_addr = htonl(INADDR_ANY);
81 saddr.sin_port = 0;
82
83 daddr.sin_family = AF_INET;
84 daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
85 daddr.sin_port = htons(PORT);
86
87 fd = socket(AF_INET, proto, 0);
88 if (fd < 0)
89 error(1, errno, "failed to create send socket");
90
91 if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)))
92 error(1, errno, "failed to bind send socket");
93
94 if (connect(fd, (struct sockaddr *)&daddr, sizeof(daddr)))
95 error(1, errno, "failed to connect send socket");
96
97 if (send(fd, "a", 1, 0) < 0)
98 error(1, errno, "failed to send message");
99
100 close(fd);
101}
102
103static int receive_once(int epfd, int proto)
104{
105 struct epoll_event ev;
106 int i, fd;
107 char buf[8];
108
109 i = epoll_wait(epfd, &ev, 1, -1);
110 if (i < 0)
111 error(1, errno, "epoll_wait failed");
112
113 if (proto == SOCK_STREAM) {
114 fd = accept(ev.data.fd, NULL, NULL);
115 if (fd < 0)
116 error(1, errno, "failed to accept");
117 i = recv(fd, buf, sizeof(buf), 0);
118 close(fd);
119 } else {
120 i = recv(ev.data.fd, buf, sizeof(buf), 0);
121 }
122
123 if (i < 0)
124 error(1, errno, "failed to recv");
125
126 return ev.data.fd;
127}
128
129static void test(int *rcv_fds, int count, int proto)
130{
131 struct epoll_event ev;
132 int epfd, i, test_fd;
133 int test_family;
134 socklen_t len;
135
136 epfd = epoll_create(1);
137 if (epfd < 0)
138 error(1, errno, "failed to create epoll");
139
140 ev.events = EPOLLIN;
141 for (i = 0; i < count; ++i) {
142 ev.data.fd = rcv_fds[i];
143 if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev))
144 error(1, errno, "failed to register sock epoll");
145 }
146
147 send_from_v4(proto);
148
149 test_fd = receive_once(epfd, proto);
150 len = sizeof(test_family);
151 if (getsockopt(test_fd, SOL_SOCKET, SO_DOMAIN, &test_family, &len))
152 error(1, errno, "failed to read socket domain");
153 if (test_family != AF_INET)
154 error(1, 0, "expected to receive on v4 socket but got v6 (%d)",
155 test_family);
156
157 close(epfd);
158}
159
160static void setup_netns(void)
161{
162 if (unshare(CLONE_NEWNET))
163 error(1, errno, "failed to unshare netns");
164 if (system("ip link set lo up"))
165 error(1, 0, "failed to bring up lo interface in netns");
166}
167
168int main(void)
169{
170 int rcv_fds[32], i;
171
172 setup_netns();
173
174 fprintf(stderr, "---- UDP IPv4 created before IPv6 ----\n");
175 build_rcv_fd(AF_INET, SOCK_DGRAM, rcv_fds, 5);
176 build_rcv_fd(AF_INET6, SOCK_DGRAM, &(rcv_fds[5]), 5);
177 test(rcv_fds, 10, SOCK_DGRAM);
178 for (i = 0; i < 10; ++i)
179 close(rcv_fds[i]);
180
181 fprintf(stderr, "---- UDP IPv6 created before IPv4 ----\n");
182 build_rcv_fd(AF_INET6, SOCK_DGRAM, rcv_fds, 5);
183 build_rcv_fd(AF_INET, SOCK_DGRAM, &(rcv_fds[5]), 5);
184 test(rcv_fds, 10, SOCK_DGRAM);
185 for (i = 0; i < 10; ++i)
186 close(rcv_fds[i]);
187
188 /* NOTE: UDP socket lookups traverse a different code path when there
189 * are > 10 sockets in a group.
190 */
191 fprintf(stderr, "---- UDP IPv4 created before IPv6 (large) ----\n");
192 build_rcv_fd(AF_INET, SOCK_DGRAM, rcv_fds, 16);
193 build_rcv_fd(AF_INET6, SOCK_DGRAM, &(rcv_fds[16]), 16);
194 test(rcv_fds, 32, SOCK_DGRAM);
195 for (i = 0; i < 32; ++i)
196 close(rcv_fds[i]);
197
198 fprintf(stderr, "---- UDP IPv6 created before IPv4 (large) ----\n");
199 build_rcv_fd(AF_INET6, SOCK_DGRAM, rcv_fds, 16);
200 build_rcv_fd(AF_INET, SOCK_DGRAM, &(rcv_fds[16]), 16);
201 test(rcv_fds, 32, SOCK_DGRAM);
202 for (i = 0; i < 32; ++i)
203 close(rcv_fds[i]);
204
205 fprintf(stderr, "---- TCP IPv4 created before IPv6 ----\n");
206 build_rcv_fd(AF_INET, SOCK_STREAM, rcv_fds, 5);
207 build_rcv_fd(AF_INET6, SOCK_STREAM, &(rcv_fds[5]), 5);
208 test(rcv_fds, 10, SOCK_STREAM);
209 for (i = 0; i < 10; ++i)
210 close(rcv_fds[i]);
211
212 fprintf(stderr, "---- TCP IPv6 created before IPv4 ----\n");
213 build_rcv_fd(AF_INET6, SOCK_STREAM, rcv_fds, 5);
214 build_rcv_fd(AF_INET, SOCK_STREAM, &(rcv_fds[5]), 5);
215 test(rcv_fds, 10, SOCK_STREAM);
216 for (i = 0; i < 10; ++i)
217 close(rcv_fds[i]);
218
219 fprintf(stderr, "SUCCESS\n");
220 return 0;
221}