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