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