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
19static 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$");
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 */
49struct 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
59struct 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
69struct 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
80struct pgm_poll {
81    u_int32_t	pgmp_seq;
82    u_int16_t	pgmp_round;
83    u_int16_t	pgmp_reserved;
84    /* ... options */
85};
86
87struct 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
97struct pgm_data {
98    u_int32_t	pgmd_seq;
99    u_int32_t	pgmd_trailseq;
100    /* ... options */
101};
102
103typedef 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
153void
154pgm_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
760trunc:
761	fputs("[|pgm]", stdout);
762	if (ch != '\0')
763		putchar('>');
764}
765