xref: /netbsd-src/external/bsd/tcpdump/dist/print-pgm.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 /*
2  * Redistribution and use in source and binary forms, with or without
3  * modification, are permitted provided that: (1) source code
4  * distributions retain the above copyright notice and this paragraph
5  * in its entirety, and (2) distributions including binary code include
6  * the above copyright notice and this paragraph in its entirety in
7  * the documentation or other materials provided with the distribution.
8  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
9  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
10  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11  * FOR A PARTICULAR PURPOSE.
12  *
13  * Original code by Andy Heffernan (ahh@juniper.net)
14  */
15 
16 #include <sys/cdefs.h>
17 #ifndef lint
18 __RCSID("$NetBSD: print-pgm.c,v 1.12 2024/09/02 16:15:32 christos Exp $");
19 #endif
20 
21 /* \summary: Pragmatic General Multicast (PGM) printer */
22 
23 #include <config.h>
24 
25 #include "netdissect-stdinc.h"
26 
27 #include "netdissect.h"
28 #include "extract.h"
29 #include "addrtoname.h"
30 #include "addrtostr.h"
31 
32 #include "ip.h"
33 #include "ip6.h"
34 #include "ipproto.h"
35 #include "af.h"
36 
37 /*
38  * PGM header (RFC 3208)
39  */
40 struct pgm_header {
41     nd_uint16_t	pgm_sport;
42     nd_uint16_t	pgm_dport;
43     nd_uint8_t	pgm_type;
44     nd_uint8_t	pgm_options;
45     nd_uint16_t	pgm_sum;
46     nd_byte	pgm_gsid[6];
47     nd_uint16_t	pgm_length;
48 };
49 
50 struct pgm_spm {
51     nd_uint32_t	pgms_seq;
52     nd_uint32_t	pgms_trailseq;
53     nd_uint32_t	pgms_leadseq;
54     nd_uint16_t	pgms_nla_afi;
55     nd_uint16_t	pgms_reserved;
56     /* ... uint8_t	pgms_nla[0]; */
57     /* ... options */
58 };
59 
60 struct pgm_nak {
61     nd_uint32_t	pgmn_seq;
62     nd_uint16_t	pgmn_source_afi;
63     nd_uint16_t	pgmn_reserved;
64     /* ... uint8_t	pgmn_source[0]; */
65     /* ... uint16_t	pgmn_group_afi */
66     /* ... uint16_t	pgmn_reserved2; */
67     /* ... uint8_t	pgmn_group[0]; */
68     /* ... options */
69 };
70 
71 struct pgm_ack {
72     nd_uint32_t	pgma_rx_max_seq;
73     nd_uint32_t	pgma_bitmap;
74     /* ... options */
75 };
76 
77 struct pgm_poll {
78     nd_uint32_t	pgmp_seq;
79     nd_uint16_t	pgmp_round;
80     nd_uint16_t	pgmp_subtype;
81     nd_uint16_t	pgmp_nla_afi;
82     nd_uint16_t	pgmp_reserved;
83     /* ... uint8_t	pgmp_nla[0]; */
84     /* ... options */
85 };
86 
87 struct pgm_polr {
88     nd_uint32_t	pgmp_seq;
89     nd_uint16_t	pgmp_round;
90     nd_uint16_t	pgmp_reserved;
91     /* ... options */
92 };
93 
94 struct pgm_data {
95     nd_uint32_t	pgmd_seq;
96     nd_uint32_t	pgmd_trailseq;
97     /* ... options */
98 };
99 
100 typedef enum _pgm_type {
101     PGM_SPM = 0,		/* source path message */
102     PGM_POLL = 1,		/* POLL Request */
103     PGM_POLR = 2,		/* POLL Response */
104     PGM_ODATA = 4,		/* original data */
105     PGM_RDATA = 5,		/* repair data */
106     PGM_NAK = 8,		/* NAK */
107     PGM_NULLNAK = 9,		/* Null NAK */
108     PGM_NCF = 10,		/* NAK Confirmation */
109     PGM_ACK = 11,		/* ACK for congestion control */
110     PGM_SPMR = 12,		/* SPM request */
111     PGM_MAX = 255
112 } pgm_type;
113 
114 #define PGM_OPT_BIT_PRESENT	0x01
115 #define PGM_OPT_BIT_NETWORK	0x02
116 #define PGM_OPT_BIT_VAR_PKTLEN	0x40
117 #define PGM_OPT_BIT_PARITY	0x80
118 
119 #define PGM_OPT_LENGTH		0x00
120 #define PGM_OPT_FRAGMENT        0x01
121 #define PGM_OPT_NAK_LIST        0x02
122 #define PGM_OPT_JOIN            0x03
123 #define PGM_OPT_NAK_BO_IVL	0x04
124 #define PGM_OPT_NAK_BO_RNG	0x05
125 
126 #define PGM_OPT_REDIRECT        0x07
127 #define PGM_OPT_PARITY_PRM      0x08
128 #define PGM_OPT_PARITY_GRP      0x09
129 #define PGM_OPT_CURR_TGSIZE     0x0A
130 #define PGM_OPT_NBR_UNREACH	0x0B
131 #define PGM_OPT_PATH_NLA	0x0C
132 
133 #define PGM_OPT_SYN             0x0D
134 #define PGM_OPT_FIN             0x0E
135 #define PGM_OPT_RST             0x0F
136 #define PGM_OPT_CR		0x10
137 #define PGM_OPT_CRQST		0x11
138 
139 #define PGM_OPT_PGMCC_DATA	0x12
140 #define PGM_OPT_PGMCC_FEEDBACK	0x13
141 
142 #define PGM_OPT_MASK		0x7f
143 
144 #define PGM_OPT_END		0x80    /* end of options marker */
145 
146 #define PGM_MIN_OPT_LEN		4
147 
148 UNALIGNED_OK
149 void
150 pgm_print(netdissect_options *ndo,
151           const u_char *bp, u_int length,
152           const u_char *bp2)
153 {
154 	const struct pgm_header *pgm;
155 	const struct ip *ip;
156 	uint8_t pgm_type_val;
157 	uint16_t sport, dport;
158 	u_int nla_afnum;
159 	char nla_buf[INET6_ADDRSTRLEN];
160 	const struct ip6_hdr *ip6;
161 	uint8_t opt_type, opt_len;
162 	uint32_t seq, opts_len, len, offset;
163 
164 	ndo->ndo_protocol = "pgm";
165 	pgm = (const struct pgm_header *)bp;
166 	ip = (const struct ip *)bp2;
167 	if (IP_V(ip) == 6)
168 		ip6 = (const struct ip6_hdr *)bp2;
169 	else
170 		ip6 = NULL;
171 	if (!ND_TTEST_2(pgm->pgm_dport)) {
172 		if (ip6) {
173 			ND_PRINT("%s > %s:",
174 				GET_IP6ADDR_STRING(ip6->ip6_src),
175 				GET_IP6ADDR_STRING(ip6->ip6_dst));
176 		} else {
177 			ND_PRINT("%s > %s:",
178 				GET_IPADDR_STRING(ip->ip_src),
179 				GET_IPADDR_STRING(ip->ip_dst));
180 		}
181 		nd_print_trunc(ndo);
182 		return;
183 	}
184 
185 	sport = GET_BE_U_2(pgm->pgm_sport);
186 	dport = GET_BE_U_2(pgm->pgm_dport);
187 
188 	if (ip6) {
189 		if (GET_U_1(ip6->ip6_nxt) == IPPROTO_PGM) {
190 			ND_PRINT("%s.%s > %s.%s: ",
191 				GET_IP6ADDR_STRING(ip6->ip6_src),
192 				tcpport_string(ndo, sport),
193 				GET_IP6ADDR_STRING(ip6->ip6_dst),
194 				tcpport_string(ndo, dport));
195 		} else {
196 			ND_PRINT("%s > %s: ",
197 				tcpport_string(ndo, sport), tcpport_string(ndo, dport));
198 		}
199 	} else {
200 		if (GET_U_1(ip->ip_p) == IPPROTO_PGM) {
201 			ND_PRINT("%s.%s > %s.%s: ",
202 				GET_IPADDR_STRING(ip->ip_src),
203 				tcpport_string(ndo, sport),
204 				GET_IPADDR_STRING(ip->ip_dst),
205 				tcpport_string(ndo, dport));
206 		} else {
207 			ND_PRINT("%s > %s: ",
208 				tcpport_string(ndo, sport), tcpport_string(ndo, dport));
209 		}
210 	}
211 
212 	ND_TCHECK_SIZE(pgm);
213 
214         ND_PRINT("PGM, length %u", GET_BE_U_2(pgm->pgm_length));
215 
216         if (!ndo->ndo_vflag)
217             return;
218 
219 	pgm_type_val = GET_U_1(pgm->pgm_type);
220 	ND_PRINT(" 0x%02x%02x%02x%02x%02x%02x ",
221 		     pgm->pgm_gsid[0],
222                      pgm->pgm_gsid[1],
223                      pgm->pgm_gsid[2],
224 		     pgm->pgm_gsid[3],
225                      pgm->pgm_gsid[4],
226                      pgm->pgm_gsid[5]);
227 	switch (pgm_type_val) {
228 	case PGM_SPM: {
229 	    const struct pgm_spm *spm;
230 
231 	    spm = (const struct pgm_spm *)(pgm + 1);
232 	    ND_TCHECK_SIZE(spm);
233 	    bp = (const u_char *) (spm + 1);
234 
235 	    switch (GET_BE_U_2(spm->pgms_nla_afi)) {
236 	    case AFNUM_INET:
237 		ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
238 		addrtostr(bp, nla_buf, sizeof(nla_buf));
239 		bp += sizeof(nd_ipv4);
240 		break;
241 	    case AFNUM_INET6:
242 		ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
243 		addrtostr6(bp, nla_buf, sizeof(nla_buf));
244 		bp += sizeof(nd_ipv6);
245 		break;
246 	    default:
247 		goto trunc;
248 		break;
249 	    }
250 
251 	    ND_PRINT("SPM seq %u trail %u lead %u nla %s",
252 			 GET_BE_U_4(spm->pgms_seq),
253 			 GET_BE_U_4(spm->pgms_trailseq),
254 			 GET_BE_U_4(spm->pgms_leadseq),
255 			 nla_buf);
256 	    break;
257 	}
258 
259 	case PGM_POLL: {
260 	    const struct pgm_poll *pgm_poll;
261 	    uint32_t ivl, rnd, mask;
262 
263 	    pgm_poll = (const struct pgm_poll *)(pgm + 1);
264 	    ND_TCHECK_SIZE(pgm_poll);
265 	    bp = (const u_char *) (pgm_poll + 1);
266 
267 	    switch (GET_BE_U_2(pgm_poll->pgmp_nla_afi)) {
268 	    case AFNUM_INET:
269 		ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
270 		addrtostr(bp, nla_buf, sizeof(nla_buf));
271 		bp += sizeof(nd_ipv4);
272 		break;
273 	    case AFNUM_INET6:
274 		ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
275 		addrtostr6(bp, nla_buf, sizeof(nla_buf));
276 		bp += sizeof(nd_ipv6);
277 		break;
278 	    default:
279 		goto trunc;
280 		break;
281 	    }
282 
283 	    ivl = GET_BE_U_4(bp);
284 	    bp += sizeof(uint32_t);
285 
286 	    rnd = GET_BE_U_4(bp);
287 	    bp += sizeof(uint32_t);
288 
289 	    mask = GET_BE_U_4(bp);
290 	    bp += sizeof(uint32_t);
291 
292 	    ND_PRINT("POLL seq %u round %u nla %s ivl %u rnd 0x%08x "
293 			 "mask 0x%08x", GET_BE_U_4(pgm_poll->pgmp_seq),
294 			 GET_BE_U_2(pgm_poll->pgmp_round), nla_buf, ivl, rnd,
295 			 mask);
296 	    break;
297 	}
298 	case PGM_POLR: {
299 	    const struct pgm_polr *polr_msg;
300 
301 	    polr_msg = (const struct pgm_polr *)(pgm + 1);
302 	    ND_TCHECK_SIZE(polr_msg);
303 	    ND_PRINT("POLR seq %u round %u",
304 			 GET_BE_U_4(polr_msg->pgmp_seq),
305 			 GET_BE_U_2(polr_msg->pgmp_round));
306 	    bp = (const u_char *) (polr_msg + 1);
307 	    break;
308 	}
309 	case PGM_ODATA: {
310 	    const struct pgm_data *odata;
311 
312 	    odata = (const struct pgm_data *)(pgm + 1);
313 	    ND_TCHECK_SIZE(odata);
314 	    ND_PRINT("ODATA trail %u seq %u",
315 			 GET_BE_U_4(odata->pgmd_trailseq),
316 			 GET_BE_U_4(odata->pgmd_seq));
317 	    bp = (const u_char *) (odata + 1);
318 	    break;
319 	}
320 
321 	case PGM_RDATA: {
322 	    const struct pgm_data *rdata;
323 
324 	    rdata = (const struct pgm_data *)(pgm + 1);
325 	    ND_TCHECK_SIZE(rdata);
326 	    ND_PRINT("RDATA trail %u seq %u",
327 			 GET_BE_U_4(rdata->pgmd_trailseq),
328 			 GET_BE_U_4(rdata->pgmd_seq));
329 	    bp = (const u_char *) (rdata + 1);
330 	    break;
331 	}
332 
333 	case PGM_NAK:
334 	case PGM_NULLNAK:
335 	case PGM_NCF: {
336 	    const struct pgm_nak *nak;
337 	    char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];
338 
339 	    nak = (const struct pgm_nak *)(pgm + 1);
340 	    ND_TCHECK_SIZE(nak);
341 	    bp = (const u_char *) (nak + 1);
342 
343 	    /*
344 	     * Skip past the source, saving info along the way
345 	     * and stopping if we don't have enough.
346 	     */
347 	    switch (GET_BE_U_2(nak->pgmn_source_afi)) {
348 	    case AFNUM_INET:
349 		ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
350 		addrtostr(bp, source_buf, sizeof(source_buf));
351 		bp += sizeof(nd_ipv4);
352 		break;
353 	    case AFNUM_INET6:
354 		ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
355 		addrtostr6(bp, source_buf, sizeof(source_buf));
356 		bp += sizeof(nd_ipv6);
357 		break;
358 	    default:
359 		goto trunc;
360 		break;
361 	    }
362 
363 	    /*
364 	     * Skip past the group, saving info along the way
365 	     * and stopping if we don't have enough.
366 	     */
367 	    bp += (2 * sizeof(uint16_t));
368 	    switch (GET_BE_U_2(bp)) {
369 	    case AFNUM_INET:
370 		ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
371 		addrtostr(bp, group_buf, sizeof(group_buf));
372 		bp += sizeof(nd_ipv4);
373 		break;
374 	    case AFNUM_INET6:
375 		ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
376 		addrtostr6(bp, group_buf, sizeof(group_buf));
377 		bp += sizeof(nd_ipv6);
378 		break;
379 	    default:
380 		goto trunc;
381 		break;
382 	    }
383 
384 	    /*
385 	     * Options decoding can go here.
386 	     */
387 	    switch (pgm_type_val) {
388 		case PGM_NAK:
389 		    ND_PRINT("NAK ");
390 		    break;
391 		case PGM_NULLNAK:
392 		    ND_PRINT("NNAK ");
393 		    break;
394 		case PGM_NCF:
395 		    ND_PRINT("NCF ");
396 		    break;
397 		default:
398                     break;
399 	    }
400 	    ND_PRINT("(%s -> %s), seq %u",
401 			 source_buf, group_buf, GET_BE_U_4(nak->pgmn_seq));
402 	    break;
403 	}
404 
405 	case PGM_ACK: {
406 	    const struct pgm_ack *ack;
407 
408 	    ack = (const struct pgm_ack *)(pgm + 1);
409 	    ND_TCHECK_SIZE(ack);
410 	    ND_PRINT("ACK seq %u",
411 			 GET_BE_U_4(ack->pgma_rx_max_seq));
412 	    bp = (const u_char *) (ack + 1);
413 	    break;
414 	}
415 
416 	case PGM_SPMR:
417 	    ND_PRINT("SPMR");
418 	    break;
419 
420 	default:
421 	    ND_PRINT("UNKNOWN type 0x%02x", pgm_type_val);
422 	    break;
423 
424 	}
425 	if (GET_U_1(pgm->pgm_options) & PGM_OPT_BIT_PRESENT) {
426 
427 	    /*
428 	     * make sure there's enough for the first option header
429 	     */
430 	    ND_TCHECK_LEN(bp, PGM_MIN_OPT_LEN);
431 
432 	    /*
433 	     * That option header MUST be an OPT_LENGTH option
434 	     * (see the first paragraph of section 9.1 in RFC 3208).
435 	     */
436 	    opt_type = GET_U_1(bp);
437 	    bp++;
438 	    if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
439 		ND_PRINT("[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK);
440 		return;
441 	    }
442 	    opt_len = GET_U_1(bp);
443 	    bp++;
444 	    if (opt_len != 4) {
445 		ND_PRINT("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
446 		return;
447 	    }
448 	    opts_len = GET_BE_U_2(bp);
449 	    bp += sizeof(uint16_t);
450 	    if (opts_len < 4) {
451 		ND_PRINT("[Bad total option length %u < 4]", opts_len);
452 		return;
453 	    }
454 	    ND_PRINT(" OPTS LEN %u", opts_len);
455 	    opts_len -= 4;
456 
457 	    while (opts_len) {
458 		if (opts_len < PGM_MIN_OPT_LEN) {
459 		    ND_PRINT("[Total option length leaves no room for final option]");
460 		    return;
461 		}
462 		opt_type = GET_U_1(bp);
463 		bp++;
464 		opt_len = GET_U_1(bp);
465 		bp++;
466 		if (opt_len < PGM_MIN_OPT_LEN) {
467 		    ND_PRINT("[Bad option, length %u < %u]", opt_len,
468 		        PGM_MIN_OPT_LEN);
469 		    break;
470 		}
471 		if (opts_len < opt_len) {
472 		    ND_PRINT("[Total option length leaves no room for final option]");
473 		    return;
474 		}
475 		ND_TCHECK_LEN(bp, opt_len - 2);
476 
477 		switch (opt_type & PGM_OPT_MASK) {
478 		case PGM_OPT_LENGTH:
479 #define PGM_OPT_LENGTH_LEN	(2+2)
480 		    if (opt_len != PGM_OPT_LENGTH_LEN) {
481 			ND_PRINT("[Bad OPT_LENGTH option, length %u != %u]",
482 			    opt_len, PGM_OPT_LENGTH_LEN);
483 			return;
484 		    }
485 		    ND_PRINT(" OPTS LEN (extra?) %u", GET_BE_U_2(bp));
486 		    bp += 2;
487 		    opts_len -= PGM_OPT_LENGTH_LEN;
488 		    break;
489 
490 		case PGM_OPT_FRAGMENT:
491 #define PGM_OPT_FRAGMENT_LEN	(2+2+4+4+4)
492 		    if (opt_len != PGM_OPT_FRAGMENT_LEN) {
493 			ND_PRINT("[Bad OPT_FRAGMENT option, length %u != %u]",
494 			    opt_len, PGM_OPT_FRAGMENT_LEN);
495 			return;
496 		    }
497 		    bp += 2;
498 		    seq = GET_BE_U_4(bp);
499 		    bp += 4;
500 		    offset = GET_BE_U_4(bp);
501 		    bp += 4;
502 		    len = GET_BE_U_4(bp);
503 		    bp += 4;
504 		    ND_PRINT(" FRAG seq %u off %u len %u", seq, offset, len);
505 		    opts_len -= PGM_OPT_FRAGMENT_LEN;
506 		    break;
507 
508 		case PGM_OPT_NAK_LIST:
509 		    bp += 2;
510 		    opt_len -= 4;	/* option header */
511 		    ND_PRINT(" NAK LIST");
512 		    while (opt_len) {
513 			if (opt_len < 4) {
514 			    ND_PRINT("[Option length not a multiple of 4]");
515 			    return;
516 			}
517 			ND_PRINT(" %u", GET_BE_U_4(bp));
518 			bp += 4;
519 			opt_len -= 4;
520 			opts_len -= 4;
521 		    }
522 		    break;
523 
524 		case PGM_OPT_JOIN:
525 #define PGM_OPT_JOIN_LEN	(2+2+4)
526 		    if (opt_len != PGM_OPT_JOIN_LEN) {
527 			ND_PRINT("[Bad OPT_JOIN option, length %u != %u]",
528 			    opt_len, PGM_OPT_JOIN_LEN);
529 			return;
530 		    }
531 		    bp += 2;
532 		    seq = GET_BE_U_4(bp);
533 		    bp += 4;
534 		    ND_PRINT(" JOIN %u", seq);
535 		    opts_len -= PGM_OPT_JOIN_LEN;
536 		    break;
537 
538 		case PGM_OPT_NAK_BO_IVL:
539 #define PGM_OPT_NAK_BO_IVL_LEN	(2+2+4+4)
540 		    if (opt_len != PGM_OPT_NAK_BO_IVL_LEN) {
541 			ND_PRINT("[Bad OPT_NAK_BO_IVL option, length %u != %u]",
542 			    opt_len, PGM_OPT_NAK_BO_IVL_LEN);
543 			return;
544 		    }
545 		    bp += 2;
546 		    offset = GET_BE_U_4(bp);
547 		    bp += 4;
548 		    seq = GET_BE_U_4(bp);
549 		    bp += 4;
550 		    ND_PRINT(" BACKOFF ivl %u ivlseq %u", offset, seq);
551 		    opts_len -= PGM_OPT_NAK_BO_IVL_LEN;
552 		    break;
553 
554 		case PGM_OPT_NAK_BO_RNG:
555 #define PGM_OPT_NAK_BO_RNG_LEN	(2+2+4+4)
556 		    if (opt_len != PGM_OPT_NAK_BO_RNG_LEN) {
557 			ND_PRINT("[Bad OPT_NAK_BO_RNG option, length %u != %u]",
558 			    opt_len, PGM_OPT_NAK_BO_RNG_LEN);
559 			return;
560 		    }
561 		    bp += 2;
562 		    offset = GET_BE_U_4(bp);
563 		    bp += 4;
564 		    seq = GET_BE_U_4(bp);
565 		    bp += 4;
566 		    ND_PRINT(" BACKOFF max %u min %u", offset, seq);
567 		    opts_len -= PGM_OPT_NAK_BO_RNG_LEN;
568 		    break;
569 
570 		case PGM_OPT_REDIRECT:
571 #define PGM_OPT_REDIRECT_FIXED_LEN	(2+2+2+2)
572 		    if (opt_len < PGM_OPT_REDIRECT_FIXED_LEN) {
573 			ND_PRINT("[Bad OPT_REDIRECT option, length %u < %u]",
574 			    opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
575 			return;
576 		    }
577 		    bp += 2;
578 		    nla_afnum = GET_BE_U_2(bp);
579 		    bp += 2+2;
580 		    switch (nla_afnum) {
581 		    case AFNUM_INET:
582 			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv4)) {
583 			    ND_PRINT("[Bad OPT_REDIRECT option, length %u != %u + address size]",
584 			        opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
585 			    return;
586 			}
587 			ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
588 			addrtostr(bp, nla_buf, sizeof(nla_buf));
589 			bp += sizeof(nd_ipv4);
590 			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv4);
591 			break;
592 		    case AFNUM_INET6:
593 			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv6)) {
594 			    ND_PRINT("[Bad OPT_REDIRECT option, length %u != %u + address size]",
595 			        opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
596 			    return;
597 			}
598 			ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
599 			addrtostr6(bp, nla_buf, sizeof(nla_buf));
600 			bp += sizeof(nd_ipv6);
601 			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv6);
602 			break;
603 		    default:
604 			goto trunc;
605 			break;
606 		    }
607 
608 		    ND_PRINT(" REDIRECT %s",  nla_buf);
609 		    break;
610 
611 		case PGM_OPT_PARITY_PRM:
612 #define PGM_OPT_PARITY_PRM_LEN	(2+2+4)
613 		    if (opt_len != PGM_OPT_PARITY_PRM_LEN) {
614 			ND_PRINT("[Bad OPT_PARITY_PRM option, length %u != %u]",
615 			    opt_len, PGM_OPT_PARITY_PRM_LEN);
616 			return;
617 		    }
618 		    bp += 2;
619 		    len = GET_BE_U_4(bp);
620 		    bp += 4;
621 		    ND_PRINT(" PARITY MAXTGS %u", len);
622 		    opts_len -= PGM_OPT_PARITY_PRM_LEN;
623 		    break;
624 
625 		case PGM_OPT_PARITY_GRP:
626 #define PGM_OPT_PARITY_GRP_LEN	(2+2+4)
627 		    if (opt_len != PGM_OPT_PARITY_GRP_LEN) {
628 			ND_PRINT("[Bad OPT_PARITY_GRP option, length %u != %u]",
629 			    opt_len, PGM_OPT_PARITY_GRP_LEN);
630 			return;
631 		    }
632 		    bp += 2;
633 		    seq = GET_BE_U_4(bp);
634 		    bp += 4;
635 		    ND_PRINT(" PARITY GROUP %u", seq);
636 		    opts_len -= PGM_OPT_PARITY_GRP_LEN;
637 		    break;
638 
639 		case PGM_OPT_CURR_TGSIZE:
640 #define PGM_OPT_CURR_TGSIZE_LEN	(2+2+4)
641 		    if (opt_len != PGM_OPT_CURR_TGSIZE_LEN) {
642 			ND_PRINT("[Bad OPT_CURR_TGSIZE option, length %u != %u]",
643 			    opt_len, PGM_OPT_CURR_TGSIZE_LEN);
644 			return;
645 		    }
646 		    bp += 2;
647 		    len = GET_BE_U_4(bp);
648 		    bp += 4;
649 		    ND_PRINT(" PARITY ATGS %u", len);
650 		    opts_len -= PGM_OPT_CURR_TGSIZE_LEN;
651 		    break;
652 
653 		case PGM_OPT_NBR_UNREACH:
654 #define PGM_OPT_NBR_UNREACH_LEN	(2+2)
655 		    if (opt_len != PGM_OPT_NBR_UNREACH_LEN) {
656 			ND_PRINT("[Bad OPT_NBR_UNREACH option, length %u != %u]",
657 			    opt_len, PGM_OPT_NBR_UNREACH_LEN);
658 			return;
659 		    }
660 		    bp += 2;
661 		    ND_PRINT(" NBR_UNREACH");
662 		    opts_len -= PGM_OPT_NBR_UNREACH_LEN;
663 		    break;
664 
665 		case PGM_OPT_PATH_NLA:
666 		    ND_PRINT(" PATH_NLA [%u]", opt_len);
667 		    bp += opt_len;
668 		    opts_len -= opt_len;
669 		    break;
670 
671 		case PGM_OPT_SYN:
672 #define PGM_OPT_SYN_LEN	(2+2)
673 		    if (opt_len != PGM_OPT_SYN_LEN) {
674 			ND_PRINT("[Bad OPT_SYN option, length %u != %u]",
675 			    opt_len, PGM_OPT_SYN_LEN);
676 			return;
677 		    }
678 		    bp += 2;
679 		    ND_PRINT(" SYN");
680 		    opts_len -= PGM_OPT_SYN_LEN;
681 		    break;
682 
683 		case PGM_OPT_FIN:
684 #define PGM_OPT_FIN_LEN	(2+2)
685 		    if (opt_len != PGM_OPT_FIN_LEN) {
686 			ND_PRINT("[Bad OPT_FIN option, length %u != %u]",
687 			    opt_len, PGM_OPT_FIN_LEN);
688 			return;
689 		    }
690 		    bp += 2;
691 		    ND_PRINT(" FIN");
692 		    opts_len -= PGM_OPT_FIN_LEN;
693 		    break;
694 
695 		case PGM_OPT_RST:
696 #define PGM_OPT_RST_LEN	(2+2)
697 		    if (opt_len != PGM_OPT_RST_LEN) {
698 			ND_PRINT("[Bad OPT_RST option, length %u != %u]",
699 			    opt_len, PGM_OPT_RST_LEN);
700 			return;
701 		    }
702 		    bp += 2;
703 		    ND_PRINT(" RST");
704 		    opts_len -= PGM_OPT_RST_LEN;
705 		    break;
706 
707 		case PGM_OPT_CR:
708 		    ND_PRINT(" CR");
709 		    bp += opt_len;
710 		    opts_len -= opt_len;
711 		    break;
712 
713 		case PGM_OPT_CRQST:
714 #define PGM_OPT_CRQST_LEN	(2+2)
715 		    if (opt_len != PGM_OPT_CRQST_LEN) {
716 			ND_PRINT("[Bad OPT_CRQST option, length %u != %u]",
717 			    opt_len, PGM_OPT_CRQST_LEN);
718 			return;
719 		    }
720 		    bp += 2;
721 		    ND_PRINT(" CRQST");
722 		    opts_len -= PGM_OPT_CRQST_LEN;
723 		    break;
724 
725 		case PGM_OPT_PGMCC_DATA:
726 #define PGM_OPT_PGMCC_DATA_FIXED_LEN	(2+2+4+2+2)
727 		    if (opt_len < PGM_OPT_PGMCC_DATA_FIXED_LEN) {
728 			ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u < %u]",
729 			    opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
730 			return;
731 		    }
732 		    bp += 2;
733 		    offset = GET_BE_U_4(bp);
734 		    bp += 4;
735 		    nla_afnum = GET_BE_U_2(bp);
736 		    bp += 2+2;
737 		    switch (nla_afnum) {
738 		    case AFNUM_INET:
739 			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv4)) {
740 			    ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
741 			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
742 			    return;
743 			}
744 			ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
745 			addrtostr(bp, nla_buf, sizeof(nla_buf));
746 			bp += sizeof(nd_ipv4);
747 			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv4);
748 			break;
749 		    case AFNUM_INET6:
750 			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv6)) {
751 			    ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
752 			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
753 			    return;
754 			}
755 			ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
756 			addrtostr6(bp, nla_buf, sizeof(nla_buf));
757 			bp += sizeof(nd_ipv6);
758 			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv6);
759 			break;
760 		    default:
761 			goto trunc;
762 			break;
763 		    }
764 
765 		    ND_PRINT(" PGMCC DATA %u %s", offset, nla_buf);
766 		    break;
767 
768 		case PGM_OPT_PGMCC_FEEDBACK:
769 #define PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN	(2+2+4+2+2)
770 		    if (opt_len < PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN) {
771 			ND_PRINT("[Bad PGM_OPT_PGMCC_FEEDBACK option, length %u < %u]",
772 			    opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
773 			return;
774 		    }
775 		    bp += 2;
776 		    offset = GET_BE_U_4(bp);
777 		    bp += 4;
778 		    nla_afnum = GET_BE_U_2(bp);
779 		    bp += 2+2;
780 		    switch (nla_afnum) {
781 		    case AFNUM_INET:
782 			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv4)) {
783 			    ND_PRINT("[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
784 			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
785 			    return;
786 			}
787 			ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
788 			addrtostr(bp, nla_buf, sizeof(nla_buf));
789 			bp += sizeof(nd_ipv4);
790 			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv4);
791 			break;
792 		    case AFNUM_INET6:
793 			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv6)) {
794 			    ND_PRINT("[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
795 			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
796 			    return;
797 			}
798 			ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
799 			addrtostr6(bp, nla_buf, sizeof(nla_buf));
800 			bp += sizeof(nd_ipv6);
801 			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv6);
802 			break;
803 		    default:
804 			goto trunc;
805 			break;
806 		    }
807 
808 		    ND_PRINT(" PGMCC FEEDBACK %u %s", offset, nla_buf);
809 		    break;
810 
811 		default:
812 		    ND_PRINT(" OPT_%02X [%u] ", opt_type, opt_len);
813 		    bp += opt_len;
814 		    opts_len -= opt_len;
815 		    break;
816 		}
817 
818 		if (opt_type & PGM_OPT_END)
819 		    break;
820 	     }
821 	}
822 
823 	ND_PRINT(" [%u]", length);
824 	if (ndo->ndo_packettype == PT_PGM_ZMTP1 &&
825 	    (pgm_type_val == PGM_ODATA || pgm_type_val == PGM_RDATA))
826 		zmtp1_datagram_print(ndo, bp,
827 				     GET_BE_U_2(pgm->pgm_length));
828 
829 	return;
830 
831 trunc:
832 	nd_print_trunc(ndo);
833 }
834