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#include <arpa/inet.h>
4#include <error.h>
5#include <linux/errqueue.h>
6#include <linux/icmp.h>
7#include <linux/icmpv6.h>
8#include <linux/in6.h>
9#include <linux/ip.h>
10#include <linux/ipv6.h>
11#include <netinet/in.h>
12#include <netinet/udp.h>
13#include <poll.h>
14#include <sched.h>
15#include <stdbool.h>
16#include <stdint.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/ioctl.h>
20#include <sys/socket.h>
21
22#include "../kselftest_harness.h"
23
24static const unsigned short src_port = 44444;
25static const unsigned short dst_port = 55555;
26static const int min_orig_dgram_len = 128;
27static const int min_payload_len_v4 =
28 min_orig_dgram_len - sizeof(struct iphdr) - sizeof(struct udphdr);
29static const int min_payload_len_v6 =
30 min_orig_dgram_len - sizeof(struct ipv6hdr) - sizeof(struct udphdr);
31static const uint8_t orig_payload_byte = 0xAA;
32
33struct sockaddr_inet {
34 union {
35 struct sockaddr_in6 v6;
36 struct sockaddr_in v4;
37 struct sockaddr sa;
38 };
39 socklen_t len;
40};
41
42struct ip_case_info {
43 int domain;
44 int level;
45 int opt1;
46 int opt2;
47 int proto;
48 int (*build_func)(uint8_t *buf, ssize_t buflen, bool with_ext,
49 int payload_len, bool bad_csum, bool bad_len,
50 bool smaller_len);
51 int min_payload;
52};
53
54static int bringup_loopback(void)
55{
56 struct ifreq ifr = {
57 .ifr_name = "lo"
58 };
59 int fd;
60
61 fd = socket(AF_INET, SOCK_DGRAM, 0);
62 if (fd < 0)
63 return -1;
64
65 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
66 goto err;
67
68 ifr.ifr_flags = ifr.ifr_flags | IFF_UP;
69
70 if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
71 goto err;
72
73 close(fd);
74 return 0;
75
76err:
77 close(fd);
78 return -1;
79}
80
81static uint16_t csum(const void *buf, size_t len)
82{
83 const uint8_t *data = buf;
84 uint32_t sum = 0;
85
86 while (len > 1) {
87 sum += (data[0] << 8) | data[1];
88 data += 2;
89 len -= 2;
90 }
91
92 if (len == 1)
93 sum += data[0] << 8;
94
95 while (sum >> 16)
96 sum = (sum & 0xFFFF) + (sum >> 16);
97
98 return ~sum & 0xFFFF;
99}
100
101static int poll_err(int fd)
102{
103 struct pollfd pfd;
104
105 memset(&pfd, 0, sizeof(pfd));
106 pfd.fd = fd;
107
108 if (poll(&pfd, 1, 5000) != 1 || pfd.revents != POLLERR)
109 return -1;
110
111 return 0;
112}
113
114static void set_addr(struct sockaddr_inet *addr, int domain,
115 unsigned short port)
116{
117 memset(addr, 0, sizeof(*addr));
118
119 switch (domain) {
120 case AF_INET:
121 addr->v4.sin_family = AF_INET;
122 addr->v4.sin_port = htons(port);
123 addr->v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
124 addr->len = sizeof(addr->v4);
125 break;
126 case AF_INET6:
127 addr->v6.sin6_family = AF_INET6;
128 addr->v6.sin6_port = htons(port);
129 addr->v6.sin6_addr = in6addr_loopback;
130 addr->len = sizeof(addr->v6);
131 break;
132 }
133}
134
135static int bind_and_setsockopt(int fd, const struct ip_case_info *info)
136{
137 struct sockaddr_inet addr;
138 int opt = 1;
139
140 set_addr(&addr, info->domain, src_port);
141
142 if (setsockopt(fd, info->level, info->opt1, &opt, sizeof(opt)) < 0)
143 return -1;
144
145 if (setsockopt(fd, info->level, info->opt2, &opt, sizeof(opt)) < 0)
146 return -1;
147
148 return bind(fd, &addr.sa, addr.len);
149}
150
151static int build_rfc4884_ext(uint8_t *buf, size_t buflen, bool bad_csum,
152 bool bad_len, bool smaller_len)
153{
154 struct icmp_extobj_hdr *objh;
155 struct icmp_ext_hdr *exthdr;
156 size_t obj_len, ext_len;
157 uint16_t sum;
158
159 /* Use an object payload of 4 bytes */
160 obj_len = sizeof(*objh) + sizeof(uint32_t);
161 ext_len = sizeof(*exthdr) + obj_len;
162
163 if (ext_len > buflen)
164 return -EINVAL;
165
166 exthdr = (struct icmp_ext_hdr *)buf;
167 objh = (struct icmp_extobj_hdr *)(buf + sizeof(*exthdr));
168
169 exthdr->version = 2;
170 /* When encoding a bad object length, either encode a length too small
171 * to fit the object header or too big to fit in the packet.
172 */
173 if (bad_len)
174 obj_len = smaller_len ? sizeof(*objh) - 1 : obj_len * 2;
175 objh->length = htons(obj_len);
176
177 sum = csum(buf, ext_len);
178 exthdr->checksum = htons(bad_csum ? sum - 1 : sum);
179
180 return ext_len;
181}
182
183static int build_orig_dgram_v4(uint8_t *buf, ssize_t buflen, int payload_len)
184{
185 struct udphdr *udph;
186 struct iphdr *iph;
187 size_t len = 0;
188
189 len = sizeof(*iph) + sizeof(*udph) + payload_len;
190 if (len > buflen)
191 return -EINVAL;
192
193 iph = (struct iphdr *)buf;
194 udph = (struct udphdr *)(buf + sizeof(*iph));
195
196 iph->version = 4;
197 iph->ihl = 5;
198 iph->protocol = IPPROTO_UDP;
199 iph->saddr = htonl(INADDR_LOOPBACK);
200 iph->daddr = htonl(INADDR_LOOPBACK);
201 iph->tot_len = htons(len);
202 iph->check = htons(csum(iph, sizeof(*iph)));
203
204 udph->source = htons(src_port);
205 udph->dest = htons(dst_port);
206 udph->len = htons(sizeof(*udph) + payload_len);
207
208 memset(buf + sizeof(*iph) + sizeof(*udph), orig_payload_byte,
209 payload_len);
210
211 return len;
212}
213
214static int build_orig_dgram_v6(uint8_t *buf, ssize_t buflen, int payload_len)
215{
216 struct udphdr *udph;
217 struct ipv6hdr *iph;
218 size_t len = 0;
219
220 len = sizeof(*iph) + sizeof(*udph) + payload_len;
221 if (len > buflen)
222 return -EINVAL;
223
224 iph = (struct ipv6hdr *)buf;
225 udph = (struct udphdr *)(buf + sizeof(*iph));
226
227 iph->version = 6;
228 iph->payload_len = htons(sizeof(*udph) + payload_len);
229 iph->nexthdr = IPPROTO_UDP;
230 iph->saddr = in6addr_loopback;
231 iph->daddr = in6addr_loopback;
232
233 udph->source = htons(src_port);
234 udph->dest = htons(dst_port);
235 udph->len = htons(sizeof(*udph) + payload_len);
236
237 memset(buf + sizeof(*iph) + sizeof(*udph), orig_payload_byte,
238 payload_len);
239
240 return len;
241}
242
243static int build_icmpv4_pkt(uint8_t *buf, ssize_t buflen, bool with_ext,
244 int payload_len, bool bad_csum, bool bad_len,
245 bool smaller_len)
246{
247 struct icmphdr *icmph;
248 int len, ret;
249
250 len = sizeof(*icmph);
251 memset(buf, 0, buflen);
252
253 icmph = (struct icmphdr *)buf;
254 icmph->type = ICMP_DEST_UNREACH;
255 icmph->code = ICMP_PORT_UNREACH;
256 icmph->checksum = 0;
257
258 ret = build_orig_dgram_v4(buf + len, buflen - len, payload_len);
259 if (ret < 0)
260 return ret;
261
262 len += ret;
263
264 icmph->un.reserved[1] = (len - sizeof(*icmph)) / sizeof(uint32_t);
265
266 if (with_ext) {
267 ret = build_rfc4884_ext(buf + len, buflen - len,
268 bad_csum, bad_len, smaller_len);
269 if (ret < 0)
270 return ret;
271
272 len += ret;
273 }
274
275 icmph->checksum = htons(csum(icmph, len));
276 return len;
277}
278
279static int build_icmpv6_pkt(uint8_t *buf, ssize_t buflen, bool with_ext,
280 int payload_len, bool bad_csum, bool bad_len,
281 bool smaller_len)
282{
283 struct icmp6hdr *icmph;
284 int len, ret;
285
286 len = sizeof(*icmph);
287 memset(buf, 0, buflen);
288
289 icmph = (struct icmp6hdr *)buf;
290 icmph->icmp6_type = ICMPV6_DEST_UNREACH;
291 icmph->icmp6_code = ICMPV6_PORT_UNREACH;
292 icmph->icmp6_cksum = 0;
293
294 ret = build_orig_dgram_v6(buf + len, buflen - len, payload_len);
295 if (ret < 0)
296 return ret;
297
298 len += ret;
299
300 icmph->icmp6_datagram_len = (len - sizeof(*icmph)) / sizeof(uint64_t);
301
302 if (with_ext) {
303 ret = build_rfc4884_ext(buf + len, buflen - len,
304 bad_csum, bad_len, smaller_len);
305 if (ret < 0)
306 return ret;
307
308 len += ret;
309 }
310
311 icmph->icmp6_cksum = htons(csum(icmph, len));
312 return len;
313}
314
315FIXTURE(rfc4884) {};
316
317FIXTURE_SETUP(rfc4884)
318{
319 int ret;
320
321 ret = unshare(CLONE_NEWNET);
322 ASSERT_EQ(ret, 0) {
323 TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno));
324 }
325
326 ret = bringup_loopback();
327 ASSERT_EQ(ret, 0) TH_LOG("Failed to bring up loopback interface");
328}
329
330FIXTURE_TEARDOWN(rfc4884)
331{
332}
333
334const struct ip_case_info ipv4_info = {
335 .domain = AF_INET,
336 .level = SOL_IP,
337 .opt1 = IP_RECVERR,
338 .opt2 = IP_RECVERR_RFC4884,
339 .proto = IPPROTO_ICMP,
340 .build_func = build_icmpv4_pkt,
341 .min_payload = min_payload_len_v4,
342};
343
344const struct ip_case_info ipv6_info = {
345 .domain = AF_INET6,
346 .level = SOL_IPV6,
347 .opt1 = IPV6_RECVERR,
348 .opt2 = IPV6_RECVERR_RFC4884,
349 .proto = IPPROTO_ICMPV6,
350 .build_func = build_icmpv6_pkt,
351 .min_payload = min_payload_len_v6,
352};
353
354FIXTURE_VARIANT(rfc4884) {
355 /* IPv4/v6 related information */
356 struct ip_case_info info;
357 /* Whether to append an ICMP extension or not */
358 bool with_ext;
359 /* UDP payload length */
360 int payload_len;
361 /* Whether to generate a bad checksum in the ICMP extension structure */
362 bool bad_csum;
363 /* Whether to generate a bad length in the ICMP object header */
364 bool bad_len;
365 /* Whether it is too small to fit the object header or too big to fit
366 * in the packet
367 */
368 bool smaller_len;
369};
370
371/* Tests that a valid ICMPv4 error message with extension and the original
372 * datagram is smaller than 128 bytes, generates an error with zero offset,
373 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
374 */
375FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext_small_payload) {
376 .info = ipv4_info,
377 .with_ext = true,
378 .payload_len = 64,
379 .bad_csum = false,
380 .bad_len = false,
381};
382
383/* Tests that a valid ICMPv4 error message with extension and 128 bytes original
384 * datagram, generates an error with the expected offset, and does not raise the
385 * SO_EE_RFC4884_FLAG_INVALID flag.
386 */
387FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext) {
388 .info = ipv4_info,
389 .with_ext = true,
390 .payload_len = min_payload_len_v4,
391 .bad_csum = false,
392 .bad_len = false,
393};
394
395/* Tests that a valid ICMPv4 error message with extension and the original
396 * datagram is larger than 128 bytes, generates an error with the expected
397 * offset, and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
398 */
399FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext_large_payload) {
400 .info = ipv4_info,
401 .with_ext = true,
402 .payload_len = 256,
403 .bad_csum = false,
404 .bad_len = false,
405};
406
407/* Tests that a valid ICMPv4 error message without extension and the original
408 * datagram is smaller than 128 bytes, generates an error with zero offset,
409 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
410 */
411FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_small_payload) {
412 .info = ipv4_info,
413 .with_ext = false,
414 .payload_len = 64,
415 .bad_csum = false,
416 .bad_len = false,
417};
418
419/* Tests that a valid ICMPv4 error message without extension and 128 bytes
420 * original datagram, generates an error with zero offset, and does not raise
421 * the SO_EE_RFC4884_FLAG_INVALID flag.
422 */
423FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_min_payload) {
424 .info = ipv4_info,
425 .with_ext = false,
426 .payload_len = min_payload_len_v4,
427 .bad_csum = false,
428 .bad_len = false,
429};
430
431/* Tests that a valid ICMPv4 error message without extension and the original
432 * datagram is larger than 128 bytes, generates an error with zero offset,
433 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
434 */
435FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_large_payload) {
436 .info = ipv4_info,
437 .with_ext = false,
438 .payload_len = 256,
439 .bad_csum = false,
440 .bad_len = false,
441};
442
443/* Tests that an ICMPv4 error message with extension and an invalid checksum,
444 * generates an error with the expected offset, and raises the
445 * SO_EE_RFC4884_FLAG_INVALID flag.
446 */
447FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_checksum) {
448 .info = ipv4_info,
449 .with_ext = true,
450 .payload_len = min_payload_len_v4,
451 .bad_csum = true,
452 .bad_len = false,
453};
454
455/* Tests that an ICMPv4 error message with extension and an object length
456 * smaller than the object header, generates an error with the expected offset,
457 * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
458 */
459FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_length_small) {
460 .info = ipv4_info,
461 .with_ext = true,
462 .payload_len = min_payload_len_v4,
463 .bad_csum = false,
464 .bad_len = true,
465 .smaller_len = true,
466};
467
468/* Tests that an ICMPv4 error message with extension and an object length that
469 * is too big to fit in the packet, generates an error with the expected offset,
470 * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
471 */
472FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_length_large) {
473 .info = ipv4_info,
474 .with_ext = true,
475 .payload_len = min_payload_len_v4,
476 .bad_csum = false,
477 .bad_len = true,
478 .smaller_len = false,
479};
480
481/* Tests that a valid ICMPv6 error message with extension and the original
482 * datagram is smaller than 128 bytes, generates an error with zero offset,
483 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
484 */
485FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext_small_payload) {
486 .info = ipv6_info,
487 .with_ext = true,
488 .payload_len = 64,
489 .bad_csum = false,
490 .bad_len = false,
491};
492
493/* Tests that a valid ICMPv6 error message with extension and 128 bytes original
494 * datagram, generates an error with the expected offset, and does not raise the
495 * SO_EE_RFC4884_FLAG_INVALID flag.
496 */
497FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext) {
498 .info = ipv6_info,
499 .with_ext = true,
500 .payload_len = min_payload_len_v6,
501 .bad_csum = false,
502 .bad_len = false,
503};
504
505/* Tests that a valid ICMPv6 error message with extension and the original
506 * datagram is larger than 128 bytes, generates an error with the expected
507 * offset, and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
508 */
509FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext_large_payload) {
510 .info = ipv6_info,
511 .with_ext = true,
512 .payload_len = 256,
513 .bad_csum = false,
514 .bad_len = false,
515};
516/* Tests that a valid ICMPv6 error message without extension and the original
517 * datagram is smaller than 128 bytes, generates an error with zero offset,
518 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
519 */
520FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_small_payload) {
521 .info = ipv6_info,
522 .with_ext = false,
523 .payload_len = 64,
524 .bad_csum = false,
525 .bad_len = false,
526};
527
528/* Tests that a valid ICMPv6 error message without extension and 128 bytes
529 * original datagram, generates an error with zero offset, and does not
530 * raise the SO_EE_RFC4884_FLAG_INVALID flag.
531 */
532FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_min_payload) {
533 .info = ipv6_info,
534 .with_ext = false,
535 .payload_len = min_payload_len_v6,
536 .bad_csum = false,
537 .bad_len = false,
538};
539
540/* Tests that a valid ICMPv6 error message without extension and the original
541 * datagram is larger than 128 bytes, generates an error with zero offset,
542 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
543 */
544FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_large_payload) {
545 .info = ipv6_info,
546 .with_ext = false,
547 .payload_len = 256,
548 .bad_csum = false,
549 .bad_len = false,
550};
551
552/* Tests that an ICMPv6 error message with extension and an invalid checksum,
553 * generates an error with the expected offset, and raises the
554 * SO_EE_RFC4884_FLAG_INVALID flag.
555 */
556FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_checksum) {
557 .info = ipv6_info,
558 .with_ext = true,
559 .payload_len = min_payload_len_v6,
560 .bad_csum = true,
561 .bad_len = false,
562};
563
564/* Tests that an ICMPv6 error message with extension and an object length
565 * smaller than the object header, generates an error with the expected offset,
566 * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
567 */
568FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_length_small) {
569 .info = ipv6_info,
570 .with_ext = true,
571 .payload_len = min_payload_len_v6,
572 .bad_csum = false,
573 .bad_len = true,
574 .smaller_len = true,
575};
576
577/* Tests that an ICMPv6 error message with extension and an object length that
578 * is too big to fit in the packet, generates an error with the expected offset,
579 * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
580 */
581FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_length_large) {
582 .info = ipv6_info,
583 .with_ext = true,
584 .payload_len = min_payload_len_v6,
585 .bad_csum = false,
586 .bad_len = true,
587 .smaller_len = false,
588};
589
590static void
591check_rfc4884_offset(struct __test_metadata *_metadata, int sock,
592 const FIXTURE_VARIANT(rfc4884) *v)
593{
594 char rxbuf[1024];
595 char ctrl[1024];
596 struct iovec iov = {
597 .iov_base = rxbuf,
598 .iov_len = sizeof(rxbuf)
599 };
600 struct msghdr msg = {
601 .msg_iov = &iov,
602 .msg_iovlen = 1,
603 .msg_control = ctrl,
604 .msg_controllen = sizeof(ctrl),
605 };
606 struct cmsghdr *cmsg;
607 int recv;
608
609 ASSERT_EQ(poll_err(sock), 0);
610
611 recv = recvmsg(sock, &msg, MSG_ERRQUEUE);
612 ASSERT_GE(recv, 0) TH_LOG("recvmsg(MSG_ERRQUEUE) failed");
613
614 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
615 bool is_invalid, expected_invalid;
616 struct sock_extended_err *ee;
617 int expected_off;
618 uint16_t off;
619
620 if (cmsg->cmsg_level != v->info.level ||
621 cmsg->cmsg_type != v->info.opt1) {
622 TH_LOG("Unrelated cmsgs were encountered in recvmsg()");
623 continue;
624 }
625
626 ee = (struct sock_extended_err *)CMSG_DATA(cmsg);
627 off = ee->ee_rfc4884.len;
628 is_invalid = ee->ee_rfc4884.flags & SO_EE_RFC4884_FLAG_INVALID;
629
630 expected_invalid = v->bad_csum || v->bad_len;
631 ASSERT_EQ(is_invalid, expected_invalid) {
632 TH_LOG("Expected invalidity flag to be %d, but got %d",
633 expected_invalid, is_invalid);
634 }
635
636 expected_off =
637 (v->with_ext && v->payload_len >= v->info.min_payload) ?
638 v->payload_len : 0;
639 ASSERT_EQ(off, expected_off) {
640 TH_LOG("Expected RFC4884 offset %u, got %u",
641 expected_off, off);
642 }
643 break;
644 }
645}
646
647TEST_F(rfc4884, rfc4884)
648{
649 const typeof(variant) v = variant;
650 struct sockaddr_inet addr;
651 uint8_t pkt[1024];
652 int dgram, raw;
653 int len, sent;
654 int err;
655
656 dgram = socket(v->info.domain, SOCK_DGRAM, 0);
657 ASSERT_GE(dgram, 0) TH_LOG("Opening datagram socket failed");
658
659 err = bind_and_setsockopt(dgram, &v->info);
660 ASSERT_EQ(err, 0) TH_LOG("Bind failed");
661
662 raw = socket(v->info.domain, SOCK_RAW, v->info.proto);
663 ASSERT_GE(raw, 0) TH_LOG("Opening raw socket failed");
664
665 len = v->info.build_func(pkt, sizeof(pkt), v->with_ext, v->payload_len,
666 v->bad_csum, v->bad_len, v->smaller_len);
667 ASSERT_GT(len, 0) TH_LOG("Building packet failed");
668
669 set_addr(&addr, v->info.domain, 0);
670 sent = sendto(raw, pkt, len, 0, &addr.sa, addr.len);
671 ASSERT_EQ(len, sent) TH_LOG("Sending packet failed");
672
673 check_rfc4884_offset(_metadata, dgram, v);
674
675 close(dgram);
676 close(raw);
677}
678
679TEST_HARNESS_MAIN