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