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 17static 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#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 */ 44struct 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 54struct 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 64struct 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 75struct pgm_poll { 76 u_int32_t pgmp_seq; 77 u_int16_t pgmp_round; 78 u_int16_t pgmp_reserved; 79 /* ... options */ 80}; 81 82struct 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 92struct pgm_data { 93 u_int32_t pgmd_seq; 94 u_int32_t pgmd_trailseq; 95 /* ... options */ 96}; 97 98typedef 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 148void 149pgm_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 755trunc: 756 fputs("[|pgm]", stdout); 757 if (ch != '\0') 758 putchar('>'); 759} 760