]> The Tcpdump Group git mirrors - tcpdump/blob - print-pgm.c
EXTRACT_32BITS(), like EXTRACT_16BITS(), takes a pointer to the value
[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.3 2005-05-24 07:56:23 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 bp = (u_char *) (poll + 1);
280 break;
281 }
282 case PGM_POLR: {
283 struct pgm_polr *polr;
284 u_int32_t ivl, rnd, mask;
285
286 polr = (struct pgm_polr *)(pgm + 1);
287 TCHECK(*polr);
288
289 switch (EXTRACT_16BITS(&polr->pgmp_nla_afi)) {
290 case AFI_IP:
291 addr_size = sizeof(struct in_addr);
292 nla_af = AF_INET;
293 break;
294 case AFI_IP6:
295 addr_size = sizeof(struct in6_addr);
296 nla_af = AF_INET6;
297 break;
298 default:
299 goto trunc;
300 break;
301 }
302 bp = (u_char *) (polr + 1);
303 TCHECK2(*bp, addr_size);
304 nla = bp;
305 bp += addr_size;
306
307 inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
308
309 TCHECK2(*bp, sizeof(u_int32_t));
310 ivl = EXTRACT_32BITS(bp);
311 bp += sizeof(u_int32_t);
312
313 TCHECK2(*bp, sizeof(u_int32_t));
314 rnd = EXTRACT_32BITS(bp);
315 bp += sizeof(u_int32_t);
316
317 TCHECK2(*bp, sizeof(u_int32_t));
318 mask = EXTRACT_32BITS(bp);
319 bp += sizeof(u_int32_t);
320
321 (void)printf("POLR seq %u round %u nla %s ivl %u rnd 0x%08x "
322 "mask 0x%08x", EXTRACT_32BITS(&polr->pgmp_seq),
323 EXTRACT_16BITS(&polr->pgmp_round), nla_buf, ivl, rnd, mask);
324 break;
325 }
326 case PGM_ODATA: {
327 struct pgm_data *odata;
328
329 odata = (struct pgm_data *)(pgm + 1);
330 TCHECK(*odata);
331 (void)printf("ODATA trail %u seq %u",
332 EXTRACT_32BITS(&odata->pgmd_trailseq),
333 EXTRACT_32BITS(&odata->pgmd_seq));
334 bp = (u_char *) (odata + 1);
335 break;
336 }
337
338 case PGM_RDATA: {
339 struct pgm_data *rdata;
340
341 rdata = (struct pgm_data *)(pgm + 1);
342 TCHECK(*rdata);
343 (void)printf("RDATA trail %u seq %u",
344 EXTRACT_32BITS(&rdata->pgmd_trailseq),
345 EXTRACT_32BITS(&rdata->pgmd_seq));
346 bp = (u_char *) (rdata + 1);
347 break;
348 }
349
350 case PGM_NAK:
351 case PGM_NULLNAK:
352 case PGM_NCF: {
353 struct pgm_nak *nak;
354 const void *source, *group;
355 int source_af, group_af;
356 char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];
357
358 nak = (struct pgm_nak *)(pgm + 1);
359 TCHECK(*nak);
360
361 /*
362 * Skip past the source, saving info along the way
363 * and stopping if we don't have enough.
364 */
365 switch (EXTRACT_16BITS(&nak->pgmn_source_afi)) {
366 case AFI_IP:
367 addr_size = sizeof(struct in_addr);
368 source_af = AF_INET;
369 break;
370 case AFI_IP6:
371 addr_size = sizeof(struct in6_addr);
372 source_af = AF_INET6;
373 break;
374 default:
375 goto trunc;
376 break;
377 }
378 bp = (u_char *) (nak + 1);
379 TCHECK2(*bp, addr_size);
380 source = bp;
381 bp += addr_size;
382
383 /*
384 * Skip past the group, saving info along the way
385 * and stopping if we don't have enough.
386 */
387 switch (EXTRACT_16BITS(bp)) {
388 case AFI_IP:
389 addr_size = sizeof(struct in_addr);
390 group_af = AF_INET;
391 break;
392 case AFI_IP6:
393 addr_size = sizeof(struct in6_addr);
394 group_af = AF_INET6;
395 break;
396 default:
397 goto trunc;
398 break;
399 }
400 bp += (2 * sizeof(u_int16_t));
401 TCHECK2(*bp, addr_size);
402 group = bp;
403 bp += addr_size;
404
405 /*
406 * Options decoding can go here.
407 */
408 inet_ntop(source_af, source, source_buf, sizeof(source_buf));
409 inet_ntop(group_af, group, group_buf, sizeof(group_buf));
410 switch (pgm->pgm_type) {
411 case PGM_NAK:
412 (void)printf("NAK ");
413 break;
414 case PGM_NULLNAK:
415 (void)printf("NNAK ");
416 break;
417 case PGM_NCF:
418 (void)printf("NCF ");
419 break;
420 default:
421 break;
422 }
423 (void)printf("(%s -> %s), seq %u",
424 source_buf, group_buf, EXTRACT_32BITS(&nak->pgmn_seq));
425 break;
426 }
427
428 case PGM_SPMR:
429 (void)printf("SPMR");
430 break;
431
432 default:
433 (void)printf("UNKNOWN type %0x02x", pgm->pgm_type);
434 break;
435
436 }
437 if (pgm->pgm_options & PGM_OPT_BIT_PRESENT) {
438
439 /*
440 * make sure there's enough for the first option header
441 */
442 if (!TTEST2(*bp, PGM_MIN_OPT_LEN)) {
443 (void)printf("[|OPT]");
444 return;
445 }
446
447 /*
448 * That option header MUST be an OPT_LENGTH option
449 * (see the first paragraph of section 9.1 in RFC 3208).
450 */
451 opt_type = *bp++;
452 if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
453 (void)printf("[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK);
454 return;
455 }
456 opt_len = *bp++;
457 if (opt_len != 4) {
458 (void)printf("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
459 return;
460 }
461 opts_len = EXTRACT_16BITS(bp);
462 if (opts_len < 4) {
463 (void)printf("[Bad total option length %u < 4]", opts_len);
464 return;
465 }
466 bp += sizeof(u_int16_t);
467 (void)printf(" OPTS LEN %d", opts_len);
468 opts_len -= 4;
469
470 while (opts_len) {
471 if (opts_len < PGM_MIN_OPT_LEN) {
472 (void)printf("[Total option length leaves no room for final option]");
473 return;
474 }
475 opt_type = *bp++;
476 opt_len = *bp++;
477 if (opt_len < PGM_MIN_OPT_LEN) {
478 (void)printf("[Bad option, length %u < %u]", opt_len,
479 PGM_MIN_OPT_LEN);
480 break;
481 }
482 if (opts_len < opt_len) {
483 (void)printf("[Total option length leaves no room for final option]");
484 return;
485 }
486 if (!TTEST2(*bp, opt_len - 2)) {
487 (void)printf(" [|OPT]");
488 return;
489 }
490
491 switch (opt_type & PGM_OPT_MASK) {
492 case PGM_OPT_LENGTH:
493 if (opt_len != 4) {
494 (void)printf("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
495 return;
496 }
497 (void)printf(" OPTS LEN (extra?) %d", EXTRACT_16BITS(bp));
498 bp += sizeof(u_int16_t);
499 opts_len -= 4;
500 break;
501
502 case PGM_OPT_FRAGMENT:
503 if (opt_len != 16) {
504 (void)printf("[Bad OPT_FRAGMENT option, length %u != 16]", opt_len);
505 return;
506 }
507 flags1 = *bp++;
508 flags2 = *bp++;
509 seq = EXTRACT_32BITS(bp);
510 bp += sizeof(u_int32_t);
511 offset = EXTRACT_32BITS(bp);
512 bp += sizeof(u_int32_t);
513 len = EXTRACT_32BITS(bp);
514 bp += sizeof(u_int32_t);
515 (void)printf(" FRAG seq %u off %u len %u", seq, offset, len);
516 opts_len -= 16;
517 break;
518
519 case PGM_OPT_NAK_LIST:
520 flags1 = *bp++;
521 flags2 = *bp++;
522 opt_len -= sizeof(u_int32_t); /* option header */
523 (void)printf(" NAK LIST");
524 while (opt_len) {
525 if (opt_len < sizeof(u_int32_t)) {
526 (void)printf("[Option length not a multiple of 4]");
527 return;
528 }
529 TCHECK2(*bp, sizeof(u_int32_t));
530 (void)printf(" %u", EXTRACT_32BITS(bp));
531 bp += sizeof(u_int32_t);
532 opt_len -= sizeof(u_int32_t);
533 opts_len -= sizeof(u_int32_t);
534 }
535 break;
536
537 case PGM_OPT_JOIN:
538 if (opt_len != 8) {
539 (void)printf("[Bad OPT_JOIN option, length %u != 8]", opt_len);
540 return;
541 }
542 flags1 = *bp++;
543 flags2 = *bp++;
544 seq = EXTRACT_32BITS(bp);
545 bp += sizeof(u_int32_t);
546 (void)printf(" JOIN %u", seq);
547 opts_len -= 8;
548 break;
549
550 case PGM_OPT_NAK_BO_IVL:
551 if (opt_len != 12) {
552 (void)printf("[Bad OPT_NAK_BO_IVL option, length %u != 12]", opt_len);
553 return;
554 }
555 flags1 = *bp++;
556 flags2 = *bp++;
557 offset = EXTRACT_32BITS(bp);
558 bp += sizeof(u_int32_t);
559 seq = EXTRACT_32BITS(bp);
560 bp += sizeof(u_int32_t);
561 (void)printf(" BACKOFF ivl %u ivlseq %u", offset, seq);
562 opts_len -= 12;
563 break;
564
565 case PGM_OPT_NAK_BO_RNG:
566 if (opt_len != 12) {
567 (void)printf("[Bad OPT_NAK_BO_RNG option, length %u != 12]", opt_len);
568 return;
569 }
570 flags1 = *bp++;
571 flags2 = *bp++;
572 offset = EXTRACT_32BITS(bp);
573 bp += sizeof(u_int32_t);
574 seq = EXTRACT_32BITS(bp);
575 bp += sizeof(u_int32_t);
576 (void)printf(" BACKOFF max %u min %u", offset, seq);
577 opts_len -= 12;
578 break;
579
580 case PGM_OPT_REDIRECT:
581 flags1 = *bp++;
582 flags2 = *bp++;
583 switch (EXTRACT_16BITS(bp)) {
584 case AFI_IP:
585 addr_size = sizeof(struct in_addr);
586 nla_af = AF_INET;
587 break;
588 case AFI_IP6:
589 addr_size = sizeof(struct in6_addr);
590 nla_af = AF_INET6;
591 break;
592 default:
593 goto trunc;
594 break;
595 }
596 bp += (2 * sizeof(u_int16_t));
597 if (opt_len != 4 + addr_size) {
598 (void)printf("[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len);
599 return;
600 }
601 TCHECK2(*bp, addr_size);
602 nla = bp;
603 bp += addr_size;
604
605 inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
606 (void)printf(" REDIRECT %s", (char *)nla);
607 opts_len -= 4 + addr_size;
608 break;
609
610 case PGM_OPT_PARITY_PRM:
611 if (opt_len != 8) {
612 (void)printf("[Bad OPT_PARITY_PRM option, length %u != 8]", opt_len);
613 return;
614 }
615 flags1 = *bp++;
616 flags2 = *bp++;
617 len = EXTRACT_32BITS(bp);
618 bp += sizeof(u_int32_t);
619 (void)printf(" PARITY MAXTGS %u", len);
620 opts_len -= 8;
621 break;
622
623 case PGM_OPT_PARITY_GRP:
624 if (opt_len != 8) {
625 (void)printf("[Bad OPT_PARITY_GRP option, length %u != 8]", opt_len);
626 return;
627 }
628 flags1 = *bp++;
629 flags2 = *bp++;
630 seq = EXTRACT_32BITS(bp);
631 bp += sizeof(u_int32_t);
632 (void)printf(" PARITY GROUP %u", seq);
633 opts_len -= 8;
634 break;
635
636 case PGM_OPT_CURR_TGSIZE:
637 if (opt_len != 8) {
638 (void)printf("[Bad OPT_CURR_TGSIZE option, length %u != 8]", opt_len);
639 return;
640 }
641 flags1 = *bp++;
642 flags2 = *bp++;
643 len = EXTRACT_32BITS(bp);
644 bp += sizeof(u_int32_t);
645 (void)printf(" PARITY ATGS %u", len);
646 opts_len -= 8;
647 break;
648
649 case PGM_OPT_NBR_UNREACH:
650 if (opt_len != 4) {
651 (void)printf("[Bad OPT_NBR_UNREACH option, length %u != 4]", opt_len);
652 return;
653 }
654 flags1 = *bp++;
655 flags2 = *bp++;
656 (void)printf(" NBR_UNREACH");
657 opts_len -= 4;
658 break;
659
660 case PGM_OPT_PATH_NLA:
661 (void)printf(" PATH_NLA [%d]", opt_len);
662 bp += opt_len;
663 opts_len -= opt_len;
664 break;
665
666 case PGM_OPT_SYN:
667 if (opt_len != 4) {
668 (void)printf("[Bad OPT_SYN option, length %u != 4]", opt_len);
669 return;
670 }
671 flags1 = *bp++;
672 flags2 = *bp++;
673 (void)printf(" SYN");
674 opts_len -= 4;
675 break;
676
677 case PGM_OPT_FIN:
678 if (opt_len != 4) {
679 (void)printf("[Bad OPT_FIN option, length %u != 4]", opt_len);
680 return;
681 }
682 flags1 = *bp++;
683 flags2 = *bp++;
684 (void)printf(" FIN");
685 opts_len -= 4;
686 break;
687
688 case PGM_OPT_RST:
689 if (opt_len != 4) {
690 (void)printf("[Bad OPT_RST option, length %u != 4]", opt_len);
691 return;
692 }
693 flags1 = *bp++;
694 flags2 = *bp++;
695 (void)printf(" RST");
696 opts_len -= 4;
697 break;
698
699 case PGM_OPT_CR:
700 (void)printf(" CR");
701 bp += opt_len;
702 opts_len -= opt_len;
703 break;
704
705 case PGM_OPT_CRQST:
706 if (opt_len != 4) {
707 (void)printf("[Bad OPT_CRQST option, length %u != 4]", opt_len);
708 return;
709 }
710 flags1 = *bp++;
711 flags2 = *bp++;
712 (void)printf(" CRQST");
713 opts_len -= 4;
714 break;
715
716 default:
717 (void)printf(" OPT_%02X [%d] ", opt_type, opt_len);
718 bp += opt_len;
719 opts_len -= opt_len;
720 break;
721 }
722
723 if (opt_type & PGM_OPT_END)
724 break;
725 }
726 }
727
728 (void)printf(" [%u]", EXTRACT_16BITS(&pgm->pgm_length));
729
730 return;
731
732 trunc:
733 fputs("[|pgm]", stdout);
734 if (ch != '\0')
735 putchar('>');
736 }