Merge branch 'vendor/MDOCML'
[dragonfly.git] / contrib / tcpdump / print-pgm.c
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
17 static const char rcsid[] _U_ =
18     "@(#) $Header: /tcpdump/master/tcpdump/print-pgm.c,v 1.5 2005-06-07 22:05:58 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  */
44 struct 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
54 struct 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
64 struct 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
75 struct pgm_poll {
76     u_int32_t   pgmp_seq;
77     u_int16_t   pgmp_round;
78     u_int16_t   pgmp_reserved;
79     /* ... options */
80 };
81
82 struct 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
92 struct pgm_data {
93     u_int32_t   pgmd_seq;
94     u_int32_t   pgmd_trailseq;
95     /* ... options */
96 };
97
98 typedef 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
148 void
149 pgm_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
755 trunc:
756         fputs("[|pgm]", stdout);
757         if (ch != '\0')
758                 putchar('>');
759 }