: server IP address\n");
59 | printf(" --port/-p : server port\n");
60 | printf(" --pkey/-k : private key file\n");
61 | printf(" --cert/-c : certificate file\n");
62 | printf(" --psk/-i : pre-shared key file\n");
63 | printf(" --ca/-s : ca file\n");
64 | printf(" --help/-h : show help\n");
65 | printf(" --msg_len/-m : msg_len to send\n");
66 | printf(" --tot_len/-t : tot_len to send\n");
67 | printf(" --no_crypt/-x : disable 1rtt encryption\n\n");
68 | }
69 |
70 | static int parse_options(int argc, char *argv[], struct options *opts)
71 | {
72 | int c, option_index = 0;
73 |
74 | while (1) {
75 | c = getopt_long(argc, argv, "la:p:m:t:k:c:s:i:xh", long_options, &option_index);
76 | if (c == -1)
77 | break;
78 |
79 | switch (c) {
80 | case 'l':
81 | opts->is_serv = 1;
82 | break;
83 | case 'a':
84 | opts->addr = optarg;
85 | break;
86 | case 'p':
87 | opts->port = optarg;
88 | break;
89 | case 'c':
90 | opts->cert = optarg;
91 | break;
92 | case 's':
93 | opts->ca = optarg;
94 | break;
95 | case 'i':
96 | case 'k':
97 | opts->pkey = optarg;
98 | break;
99 | case 'm':
100 | opts->msg_len = atoi(optarg);
101 | if (opts->msg_len > SND_MSG_LEN)
102 | return -1;
103 | break;
104 | case 't':
105 | opts->tot_len = atoll(optarg);
106 | if (opts->tot_len > TOT_LEN)
107 | return -1;
108 | break;
109 | case 'x':
110 | opts->no_crypt = 1;
111 | break;
112 | case 'h':
113 | print_usage(argv[0]);
114 | return 1;
115 | default:
116 | return -1;
117 | }
118 | }
119 |
120 | if (opts->is_serv && (!opts->cert && !opts->pkey))
121 | return -1;
122 | return 0;
123 | }
124 |
125 | static int do_server(struct options *opts)
126 | {
127 | struct quic_transport_param param = {};
128 | uint32_t flags = 0, addrlen, len = 0;
129 | struct sockaddr_storage ra = {};
130 | struct sockaddr_in la = {};
131 | int ret, sockfd, listenfd;
132 | struct addrinfo *rp;
133 | int64_t sid = 0;
134 |
135 | if (getaddrinfo(opts->addr, opts->port, NULL, &rp)) {
136 | printf("getaddrinfo error\n");
137 | return -1;
138 | }
139 |
140 | if (rp->ai_family == AF_INET6) {
141 | struct sockaddr_in6 la = {};
142 |
143 | la.sin6_family = AF_INET6;
144 | la.sin6_port = htons(atoi(opts->port));
145 | inet_pton(AF_INET6, opts->addr, &la.sin6_addr);
146 | listenfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_QUIC);
147 | if (listenfd < 0) {
148 | printf("socket create failed\n");
149 | return -1;
150 | }
151 | if (bind(listenfd, (struct sockaddr *)&la, sizeof(la))) {
152 | printf("socket bind failed\n");
153 | return -1;
154 | }
155 | goto listen;
156 | }
157 |
158 | la.sin_family = AF_INET;
159 | la.sin_port = htons(atoi(opts->port));
160 | inet_pton(AF_INET, opts->addr, &la.sin_addr.s_addr);
161 | listenfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_QUIC);
162 | if (listenfd < 0) {
163 | printf("socket create failed\n");
164 | return -1;
165 | }
166 | if (bind(listenfd, (struct sockaddr *)&la, sizeof(la))) {
167 | printf("socket bind failed\n");
168 | return -1;
169 | }
170 |
171 | listen:
172 | param.grease_quic_bit = 1;
173 | param.stateless_reset = 1;
174 | param.max_idle_timeout = 120 * SECONDS;
175 | param.disable_1rtt_encryption = opts->no_crypt;
176 | if (setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, sizeof(param))) {
177 | printf("socket setsockopt transport param failed\n");
178 | return -1;
179 | }
180 | if (setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpn, strlen(alpn))) {
181 | printf("socket setsockopt alpn failed\n");
182 | return -1;
183 | }
184 |
185 | if (listen(listenfd, 1)) {
186 | printf("socket listen failed\n");
187 | return -1;
188 | }
189 |
190 | loop:
191 | printf("Waiting for New Socket...\n");
192 | addrlen = sizeof(ra);
193 | sockfd = accept(listenfd, (struct sockaddr *)&ra, &addrlen);
194 | if (sockfd < 0) {
195 | printf("socket accept failed %d %d\n", errno, sockfd);
196 | return -1;
197 | }
198 |
199 | printf("accept %d\n", sockfd);
200 |
201 | if (quic_server_handshake(sockfd, opts->pkey, opts->cert, alpn))
202 | return -1;
203 |
204 | printf("HANDSHAKE DONE\n");
205 |
206 | while (1) {
207 | ret = quic_recvmsg(sockfd, &rcv_msg, opts->msg_len * 16, &sid, &flags);
208 | if (ret == -1) {
209 | printf("recv error %d %d\n", ret, errno);
210 | return 1;
211 | }
212 | len += ret;
213 | usleep(20);
214 | if (flags & MSG_STREAM_FIN)
215 | break;
216 | printf(" recv len: %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags);
217 | }
218 |
219 | printf("RECV DONE: tot_len %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags);
220 |
221 | flags = MSG_STREAM_FIN;
222 | strcpy(snd_msg, "recv done");
223 | ret = quic_sendmsg(sockfd, snd_msg, strlen(snd_msg), sid, flags);
224 | if (ret == -1) {
225 | printf("send %d %d\n", ret, errno);
226 | return -1;
227 | }
228 |
229 | flags = 0;
230 | quic_recvmsg(sockfd, &rcv_msg, sizeof(rcv_msg), &sid, &flags);
231 |
232 | close(sockfd);
233 | printf("CLOSE DONE\n");
234 |
235 | len = 0;
236 | goto loop;
237 | return 0;
238 | }
239 |
240 | static uint64_t get_now_time()
241 | {
242 | struct timespec t ;
243 | clock_gettime ( CLOCK_REALTIME , & t ) ;
244 | return t.tv_sec * 1000 + ( t.tv_nsec + 500000 ) / 1000000 ;
245 | }
246 |
247 | static int do_client(struct options *opts)
248 | {
249 | struct quic_transport_param param = {};
250 | struct sockaddr_in ra = {};
251 | uint32_t len = 0, flags;
252 | struct addrinfo *rp;
253 | uint64_t start, end;
254 | int ret, sockfd;
255 | int64_t sid = 0;
256 | float rate;
257 |
258 | if (getaddrinfo(opts->addr, opts->port, NULL, &rp)) {
259 | printf("getaddrinfo error\n");
260 | return -1;
261 | }
262 |
263 | if (rp->ai_family == AF_INET6) {
264 | struct sockaddr_in6 ra = {};
265 |
266 | sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_QUIC);
267 | if (sockfd < 0) {
268 | printf("socket create failed\n");
269 | return -1;
270 | }
271 |
272 | param.max_idle_timeout = 120 * SECONDS;
273 | param.disable_1rtt_encryption = opts->no_crypt;
274 | if (setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, sizeof(param))) {
275 | printf("socket setsockopt transport param failed\n");
276 | return -1;
277 | }
278 |
279 | ra.sin6_family = AF_INET6;
280 | ra.sin6_port = htons(atoi(opts->port));
281 | inet_pton(AF_INET6, opts->addr, &ra.sin6_addr);
282 |
283 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) {
284 | printf("socket connect failed\n");
285 | return -1;
286 | }
287 | goto handshake;
288 | }
289 |
290 | sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_QUIC);
291 | if (sockfd < 0) {
292 | printf("socket create failed\n");
293 | return -1;
294 | }
295 |
296 | param.max_idle_timeout = 120 * SECONDS;
297 | param.disable_1rtt_encryption = opts->no_crypt;
298 | if (setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, sizeof(param))) {
299 | printf("socket setsockopt transport param failed\n");
300 | return -1;
301 | }
302 |
303 | ra.sin_family = AF_INET;
304 | ra.sin_port = htons(atoi(opts->port));
305 | inet_pton(AF_INET, opts->addr, &ra.sin_addr.s_addr);
306 |
307 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) {
308 | printf("socket connect failed\n");
309 | return -1;
310 | }
311 |
312 | handshake:
313 | if (quic_client_handshake(sockfd, opts->pkey, NULL, alpn))
314 | return -1;
315 |
316 | printf("HANDSHAKE DONE.\n");
317 |
318 | start = get_now_time();
319 | flags = MSG_STREAM_NEW; /* open stream when send first msg */
320 | ret = quic_sendmsg(sockfd, snd_msg, opts->msg_len, sid, flags);
321 | if (ret == -1) {
322 | printf("send %d %d\n", ret, errno);
323 | return -1;
324 | }
325 | len += ret;
326 | flags = 0;
327 | while (1) {
328 | ret = quic_sendmsg(sockfd, snd_msg, opts->msg_len, sid, flags);
329 | if (ret == -1) {
330 | printf("send %d %d\n", ret, errno);
331 | return -1;
332 | }
333 | len += ret;
334 | if (!(len % (opts->msg_len * 1024)))
335 | printf(" send len: %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags);
336 | if (len > opts->tot_len - opts->msg_len)
337 | break;
338 | }
339 | flags = MSG_STREAM_FIN; /* close stream when send last msg */
340 | ret = quic_sendmsg(sockfd, snd_msg, opts->msg_len, sid, flags);
341 | if (ret == -1) {
342 | printf("send %d %d\n", ret, errno);
343 | return -1;
344 | }
345 | len += ret;
346 | printf("SEND DONE: tot_len: %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags);
347 |
348 | memset(rcv_msg, 0, sizeof(rcv_msg));
349 | ret = quic_recvmsg(sockfd, rcv_msg, opts->msg_len * 16, &sid, &flags);
350 | if (ret == -1) {
351 | printf("recv error %d %d\n", ret, errno);
352 | return 1;
353 | }
354 | end = get_now_time();
355 | start = end - start;
356 | rate = ((float)opts->tot_len * 8 * 1000) / 1024 / start;
357 | if (rate < 1024)
358 | printf("ALL RECVD: %.1f Kbits/Sec\n", rate);
359 | else
360 | printf("ALL RECVD: %.1f Mbits/Sec\n", rate / 1024);
361 |
362 | close(sockfd);
363 | return 0;
364 | }
365 |
366 | int main(int argc, char *argv[])
367 | {
368 | struct options opts = {};
369 | int ret;
370 |
371 | opts.msg_len = SND_MSG_LEN;
372 | opts.tot_len = TOT_LEN;
373 | opts.addr = "::";
374 | opts.port = "1234";
375 |
376 | ret = parse_options(argc, argv, &opts);
377 | if (ret) {
378 | if (ret < 0)
379 | printf("parse options error\n");
380 | return -1;
381 | }
382 |
383 | quic_set_log_level(LOG_NOTICE);
384 |
385 | if (!opts.is_serv)
386 | return do_client(&opts);
387 |
388 | return do_server(&opts);
389 | }
390 |
--------------------------------------------------------------------------------
/tests/runtest.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | print_start()
4 | {
5 | echo ""
6 | echo "****** ["$1"] ******"
7 | echo ""
8 | sleep 1
9 | }
10 |
11 | daemon_stop()
12 | {
13 | [ "$1" != "" ] && pkill $1 > /dev/null 2>&1
14 | sleep 3
15 | }
16 |
17 | daemon_run()
18 | {
19 | $@ > /dev/null 2>&1 &
20 | sleep 2
21 | }
22 |
23 | cleanup()
24 | {
25 | exit_code=$?
26 | ip -6 addr del ::2/128 dev lo > /dev/null 2>&1
27 | ip addr del 127.0.0.2/8 dev lo > /dev/null 2>&1
28 | tc qdisc del dev lo root netem loss 30% > /dev/null 2>&1
29 | pkill func_test > /dev/null 2>&1
30 | pkill perf_test > /dev/null 2>&1
31 | pkill alpn_test > /dev/null 2>&1
32 | pkill ticket_test > /dev/null 2>&1
33 | pkill sample_test > /dev/null 2>&1
34 | pkill http3_test > /dev/null 2>&1
35 | rmmod quic_sample_test > /dev/null 2>&1
36 | rmmod quic > /dev/null 2>&1
37 | exit $exit_code
38 | }
39 |
40 | start_tests()
41 | {
42 | make || return 1
43 | setenforce 0 > /dev/null 2>&1
44 | modprobe -a udp_tunnel ip6_udp_tunnel || return 1
45 | if [ -f ../modules/net/quic/quic.ko ]; then
46 | [ -d /sys/module/quic ] || insmod ../modules/net/quic/quic.ko || return 1
47 | else
48 | modprobe quic || return 1
49 | fi
50 |
51 | print_start "Install Keys & Certificates"
52 | pushd keys/
53 | sh ca_cert_pkey_psk.sh || return 1
54 | if systemctl is-active --quiet tlshd; then
55 | sh ca_cert_pkey_psk.sh psk-keyring || return 1
56 | systemctl restart tlshd || return 1
57 | fi
58 | popd
59 | if [ -d /etc/pki/ca-trust/source/anchors/ ]; then
60 | install keys/ca-cert.pem /etc/pki/ca-trust/source/anchors/ca-cert.pem
61 | update-ca-trust
62 | elif [ -d /usr/local/share/ca-certificates/ ]; then
63 | install keys/ca-cert.pem /usr/local/share/ca-certificates/ca-cert.crt
64 | update-ca-certificates
65 | fi
66 | }
67 |
68 | done_tests()
69 | {
70 | echo ""
71 | echo "ALL TESTS DONE!"
72 | }
73 |
74 | http3_connect()
75 | {
76 | local url=$1
77 |
78 | echo "- $url"
79 |
80 | for i in `seq 3`; do
81 | ./http3_test -c $url > /dev/null && break
82 | echo "WARNING: retrying $i ..."
83 | done
84 | }
85 |
86 | func_tests()
87 | {
88 | print_start "Function Tests (PSK)"
89 | daemon_run ./func_test server 0.0.0.0 1234 ./keys/server-psk.txt
90 | ./func_test client 127.0.0.1 1234 ./keys/client-psk.txt || return 1
91 | daemon_stop "func_test"
92 |
93 | print_start "Function Tests (Certificate)"
94 | daemon_run ./func_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem
95 | ./func_test client 127.0.0.1 1234 || return 1
96 | daemon_stop "func_test"
97 | }
98 |
99 | perf_tests()
100 | {
101 | print_start "Performance Tests (IPv4)"
102 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem
103 | ./perf_test --addr 127.0.0.1 || return 1
104 | daemon_stop "perf_test"
105 |
106 | print_start "Performance Tests (IPv6, Disable 1RTT Encryption)"
107 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem \
108 | --cert ./keys/server-cert.pem --no_crypt
109 | ./perf_test --addr ::1 --no_crypt || return 1
110 | daemon_stop "perf_test"
111 |
112 | print_start "Performance Tests (IPv6)"
113 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem
114 | ./perf_test --addr ::1 || return 1
115 | daemon_stop "perf_test"
116 | }
117 |
118 | netem_tests()
119 | {
120 | modprobe -q sch_netem || return 0
121 | tc qdisc add dev lo root netem loss 30%
122 | print_start "Performance Tests (IPv4, 30% packet loss on both sides)"
123 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem
124 | ./perf_test --addr 127.0.0.1 --tot_len 1048576 --msg_len 1024 || return 1
125 | daemon_stop "perf_test"
126 |
127 | print_start "Performance Tests (IPv6, 30% packet loss on both sides)"
128 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem
129 | ./perf_test --addr ::1 --tot_len 1048576 --msg_len 1024 || return 1
130 | daemon_stop "perf_test"
131 | tc qdisc del dev lo root netem loss 30%
132 | }
133 |
134 | http3_tests() {
135 | [ -f /usr/local/include/nghttp3/nghttp3.h -o -f /usr/include/nghttp3/nghttp3.h ] || return 0
136 |
137 | print_start "Http/3 Tests (http3_test -> Public Websites)"
138 | make http3_test > /dev/null || return 1
139 |
140 | http3_connect https://d.moritzbuhl.de/pub || return 1 # linuxquic
141 | http3_connect https://cloudflare-quic.com/ || return 1 # Cloudflare Quiche
142 | http3_connect https://quic.aiortc.org/ || return 1 # aioquic
143 | http3_connect https://facebook.com/ || return 1 # mvfst
144 | http3_connect https://nghttp2.org:4433/ || return 1 # ngtcp2
145 | http3_connect https://outlook.office.com/ || return 1 # msquic
146 | http3_connect https://www.litespeedtech.com/ || return 1 # lsquic
147 | http3_connect https://www.google.com/ || return 1 # Google quiche
148 | http3_connect https://quic.tech:8443/ || return 1 # Cloudflare Quiche
149 | http3_connect https://test.privateoctopus.com:4433 || return 1 # picoquic
150 | http3_connect https://www.haproxy.org/ || return 1 # haproxy
151 | http3_connect https://quic.nginx.org:443 || return 1 # nginx
152 | http3_connect https://interop.seemann.io || return 1 # quic-go
153 | http3_connect https://mew.org:443 || return 1 # Haskell
154 |
155 | print_start "Http/3 Tests (http3_test client -> http3_test server)"
156 | daemon_run ./http3_test -s 127.0.0.1:443 ./keys/server-key.pem ./keys/server-cert.pem
157 | ./http3_test -c https://localhost/ || return 1
158 | daemon_stop "http3_test"
159 | }
160 |
161 | tlshd_tests()
162 | {
163 | systemctl is-active --quiet tlshd || return 0
164 |
165 | print_start "Kernel Tests (kernel -> lkquic, Certificate, Sample)"
166 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then
167 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-key.pem \
168 | ./keys/server-cert.pem sample
169 | insmod ../modules/net/quic/quic_sample_test.ko || return 1
170 | else
171 | modprobe -n quic_sample_test || return 0
172 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-key.pem \
173 | ./keys/server-cert.pem sample
174 | modprobe quic_sample_test || return 1
175 | fi
176 | rmmod quic_sample_test
177 | dmesg | tail -n 5
178 | daemon_stop "sample_test"
179 |
180 | print_start "Kernel Tests (lkquic -> kernel, Certificate, Sample)"
181 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then
182 | daemon_run ./sample_test client 127.0.0.1 1234 none none sample
183 | insmod ../modules/net/quic/quic_sample_test.ko role=server || return 1
184 | else
185 | modprobe -n quic_sample_test || return 0
186 | daemon_run ./sample_test client 127.0.0.1 1234 none none sample
187 | modprobe quic_sample_test role=server || return 1
188 | fi
189 | rmmod quic_sample_test
190 | dmesg | tail -n 5
191 | daemon_stop "sample_test"
192 |
193 | print_start "Kernel Tests (kernel -> lkquic, PSK, Sample)"
194 | PSK=`keyctl show @u |grep test1 |awk '{print $1}'`
195 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then
196 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-psk.txt none sample
197 | insmod ../modules/net/quic/quic_sample_test.ko psk=$PSK || return 1
198 | else
199 | modprobe -n quic_sample_test || return 0
200 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-psk.txt none sample
201 | modprobe quic_sample_test psk=$PSK || return 1
202 | fi
203 | rmmod quic_sample_test
204 | dmesg | tail -n 5
205 | daemon_stop "sample_test"
206 |
207 | print_start "Kernel Tests (lkquic -> kernel, PSK, Sample)"
208 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then
209 | daemon_run ./sample_test client 127.0.0.1 1234 ./keys/client-psk.txt none sample
210 | insmod ../modules/net/quic/quic_sample_test.ko role=server psk=1 || return 1
211 | else
212 | modprobe -n quic_sample_test || return 0
213 | daemon_run ./sample_test client 127.0.0.1 1234 ./keys/client-psk.txt none sample
214 | modprobe quic_sample_test role=server psk=1 || return 1
215 | fi
216 | rmmod quic_sample_test
217 | dmesg | tail -n 5
218 | daemon_stop "sample_test"
219 |
220 | print_start "Kernel Tests (kernel -> lkquic, Certificate, Session Resumption)"
221 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then
222 | daemon_run ./ticket_test server 0.0.0.0 1234 ./keys/server-key.pem \
223 | ./keys/server-cert.pem ticket
224 | insmod ../modules/net/quic/quic_sample_test.ko alpn=ticket || return 1
225 | else
226 | modprobe -n quic_sample_test || return 0
227 | daemon_run ./ticket_test server 0.0.0.0 1234 ./keys/server-key.pem \
228 | ./keys/server-cert.pem ticket
229 | modprobe quic_sample_test alpn=ticket || return 1
230 | fi
231 | rmmod quic_sample_test
232 | dmesg | tail -n 5
233 | daemon_stop "ticket_test"
234 |
235 | print_start "Kernel Tests (lkquic -> kernel, Certificate, Session Resumption)"
236 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then
237 | daemon_run ./ticket_test client 127.0.0.1 1234 ticket
238 | insmod ../modules/net/quic/quic_sample_test.ko role=server alpn=ticket || return 1
239 | else
240 | modprobe -n quic_sample_test || return 0
241 | daemon_run ./ticket_test client 127.0.0.1 1234 ticket
242 | modprobe quic_sample_test role=server alpn=ticket || return 1
243 | fi
244 | rmmod quic_sample_test
245 | dmesg | tail -n 5
246 | daemon_stop "ticket_test"
247 | }
248 |
249 | alpn_tests()
250 | {
251 | print_start "ALPN and Preferred Address Tests (IPv4 -> IPv6)"
252 | daemon_run ./alpn_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem ::1
253 | ./alpn_test client 127.0.0.1 1234 ::1 || return 1
254 | daemon_stop "alpn_test"
255 |
256 | print_start "ALPN and Preferred Address Tests (IPv6 -> IPv4)"
257 | daemon_run ./alpn_test server :: 1234 ./keys/server-key.pem ./keys/server-cert.pem 127.0.0.1
258 | ./alpn_test client ::1 1234 127.0.0.1 || return 1
259 | daemon_stop "alpn_test"
260 |
261 | print_start "ALPN and Preferred Address Tests (IPv4 -> IPv4)"
262 | ip addr add 127.0.0.2/8 dev lo
263 | daemon_run ./alpn_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem \
264 | 127.0.0.2
265 | ./alpn_test client 127.0.0.1 1234 127.0.0.2 || return 1
266 | ip addr del 127.0.0.2/8 dev lo
267 | daemon_stop "alpn_test"
268 |
269 | print_start "ALPN and Preferred Address Tests (IPv6 -> IPv6)"
270 | ip -6 addr add ::2/128 dev lo
271 | daemon_run ./alpn_test server :: 1234 ./keys/server-key.pem ./keys/server-cert.pem ::2
272 | ./alpn_test client ::1 1234 ::2 || return 1
273 | ip -6 addr del ::2/128 dev lo
274 | daemon_stop "alpn_test"
275 | }
276 |
277 | ticket_tests()
278 | {
279 | print_start "Session Resumption Tests"
280 | daemon_run ./ticket_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem
281 | ./ticket_test client 127.0.0.1 1234 || return 1
282 | daemon_stop "ticket_test"
283 | }
284 |
285 | sample_tests()
286 | {
287 | print_start "Sample Tests"
288 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem
289 | ./sample_test client 127.0.0.1 1234 none none || return 1
290 | daemon_stop "sample_test"
291 |
292 | }
293 |
294 | TESTS="func perf netem http3 tlshd alpn ticket sample"
295 | trap cleanup EXIT
296 |
297 | [ "$1" = "" ] || TESTS=$1
298 |
299 | start_tests || exit $?
300 |
301 | for name in $TESTS; do
302 | eval ${name}_tests || exit $?
303 | done
304 |
305 | done_tests
306 |
--------------------------------------------------------------------------------
/tests/sample_test.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | static const char *parse_address(
13 | char const *address, char const *port, struct sockaddr_storage *sas)
14 | {
15 | struct addrinfo hints = {0};
16 | struct addrinfo *res;
17 | int rc;
18 |
19 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
20 | hints.ai_family = AF_UNSPEC;
21 | hints.ai_socktype = SOCK_DGRAM;
22 |
23 | rc = getaddrinfo(address, port, &hints, &res);
24 | if (rc != 0)
25 | return gai_strerror(rc);
26 | memcpy(sas, res->ai_addr, res->ai_addrlen);
27 | freeaddrinfo(res);
28 | return NULL;
29 | }
30 |
31 | static int do_client(int argc, char *argv[])
32 | {
33 | struct sockaddr_storage ra = {};
34 | char msg[50], *psk, *host;
35 | unsigned int flags;
36 | int ret, sockfd;
37 | const char *rc;
38 | int64_t sid;
39 |
40 | if (argc < 6) {
41 | printf("%s client "
42 | " [ALPN]\n", argv[0]);
43 | return 0;
44 | }
45 |
46 | rc = parse_address(argv[2], argv[3], &ra);
47 | if (rc != NULL) {
48 | printf("parse address failed: %s\n", rc);
49 | return -1;
50 | }
51 | sockfd = socket(ra.ss_family, SOCK_DGRAM, IPPROTO_QUIC);
52 | if (sockfd < 0) {
53 | printf("socket create failed\n");
54 | return -1;
55 | }
56 |
57 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) {
58 | printf("socket connect failed\n");
59 | return -1;
60 | }
61 |
62 | psk = strcmp(argv[4], "none") ? argv[4] : NULL;
63 | host = strcmp(argv[5], "none") ? argv[5] : NULL;
64 | if (quic_client_handshake(sockfd, psk, host, argv[6]))
65 | return -1;
66 |
67 | /* set MSG_STREAM_NEW flag to open a stream while sending first data
68 | * or call getsockopt(QUIC_SOCKOPT_STREAM_OPEN) to open a stream.
69 | * set MSG_STREAM_FIN to mark the last data on this stream.
70 | */
71 | strcpy(msg, "hello quic server!");
72 | sid = QUIC_STREAM_TYPE_UNI_MASK;
73 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN;
74 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags);
75 | if (ret == -1) {
76 | printf("send error %d %d\n", ret, errno);
77 | return -1;
78 | }
79 | printf("send '%s' on stream %d\n", msg, (int)sid);
80 |
81 | flags = 0;
82 | memset(msg, 0, sizeof(msg));
83 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
84 | if (ret == -1) {
85 | printf("recv error %d %d\n", ret, errno);
86 | return 1;
87 | }
88 | printf("recv '%s' on stream %d\n", msg, (int)sid);
89 |
90 | close(sockfd);
91 | return 0;
92 | }
93 |
94 | static int do_server(int argc, char *argv[])
95 | {
96 | unsigned int addrlen, flags;
97 | struct sockaddr_storage sa = {};
98 | char msg[50], *alpn, *cert;
99 | int listenfd, sockfd, ret;
100 | const char *rc;
101 | int64_t sid;
102 |
103 | if (argc < 6) {
104 | printf("%s server "
105 | " [ALPN]\n", argv[0]);
106 | return 0;
107 | }
108 |
109 | rc = parse_address(argv[2], argv[3], &sa);
110 | if (rc != NULL) {
111 | printf("parse address failed: %s\n", rc);
112 | return -1;
113 | }
114 | listenfd = socket(sa.ss_family, SOCK_DGRAM, IPPROTO_QUIC);
115 | if (listenfd < 0) {
116 | printf("socket create failed\n");
117 | return -1;
118 | }
119 | if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa))) {
120 | printf("socket bind failed\n");
121 | return -1;
122 | }
123 | alpn = argv[6]; /* For kernel ALPN match */
124 | if (alpn && setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpn, strlen(alpn))) {
125 | printf("socket setsockopt alpn failed\n");
126 | return -1;
127 | }
128 | if (listen(listenfd, 1)) {
129 | printf("socket listen failed\n");
130 | return -1;
131 | }
132 | addrlen = sizeof(sa);
133 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen);
134 | if (sockfd < 0) {
135 | printf("socket accept failed %d %d\n", errno, sockfd);
136 | return -1;
137 | }
138 |
139 | cert = strcmp(argv[5], "none") ? argv[5] : NULL;
140 | if (quic_server_handshake(sockfd, argv[4], cert, argv[6]))
141 | return -1;
142 |
143 | flags = 0;
144 | memset(msg, 0, sizeof(msg));
145 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
146 | if (ret == -1) {
147 | printf("recv error %d %d\n", ret, errno);
148 | return 1;
149 | }
150 | printf("recv '%s' on stream %d\n", msg, (int)sid);
151 |
152 | strcpy(msg, "hello quic client!");
153 | sid = QUIC_STREAM_TYPE_SERVER_MASK;
154 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN;
155 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags);
156 | if (ret == -1) {
157 | printf("send error %d %d\n", ret, errno);
158 | return -1;
159 | }
160 | printf("send '%s' on stream %d\n", msg, (int)sid);
161 |
162 | flags = 0;
163 | quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
164 |
165 | close(sockfd);
166 | close(listenfd);
167 | return 0;
168 | }
169 |
170 | int main(int argc, char *argv[])
171 | {
172 | if (argc < 2 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) {
173 | printf("%s server|client ...\n", argv[0]);
174 | return 0;
175 | }
176 |
177 | quic_set_log_level(LOG_NOTICE);
178 |
179 | if (!strcmp(argv[1], "client"))
180 | return do_client(argc, argv);
181 |
182 | return do_server(argc, argv);
183 | }
184 |
--------------------------------------------------------------------------------
/tests/syzkaller/net_quic_syscall.list:
--------------------------------------------------------------------------------
1 | "socket", "socketpair", "setsockopt", "getsockopt", "getsockname", "getpeername",
2 | "bind", "listen", "connect", "accept", "accept4", "close", "shutdown",
3 | "sendmsg", "sendmmsg", "sendto", "recvmsg", "recvmmsg", "recvfrom",
4 | "socket$inet_quic", "socket$inet6_quic",
5 | "epoll_create", "epoll_create1", "epoll_ctl", "epoll_wait", "epoll_pwait", "epoll_pwait2",
6 | "mmap", "poll", "ppoll", "pread64", "preadv", "select", "pselect6", "sendfile"
7 |
--------------------------------------------------------------------------------
/tests/syzkaller/socket_inet_quic.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2017 syzkaller project authors. All rights reserved.
2 | # Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3 |
4 | # AF_INET and AF_INET6: QUIC support
5 |
6 | include
7 | include
8 | include
9 |
10 | resource sock_quic[sock_in]
11 |
12 | socket$inet_quic(domain const[AF_INET], type flags[quic_socket_type], proto const[IPPROTO_QUIC]) sock_quic
13 |
14 | quic_socket_type = SOCK_STREAM, SOCK_DGRAM
15 |
16 | resource sock_quic6[sock_in6]
17 |
18 | socket$inet6_quic(domain const[AF_INET6], type flags[quic_socket_type], proto const[IPPROTO_QUIC]) sock_quic6
19 |
20 | # TODO: separate for ip & ipv6
21 | sendmsg$inet_quic(fd sock_quic, msg ptr[in, msghdr_quic], f flags[send_flags])
22 | sendmmsg$inet_quic(fd sock_quic, mmsg ptr[in, array[msghdr_quic]], vlen len[mmsg], f flags[send_flags])
23 |
24 | msghdr_quic {
25 | addr ptr[in, sockaddr_quic]
26 | addrlen len[addr, int32]
27 | vec ptr[in, array[iovec_in]]
28 | vlen len[vec, intptr]
29 | ctrl ptr[in, array[cmsghdr_quic], opt]
30 | ctrllen bytesize[ctrl, intptr]
31 | f flags[send_flags, int32]
32 | }
33 |
34 | cmsghdr_quic [
35 | handshake cmsghdr_quic_handshake_info
36 | stream cmsghdr_quic_stream_info
37 | ] [varlen]
38 |
39 | quic_handshake_info {
40 | crypto_level int8
41 | }
42 |
43 | cmsghdr_quic_handshake_info {
44 | len len[parent, intptr]
45 | level const[SOL_QUIC, int32]
46 | type const[QUIC_HANDSHAKE_INFO, int32]
47 | msg quic_handshake_info
48 | } [align[PTR_SIZE]]
49 |
50 | quic_stream_info {
51 | stream_id int64
52 | stream_flags int32
53 | }
54 |
55 | cmsghdr_quic_stream_info {
56 | len len[parent, intptr]
57 | level const[SOL_QUIC, int32]
58 | type const[QUIC_STREAM_INFO, int32]
59 | msg quic_stream_info
60 | } [align[PTR_SIZE]]
61 |
62 | # Generic QUIC socket options
63 | quic_option_types_buf = QUIC_SOCKOPT_TOKEN, QUIC_SOCKOPT_ALPN, QUIC_SOCKOPT_SESSION_TICKET, QUIC_SOCKOPT_TRANSPORT_PARAM_EXT
64 |
65 | getsockopt$inet_quic_buf(fd sock_quic, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[out], optlen ptr[inout, len[optval, int32]])
66 | setsockopt$inet_quic_buf(fd sock_quic, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[in], optlen len[optval])
67 | getsockopt$inet6_quic_buf(fd sock_quic6, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[out], optlen ptr[inout, len[optval, int32]])
68 | setsockopt$inet6_quic_buf(fd sock_quic6, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[in], optlen len[optval])
69 |
70 | # Specific QUIC socket options
71 |
72 | setsockopt$inet_quic_QUIC_SOCKOPT_EVENT(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[in, quic_event_option], len len[val])
73 | setsockopt$inet_quic6_QUIC_SOCKOPT_EVENT(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[in, quic_event_option], len len[val])
74 | getsockopt$inet_quic_QUIC_SOCKOPT_EVENT(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[inout, quic_event_option], len ptr[inout, len[val, int32]])
75 | getsockopt$inet_quic6_QUIC_SOCKOPT_EVENT(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[inout, quic_event_option], len ptr[inout, len[val, int32]])
76 |
77 | getsockopt$inet_quic_QUIC_SOCKOPT_STREAM_OPEN(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_OPEN], val ptr[inout, quic_stream_info], len ptr[inout, len[val, int32]])
78 | getsockopt$inet_quic6_QUIC_SOCKOPT_STREAM_OPEN(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_OPEN], val ptr[inout, quic_stream_info], len ptr[inout, len[val, int32]])
79 |
80 | setsockopt$inet_quic_QUIC_SOCKOPT_STREAM_RESET(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_RESET], val ptr[in, quic_errinfo], len len[val])
81 | setsockopt$inet_quic6_QUIC_SOCKOPT_STREAM_RESET(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_RESET], val ptr[in, quic_errinfo], len len[val])
82 |
83 | setsockopt$inet_quic_QUIC_SOCKOPT_STREAM_STOP_SENDING(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_STOP_SENDING], val ptr[in, quic_errinfo], len len[val])
84 | setsockopt$inet_quic6_QUIC_SOCKOPT_STREAM_STOP_SENDING(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_STOP_SENDING], val ptr[in, quic_errinfo], len len[val])
85 |
86 | setsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[in, quic_connection_id_info], len len[val])
87 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[in, quic_connection_id_info], len len[val])
88 | getsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[inout, quic_connection_id_info], len ptr[inout, len[val, int32]])
89 | getsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[inout, quic_connection_id_info], len ptr[inout, len[val, int32]])
90 |
91 | setsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[in, quic_connection_close], len len[val])
92 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[in, quic_connection_close], len len[val])
93 | getsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[inout, quic_connection_close], len ptr[inout, len[val, int32]])
94 | getsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[inout, quic_connection_close], len ptr[inout, len[val, int32]])
95 |
96 | setsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_MIGRATION(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_MIGRATION], val ptr[in, sockaddr_quic], len len[val])
97 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_MIGRATION(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_MIGRATION], val ptr[in, sockaddr_quic], len len[val])
98 |
99 | setsockopt$inet_quic_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[in, quic_transport_param], len len[val])
100 | setsockopt$inet_quic6_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[in, quic_transport_param], len len[val])
101 | getsockopt$inet_quic_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[inout, quic_transport_param], len ptr[inout, len[val, int32]])
102 | getsockopt$inet_quic6_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[inout, quic_transport_param], len ptr[inout, len[val, int32]])
103 |
104 | setsockopt$inet_quic_QUIC_SOCKOPT_CONFIG(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[in, quic_config], len len[val])
105 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONFIG(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[in, quic_config], len len[val])
106 | getsockopt$inet_quic_QUIC_SOCKOPT_CONFIG(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[inout, quic_config], len ptr[inout, len[val, int32]])
107 | getsockopt$inet_quic6_QUIC_SOCKOPT_CONFIG(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[inout, quic_config], len ptr[inout, len[val, int32]])
108 |
109 | setsockopt$inet_quic_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[in, quic_crypto_secret], len len[val])
110 | setsockopt$inet_quic6_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[in, quic_crypto_secret], len len[val])
111 | getsockopt$inet_quic_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[inout, quic_crypto_secret], len ptr[inout, len[val, int32]])
112 | getsockopt$inet_quic6_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[inout, quic_crypto_secret], len ptr[inout, len[val, int32]])
113 |
114 | setsockopt$inet_quic_QUIC_SOCKOPT_KEY_UPDATE(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_KEY_UPDATE], optval buffer[in], len len[optval])
115 | setsockopt$inet_quic6_QUIC_SOCKOPT_KEY_UPDATE(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_KEY_UPDATE], optval buffer[in], len len[optval])
116 |
117 | sockaddr_quic [
118 | in sockaddr_in
119 | in6 sockaddr_in6
120 | ] [varlen]
121 |
122 | quic_event_option {
123 | type int8
124 | on int8
125 | }
126 |
127 | quic_errinfo {
128 | stream_id int64
129 | errcode int32
130 | }
131 |
132 | quic_connection_id_info {
133 | dest int8
134 | active int32
135 | prior_to int32
136 | }
137 |
138 | quic_connection_close {
139 | errcode int32
140 | frame int8
141 | phrase array[int8]
142 | }
143 |
144 | quic_transport_param {
145 | remote int8
146 | disable_active_migration int8
147 | grease_quic_bit int8
148 | stateless_reset int8
149 | disable_1rtt_encryption int8
150 | disable_compatible_version int8
151 | active_connection_id_limit int8
152 | ack_delay_exponent int8
153 | max_datagram_frame_size int16
154 | max_udp_payload_size int16
155 | max_idle_timeout int32
156 | max_ack_delay int32
157 | max_streams_bidi int16
158 | max_streams_uni int16
159 | max_data int64
160 | max_stream_data_bidi_local int64
161 | max_stream_data_bidi_remote int64
162 | max_stream_data_uni int64
163 | reserved int64
164 | }
165 |
166 | quic_config {
167 | version int32
168 | plpmtud_probe_interval int32
169 | initial_smoothed_rtt int32
170 | payload_cipher_type int32
171 | congestion_control_algo int8
172 | validate_peer_address int8
173 | stream_data_nodelay int8
174 | receive_session_ticket int8
175 | certificate_request int8
176 | reserved array[int8, 3]
177 | }
178 |
179 | quic_crypto_secret {
180 | send int8
181 | level int8
182 | type int32
183 | secret array[int8, 48]
184 | }
185 |
--------------------------------------------------------------------------------
/tests/syzkaller/socket_inet_quic.txt.const:
--------------------------------------------------------------------------------
1 | # Code generated by syz-sysgen. DO NOT EDIT.
2 | arches = 386, amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x
3 | AF_INET = 2
4 | AF_INET6 = 10
5 | IPPROTO_QUIC = 261
6 | SOL_QUIC = 288
7 | QUIC_STREAM_INFO = 0
8 | QUIC_HANDSHAKE_INFO = 1
9 | QUIC_SOCKOPT_EVENT = 0
10 | QUIC_SOCKOPT_STREAM_OPEN = 1
11 | QUIC_SOCKOPT_STREAM_RESET = 2
12 | QUIC_SOCKOPT_STREAM_STOP_SENDING = 3
13 | QUIC_SOCKOPT_CONNECTION_ID = 4
14 | QUIC_SOCKOPT_CONNECTION_CLOSE = 5
15 | QUIC_SOCKOPT_CONNECTION_MIGRATION = 6
16 | QUIC_SOCKOPT_KEY_UPDATE = 7
17 | QUIC_SOCKOPT_TRANSPORT_PARAM = 8
18 | QUIC_SOCKOPT_CONFIG = 9
19 | QUIC_SOCKOPT_TOKEN = 10
20 | QUIC_SOCKOPT_ALPN = 11
21 | QUIC_SOCKOPT_SESSION_TICKET = 12
22 | QUIC_SOCKOPT_CRYPTO_SECRET = 13
23 | QUIC_SOCKOPT_TRANSPORT_PARAM_EXT = 14
24 | SOCK_STREAM = 1, mips64le:2
25 | SOCK_DGRAM = 2, mips64le:1
26 | __NR_getsockopt = 209, 386:s390x:365, amd64:55, arm:295, mips64le:5054, ppc64le:340
27 | __NR_sendmmsg = 269, 386:345, amd64:307, arm:374, mips64le:5302, ppc64le:349, s390x:358
28 | __NR_sendmsg = 211, 386:s390x:370, amd64:46, arm:296, mips64le:5045, ppc64le:341
29 | __NR_setsockopt = 208, 386:s390x:366, amd64:54, arm:294, mips64le:5053, ppc64le:339
30 | __NR_socket = 198, 386:s390x:359, amd64:41, arm:281, mips64le:5040, ppc64le:326
31 |
--------------------------------------------------------------------------------
/tests/ticket_test.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | static uint8_t ticket[4096];
13 |
14 | static const char *parse_address(
15 | char const *address, char const *port, struct sockaddr_storage *sas)
16 | {
17 | struct addrinfo hints = {0};
18 | struct addrinfo *res;
19 | int rc;
20 |
21 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
22 | hints.ai_family = AF_UNSPEC;
23 | hints.ai_socktype = SOCK_DGRAM;
24 |
25 | rc = getaddrinfo(address, port, &hints, &res);
26 | if (rc != 0)
27 | return gai_strerror(rc);
28 | memcpy(sas, res->ai_addr, res->ai_addrlen);
29 | freeaddrinfo(res);
30 | return NULL;
31 | }
32 |
33 | static int client_handshake(int sockfd, const char *alpns, const char *host,
34 | const uint8_t *ticket_in, size_t ticket_in_len,
35 | uint8_t *ticket_out, size_t *ticket_out_len)
36 | {
37 | gnutls_certificate_credentials_t cred;
38 | gnutls_session_t session;
39 | size_t alpn_len;
40 | char alpn[64];
41 | int ret;
42 |
43 | ret = gnutls_certificate_allocate_credentials(&cred);
44 | if (ret)
45 | goto err;
46 | ret = gnutls_certificate_set_x509_system_trust(cred);
47 | if (ret < 0)
48 | goto err_cred;
49 |
50 | ret = gnutls_init(&session, GNUTLS_CLIENT |
51 | GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA);
52 | if (ret)
53 | goto err_cred;
54 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
55 | if (ret)
56 | goto err_session;
57 |
58 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL);
59 | if (ret)
60 | goto err_session;
61 |
62 | if (alpns) {
63 | ret = quic_session_set_alpn(session, alpns, strlen(alpns));
64 | if (ret)
65 | goto err_session;
66 | }
67 |
68 | if (host) {
69 | ret = gnutls_server_name_set(session, GNUTLS_NAME_DNS, host, strlen(host));
70 | if (ret)
71 | goto err_session;
72 | }
73 |
74 | gnutls_transport_set_int(session, sockfd);
75 |
76 | if (ticket_in) {
77 | ret = quic_session_set_data(session, ticket_in, ticket_in_len);
78 | if (ret)
79 | goto err_session;
80 | }
81 |
82 | ret = quic_handshake(session);
83 | if (ret)
84 | goto err_session;
85 |
86 | if (alpns) {
87 | alpn_len = sizeof(alpn);
88 | ret = quic_session_get_alpn(session, alpn, &alpn_len);
89 | if (ret)
90 | goto err_session;
91 | }
92 |
93 | if (ticket_out) {
94 | sleep(1);
95 | ret = quic_session_get_data(session, ticket_out, ticket_out_len);
96 | if (ret)
97 | goto err_session;
98 | }
99 |
100 | err_session:
101 | gnutls_deinit(session);
102 | err_cred:
103 | gnutls_certificate_free_credentials(cred);
104 | err:
105 | return ret;
106 | }
107 |
108 | static int do_client(int argc, char *argv[])
109 | {
110 | struct quic_transport_param param = {};
111 | unsigned int param_len, flags;
112 | struct sockaddr_storage ra = {};
113 | char msg[50], *alpn;
114 | size_t ticket_len;
115 | int ret, sockfd;
116 | const char *rc;
117 | int64_t sid;
118 |
119 | if (argc < 3) {
120 | printf("%s client [ALPN]\n", argv[0]);
121 | return 0;
122 | }
123 |
124 | rc = parse_address(argv[2], argv[3], &ra);
125 | if (rc != NULL) {
126 | printf("parse address failed: %s\n", rc);
127 | return -1;
128 | }
129 |
130 | sockfd = socket(ra.ss_family, SOCK_DGRAM, IPPROTO_QUIC);
131 | if (sockfd < 0) {
132 | printf("socket create failed\n");
133 | return -1;
134 | }
135 |
136 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) {
137 | printf("socket connect failed\n");
138 | return -1;
139 | }
140 |
141 | /* get session ticket, remote tranaport param for session resumption */
142 | alpn = argv[4];
143 | ticket_len = sizeof(ticket);
144 | if (client_handshake(sockfd, alpn, NULL, NULL, 0, ticket, &ticket_len))
145 | return -1;
146 |
147 | param_len = sizeof(param);
148 | param.remote = 1;
149 | ret = getsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, ¶m_len);
150 | if (ret == -1) {
151 | printf("socket getsockopt remote transport param\n");
152 | return -1;
153 | }
154 |
155 | printf("get the session ticket %u and transport param %u, save it\n",
156 | (unsigned int)ticket_len, param_len);
157 |
158 | strcpy(msg, "hello quic server!");
159 | sid = QUIC_STREAM_TYPE_UNI_MASK;
160 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN;
161 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags);
162 | if (ret == -1) {
163 | printf("send error %d %d\n", ret, errno);
164 | return -1;
165 | }
166 | printf("send '%s' on stream %d\n", msg, (int)sid);
167 |
168 | flags = 0;
169 | memset(msg, 0, sizeof(msg));
170 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
171 | if (ret == -1) {
172 | printf("recv error %d %d\n", ret, errno);
173 | return 1;
174 | }
175 | printf("recv '%s' on stream %d\n", msg, (int)sid);
176 |
177 | close(sockfd);
178 |
179 | printf("start new connection with the session ticket used...\n");
180 | sleep(2);
181 |
182 | rc = parse_address(argv[2], argv[3], &ra);
183 | if (rc != NULL) {
184 | printf("parse address failed: %s\n", rc);
185 | return -1;
186 | }
187 |
188 | sockfd = socket(ra.ss_family, SOCK_DGRAM, IPPROTO_QUIC);
189 | if (sockfd < 0) {
190 | printf("socket create failed\n");
191 | return -1;
192 | }
193 |
194 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) {
195 | printf("socket connect failed\n");
196 | return -1;
197 | }
198 |
199 | ret = setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, param_len);
200 | if (ret == -1) {
201 | printf("socket setsockopt remote transport param\n");
202 | return -1;
203 | }
204 |
205 | /* send early data before handshake */
206 | strcpy(msg, "hello quic server, I'm back!");
207 | sid = QUIC_STREAM_TYPE_UNI_MASK;
208 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN;
209 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags);
210 | if (ret == -1) {
211 | printf("send error %d %d\n", ret, errno);
212 | return -1;
213 | }
214 | printf("send '%s' on stream %d\n", msg, (int)sid);
215 |
216 | if (client_handshake(sockfd, alpn, NULL, ticket, ticket_len, NULL, NULL))
217 | return -1;
218 |
219 | flags = 0;
220 | memset(msg, 0, sizeof(msg));
221 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
222 | if (ret == -1) {
223 | printf("recv error %d %d\n", ret, errno);
224 | return 1;
225 | }
226 | printf("recv '%s' on stream %d\n", msg, (int)sid);
227 |
228 | close(sockfd);
229 | return 0;
230 | }
231 |
232 | static int server_handshake(int sockfd, const char *pkey, const char *cert, const char *alpns,
233 | uint8_t *key, unsigned int keylen)
234 | {
235 | gnutls_certificate_credentials_t cred;
236 | gnutls_datum_t skey = {key, keylen};
237 | gnutls_session_t session;
238 | size_t alpn_len;
239 | char alpn[64];
240 | int ret;
241 |
242 | ret = gnutls_certificate_allocate_credentials(&cred);
243 | if (ret)
244 | goto err;
245 | ret = gnutls_certificate_set_x509_system_trust(cred);
246 | if (ret < 0)
247 | goto err_cred;
248 | ret = gnutls_certificate_set_x509_key_file(cred, cert, pkey, GNUTLS_X509_FMT_PEM);
249 | if (ret)
250 | goto err_cred;
251 | ret = gnutls_init(&session, GNUTLS_SERVER | GNUTLS_NO_AUTO_SEND_TICKET |
252 | GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA);
253 | if (ret)
254 | goto err_cred;
255 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
256 | if (ret)
257 | goto err_session;
258 |
259 | ret = gnutls_session_ticket_enable_server(session, &skey);
260 | if (ret)
261 | goto err_session;
262 |
263 | ret = gnutls_record_set_max_early_data_size(session, 0xffffffffu);
264 | if (ret)
265 | goto err_session;
266 |
267 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL);
268 | if (ret)
269 | goto err_session;
270 |
271 | if (alpns) {
272 | ret = quic_session_set_alpn(session, alpns, strlen(alpns));
273 | if (ret)
274 | goto err_session;
275 | }
276 |
277 | gnutls_transport_set_int(session, sockfd);
278 |
279 | ret = quic_handshake(session);
280 | if (ret)
281 | goto err_session;
282 |
283 | if (alpns) {
284 | alpn_len = sizeof(alpn);
285 | ret = quic_session_get_alpn(session, alpn, &alpn_len);
286 | }
287 |
288 | err_session:
289 | gnutls_deinit(session);
290 | err_cred:
291 | gnutls_certificate_free_credentials(cred);
292 | err:
293 | return ret;
294 | }
295 |
296 | static int do_server(int argc, char *argv[])
297 | {
298 | unsigned int addrlen, keylen, flags;
299 | struct sockaddr_storage sa = {};
300 | int listenfd, sockfd, ret;
301 | char msg[50], *alpn;
302 | uint8_t key[64];
303 | const char *rc;
304 | int64_t sid;
305 |
306 | if (argc < 5) {
307 | printf("%s server "
308 | "\n", argv[0]);
309 | return 0;
310 | }
311 |
312 | rc = parse_address(argv[2], argv[3], &sa);
313 | if (rc != NULL) {
314 | printf("parse address failed: %s\n", rc);
315 | return -1;
316 | }
317 |
318 | listenfd = socket(sa.ss_family, SOCK_DGRAM, IPPROTO_QUIC);
319 | if (listenfd < 0) {
320 | printf("socket create failed\n");
321 | return -1;
322 | }
323 | if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa))) {
324 | printf("socket bind failed\n");
325 | return -1;
326 | }
327 | alpn = argv[6];
328 | if (alpn && setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpn, strlen(alpn))) {
329 | printf("socket setsockopt alpn failed\n");
330 | return -1;
331 | }
332 |
333 | if (listen(listenfd, 1)) {
334 | printf("socket listen failed\n");
335 | return -1;
336 | }
337 |
338 | addrlen = sizeof(sa);
339 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen);
340 | if (sockfd < 0) {
341 | printf("socket accept failed %d %d\n", errno, sockfd);
342 | return -1;
343 | }
344 |
345 | keylen = sizeof(key);
346 | if (getsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_SESSION_TICKET, key, &keylen)) {
347 | printf("socket getsockopt session ticket error %d", errno);
348 | return -1;
349 | }
350 |
351 | if (server_handshake(sockfd, argv[4], argv[5], alpn, key, keylen))
352 | return -1;
353 |
354 | flags = 0;
355 | memset(msg, 0, sizeof(msg));
356 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
357 | if (ret == -1) {
358 | printf("recv error %d %d\n", ret, errno);
359 | return 1;
360 | }
361 | printf("recv '%s' on stream %d\n", msg, (int)sid);
362 |
363 | strcpy(msg, "hello quic client!");
364 | sid = QUIC_STREAM_TYPE_SERVER_MASK;
365 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN;
366 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags);
367 | if (ret == -1) {
368 | printf("send error %d %d\n", ret, errno);
369 | return -1;
370 | }
371 | printf("send '%s' on stream %d\n", msg, (int)sid);
372 |
373 | close(sockfd);
374 |
375 | printf("wait for the client next connection...\n");
376 |
377 | addrlen = sizeof(sa);
378 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen);
379 | if (sockfd < 0) {
380 | printf("socket accept failed %d %d\n", errno, sockfd);
381 | return -1;
382 | }
383 |
384 | if (server_handshake(sockfd, argv[4], argv[5], alpn, key, keylen))
385 | return -1;
386 |
387 | flags = 0;
388 | memset(msg, 0, sizeof(msg));
389 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
390 | if (ret == -1) {
391 | printf("recv error %d %d\n", ret, errno);
392 | return 1;
393 | }
394 | printf("recv '%s' on stream %d\n", msg, (int)sid);
395 |
396 | strcpy(msg, "hello quic client! welcome back!");
397 | sid = QUIC_STREAM_TYPE_SERVER_MASK;
398 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN;
399 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags);
400 | if (ret == -1) {
401 | printf("send error %d %d\n", ret, errno);
402 | return -1;
403 | }
404 | printf("send '%s' on stream %d\n", msg, (int)sid);
405 |
406 | flags = 0;
407 | quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags);
408 |
409 | close(sockfd);
410 | close(listenfd);
411 | return 0;
412 | }
413 |
414 | int main(int argc, char *argv[])
415 | {
416 | if (argc < 2 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) {
417 | printf("%s server|client ...\n", argv[0]);
418 | return 0;
419 | }
420 |
421 | quic_set_log_level(LOG_NOTICE);
422 |
423 | if (!strcmp(argv[1], "client"))
424 | return do_client(argc, argv);
425 |
426 | return do_server(argc, argv);
427 | }
428 |
--------------------------------------------------------------------------------