xref: /netbsd-src/external/bsd/tcpdump/dist/print-pgm.c (revision 5bbd2a12505d72a8177929a37b5cee489d0a1cfd)
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 #if 0
19 static const char rcsid[] _U_ =
20     "@(#) Header: /tcpdump/master/tcpdump/print-pgm.c,v 1.5 2005-06-07 22:05:58 guy Exp";
21 #else
22 __RCSID("$NetBSD: print-pgm.c,v 1.2 2010/12/05 05:11:30 christos Exp $");
23 #endif
24 #endif
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include <tcpdump-stdinc.h>
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include "interface.h"
37 #include "extract.h"
38 #include "addrtoname.h"
39 
40 #include "ip.h"
41 #ifdef INET6
42 #include "ip6.h"
43 #endif
44 #include "ipproto.h"
45 
46 /*
47  * PGM header (RFC 3208)
48  */
49 struct pgm_header {
50     u_int16_t	pgm_sport;
51     u_int16_t	pgm_dport;
52     u_int8_t	pgm_type;
53     u_int8_t	pgm_options;
54     u_int16_t	pgm_sum;
55     u_int8_t	pgm_gsid[6];
56     u_int16_t	pgm_length;
57 };
58 
59 struct pgm_spm {
60     u_int32_t	pgms_seq;
61     u_int32_t	pgms_trailseq;
62     u_int32_t	pgms_leadseq;
63     u_int16_t	pgms_nla_afi;
64     u_int16_t	pgms_reserved;
65     /* ... u_int8_t	pgms_nla[0]; */
66     /* ... options */
67 };
68 
69 struct pgm_nak {
70     u_int32_t	pgmn_seq;
71     u_int16_t	pgmn_source_afi;
72     u_int16_t	pgmn_reserved;
73     /* ... u_int8_t	pgmn_source[0]; */
74     /* ... u_int16_t	pgmn_group_afi */
75     /* ... u_int16_t	pgmn_reserved2; */
76     /* ... u_int8_t	pgmn_group[0]; */
77     /* ... options */
78 };
79 
80 struct pgm_poll {
81     u_int32_t	pgmp_seq;
82     u_int16_t	pgmp_round;
83     u_int16_t	pgmp_reserved;
84     /* ... options */
85 };
86 
87 struct pgm_polr {
88     u_int32_t	pgmp_seq;
89     u_int16_t	pgmp_round;
90     u_int16_t	pgmp_subtype;
91     u_int16_t	pgmp_nla_afi;
92     u_int16_t	pgmp_reserved;
93     /* ... u_int8_t	pgmp_nla[0]; */
94     /* ... options */
95 };
96 
97 struct pgm_data {
98     u_int32_t	pgmd_seq;
99     u_int32_t	pgmd_trailseq;
100     /* ... options */
101 };
102 
103 typedef enum _pgm_type {
104     PGM_SPM = 0,		/* source path message */
105     PGM_POLL = 1,		/* POLL Request */
106     PGM_POLR = 2,		/* POLL Response */
107     PGM_ODATA = 4,		/* original data */
108     PGM_RDATA = 5,		/* repair data */
109     PGM_NAK = 8,		/* NAK */
110     PGM_NULLNAK = 9,		/* Null NAK */
111     PGM_NCF = 10,		/* NAK Confirmation */
112     PGM_ACK = 11,		/* ACK for congestion control */
113     PGM_SPMR = 12,		/* SPM request */
114     PGM_MAX = 255
115 } pgm_type;
116 
117 #define PGM_OPT_BIT_PRESENT	0x01
118 #define PGM_OPT_BIT_NETWORK	0x02
119 #define PGM_OPT_BIT_VAR_PKTLEN	0x40
120 #define PGM_OPT_BIT_PARITY	0x80
121 
122 #define PGM_OPT_LENGTH		0x00
123 #define PGM_OPT_FRAGMENT        0x01
124 #define PGM_OPT_NAK_LIST        0x02
125 #define PGM_OPT_JOIN            0x03
126 #define PGM_OPT_NAK_BO_IVL	0x04
127 #define PGM_OPT_NAK_BO_RNG	0x05
128 
129 #define PGM_OPT_REDIRECT        0x07
130 #define PGM_OPT_PARITY_PRM      0x08
131 #define PGM_OPT_PARITY_GRP      0x09
132 #define PGM_OPT_CURR_TGSIZE     0x0A
133 #define PGM_OPT_NBR_UNREACH	0x0B
134 #define PGM_OPT_PATH_NLA	0x0C
135 
136 #define PGM_OPT_SYN             0x0D
137 #define PGM_OPT_FIN             0x0E
138 #define PGM_OPT_RST             0x0F
139 #define PGM_OPT_CR		0x10
140 #define PGM_OPT_CRQST		0x11
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 #ifndef AFI_IP
149 #define AFI_IP		1
150 #define AFI_IP6	        2
151 #endif
152 
153 void
154 pgm_print(register const u_char *bp, register u_int length,
155 	  register const u_char *bp2)
156 {
157 	register const struct pgm_header *pgm;
158 	register const struct ip *ip;
159 	register char ch;
160 	u_int16_t sport, dport;
161 	int addr_size;
162 	const void *nla;
163 	int nla_af;
164 #ifdef INET6
165 	char nla_buf[INET6_ADDRSTRLEN];
166 	register const struct ip6_hdr *ip6;
167 #else
168 	char nla_buf[INET_ADDRSTRLEN];
169 #endif
170 	u_int8_t opt_type, opt_len, flags1, flags2;
171 	u_int32_t seq, opts_len, len, offset;
172 
173 	pgm = (struct pgm_header *)bp;
174 	ip = (struct ip *)bp2;
175 #ifdef INET6
176 	if (IP_V(ip) == 6)
177 		ip6 = (struct ip6_hdr *)bp2;
178 	else
179 		ip6 = NULL;
180 #else /* INET6 */
181 	if (IP_V(ip) == 6) {
182 		(void)printf("Can't handle IPv6");
183 		return;
184 	}
185 #endif /* INET6 */
186 	ch = '\0';
187 	if (!TTEST(pgm->pgm_dport)) {
188 #ifdef INET6
189 		if (ip6) {
190 			(void)printf("%s > %s: [|pgm]",
191 				ip6addr_string(&ip6->ip6_src),
192 				ip6addr_string(&ip6->ip6_dst));
193 			return;
194 		} else
195 #endif /* INET6 */
196 		{
197 			(void)printf("%s > %s: [|pgm]",
198 				ipaddr_string(&ip->ip_src),
199 				ipaddr_string(&ip->ip_dst));
200 			return;
201 		}
202 	}
203 
204 	sport = EXTRACT_16BITS(&pgm->pgm_sport);
205 	dport = EXTRACT_16BITS(&pgm->pgm_dport);
206 
207 #ifdef INET6
208 	if (ip6) {
209 		if (ip6->ip6_nxt == IPPROTO_PGM) {
210 			(void)printf("%s.%s > %s.%s: ",
211 				ip6addr_string(&ip6->ip6_src),
212 				tcpport_string(sport),
213 				ip6addr_string(&ip6->ip6_dst),
214 				tcpport_string(dport));
215 		} else {
216 			(void)printf("%s > %s: ",
217 				tcpport_string(sport), tcpport_string(dport));
218 		}
219 	} else
220 #endif /*INET6*/
221 	{
222 		if (ip->ip_p == IPPROTO_PGM) {
223 			(void)printf("%s.%s > %s.%s: ",
224 				ipaddr_string(&ip->ip_src),
225 				tcpport_string(sport),
226 				ipaddr_string(&ip->ip_dst),
227 				tcpport_string(dport));
228 		} else {
229 			(void)printf("%s > %s: ",
230 				tcpport_string(sport), tcpport_string(dport));
231 		}
232 	}
233 
234 	TCHECK(*pgm);
235 
236         (void)printf("PGM, length %u", pgm->pgm_length);
237 
238         if (!vflag)
239             return;
240 
241         if (length > pgm->pgm_length)
242             length = pgm->pgm_length;
243 
244 	(void)printf(" 0x%02x%02x%02x%02x%02x%02x ",
245 		     pgm->pgm_gsid[0],
246                      pgm->pgm_gsid[1],
247                      pgm->pgm_gsid[2],
248 		     pgm->pgm_gsid[3],
249                      pgm->pgm_gsid[4],
250                      pgm->pgm_gsid[5]);
251 	switch (pgm->pgm_type) {
252 	case PGM_SPM: {
253 	    struct pgm_spm *spm;
254 
255 	    spm = (struct pgm_spm *)(pgm + 1);
256 	    TCHECK(*spm);
257 
258 	    switch (EXTRACT_16BITS(&spm->pgms_nla_afi)) {
259 	    case AFI_IP:
260 		addr_size = sizeof(struct in_addr);
261 		nla_af = AF_INET;
262 		break;
263 #ifdef INET6
264 	    case AFI_IP6:
265 		addr_size = sizeof(struct in6_addr);
266 		nla_af = AF_INET6;
267 		break;
268 #endif
269 	    default:
270 		goto trunc;
271 		break;
272 	    }
273 	    bp = (u_char *) (spm + 1);
274 	    TCHECK2(*bp, addr_size);
275 	    nla = bp;
276 	    bp += addr_size;
277 
278 	    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
279 	    (void)printf("SPM seq %u trail %u lead %u nla %s",
280 			 EXTRACT_32BITS(&spm->pgms_seq),
281                          EXTRACT_32BITS(&spm->pgms_trailseq),
282 			 EXTRACT_32BITS(&spm->pgms_leadseq),
283                          nla_buf);
284 	    break;
285 	}
286 
287 	case PGM_POLL: {
288 	    struct pgm_poll *poll;
289 
290 	    poll = (struct pgm_poll *)(pgm + 1);
291 	    TCHECK(*poll);
292 	    (void)printf("POLL seq %u round %u",
293 			 EXTRACT_32BITS(&poll->pgmp_seq),
294                          EXTRACT_16BITS(&poll->pgmp_round));
295 	    bp = (u_char *) (poll + 1);
296 	    break;
297 	}
298 	case PGM_POLR: {
299 	    struct pgm_polr *polr;
300 	    u_int32_t ivl, rnd, mask;
301 
302 	    polr = (struct pgm_polr *)(pgm + 1);
303 	    TCHECK(*polr);
304 
305 	    switch (EXTRACT_16BITS(&polr->pgmp_nla_afi)) {
306 	    case AFI_IP:
307 		addr_size = sizeof(struct in_addr);
308 		nla_af = AF_INET;
309 		break;
310 #ifdef INET6
311 	    case AFI_IP6:
312 		addr_size = sizeof(struct in6_addr);
313 		nla_af = AF_INET6;
314 		break;
315 #endif
316 	    default:
317 		goto trunc;
318 		break;
319 	    }
320 	    bp = (u_char *) (polr + 1);
321 	    TCHECK2(*bp, addr_size);
322 	    nla = bp;
323 	    bp += addr_size;
324 
325 	    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
326 
327 	    TCHECK2(*bp, sizeof(u_int32_t));
328 	    ivl = EXTRACT_32BITS(bp);
329 	    bp += sizeof(u_int32_t);
330 
331 	    TCHECK2(*bp, sizeof(u_int32_t));
332 	    rnd = EXTRACT_32BITS(bp);
333 	    bp += sizeof(u_int32_t);
334 
335 	    TCHECK2(*bp, sizeof(u_int32_t));
336 	    mask = EXTRACT_32BITS(bp);
337 	    bp += sizeof(u_int32_t);
338 
339 	    (void)printf("POLR seq %u round %u nla %s ivl %u rnd 0x%08x "
340 			 "mask 0x%08x", EXTRACT_32BITS(&polr->pgmp_seq),
341 			 EXTRACT_16BITS(&polr->pgmp_round), nla_buf, ivl, rnd, mask);
342 	    break;
343 	}
344 	case PGM_ODATA: {
345 	    struct pgm_data *odata;
346 
347 	    odata = (struct pgm_data *)(pgm + 1);
348 	    TCHECK(*odata);
349 	    (void)printf("ODATA trail %u seq %u",
350 			 EXTRACT_32BITS(&odata->pgmd_trailseq),
351 			 EXTRACT_32BITS(&odata->pgmd_seq));
352 	    bp = (u_char *) (odata + 1);
353 	    break;
354 	}
355 
356 	case PGM_RDATA: {
357 	    struct pgm_data *rdata;
358 
359 	    rdata = (struct pgm_data *)(pgm + 1);
360 	    TCHECK(*rdata);
361 	    (void)printf("RDATA trail %u seq %u",
362 			 EXTRACT_32BITS(&rdata->pgmd_trailseq),
363 			 EXTRACT_32BITS(&rdata->pgmd_seq));
364 	    bp = (u_char *) (rdata + 1);
365 	    break;
366 	}
367 
368 	case PGM_NAK:
369 	case PGM_NULLNAK:
370 	case PGM_NCF: {
371 	    struct pgm_nak *nak;
372 	    const void *source, *group;
373 	    int source_af, group_af;
374 #ifdef INET6
375 	    char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];
376 #else
377 	    char source_buf[INET_ADDRSTRLEN], group_buf[INET_ADDRSTRLEN];
378 #endif
379 
380 	    nak = (struct pgm_nak *)(pgm + 1);
381 	    TCHECK(*nak);
382 
383 	    /*
384 	     * Skip past the source, saving info along the way
385 	     * and stopping if we don't have enough.
386 	     */
387 	    switch (EXTRACT_16BITS(&nak->pgmn_source_afi)) {
388 	    case AFI_IP:
389 		addr_size = sizeof(struct in_addr);
390 		source_af = AF_INET;
391 		break;
392 #ifdef INET6
393 	    case AFI_IP6:
394 		addr_size = sizeof(struct in6_addr);
395 		source_af = AF_INET6;
396 		break;
397 #endif
398 	    default:
399 		goto trunc;
400 		break;
401 	    }
402 	    bp = (u_char *) (nak + 1);
403 	    TCHECK2(*bp, addr_size);
404 	    source = bp;
405 	    bp += addr_size;
406 
407 	    /*
408 	     * Skip past the group, saving info along the way
409 	     * and stopping if we don't have enough.
410 	     */
411 	    switch (EXTRACT_16BITS(bp)) {
412 	    case AFI_IP:
413 		addr_size = sizeof(struct in_addr);
414 		group_af = AF_INET;
415 		break;
416 #ifdef INET6
417 	    case AFI_IP6:
418 		addr_size = sizeof(struct in6_addr);
419 		group_af = AF_INET6;
420 		break;
421 #endif
422 	    default:
423 		goto trunc;
424 		break;
425 	    }
426 	    bp += (2 * sizeof(u_int16_t));
427 	    TCHECK2(*bp, addr_size);
428 	    group = bp;
429 	    bp += addr_size;
430 
431 	    /*
432 	     * Options decoding can go here.
433 	     */
434 	    inet_ntop(source_af, source, source_buf, sizeof(source_buf));
435 	    inet_ntop(group_af, group, group_buf, sizeof(group_buf));
436 	    switch (pgm->pgm_type) {
437 		case PGM_NAK:
438 		    (void)printf("NAK ");
439 		    break;
440 		case PGM_NULLNAK:
441 		    (void)printf("NNAK ");
442 		    break;
443 		case PGM_NCF:
444 		    (void)printf("NCF ");
445 		    break;
446 		default:
447                     break;
448 	    }
449 	    (void)printf("(%s -> %s), seq %u",
450 			 source_buf, group_buf, EXTRACT_32BITS(&nak->pgmn_seq));
451 	    break;
452 	}
453 
454 	case PGM_SPMR:
455 	    (void)printf("SPMR");
456 	    break;
457 
458 	default:
459 	    (void)printf("UNKNOWN type %0x02x", pgm->pgm_type);
460 	    break;
461 
462 	}
463 	if (pgm->pgm_options & PGM_OPT_BIT_PRESENT) {
464 
465 	    /*
466 	     * make sure there's enough for the first option header
467 	     */
468 	    if (!TTEST2(*bp, PGM_MIN_OPT_LEN)) {
469 		(void)printf("[|OPT]");
470 		return;
471 	    }
472 
473 	    /*
474 	     * That option header MUST be an OPT_LENGTH option
475 	     * (see the first paragraph of section 9.1 in RFC 3208).
476 	     */
477 	    opt_type = *bp++;
478 	    if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
479 		(void)printf("[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK);
480 		return;
481 	    }
482 	    opt_len = *bp++;
483 	    if (opt_len != 4) {
484 		(void)printf("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
485 		return;
486 	    }
487 	    opts_len = EXTRACT_16BITS(bp);
488 	    if (opts_len < 4) {
489 		(void)printf("[Bad total option length %u < 4]", opts_len);
490 		return;
491 	    }
492 	    bp += sizeof(u_int16_t);
493 	    (void)printf(" OPTS LEN %d", opts_len);
494 	    opts_len -= 4;
495 
496 	    while (opts_len) {
497 		if (opts_len < PGM_MIN_OPT_LEN) {
498 		    (void)printf("[Total option length leaves no room for final option]");
499 		    return;
500 		}
501 		opt_type = *bp++;
502 		opt_len = *bp++;
503 		if (opt_len < PGM_MIN_OPT_LEN) {
504 		    (void)printf("[Bad option, length %u < %u]", opt_len,
505 		        PGM_MIN_OPT_LEN);
506 		    break;
507 		}
508 		if (opts_len < opt_len) {
509 		    (void)printf("[Total option length leaves no room for final option]");
510 		    return;
511 		}
512 		if (!TTEST2(*bp, opt_len - 2)) {
513 		    (void)printf(" [|OPT]");
514 		    return;
515 		}
516 
517 		switch (opt_type & PGM_OPT_MASK) {
518 		case PGM_OPT_LENGTH:
519 		    if (opt_len != 4) {
520 			(void)printf("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
521 			return;
522 		    }
523 		    (void)printf(" OPTS LEN (extra?) %d", EXTRACT_16BITS(bp));
524 		    bp += sizeof(u_int16_t);
525 		    opts_len -= 4;
526 		    break;
527 
528 		case PGM_OPT_FRAGMENT:
529 		    if (opt_len != 16) {
530 			(void)printf("[Bad OPT_FRAGMENT option, length %u != 16]", opt_len);
531 			return;
532 		    }
533 		    flags1 = *bp++;
534 		    flags2 = *bp++;
535 		    seq = EXTRACT_32BITS(bp);
536 		    bp += sizeof(u_int32_t);
537 		    offset = EXTRACT_32BITS(bp);
538 		    bp += sizeof(u_int32_t);
539 		    len = EXTRACT_32BITS(bp);
540 		    bp += sizeof(u_int32_t);
541 		    (void)printf(" FRAG seq %u off %u len %u", seq, offset, len);
542 		    opts_len -= 16;
543 		    break;
544 
545 		case PGM_OPT_NAK_LIST:
546 		    flags1 = *bp++;
547 		    flags2 = *bp++;
548 		    opt_len -= sizeof(u_int32_t);	/* option header */
549 		    (void)printf(" NAK LIST");
550 		    while (opt_len) {
551 			if (opt_len < sizeof(u_int32_t)) {
552 			    (void)printf("[Option length not a multiple of 4]");
553 			    return;
554 			}
555 			TCHECK2(*bp, sizeof(u_int32_t));
556 			(void)printf(" %u", EXTRACT_32BITS(bp));
557 			bp += sizeof(u_int32_t);
558 			opt_len -= sizeof(u_int32_t);
559 			opts_len -= sizeof(u_int32_t);
560 		    }
561 		    break;
562 
563 		case PGM_OPT_JOIN:
564 		    if (opt_len != 8) {
565 			(void)printf("[Bad OPT_JOIN option, length %u != 8]", opt_len);
566 			return;
567 		    }
568 		    flags1 = *bp++;
569 		    flags2 = *bp++;
570 		    seq = EXTRACT_32BITS(bp);
571 		    bp += sizeof(u_int32_t);
572 		    (void)printf(" JOIN %u", seq);
573 		    opts_len -= 8;
574 		    break;
575 
576 		case PGM_OPT_NAK_BO_IVL:
577 		    if (opt_len != 12) {
578 			(void)printf("[Bad OPT_NAK_BO_IVL option, length %u != 12]", opt_len);
579 			return;
580 		    }
581 		    flags1 = *bp++;
582 		    flags2 = *bp++;
583 		    offset = EXTRACT_32BITS(bp);
584 		    bp += sizeof(u_int32_t);
585 		    seq = EXTRACT_32BITS(bp);
586 		    bp += sizeof(u_int32_t);
587 		    (void)printf(" BACKOFF ivl %u ivlseq %u", offset, seq);
588 		    opts_len -= 12;
589 		    break;
590 
591 		case PGM_OPT_NAK_BO_RNG:
592 		    if (opt_len != 12) {
593 			(void)printf("[Bad OPT_NAK_BO_RNG option, length %u != 12]", opt_len);
594 			return;
595 		    }
596 		    flags1 = *bp++;
597 		    flags2 = *bp++;
598 		    offset = EXTRACT_32BITS(bp);
599 		    bp += sizeof(u_int32_t);
600 		    seq = EXTRACT_32BITS(bp);
601 		    bp += sizeof(u_int32_t);
602 		    (void)printf(" BACKOFF max %u min %u", offset, seq);
603 		    opts_len -= 12;
604 		    break;
605 
606 		case PGM_OPT_REDIRECT:
607 		    flags1 = *bp++;
608 		    flags2 = *bp++;
609 		    switch (EXTRACT_16BITS(bp)) {
610 		    case AFI_IP:
611 			addr_size = sizeof(struct in_addr);
612 			nla_af = AF_INET;
613 			break;
614 #ifdef INET6
615 		    case AFI_IP6:
616 			addr_size = sizeof(struct in6_addr);
617 			nla_af = AF_INET6;
618 			break;
619 #endif
620 		    default:
621 			goto trunc;
622 			break;
623 		    }
624 		    bp += (2 * sizeof(u_int16_t));
625 		    if (opt_len != 4 + addr_size) {
626 			(void)printf("[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len);
627 			return;
628 		    }
629 		    TCHECK2(*bp, addr_size);
630 		    nla = bp;
631 		    bp += addr_size;
632 
633 		    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
634 		    (void)printf(" REDIRECT %s",  (char *)nla);
635 		    opts_len -= 4 + addr_size;
636 		    break;
637 
638 		case PGM_OPT_PARITY_PRM:
639 		    if (opt_len != 8) {
640 			(void)printf("[Bad OPT_PARITY_PRM option, length %u != 8]", opt_len);
641 			return;
642 		    }
643 		    flags1 = *bp++;
644 		    flags2 = *bp++;
645 		    len = EXTRACT_32BITS(bp);
646 		    bp += sizeof(u_int32_t);
647 		    (void)printf(" PARITY MAXTGS %u", len);
648 		    opts_len -= 8;
649 		    break;
650 
651 		case PGM_OPT_PARITY_GRP:
652 		    if (opt_len != 8) {
653 			(void)printf("[Bad OPT_PARITY_GRP option, length %u != 8]", opt_len);
654 			return;
655 		    }
656 		    flags1 = *bp++;
657 		    flags2 = *bp++;
658 		    seq = EXTRACT_32BITS(bp);
659 		    bp += sizeof(u_int32_t);
660 		    (void)printf(" PARITY GROUP %u", seq);
661 		    opts_len -= 8;
662 		    break;
663 
664 		case PGM_OPT_CURR_TGSIZE:
665 		    if (opt_len != 8) {
666 			(void)printf("[Bad OPT_CURR_TGSIZE option, length %u != 8]", opt_len);
667 			return;
668 		    }
669 		    flags1 = *bp++;
670 		    flags2 = *bp++;
671 		    len = EXTRACT_32BITS(bp);
672 		    bp += sizeof(u_int32_t);
673 		    (void)printf(" PARITY ATGS %u", len);
674 		    opts_len -= 8;
675 		    break;
676 
677 		case PGM_OPT_NBR_UNREACH:
678 		    if (opt_len != 4) {
679 			(void)printf("[Bad OPT_NBR_UNREACH option, length %u != 4]", opt_len);
680 			return;
681 		    }
682 		    flags1 = *bp++;
683 		    flags2 = *bp++;
684 		    (void)printf(" NBR_UNREACH");
685 		    opts_len -= 4;
686 		    break;
687 
688 		case PGM_OPT_PATH_NLA:
689 		    (void)printf(" PATH_NLA [%d]", opt_len);
690 		    bp += opt_len;
691 		    opts_len -= opt_len;
692 		    break;
693 
694 		case PGM_OPT_SYN:
695 		    if (opt_len != 4) {
696 			(void)printf("[Bad OPT_SYN option, length %u != 4]", opt_len);
697 			return;
698 		    }
699 		    flags1 = *bp++;
700 		    flags2 = *bp++;
701 		    (void)printf(" SYN");
702 		    opts_len -= 4;
703 		    break;
704 
705 		case PGM_OPT_FIN:
706 		    if (opt_len != 4) {
707 			(void)printf("[Bad OPT_FIN option, length %u != 4]", opt_len);
708 			return;
709 		    }
710 		    flags1 = *bp++;
711 		    flags2 = *bp++;
712 		    (void)printf(" FIN");
713 		    opts_len -= 4;
714 		    break;
715 
716 		case PGM_OPT_RST:
717 		    if (opt_len != 4) {
718 			(void)printf("[Bad OPT_RST option, length %u != 4]", opt_len);
719 			return;
720 		    }
721 		    flags1 = *bp++;
722 		    flags2 = *bp++;
723 		    (void)printf(" RST");
724 		    opts_len -= 4;
725 		    break;
726 
727 		case PGM_OPT_CR:
728 		    (void)printf(" CR");
729 		    bp += opt_len;
730 		    opts_len -= opt_len;
731 		    break;
732 
733 		case PGM_OPT_CRQST:
734 		    if (opt_len != 4) {
735 			(void)printf("[Bad OPT_CRQST option, length %u != 4]", opt_len);
736 			return;
737 		    }
738 		    flags1 = *bp++;
739 		    flags2 = *bp++;
740 		    (void)printf(" CRQST");
741 		    opts_len -= 4;
742 		    break;
743 
744 		default:
745 		    (void)printf(" OPT_%02X [%d] ", opt_type, opt_len);
746 		    bp += opt_len;
747 		    opts_len -= opt_len;
748 		    break;
749 		}
750 
751 		if (opt_type & PGM_OPT_END)
752 		    break;
753 	     }
754 	}
755 
756 	(void)printf(" [%u]", EXTRACT_16BITS(&pgm->pgm_length));
757 
758 	return;
759 
760 trunc:
761 	fputs("[|pgm]", stdout);
762 	if (ch != '\0')
763 		putchar('>');
764 }
765