1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Author: Justin Iurman (justin.iurman@uliege.be)
4  *
5  * IOAM tester for IPv6, see ioam6.sh for details on each test case.
6  */
7 #include <arpa/inet.h>
8 #include <errno.h>
9 #include <limits.h>
10 #include <linux/const.h>
11 #include <linux/if_ether.h>
12 #include <linux/ioam6.h>
13 #include <linux/ipv6.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 
18 struct ioam_config {
19 	__u32 id;
20 	__u64 wide;
21 	__u16 ingr_id;
22 	__u16 egr_id;
23 	__u32 ingr_wide;
24 	__u32 egr_wide;
25 	__u32 ns_data;
26 	__u64 ns_wide;
27 	__u32 sc_id;
28 	__u8 hlim;
29 	char *sc_data;
30 };
31 
32 /*
33  * Be careful if you modify structs below - everything MUST be kept synchronized
34  * with configurations inside ioam6.sh and always reflect the same.
35  */
36 
37 static struct ioam_config node1 = {
38 	.id = 1,
39 	.wide = 11111111,
40 	.ingr_id = 0xffff, /* default value */
41 	.egr_id = 101,
42 	.ingr_wide = 0xffffffff, /* default value */
43 	.egr_wide = 101101,
44 	.ns_data = 0xdeadbee0,
45 	.ns_wide = 0xcafec0caf00dc0de,
46 	.sc_id = 777,
47 	.sc_data = "something that will be 4n-aligned",
48 	.hlim = 64,
49 };
50 
51 static struct ioam_config node2 = {
52 	.id = 2,
53 	.wide = 22222222,
54 	.ingr_id = 201,
55 	.egr_id = 202,
56 	.ingr_wide = 201201,
57 	.egr_wide = 202202,
58 	.ns_data = 0xdeadbee1,
59 	.ns_wide = 0xcafec0caf11dc0de,
60 	.sc_id = 666,
61 	.sc_data = "Hello there -Obi",
62 	.hlim = 63,
63 };
64 
65 static struct ioam_config node3 = {
66 	.id = 3,
67 	.wide = 33333333,
68 	.ingr_id = 301,
69 	.egr_id = 0xffff, /* default value */
70 	.ingr_wide = 301301,
71 	.egr_wide = 0xffffffff, /* default value */
72 	.ns_data = 0xdeadbee2,
73 	.ns_wide = 0xcafec0caf22dc0de,
74 	.sc_id = 0xffffff, /* default value */
75 	.sc_data = NULL,
76 	.hlim = 62,
77 };
78 
79 enum {
80 	/**********
81 	 * OUTPUT *
82 	 **********/
83 	TEST_OUT_UNDEF_NS,
84 	TEST_OUT_NO_ROOM,
85 	TEST_OUT_BIT0,
86 	TEST_OUT_BIT1,
87 	TEST_OUT_BIT2,
88 	TEST_OUT_BIT3,
89 	TEST_OUT_BIT4,
90 	TEST_OUT_BIT5,
91 	TEST_OUT_BIT6,
92 	TEST_OUT_BIT7,
93 	TEST_OUT_BIT8,
94 	TEST_OUT_BIT9,
95 	TEST_OUT_BIT10,
96 	TEST_OUT_BIT11,
97 	TEST_OUT_BIT22,
98 	TEST_OUT_FULL_SUPP_TRACE,
99 
100 	/*********
101 	 * INPUT *
102 	 *********/
103 	TEST_IN_UNDEF_NS,
104 	TEST_IN_NO_ROOM,
105 	TEST_IN_OFLAG,
106 	TEST_IN_BIT0,
107 	TEST_IN_BIT1,
108 	TEST_IN_BIT2,
109 	TEST_IN_BIT3,
110 	TEST_IN_BIT4,
111 	TEST_IN_BIT5,
112 	TEST_IN_BIT6,
113 	TEST_IN_BIT7,
114 	TEST_IN_BIT8,
115 	TEST_IN_BIT9,
116 	TEST_IN_BIT10,
117 	TEST_IN_BIT11,
118 	TEST_IN_BIT22,
119 	TEST_IN_FULL_SUPP_TRACE,
120 
121 	/**********
122 	 * GLOBAL *
123 	 **********/
124 	TEST_FWD_FULL_SUPP_TRACE,
125 
126 	__TEST_MAX,
127 };
128 
129 static int check_ioam_header(int tid, struct ioam6_trace_hdr *ioam6h,
130 			     __u32 trace_type, __u16 ioam_ns)
131 {
132 	if (__be16_to_cpu(ioam6h->namespace_id) != ioam_ns ||
133 	    __be32_to_cpu(ioam6h->type_be32) != (trace_type << 8))
134 		return 1;
135 
136 	switch (tid) {
137 	case TEST_OUT_UNDEF_NS:
138 	case TEST_IN_UNDEF_NS:
139 		return ioam6h->overflow ||
140 		       ioam6h->nodelen != 1 ||
141 		       ioam6h->remlen != 1;
142 
143 	case TEST_OUT_NO_ROOM:
144 	case TEST_IN_NO_ROOM:
145 	case TEST_IN_OFLAG:
146 		return !ioam6h->overflow ||
147 		       ioam6h->nodelen != 2 ||
148 		       ioam6h->remlen != 1;
149 
150 	case TEST_OUT_BIT0:
151 	case TEST_IN_BIT0:
152 	case TEST_OUT_BIT1:
153 	case TEST_IN_BIT1:
154 	case TEST_OUT_BIT2:
155 	case TEST_IN_BIT2:
156 	case TEST_OUT_BIT3:
157 	case TEST_IN_BIT3:
158 	case TEST_OUT_BIT4:
159 	case TEST_IN_BIT4:
160 	case TEST_OUT_BIT5:
161 	case TEST_IN_BIT5:
162 	case TEST_OUT_BIT6:
163 	case TEST_IN_BIT6:
164 	case TEST_OUT_BIT7:
165 	case TEST_IN_BIT7:
166 	case TEST_OUT_BIT11:
167 	case TEST_IN_BIT11:
168 		return ioam6h->overflow ||
169 		       ioam6h->nodelen != 1 ||
170 		       ioam6h->remlen;
171 
172 	case TEST_OUT_BIT8:
173 	case TEST_IN_BIT8:
174 	case TEST_OUT_BIT9:
175 	case TEST_IN_BIT9:
176 	case TEST_OUT_BIT10:
177 	case TEST_IN_BIT10:
178 		return ioam6h->overflow ||
179 		       ioam6h->nodelen != 2 ||
180 		       ioam6h->remlen;
181 
182 	case TEST_OUT_BIT22:
183 	case TEST_IN_BIT22:
184 		return ioam6h->overflow ||
185 		       ioam6h->nodelen ||
186 		       ioam6h->remlen;
187 
188 	case TEST_OUT_FULL_SUPP_TRACE:
189 	case TEST_IN_FULL_SUPP_TRACE:
190 	case TEST_FWD_FULL_SUPP_TRACE:
191 		return ioam6h->overflow ||
192 		       ioam6h->nodelen != 15 ||
193 		       ioam6h->remlen;
194 
195 	default:
196 		break;
197 	}
198 
199 	return 1;
200 }
201 
202 static int check_ioam6_data(__u8 **p, struct ioam6_trace_hdr *ioam6h,
203 			    const struct ioam_config cnf)
204 {
205 	unsigned int len;
206 	__u8 aligned;
207 	__u64 raw64;
208 	__u32 raw32;
209 
210 	if (ioam6h->type.bit0) {
211 		raw32 = __be32_to_cpu(*((__u32 *)*p));
212 		if (cnf.hlim != (raw32 >> 24) || cnf.id != (raw32 & 0xffffff))
213 			return 1;
214 		*p += sizeof(__u32);
215 	}
216 
217 	if (ioam6h->type.bit1) {
218 		raw32 = __be32_to_cpu(*((__u32 *)*p));
219 		if (cnf.ingr_id != (raw32 >> 16) ||
220 		    cnf.egr_id != (raw32 & 0xffff))
221 			return 1;
222 		*p += sizeof(__u32);
223 	}
224 
225 	if (ioam6h->type.bit2)
226 		*p += sizeof(__u32);
227 
228 	if (ioam6h->type.bit3)
229 		*p += sizeof(__u32);
230 
231 	if (ioam6h->type.bit4) {
232 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
233 			return 1;
234 		*p += sizeof(__u32);
235 	}
236 
237 	if (ioam6h->type.bit5) {
238 		if (__be32_to_cpu(*((__u32 *)*p)) != cnf.ns_data)
239 			return 1;
240 		*p += sizeof(__u32);
241 	}
242 
243 	if (ioam6h->type.bit6) {
244 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
245 			return 1;
246 		*p += sizeof(__u32);
247 	}
248 
249 	if (ioam6h->type.bit7) {
250 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
251 			return 1;
252 		*p += sizeof(__u32);
253 	}
254 
255 	if (ioam6h->type.bit8) {
256 		raw64 = __be64_to_cpu(*((__u64 *)*p));
257 		if (cnf.hlim != (raw64 >> 56) ||
258 		    cnf.wide != (raw64 & 0xffffffffffffff))
259 			return 1;
260 		*p += sizeof(__u64);
261 	}
262 
263 	if (ioam6h->type.bit9) {
264 		if (__be32_to_cpu(*((__u32 *)*p)) != cnf.ingr_wide)
265 			return 1;
266 		*p += sizeof(__u32);
267 
268 		if (__be32_to_cpu(*((__u32 *)*p)) != cnf.egr_wide)
269 			return 1;
270 		*p += sizeof(__u32);
271 	}
272 
273 	if (ioam6h->type.bit10) {
274 		if (__be64_to_cpu(*((__u64 *)*p)) != cnf.ns_wide)
275 			return 1;
276 		*p += sizeof(__u64);
277 	}
278 
279 	if (ioam6h->type.bit11) {
280 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
281 			return 1;
282 		*p += sizeof(__u32);
283 	}
284 
285 	if (ioam6h->type.bit12) {
286 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
287 			return 1;
288 		*p += sizeof(__u32);
289 	}
290 
291 	if (ioam6h->type.bit13) {
292 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
293 			return 1;
294 		*p += sizeof(__u32);
295 	}
296 
297 	if (ioam6h->type.bit14) {
298 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
299 			return 1;
300 		*p += sizeof(__u32);
301 	}
302 
303 	if (ioam6h->type.bit15) {
304 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
305 			return 1;
306 		*p += sizeof(__u32);
307 	}
308 
309 	if (ioam6h->type.bit16) {
310 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
311 			return 1;
312 		*p += sizeof(__u32);
313 	}
314 
315 	if (ioam6h->type.bit17) {
316 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
317 			return 1;
318 		*p += sizeof(__u32);
319 	}
320 
321 	if (ioam6h->type.bit18) {
322 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
323 			return 1;
324 		*p += sizeof(__u32);
325 	}
326 
327 	if (ioam6h->type.bit19) {
328 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
329 			return 1;
330 		*p += sizeof(__u32);
331 	}
332 
333 	if (ioam6h->type.bit20) {
334 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
335 			return 1;
336 		*p += sizeof(__u32);
337 	}
338 
339 	if (ioam6h->type.bit21) {
340 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
341 			return 1;
342 		*p += sizeof(__u32);
343 	}
344 
345 	if (ioam6h->type.bit22) {
346 		len = cnf.sc_data ? strlen(cnf.sc_data) : 0;
347 		aligned = cnf.sc_data ? __ALIGN_KERNEL(len, 4) : 0;
348 
349 		raw32 = __be32_to_cpu(*((__u32 *)*p));
350 		if (aligned != (raw32 >> 24) * 4 ||
351 		    cnf.sc_id != (raw32 & 0xffffff))
352 			return 1;
353 		*p += sizeof(__u32);
354 
355 		if (cnf.sc_data) {
356 			if (strncmp((char *)*p, cnf.sc_data, len))
357 				return 1;
358 
359 			*p += len;
360 			aligned -= len;
361 
362 			while (aligned--) {
363 				if (**p != '\0')
364 					return 1;
365 				*p += sizeof(__u8);
366 			}
367 		}
368 	}
369 
370 	return 0;
371 }
372 
373 static int check_ioam_header_and_data(int tid, struct ioam6_trace_hdr *ioam6h,
374 				      __u32 trace_type, __u16 ioam_ns)
375 {
376 	__u8 *p;
377 
378 	if (check_ioam_header(tid, ioam6h, trace_type, ioam_ns))
379 		return 1;
380 
381 	p = ioam6h->data + ioam6h->remlen * 4;
382 
383 	switch (tid) {
384 	case TEST_OUT_BIT0:
385 	case TEST_OUT_BIT1:
386 	case TEST_OUT_BIT2:
387 	case TEST_OUT_BIT3:
388 	case TEST_OUT_BIT4:
389 	case TEST_OUT_BIT5:
390 	case TEST_OUT_BIT6:
391 	case TEST_OUT_BIT7:
392 	case TEST_OUT_BIT8:
393 	case TEST_OUT_BIT9:
394 	case TEST_OUT_BIT10:
395 	case TEST_OUT_BIT11:
396 	case TEST_OUT_BIT22:
397 	case TEST_OUT_FULL_SUPP_TRACE:
398 		return check_ioam6_data(&p, ioam6h, node1);
399 
400 	case TEST_IN_BIT0:
401 	case TEST_IN_BIT1:
402 	case TEST_IN_BIT2:
403 	case TEST_IN_BIT3:
404 	case TEST_IN_BIT4:
405 	case TEST_IN_BIT5:
406 	case TEST_IN_BIT6:
407 	case TEST_IN_BIT7:
408 	case TEST_IN_BIT8:
409 	case TEST_IN_BIT9:
410 	case TEST_IN_BIT10:
411 	case TEST_IN_BIT11:
412 	case TEST_IN_BIT22:
413 	case TEST_IN_FULL_SUPP_TRACE:
414 	{
415 		__u32 tmp32 = node2.egr_wide;
416 		__u16 tmp16 = node2.egr_id;
417 		int res;
418 
419 		node2.egr_id = 0xffff;
420 		node2.egr_wide = 0xffffffff;
421 
422 		res = check_ioam6_data(&p, ioam6h, node2);
423 
424 		node2.egr_id = tmp16;
425 		node2.egr_wide = tmp32;
426 
427 		return res;
428 	}
429 
430 	case TEST_FWD_FULL_SUPP_TRACE:
431 		if (check_ioam6_data(&p, ioam6h, node3))
432 			return 1;
433 		if (check_ioam6_data(&p, ioam6h, node2))
434 			return 1;
435 		return check_ioam6_data(&p, ioam6h, node1);
436 
437 	default:
438 		break;
439 	}
440 
441 	return 1;
442 }
443 
444 static int str2id(const char *tname)
445 {
446 	if (!strcmp("out_undef_ns", tname))
447 		return TEST_OUT_UNDEF_NS;
448 	if (!strcmp("out_no_room", tname))
449 		return TEST_OUT_NO_ROOM;
450 	if (!strcmp("out_bit0", tname))
451 		return TEST_OUT_BIT0;
452 	if (!strcmp("out_bit1", tname))
453 		return TEST_OUT_BIT1;
454 	if (!strcmp("out_bit2", tname))
455 		return TEST_OUT_BIT2;
456 	if (!strcmp("out_bit3", tname))
457 		return TEST_OUT_BIT3;
458 	if (!strcmp("out_bit4", tname))
459 		return TEST_OUT_BIT4;
460 	if (!strcmp("out_bit5", tname))
461 		return TEST_OUT_BIT5;
462 	if (!strcmp("out_bit6", tname))
463 		return TEST_OUT_BIT6;
464 	if (!strcmp("out_bit7", tname))
465 		return TEST_OUT_BIT7;
466 	if (!strcmp("out_bit8", tname))
467 		return TEST_OUT_BIT8;
468 	if (!strcmp("out_bit9", tname))
469 		return TEST_OUT_BIT9;
470 	if (!strcmp("out_bit10", tname))
471 		return TEST_OUT_BIT10;
472 	if (!strcmp("out_bit11", tname))
473 		return TEST_OUT_BIT11;
474 	if (!strcmp("out_bit22", tname))
475 		return TEST_OUT_BIT22;
476 	if (!strcmp("out_full_supp_trace", tname))
477 		return TEST_OUT_FULL_SUPP_TRACE;
478 	if (!strcmp("in_undef_ns", tname))
479 		return TEST_IN_UNDEF_NS;
480 	if (!strcmp("in_no_room", tname))
481 		return TEST_IN_NO_ROOM;
482 	if (!strcmp("in_oflag", tname))
483 		return TEST_IN_OFLAG;
484 	if (!strcmp("in_bit0", tname))
485 		return TEST_IN_BIT0;
486 	if (!strcmp("in_bit1", tname))
487 		return TEST_IN_BIT1;
488 	if (!strcmp("in_bit2", tname))
489 		return TEST_IN_BIT2;
490 	if (!strcmp("in_bit3", tname))
491 		return TEST_IN_BIT3;
492 	if (!strcmp("in_bit4", tname))
493 		return TEST_IN_BIT4;
494 	if (!strcmp("in_bit5", tname))
495 		return TEST_IN_BIT5;
496 	if (!strcmp("in_bit6", tname))
497 		return TEST_IN_BIT6;
498 	if (!strcmp("in_bit7", tname))
499 		return TEST_IN_BIT7;
500 	if (!strcmp("in_bit8", tname))
501 		return TEST_IN_BIT8;
502 	if (!strcmp("in_bit9", tname))
503 		return TEST_IN_BIT9;
504 	if (!strcmp("in_bit10", tname))
505 		return TEST_IN_BIT10;
506 	if (!strcmp("in_bit11", tname))
507 		return TEST_IN_BIT11;
508 	if (!strcmp("in_bit22", tname))
509 		return TEST_IN_BIT22;
510 	if (!strcmp("in_full_supp_trace", tname))
511 		return TEST_IN_FULL_SUPP_TRACE;
512 	if (!strcmp("fwd_full_supp_trace", tname))
513 		return TEST_FWD_FULL_SUPP_TRACE;
514 
515 	return -1;
516 }
517 
518 static int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2)
519 {
520 	return ((a1->s6_addr32[0] ^ a2->s6_addr32[0]) |
521 		(a1->s6_addr32[1] ^ a2->s6_addr32[1]) |
522 		(a1->s6_addr32[2] ^ a2->s6_addr32[2]) |
523 		(a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0;
524 }
525 
526 static int get_u32(__u32 *val, const char *arg, int base)
527 {
528 	unsigned long res;
529 	char *ptr;
530 
531 	if (!arg || !*arg)
532 		return -1;
533 	res = strtoul(arg, &ptr, base);
534 
535 	if (!ptr || ptr == arg || *ptr)
536 		return -1;
537 
538 	if (res == ULONG_MAX && errno == ERANGE)
539 		return -1;
540 
541 	if (res > 0xFFFFFFFFUL)
542 		return -1;
543 
544 	*val = res;
545 	return 0;
546 }
547 
548 static int get_u16(__u16 *val, const char *arg, int base)
549 {
550 	unsigned long res;
551 	char *ptr;
552 
553 	if (!arg || !*arg)
554 		return -1;
555 	res = strtoul(arg, &ptr, base);
556 
557 	if (!ptr || ptr == arg || *ptr)
558 		return -1;
559 
560 	if (res == ULONG_MAX && errno == ERANGE)
561 		return -1;
562 
563 	if (res > 0xFFFFUL)
564 		return -1;
565 
566 	*val = res;
567 	return 0;
568 }
569 
570 static int (*func[__TEST_MAX])(int, struct ioam6_trace_hdr *, __u32, __u16) = {
571 	[TEST_OUT_UNDEF_NS]		= check_ioam_header,
572 	[TEST_OUT_NO_ROOM]		= check_ioam_header,
573 	[TEST_OUT_BIT0]		= check_ioam_header_and_data,
574 	[TEST_OUT_BIT1]		= check_ioam_header_and_data,
575 	[TEST_OUT_BIT2]		= check_ioam_header_and_data,
576 	[TEST_OUT_BIT3]		= check_ioam_header_and_data,
577 	[TEST_OUT_BIT4]		= check_ioam_header_and_data,
578 	[TEST_OUT_BIT5]		= check_ioam_header_and_data,
579 	[TEST_OUT_BIT6]		= check_ioam_header_and_data,
580 	[TEST_OUT_BIT7]		= check_ioam_header_and_data,
581 	[TEST_OUT_BIT8]		= check_ioam_header_and_data,
582 	[TEST_OUT_BIT9]		= check_ioam_header_and_data,
583 	[TEST_OUT_BIT10]		= check_ioam_header_and_data,
584 	[TEST_OUT_BIT11]		= check_ioam_header_and_data,
585 	[TEST_OUT_BIT22]		= check_ioam_header_and_data,
586 	[TEST_OUT_FULL_SUPP_TRACE]	= check_ioam_header_and_data,
587 	[TEST_IN_UNDEF_NS]		= check_ioam_header,
588 	[TEST_IN_NO_ROOM]		= check_ioam_header,
589 	[TEST_IN_OFLAG]		= check_ioam_header,
590 	[TEST_IN_BIT0]			= check_ioam_header_and_data,
591 	[TEST_IN_BIT1]			= check_ioam_header_and_data,
592 	[TEST_IN_BIT2]			= check_ioam_header_and_data,
593 	[TEST_IN_BIT3]			= check_ioam_header_and_data,
594 	[TEST_IN_BIT4]			= check_ioam_header_and_data,
595 	[TEST_IN_BIT5]			= check_ioam_header_and_data,
596 	[TEST_IN_BIT6]			= check_ioam_header_and_data,
597 	[TEST_IN_BIT7]			= check_ioam_header_and_data,
598 	[TEST_IN_BIT8]			= check_ioam_header_and_data,
599 	[TEST_IN_BIT9]			= check_ioam_header_and_data,
600 	[TEST_IN_BIT10]		= check_ioam_header_and_data,
601 	[TEST_IN_BIT11]		= check_ioam_header_and_data,
602 	[TEST_IN_BIT22]		= check_ioam_header_and_data,
603 	[TEST_IN_FULL_SUPP_TRACE]	= check_ioam_header_and_data,
604 	[TEST_FWD_FULL_SUPP_TRACE]	= check_ioam_header_and_data,
605 };
606 
607 int main(int argc, char **argv)
608 {
609 	int fd, size, hoplen, tid, ret = 1;
610 	struct in6_addr src, dst;
611 	struct ioam6_hdr *opt;
612 	struct ipv6hdr *ip6h;
613 	__u8 buffer[400], *p;
614 	__u16 ioam_ns;
615 	__u32 tr_type;
616 
617 	if (argc != 7)
618 		goto out;
619 
620 	tid = str2id(argv[2]);
621 	if (tid < 0 || !func[tid])
622 		goto out;
623 
624 	if (inet_pton(AF_INET6, argv[3], &src) != 1 ||
625 	    inet_pton(AF_INET6, argv[4], &dst) != 1)
626 		goto out;
627 
628 	if (get_u32(&tr_type, argv[5], 16) ||
629 	    get_u16(&ioam_ns, argv[6], 0))
630 		goto out;
631 
632 	fd = socket(AF_PACKET, SOCK_DGRAM, __cpu_to_be16(ETH_P_IPV6));
633 	if (!fd)
634 		goto out;
635 
636 	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
637 		       argv[1], strlen(argv[1])))
638 		goto close;
639 
640 recv:
641 	size = recv(fd, buffer, sizeof(buffer), 0);
642 	if (size <= 0)
643 		goto close;
644 
645 	ip6h = (struct ipv6hdr *)buffer;
646 
647 	if (!ipv6_addr_equal(&ip6h->saddr, &src) ||
648 	    !ipv6_addr_equal(&ip6h->daddr, &dst))
649 		goto recv;
650 
651 	if (ip6h->nexthdr != IPPROTO_HOPOPTS)
652 		goto close;
653 
654 	p = buffer + sizeof(*ip6h);
655 	hoplen = (p[1] + 1) << 3;
656 	p += sizeof(struct ipv6_hopopt_hdr);
657 
658 	while (hoplen > 0) {
659 		opt = (struct ioam6_hdr *)p;
660 
661 		if (opt->opt_type == IPV6_TLV_IOAM &&
662 		    opt->type == IOAM6_TYPE_PREALLOC) {
663 			p += sizeof(*opt);
664 			ret = func[tid](tid, (struct ioam6_trace_hdr *)p,
665 					   tr_type, ioam_ns);
666 			break;
667 		}
668 
669 		p += opt->opt_len + 2;
670 		hoplen -= opt->opt_len + 2;
671 	}
672 close:
673 	close(fd);
674 out:
675 	return ret;
676 }
677