xref: /linux/tools/testing/selftests/net/ioam6_parser.c (revision 0526b56cbc3c489642bd6a5fe4b718dea7ef0ee8)
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 		*p += sizeof(__u32);
245 
246 	if (ioam6h->type.bit7) {
247 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
248 			return 1;
249 		*p += sizeof(__u32);
250 	}
251 
252 	if (ioam6h->type.bit8) {
253 		raw64 = __be64_to_cpu(*((__u64 *)*p));
254 		if (cnf.hlim != (raw64 >> 56) ||
255 		    cnf.wide != (raw64 & 0xffffffffffffff))
256 			return 1;
257 		*p += sizeof(__u64);
258 	}
259 
260 	if (ioam6h->type.bit9) {
261 		if (__be32_to_cpu(*((__u32 *)*p)) != cnf.ingr_wide)
262 			return 1;
263 		*p += sizeof(__u32);
264 
265 		if (__be32_to_cpu(*((__u32 *)*p)) != cnf.egr_wide)
266 			return 1;
267 		*p += sizeof(__u32);
268 	}
269 
270 	if (ioam6h->type.bit10) {
271 		if (__be64_to_cpu(*((__u64 *)*p)) != cnf.ns_wide)
272 			return 1;
273 		*p += sizeof(__u64);
274 	}
275 
276 	if (ioam6h->type.bit11) {
277 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
278 			return 1;
279 		*p += sizeof(__u32);
280 	}
281 
282 	if (ioam6h->type.bit12) {
283 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
284 			return 1;
285 		*p += sizeof(__u32);
286 	}
287 
288 	if (ioam6h->type.bit13) {
289 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
290 			return 1;
291 		*p += sizeof(__u32);
292 	}
293 
294 	if (ioam6h->type.bit14) {
295 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
296 			return 1;
297 		*p += sizeof(__u32);
298 	}
299 
300 	if (ioam6h->type.bit15) {
301 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
302 			return 1;
303 		*p += sizeof(__u32);
304 	}
305 
306 	if (ioam6h->type.bit16) {
307 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
308 			return 1;
309 		*p += sizeof(__u32);
310 	}
311 
312 	if (ioam6h->type.bit17) {
313 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
314 			return 1;
315 		*p += sizeof(__u32);
316 	}
317 
318 	if (ioam6h->type.bit18) {
319 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
320 			return 1;
321 		*p += sizeof(__u32);
322 	}
323 
324 	if (ioam6h->type.bit19) {
325 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
326 			return 1;
327 		*p += sizeof(__u32);
328 	}
329 
330 	if (ioam6h->type.bit20) {
331 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
332 			return 1;
333 		*p += sizeof(__u32);
334 	}
335 
336 	if (ioam6h->type.bit21) {
337 		if (__be32_to_cpu(*((__u32 *)*p)) != 0xffffffff)
338 			return 1;
339 		*p += sizeof(__u32);
340 	}
341 
342 	if (ioam6h->type.bit22) {
343 		len = cnf.sc_data ? strlen(cnf.sc_data) : 0;
344 		aligned = cnf.sc_data ? __ALIGN_KERNEL(len, 4) : 0;
345 
346 		raw32 = __be32_to_cpu(*((__u32 *)*p));
347 		if (aligned != (raw32 >> 24) * 4 ||
348 		    cnf.sc_id != (raw32 & 0xffffff))
349 			return 1;
350 		*p += sizeof(__u32);
351 
352 		if (cnf.sc_data) {
353 			if (strncmp((char *)*p, cnf.sc_data, len))
354 				return 1;
355 
356 			*p += len;
357 			aligned -= len;
358 
359 			while (aligned--) {
360 				if (**p != '\0')
361 					return 1;
362 				*p += sizeof(__u8);
363 			}
364 		}
365 	}
366 
367 	return 0;
368 }
369 
370 static int check_ioam_header_and_data(int tid, struct ioam6_trace_hdr *ioam6h,
371 				      __u32 trace_type, __u16 ioam_ns)
372 {
373 	__u8 *p;
374 
375 	if (check_ioam_header(tid, ioam6h, trace_type, ioam_ns))
376 		return 1;
377 
378 	p = ioam6h->data + ioam6h->remlen * 4;
379 
380 	switch (tid) {
381 	case TEST_OUT_BIT0:
382 	case TEST_OUT_BIT1:
383 	case TEST_OUT_BIT2:
384 	case TEST_OUT_BIT3:
385 	case TEST_OUT_BIT4:
386 	case TEST_OUT_BIT5:
387 	case TEST_OUT_BIT6:
388 	case TEST_OUT_BIT7:
389 	case TEST_OUT_BIT8:
390 	case TEST_OUT_BIT9:
391 	case TEST_OUT_BIT10:
392 	case TEST_OUT_BIT11:
393 	case TEST_OUT_BIT22:
394 	case TEST_OUT_FULL_SUPP_TRACE:
395 		return check_ioam6_data(&p, ioam6h, node1);
396 
397 	case TEST_IN_BIT0:
398 	case TEST_IN_BIT1:
399 	case TEST_IN_BIT2:
400 	case TEST_IN_BIT3:
401 	case TEST_IN_BIT4:
402 	case TEST_IN_BIT5:
403 	case TEST_IN_BIT6:
404 	case TEST_IN_BIT7:
405 	case TEST_IN_BIT8:
406 	case TEST_IN_BIT9:
407 	case TEST_IN_BIT10:
408 	case TEST_IN_BIT11:
409 	case TEST_IN_BIT22:
410 	case TEST_IN_FULL_SUPP_TRACE:
411 	{
412 		__u32 tmp32 = node2.egr_wide;
413 		__u16 tmp16 = node2.egr_id;
414 		int res;
415 
416 		node2.egr_id = 0xffff;
417 		node2.egr_wide = 0xffffffff;
418 
419 		res = check_ioam6_data(&p, ioam6h, node2);
420 
421 		node2.egr_id = tmp16;
422 		node2.egr_wide = tmp32;
423 
424 		return res;
425 	}
426 
427 	case TEST_FWD_FULL_SUPP_TRACE:
428 		if (check_ioam6_data(&p, ioam6h, node3))
429 			return 1;
430 		if (check_ioam6_data(&p, ioam6h, node2))
431 			return 1;
432 		return check_ioam6_data(&p, ioam6h, node1);
433 
434 	default:
435 		break;
436 	}
437 
438 	return 1;
439 }
440 
441 static int str2id(const char *tname)
442 {
443 	if (!strcmp("out_undef_ns", tname))
444 		return TEST_OUT_UNDEF_NS;
445 	if (!strcmp("out_no_room", tname))
446 		return TEST_OUT_NO_ROOM;
447 	if (!strcmp("out_bit0", tname))
448 		return TEST_OUT_BIT0;
449 	if (!strcmp("out_bit1", tname))
450 		return TEST_OUT_BIT1;
451 	if (!strcmp("out_bit2", tname))
452 		return TEST_OUT_BIT2;
453 	if (!strcmp("out_bit3", tname))
454 		return TEST_OUT_BIT3;
455 	if (!strcmp("out_bit4", tname))
456 		return TEST_OUT_BIT4;
457 	if (!strcmp("out_bit5", tname))
458 		return TEST_OUT_BIT5;
459 	if (!strcmp("out_bit6", tname))
460 		return TEST_OUT_BIT6;
461 	if (!strcmp("out_bit7", tname))
462 		return TEST_OUT_BIT7;
463 	if (!strcmp("out_bit8", tname))
464 		return TEST_OUT_BIT8;
465 	if (!strcmp("out_bit9", tname))
466 		return TEST_OUT_BIT9;
467 	if (!strcmp("out_bit10", tname))
468 		return TEST_OUT_BIT10;
469 	if (!strcmp("out_bit11", tname))
470 		return TEST_OUT_BIT11;
471 	if (!strcmp("out_bit22", tname))
472 		return TEST_OUT_BIT22;
473 	if (!strcmp("out_full_supp_trace", tname))
474 		return TEST_OUT_FULL_SUPP_TRACE;
475 	if (!strcmp("in_undef_ns", tname))
476 		return TEST_IN_UNDEF_NS;
477 	if (!strcmp("in_no_room", tname))
478 		return TEST_IN_NO_ROOM;
479 	if (!strcmp("in_oflag", tname))
480 		return TEST_IN_OFLAG;
481 	if (!strcmp("in_bit0", tname))
482 		return TEST_IN_BIT0;
483 	if (!strcmp("in_bit1", tname))
484 		return TEST_IN_BIT1;
485 	if (!strcmp("in_bit2", tname))
486 		return TEST_IN_BIT2;
487 	if (!strcmp("in_bit3", tname))
488 		return TEST_IN_BIT3;
489 	if (!strcmp("in_bit4", tname))
490 		return TEST_IN_BIT4;
491 	if (!strcmp("in_bit5", tname))
492 		return TEST_IN_BIT5;
493 	if (!strcmp("in_bit6", tname))
494 		return TEST_IN_BIT6;
495 	if (!strcmp("in_bit7", tname))
496 		return TEST_IN_BIT7;
497 	if (!strcmp("in_bit8", tname))
498 		return TEST_IN_BIT8;
499 	if (!strcmp("in_bit9", tname))
500 		return TEST_IN_BIT9;
501 	if (!strcmp("in_bit10", tname))
502 		return TEST_IN_BIT10;
503 	if (!strcmp("in_bit11", tname))
504 		return TEST_IN_BIT11;
505 	if (!strcmp("in_bit22", tname))
506 		return TEST_IN_BIT22;
507 	if (!strcmp("in_full_supp_trace", tname))
508 		return TEST_IN_FULL_SUPP_TRACE;
509 	if (!strcmp("fwd_full_supp_trace", tname))
510 		return TEST_FWD_FULL_SUPP_TRACE;
511 
512 	return -1;
513 }
514 
515 static int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2)
516 {
517 	return ((a1->s6_addr32[0] ^ a2->s6_addr32[0]) |
518 		(a1->s6_addr32[1] ^ a2->s6_addr32[1]) |
519 		(a1->s6_addr32[2] ^ a2->s6_addr32[2]) |
520 		(a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0;
521 }
522 
523 static int get_u32(__u32 *val, const char *arg, int base)
524 {
525 	unsigned long res;
526 	char *ptr;
527 
528 	if (!arg || !*arg)
529 		return -1;
530 	res = strtoul(arg, &ptr, base);
531 
532 	if (!ptr || ptr == arg || *ptr)
533 		return -1;
534 
535 	if (res == ULONG_MAX && errno == ERANGE)
536 		return -1;
537 
538 	if (res > 0xFFFFFFFFUL)
539 		return -1;
540 
541 	*val = res;
542 	return 0;
543 }
544 
545 static int get_u16(__u16 *val, const char *arg, int base)
546 {
547 	unsigned long res;
548 	char *ptr;
549 
550 	if (!arg || !*arg)
551 		return -1;
552 	res = strtoul(arg, &ptr, base);
553 
554 	if (!ptr || ptr == arg || *ptr)
555 		return -1;
556 
557 	if (res == ULONG_MAX && errno == ERANGE)
558 		return -1;
559 
560 	if (res > 0xFFFFUL)
561 		return -1;
562 
563 	*val = res;
564 	return 0;
565 }
566 
567 static int (*func[__TEST_MAX])(int, struct ioam6_trace_hdr *, __u32, __u16) = {
568 	[TEST_OUT_UNDEF_NS]		= check_ioam_header,
569 	[TEST_OUT_NO_ROOM]		= check_ioam_header,
570 	[TEST_OUT_BIT0]		= check_ioam_header_and_data,
571 	[TEST_OUT_BIT1]		= check_ioam_header_and_data,
572 	[TEST_OUT_BIT2]		= check_ioam_header_and_data,
573 	[TEST_OUT_BIT3]		= check_ioam_header_and_data,
574 	[TEST_OUT_BIT4]		= check_ioam_header_and_data,
575 	[TEST_OUT_BIT5]		= check_ioam_header_and_data,
576 	[TEST_OUT_BIT6]		= check_ioam_header_and_data,
577 	[TEST_OUT_BIT7]		= check_ioam_header_and_data,
578 	[TEST_OUT_BIT8]		= check_ioam_header_and_data,
579 	[TEST_OUT_BIT9]		= check_ioam_header_and_data,
580 	[TEST_OUT_BIT10]		= check_ioam_header_and_data,
581 	[TEST_OUT_BIT11]		= check_ioam_header_and_data,
582 	[TEST_OUT_BIT22]		= check_ioam_header_and_data,
583 	[TEST_OUT_FULL_SUPP_TRACE]	= check_ioam_header_and_data,
584 	[TEST_IN_UNDEF_NS]		= check_ioam_header,
585 	[TEST_IN_NO_ROOM]		= check_ioam_header,
586 	[TEST_IN_OFLAG]		= check_ioam_header,
587 	[TEST_IN_BIT0]			= check_ioam_header_and_data,
588 	[TEST_IN_BIT1]			= check_ioam_header_and_data,
589 	[TEST_IN_BIT2]			= check_ioam_header_and_data,
590 	[TEST_IN_BIT3]			= check_ioam_header_and_data,
591 	[TEST_IN_BIT4]			= check_ioam_header_and_data,
592 	[TEST_IN_BIT5]			= check_ioam_header_and_data,
593 	[TEST_IN_BIT6]			= check_ioam_header_and_data,
594 	[TEST_IN_BIT7]			= check_ioam_header_and_data,
595 	[TEST_IN_BIT8]			= check_ioam_header_and_data,
596 	[TEST_IN_BIT9]			= check_ioam_header_and_data,
597 	[TEST_IN_BIT10]		= check_ioam_header_and_data,
598 	[TEST_IN_BIT11]		= check_ioam_header_and_data,
599 	[TEST_IN_BIT22]		= check_ioam_header_and_data,
600 	[TEST_IN_FULL_SUPP_TRACE]	= check_ioam_header_and_data,
601 	[TEST_FWD_FULL_SUPP_TRACE]	= check_ioam_header_and_data,
602 };
603 
604 int main(int argc, char **argv)
605 {
606 	int fd, size, hoplen, tid, ret = 1;
607 	struct in6_addr src, dst;
608 	struct ioam6_hdr *opt;
609 	struct ipv6hdr *ip6h;
610 	__u8 buffer[400], *p;
611 	__u16 ioam_ns;
612 	__u32 tr_type;
613 
614 	if (argc != 7)
615 		goto out;
616 
617 	tid = str2id(argv[2]);
618 	if (tid < 0 || !func[tid])
619 		goto out;
620 
621 	if (inet_pton(AF_INET6, argv[3], &src) != 1 ||
622 	    inet_pton(AF_INET6, argv[4], &dst) != 1)
623 		goto out;
624 
625 	if (get_u32(&tr_type, argv[5], 16) ||
626 	    get_u16(&ioam_ns, argv[6], 0))
627 		goto out;
628 
629 	fd = socket(AF_PACKET, SOCK_DGRAM, __cpu_to_be16(ETH_P_IPV6));
630 	if (!fd)
631 		goto out;
632 
633 	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
634 		       argv[1], strlen(argv[1])))
635 		goto close;
636 
637 recv:
638 	size = recv(fd, buffer, sizeof(buffer), 0);
639 	if (size <= 0)
640 		goto close;
641 
642 	ip6h = (struct ipv6hdr *)buffer;
643 
644 	if (!ipv6_addr_equal(&ip6h->saddr, &src) ||
645 	    !ipv6_addr_equal(&ip6h->daddr, &dst))
646 		goto recv;
647 
648 	if (ip6h->nexthdr != IPPROTO_HOPOPTS)
649 		goto close;
650 
651 	p = buffer + sizeof(*ip6h);
652 	hoplen = (p[1] + 1) << 3;
653 	p += sizeof(struct ipv6_hopopt_hdr);
654 
655 	while (hoplen > 0) {
656 		opt = (struct ioam6_hdr *)p;
657 
658 		if (opt->opt_type == IPV6_TLV_IOAM &&
659 		    opt->type == IOAM6_TYPE_PREALLOC) {
660 			p += sizeof(*opt);
661 			ret = func[tid](tid, (struct ioam6_trace_hdr *)p,
662 					   tr_type, ioam_ns);
663 			break;
664 		}
665 
666 		p += opt->opt_len + 2;
667 		hoplen -= opt->opt_len + 2;
668 	}
669 close:
670 	close(fd);
671 out:
672 	return ret;
673 }
674