]> Git Repo - linux.git/blob - tools/testing/selftests/net/af_unix/msg_oob.c
Linux 6.14-rc3
[linux.git] / tools / testing / selftests / net / af_unix / msg_oob.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright Amazon.com Inc. or its affiliates. */
3
4 #include <fcntl.h>
5 #include <string.h>
6 #include <unistd.h>
7
8 #include <netinet/in.h>
9 #include <sys/epoll.h>
10 #include <sys/ioctl.h>
11 #include <sys/signalfd.h>
12 #include <sys/socket.h>
13
14 #include "../../kselftest_harness.h"
15
16 #define BUF_SZ  32
17
18 FIXTURE(msg_oob)
19 {
20         int fd[4];              /* 0: AF_UNIX sender
21                                  * 1: AF_UNIX receiver
22                                  * 2: TCP sender
23                                  * 3: TCP receiver
24                                  */
25         int signal_fd;
26         int epoll_fd[2];        /* 0: AF_UNIX receiver
27                                  * 1: TCP receiver
28                                  */
29         bool tcp_compliant;
30 };
31
32 FIXTURE_VARIANT(msg_oob)
33 {
34         bool peek;
35 };
36
37 FIXTURE_VARIANT_ADD(msg_oob, no_peek)
38 {
39         .peek = false,
40 };
41
42 FIXTURE_VARIANT_ADD(msg_oob, peek)
43 {
44         .peek = true
45 };
46
47 static void create_unix_socketpair(struct __test_metadata *_metadata,
48                                    FIXTURE_DATA(msg_oob) *self)
49 {
50         int ret;
51
52         ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, self->fd);
53         ASSERT_EQ(ret, 0);
54 }
55
56 static void create_tcp_socketpair(struct __test_metadata *_metadata,
57                                   FIXTURE_DATA(msg_oob) *self)
58 {
59         struct sockaddr_in addr;
60         socklen_t addrlen;
61         int listen_fd;
62         int ret;
63
64         listen_fd = socket(AF_INET, SOCK_STREAM, 0);
65         ASSERT_GE(listen_fd, 0);
66
67         ret = listen(listen_fd, -1);
68         ASSERT_EQ(ret, 0);
69
70         addrlen = sizeof(addr);
71         ret = getsockname(listen_fd, (struct sockaddr *)&addr, &addrlen);
72         ASSERT_EQ(ret, 0);
73
74         self->fd[2] = socket(AF_INET, SOCK_STREAM, 0);
75         ASSERT_GE(self->fd[2], 0);
76
77         ret = connect(self->fd[2], (struct sockaddr *)&addr, addrlen);
78         ASSERT_EQ(ret, 0);
79
80         self->fd[3] = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
81         ASSERT_GE(self->fd[3], 0);
82
83         ret = fcntl(self->fd[3], F_SETFL, O_NONBLOCK);
84         ASSERT_EQ(ret, 0);
85 }
86
87 static void setup_sigurg(struct __test_metadata *_metadata,
88                          FIXTURE_DATA(msg_oob) *self)
89 {
90         struct signalfd_siginfo siginfo;
91         int pid = getpid();
92         sigset_t mask;
93         int i, ret;
94
95         for (i = 0; i < 2; i++) {
96                 ret = ioctl(self->fd[i * 2 + 1], FIOSETOWN, &pid);
97                 ASSERT_EQ(ret, 0);
98         }
99
100         ret = sigemptyset(&mask);
101         ASSERT_EQ(ret, 0);
102
103         ret = sigaddset(&mask, SIGURG);
104         ASSERT_EQ(ret, 0);
105
106         ret = sigprocmask(SIG_BLOCK, &mask, NULL);
107         ASSERT_EQ(ret, 0);
108
109         self->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK);
110         ASSERT_GE(self->signal_fd, 0);
111
112         ret = read(self->signal_fd, &siginfo, sizeof(siginfo));
113         ASSERT_EQ(ret, -1);
114 }
115
116 static void setup_epollpri(struct __test_metadata *_metadata,
117                            FIXTURE_DATA(msg_oob) *self)
118 {
119         struct epoll_event event = {
120                 .events = EPOLLPRI,
121         };
122         int i;
123
124         for (i = 0; i < 2; i++) {
125                 int ret;
126
127                 self->epoll_fd[i] = epoll_create1(0);
128                 ASSERT_GE(self->epoll_fd[i], 0);
129
130                 ret = epoll_ctl(self->epoll_fd[i], EPOLL_CTL_ADD, self->fd[i * 2 + 1], &event);
131                 ASSERT_EQ(ret, 0);
132         }
133 }
134
135 static void close_sockets(FIXTURE_DATA(msg_oob) *self)
136 {
137         int i;
138
139         for (i = 0; i < 4; i++)
140                 close(self->fd[i]);
141 }
142
143 FIXTURE_SETUP(msg_oob)
144 {
145         create_unix_socketpair(_metadata, self);
146         create_tcp_socketpair(_metadata, self);
147
148         setup_sigurg(_metadata, self);
149         setup_epollpri(_metadata, self);
150
151         self->tcp_compliant = true;
152 }
153
154 FIXTURE_TEARDOWN(msg_oob)
155 {
156         close_sockets(self);
157 }
158
159 static void __epollpair(struct __test_metadata *_metadata,
160                         FIXTURE_DATA(msg_oob) *self,
161                         bool oob_remaining)
162 {
163         struct epoll_event event[2] = {};
164         int i, ret[2];
165
166         for (i = 0; i < 2; i++)
167                 ret[i] = epoll_wait(self->epoll_fd[i], &event[i], 1, 0);
168
169         ASSERT_EQ(ret[0], oob_remaining);
170
171         if (self->tcp_compliant)
172                 ASSERT_EQ(ret[0], ret[1]);
173
174         if (oob_remaining) {
175                 ASSERT_EQ(event[0].events, EPOLLPRI);
176
177                 if (self->tcp_compliant)
178                         ASSERT_EQ(event[0].events, event[1].events);
179         }
180 }
181
182 static void __sendpair(struct __test_metadata *_metadata,
183                        FIXTURE_DATA(msg_oob) *self,
184                        const void *buf, size_t len, int flags)
185 {
186         int i, ret[2];
187
188         for (i = 0; i < 2; i++) {
189                 struct signalfd_siginfo siginfo = {};
190                 int bytes;
191
192                 ret[i] = send(self->fd[i * 2], buf, len, flags);
193
194                 bytes = read(self->signal_fd, &siginfo, sizeof(siginfo));
195
196                 if (flags & MSG_OOB) {
197                         ASSERT_EQ(bytes, sizeof(siginfo));
198                         ASSERT_EQ(siginfo.ssi_signo, SIGURG);
199
200                         bytes = read(self->signal_fd, &siginfo, sizeof(siginfo));
201                 }
202
203                 ASSERT_EQ(bytes, -1);
204         }
205
206         ASSERT_EQ(ret[0], len);
207         ASSERT_EQ(ret[0], ret[1]);
208 }
209
210 static void __recvpair(struct __test_metadata *_metadata,
211                        FIXTURE_DATA(msg_oob) *self,
212                        const char *expected_buf, int expected_len,
213                        int buf_len, int flags)
214 {
215         int i, ret[2], recv_errno[2], expected_errno = 0;
216         char recv_buf[2][BUF_SZ] = {};
217         bool printed = false;
218
219         ASSERT_GE(BUF_SZ, buf_len);
220
221         errno = 0;
222
223         for (i = 0; i < 2; i++) {
224                 ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags);
225                 recv_errno[i] = errno;
226         }
227
228         if (expected_len < 0) {
229                 expected_errno = -expected_len;
230                 expected_len = -1;
231         }
232
233         if (ret[0] != expected_len || recv_errno[0] != expected_errno) {
234                 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
235                 TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf);
236
237                 ASSERT_EQ(ret[0], expected_len);
238                 ASSERT_EQ(recv_errno[0], expected_errno);
239         }
240
241         if (ret[0] != ret[1] || recv_errno[0] != recv_errno[1]) {
242                 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
243                 TH_LOG("TCP     :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]);
244
245                 printed = true;
246
247                 if (self->tcp_compliant) {
248                         ASSERT_EQ(ret[0], ret[1]);
249                         ASSERT_EQ(recv_errno[0], recv_errno[1]);
250                 }
251         }
252
253         if (expected_len >= 0) {
254                 int cmp;
255
256                 cmp = strncmp(expected_buf, recv_buf[0], expected_len);
257                 if (cmp) {
258                         TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
259                         TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf);
260
261                         ASSERT_EQ(cmp, 0);
262                 }
263
264                 cmp = strncmp(recv_buf[0], recv_buf[1], expected_len);
265                 if (cmp) {
266                         if (!printed) {
267                                 TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
268                                 TH_LOG("TCP     :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]);
269                         }
270
271                         if (self->tcp_compliant)
272                                 ASSERT_EQ(cmp, 0);
273                 }
274         }
275 }
276
277 static void __setinlinepair(struct __test_metadata *_metadata,
278                             FIXTURE_DATA(msg_oob) *self)
279 {
280         int i, oob_inline = 1;
281
282         for (i = 0; i < 2; i++) {
283                 int ret;
284
285                 ret = setsockopt(self->fd[i * 2 + 1], SOL_SOCKET, SO_OOBINLINE,
286                                  &oob_inline, sizeof(oob_inline));
287                 ASSERT_EQ(ret, 0);
288         }
289 }
290
291 static void __siocatmarkpair(struct __test_metadata *_metadata,
292                              FIXTURE_DATA(msg_oob) *self,
293                              bool oob_head)
294 {
295         int answ[2] = {};
296         int i;
297
298         for (i = 0; i < 2; i++) {
299                 int ret;
300
301                 ret = ioctl(self->fd[i * 2 + 1], SIOCATMARK, &answ[i]);
302                 ASSERT_EQ(ret, 0);
303         }
304
305         ASSERT_EQ(answ[0], oob_head);
306
307         if (self->tcp_compliant)
308                 ASSERT_EQ(answ[0], answ[1]);
309 }
310
311 #define sendpair(buf, len, flags)                                       \
312         __sendpair(_metadata, self, buf, len, flags)
313
314 #define recvpair(expected_buf, expected_len, buf_len, flags)            \
315         do {                                                            \
316                 if (variant->peek)                                      \
317                         __recvpair(_metadata, self,                     \
318                                    expected_buf, expected_len,          \
319                                    buf_len, (flags) | MSG_PEEK);        \
320                 __recvpair(_metadata, self,                             \
321                            expected_buf, expected_len, buf_len, flags); \
322         } while (0)
323
324 #define epollpair(oob_remaining)                                        \
325         __epollpair(_metadata, self, oob_remaining)
326
327 #define siocatmarkpair(oob_head)                                        \
328         __siocatmarkpair(_metadata, self, oob_head)
329
330 #define setinlinepair()                                                 \
331         __setinlinepair(_metadata, self)
332
333 #define tcp_incompliant                                                 \
334         for (self->tcp_compliant = false;                               \
335              self->tcp_compliant == false;                              \
336              self->tcp_compliant = true)
337
338 TEST_F(msg_oob, non_oob)
339 {
340         sendpair("x", 1, 0);
341         epollpair(false);
342         siocatmarkpair(false);
343
344         recvpair("", -EINVAL, 1, MSG_OOB);
345         epollpair(false);
346         siocatmarkpair(false);
347 }
348
349 TEST_F(msg_oob, oob)
350 {
351         sendpair("x", 1, MSG_OOB);
352         epollpair(true);
353         siocatmarkpair(true);
354
355         recvpair("x", 1, 1, MSG_OOB);
356         epollpair(false);
357         siocatmarkpair(true);
358 }
359
360 TEST_F(msg_oob, oob_drop)
361 {
362         sendpair("x", 1, MSG_OOB);
363         epollpair(true);
364         siocatmarkpair(true);
365
366         recvpair("", -EAGAIN, 1, 0);            /* Drop OOB. */
367         epollpair(false);
368         siocatmarkpair(false);
369
370         recvpair("", -EINVAL, 1, MSG_OOB);
371         epollpair(false);
372         siocatmarkpair(false);
373 }
374
375 TEST_F(msg_oob, oob_ahead)
376 {
377         sendpair("hello", 5, MSG_OOB);
378         epollpair(true);
379         siocatmarkpair(false);
380
381         recvpair("o", 1, 1, MSG_OOB);
382         epollpair(false);
383         siocatmarkpair(false);
384
385         recvpair("hell", 4, 4, 0);
386         epollpair(false);
387         siocatmarkpair(true);
388 }
389
390 TEST_F(msg_oob, oob_break)
391 {
392         sendpair("hello", 5, MSG_OOB);
393         epollpair(true);
394         siocatmarkpair(false);
395
396         recvpair("hell", 4, 5, 0);              /* Break at OOB even with enough buffer. */
397         epollpair(true);
398         siocatmarkpair(true);
399
400         recvpair("o", 1, 1, MSG_OOB);
401         epollpair(false);
402         siocatmarkpair(true);
403
404         recvpair("", -EAGAIN, 1, 0);
405         siocatmarkpair(false);
406 }
407
408 TEST_F(msg_oob, oob_ahead_break)
409 {
410         sendpair("hello", 5, MSG_OOB);
411         epollpair(true);
412         siocatmarkpair(false);
413
414         sendpair("world", 5, 0);
415         epollpair(true);
416         siocatmarkpair(false);
417
418         recvpair("o", 1, 1, MSG_OOB);
419         epollpair(false);
420         siocatmarkpair(false);
421
422         recvpair("hell", 4, 9, 0);              /* Break at OOB even after it's recv()ed. */
423         epollpair(false);
424         siocatmarkpair(true);
425
426         recvpair("world", 5, 5, 0);
427         epollpair(false);
428         siocatmarkpair(false);
429 }
430
431 TEST_F(msg_oob, oob_break_drop)
432 {
433         sendpair("hello", 5, MSG_OOB);
434         epollpair(true);
435         siocatmarkpair(false);
436
437         sendpair("world", 5, 0);
438         epollpair(true);
439         siocatmarkpair(false);
440
441         recvpair("hell", 4, 10, 0);             /* Break at OOB even with enough buffer. */
442         epollpair(true);
443         siocatmarkpair(true);
444
445         recvpair("world", 5, 10, 0);            /* Drop OOB and recv() the next skb. */
446         epollpair(false);
447         siocatmarkpair(false);
448
449         recvpair("", -EINVAL, 1, MSG_OOB);
450         epollpair(false);
451         siocatmarkpair(false);
452 }
453
454 TEST_F(msg_oob, ex_oob_break)
455 {
456         sendpair("hello", 5, MSG_OOB);
457         epollpair(true);
458         siocatmarkpair(false);
459
460         sendpair("wor", 3, MSG_OOB);
461         epollpair(true);
462         siocatmarkpair(false);
463
464         sendpair("ld", 2, 0);
465         epollpair(true);
466         siocatmarkpair(false);
467
468         recvpair("hellowo", 7, 10, 0);          /* Break at OOB but not at ex-OOB. */
469         epollpair(true);
470         siocatmarkpair(true);
471
472         recvpair("r", 1, 1, MSG_OOB);
473         epollpair(false);
474         siocatmarkpair(true);
475
476         recvpair("ld", 2, 2, 0);
477         epollpair(false);
478         siocatmarkpair(false);
479 }
480
481 TEST_F(msg_oob, ex_oob_drop)
482 {
483         sendpair("x", 1, MSG_OOB);
484         epollpair(true);
485         siocatmarkpair(true);
486
487         sendpair("y", 1, MSG_OOB);              /* TCP drops "x" at this moment. */
488         epollpair(true);
489
490         tcp_incompliant {
491                 siocatmarkpair(false);
492
493                 recvpair("x", 1, 1, 0);         /* TCP drops "y" by passing through it. */
494                 epollpair(true);
495                 siocatmarkpair(true);
496
497                 recvpair("y", 1, 1, MSG_OOB);   /* TCP returns -EINVAL. */
498                 epollpair(false);
499                 siocatmarkpair(true);
500         }
501 }
502
503 TEST_F(msg_oob, ex_oob_drop_2)
504 {
505         sendpair("x", 1, MSG_OOB);
506         epollpair(true);
507         siocatmarkpair(true);
508
509         sendpair("y", 1, MSG_OOB);              /* TCP drops "x" at this moment. */
510         epollpair(true);
511
512         tcp_incompliant {
513                 siocatmarkpair(false);
514         }
515
516         recvpair("y", 1, 1, MSG_OOB);
517         epollpair(false);
518
519         tcp_incompliant {
520                 siocatmarkpair(false);
521
522                 recvpair("x", 1, 1, 0);         /* TCP returns -EAGAIN. */
523                 epollpair(false);
524                 siocatmarkpair(true);
525         }
526 }
527
528 TEST_F(msg_oob, ex_oob_oob)
529 {
530         sendpair("x", 1, MSG_OOB);
531         epollpair(true);
532         siocatmarkpair(true);
533
534         recvpair("x", 1, 1, MSG_OOB);
535         epollpair(false);
536         siocatmarkpair(true);
537
538         sendpair("y", 1, MSG_OOB);
539         epollpair(true);
540         siocatmarkpair(true);
541
542         recvpair("", -EAGAIN, 1, 0);
543         epollpair(false);
544         siocatmarkpair(false);
545
546         recvpair("", -EINVAL, 1, MSG_OOB);
547         epollpair(false);
548         siocatmarkpair(false);
549 }
550
551 TEST_F(msg_oob, ex_oob_ahead_break)
552 {
553         sendpair("hello", 5, MSG_OOB);
554         epollpair(true);
555         siocatmarkpair(false);
556
557         sendpair("wor", 3, MSG_OOB);
558         epollpair(true);
559         siocatmarkpair(false);
560
561         recvpair("r", 1, 1, MSG_OOB);
562         epollpair(false);
563         siocatmarkpair(false);
564
565         sendpair("ld", 2, MSG_OOB);
566         epollpair(true);
567         siocatmarkpair(false);
568
569         tcp_incompliant {
570                 recvpair("hellowol", 8, 10, 0); /* TCP recv()s "helloworl", why "r" ?? */
571         }
572
573         epollpair(true);
574         siocatmarkpair(true);
575
576         recvpair("d", 1, 1, MSG_OOB);
577         epollpair(false);
578         siocatmarkpair(true);
579 }
580
581 TEST_F(msg_oob, ex_oob_siocatmark)
582 {
583         sendpair("hello", 5, MSG_OOB);
584         epollpair(true);
585         siocatmarkpair(false);
586
587         recvpair("o", 1, 1, MSG_OOB);
588         epollpair(false);
589         siocatmarkpair(false);
590
591         sendpair("world", 5, MSG_OOB);
592         epollpair(true);
593         siocatmarkpair(false);
594
595         recvpair("hell", 4, 4, 0);              /* Intentionally stop at ex-OOB. */
596         epollpair(true);
597         siocatmarkpair(false);
598 }
599
600 TEST_F(msg_oob, inline_oob)
601 {
602         setinlinepair();
603
604         sendpair("x", 1, MSG_OOB);
605         epollpair(true);
606         siocatmarkpair(true);
607
608         recvpair("", -EINVAL, 1, MSG_OOB);
609         epollpair(true);
610         siocatmarkpair(true);
611
612         recvpair("x", 1, 1, 0);
613         epollpair(false);
614         siocatmarkpair(false);
615 }
616
617 TEST_F(msg_oob, inline_oob_break)
618 {
619         setinlinepair();
620
621         sendpair("hello", 5, MSG_OOB);
622         epollpair(true);
623         siocatmarkpair(false);
624
625         recvpair("", -EINVAL, 1, MSG_OOB);
626         epollpair(true);
627         siocatmarkpair(false);
628
629         recvpair("hell", 4, 5, 0);              /* Break at OOB but not at ex-OOB. */
630         epollpair(true);
631         siocatmarkpair(true);
632
633         recvpair("o", 1, 1, 0);
634         epollpair(false);
635         siocatmarkpair(false);
636 }
637
638 TEST_F(msg_oob, inline_oob_ahead_break)
639 {
640         sendpair("hello", 5, MSG_OOB);
641         epollpair(true);
642         siocatmarkpair(false);
643
644         sendpair("world", 5, 0);
645         epollpair(true);
646         siocatmarkpair(false);
647
648         recvpair("o", 1, 1, MSG_OOB);
649         epollpair(false);
650         siocatmarkpair(false);
651
652         setinlinepair();
653
654         recvpair("hell", 4, 9, 0);              /* Break at OOB even with enough buffer. */
655         epollpair(false);
656         siocatmarkpair(true);
657
658         tcp_incompliant {
659                 recvpair("world", 5, 6, 0);     /* TCP recv()s "oworld", ... "o" ??? */
660         }
661
662         epollpair(false);
663         siocatmarkpair(false);
664 }
665
666 TEST_F(msg_oob, inline_ex_oob_break)
667 {
668         sendpair("hello", 5, MSG_OOB);
669         epollpair(true);
670         siocatmarkpair(false);
671
672         sendpair("wor", 3, MSG_OOB);
673         epollpair(true);
674         siocatmarkpair(false);
675
676         sendpair("ld", 2, 0);
677         epollpair(true);
678         siocatmarkpair(false);
679
680         setinlinepair();
681
682         recvpair("hellowo", 7, 10, 0);          /* Break at OOB but not at ex-OOB. */
683         epollpair(true);
684         siocatmarkpair(true);
685
686         recvpair("rld", 3, 3, 0);
687         epollpair(false);
688         siocatmarkpair(false);
689 }
690
691 TEST_F(msg_oob, inline_ex_oob_no_drop)
692 {
693         sendpair("x", 1, MSG_OOB);
694         epollpair(true);
695         siocatmarkpair(true);
696
697         setinlinepair();
698
699         sendpair("y", 1, MSG_OOB);              /* TCP does NOT drops "x" at this moment. */
700         epollpair(true);
701         siocatmarkpair(false);
702
703         recvpair("x", 1, 1, 0);
704         epollpair(true);
705         siocatmarkpair(true);
706
707         recvpair("y", 1, 1, 0);
708         epollpair(false);
709         siocatmarkpair(false);
710 }
711
712 TEST_F(msg_oob, inline_ex_oob_drop)
713 {
714         sendpair("x", 1, MSG_OOB);
715         epollpair(true);
716         siocatmarkpair(true);
717
718         sendpair("y", 1, MSG_OOB);              /* TCP drops "x" at this moment. */
719         epollpair(true);
720
721         setinlinepair();
722
723         tcp_incompliant {
724                 siocatmarkpair(false);
725
726                 recvpair("x", 1, 1, 0);         /* TCP recv()s "y". */
727                 epollpair(true);
728                 siocatmarkpair(true);
729
730                 recvpair("y", 1, 1, 0);         /* TCP returns -EAGAIN. */
731                 epollpair(false);
732                 siocatmarkpair(false);
733         }
734 }
735
736 TEST_F(msg_oob, inline_ex_oob_siocatmark)
737 {
738         sendpair("hello", 5, MSG_OOB);
739         epollpair(true);
740         siocatmarkpair(false);
741
742         recvpair("o", 1, 1, MSG_OOB);
743         epollpair(false);
744         siocatmarkpair(false);
745
746         setinlinepair();
747
748         sendpair("world", 5, MSG_OOB);
749         epollpair(true);
750         siocatmarkpair(false);
751
752         recvpair("hell", 4, 4, 0);              /* Intentionally stop at ex-OOB. */
753         epollpair(true);
754         siocatmarkpair(false);
755 }
756
757 TEST_HARNESS_MAIN
This page took 0.071462 seconds and 4 git commands to generate.