OSDN Git Service

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