1 /* 2 * Copyright (c) University of British Columbia, 1984 3 * Copyright (c) 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Laboratory for Computation Vision and the Computer Science Department 8 * of the University of British Columbia. 9 * 10 * %sccs.include.redist.c% 11 * 12 * @(#)pk_input.c 7.7 (Berkeley) 11/13/90 13 */ 14 15 #include "param.h" 16 #include "systm.h" 17 #include "mbuf.h" 18 #include "socket.h" 19 #include "protosw.h" 20 #include "socketvar.h" 21 #include "errno.h" 22 23 #include "../net/if.h" 24 25 #include "x25.h" 26 #include "pk.h" 27 #include "pk_var.h" 28 29 /* 30 * This procedure is called by the link level whenever the link 31 * becomes operational, is reset, or when the link goes down. 32 */ 33 34 pk_ctlinput (code, xcp) 35 register struct x25config *xcp; 36 { 37 38 register struct pkcb *pkp; 39 40 for (pkp = pkcbhead; pkp; pkp = pkp -> pk_next) 41 if (pkp -> pk_xcp == xcp) 42 break; 43 44 if (pkp == 0) 45 return (EINVAL); 46 47 switch (code) { 48 case PRC_LINKUP: 49 if (pkp -> pk_state == DTE_WAITING) 50 pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION); 51 break; 52 53 case PRC_LINKDOWN: 54 pk_restart (pkp, -1); /* Clear all active circuits */ 55 pkp -> pk_state = DTE_WAITING; 56 break; 57 58 case PRC_LINKRESET: 59 pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION); 60 break; 61 62 } 63 return (0); 64 } 65 struct ifqueue pkintrq; 66 /* 67 * This routine is called if there are semi-smart devices that do HDLC 68 * in hardware and want to queue the packet and call level 3 directly 69 */ 70 pkintr () 71 { 72 register struct mbuf *m; 73 register struct ifaddr *ifa; 74 register struct ifnet *ifp; 75 register int s; 76 77 for (;;) { 78 s = splimp (); 79 IF_DEQUEUE (&pkintrq, m); 80 splx (s); 81 if (m == 0) 82 break; 83 if (m->m_len < PKHEADERLN) { 84 printf ("pkintr: packet too short (len=%d)\n", 85 m->m_len); 86 m_freem (m); 87 continue; 88 } 89 if ((m->m_flags & M_PKTHDR) == 0) 90 panic("pkintr"); 91 ifp = m->m_pkthdr.rcvif; 92 /* 93 * look up the appropriate control block 94 */ 95 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) 96 if (ifa->ifa_addr->sa_family == AF_CCITT) 97 break; 98 if (ifa == 0) 99 continue; 100 pk_input(m, ((struct x25_ifaddr *)ifa)->ia_xcp); 101 } 102 } 103 struct mbuf *pk_bad_packet; 104 /* 105 * X.25 PACKET INPUT 106 * 107 * This procedure is called by a link level procedure whenever 108 * an information frame is received. It decodes the packet and 109 * demultiplexes based on the logical channel number. 110 * 111 */ 112 113 pk_input (m, xcp) 114 register struct mbuf *m; 115 struct x25config *xcp; 116 { 117 register struct x25_packet *xp; 118 register struct pklcd *lcp; 119 register struct socket *so = 0; 120 register struct pkcb *pkp; 121 int ptype, lcn, lcdstate = LISTEN; 122 static struct x25config *lastxcp; 123 static struct pkcb *lastpkp; 124 125 if (xcp == lastxcp) 126 pkp = lastpkp; 127 else { 128 for (pkp = pkcbhead; ; pkp = pkp -> pk_next) { 129 if (pkp == 0) { 130 pk_message (0, xcp, "pk_input: unknown network"); 131 m_freem (m); 132 return; 133 } 134 if (pkp -> pk_xcp == xcp) 135 break; 136 } 137 lastxcp = xcp; 138 lastpkp = pkp; 139 } 140 141 xp = mtod (m, struct x25_packet *); 142 ptype = pk_decode (xp); 143 lcn = LCN(xp); 144 lcp = pkp -> pk_chan[lcn]; 145 146 /* 147 * If the DTE is in Restart state, then it will ignore data, 148 * interrupt, call setup and clearing, flow control and reset 149 * packets. 150 */ 151 if (lcn < 0 || lcn > pkp -> pk_maxlcn) { 152 pk_message (lcn, pkp -> pk_xcp, "illegal lcn"); 153 m_freem (m); 154 return; 155 } 156 157 pk_trace (pkp -> pk_xcp, xp, "P-In"); 158 159 if (pkp -> pk_state != DTE_READY && ptype != RESTART && ptype != RESTART_CONF) { 160 m_freem (m); 161 return; 162 } 163 if (lcp) { 164 so = lcp -> lcd_so; 165 lcdstate = lcp -> lcd_state; 166 } else { 167 if (ptype == CLEAR) { /* idle line probe (Datapac specific) */ 168 /* send response on lcd 0's output queue */ 169 lcp -> lcd_template = pk_template (lcn, X25_CLEAR_CONFIRM); 170 pk_output (lcp); 171 m_freem (m); 172 return; 173 } 174 if (ptype != CALL) 175 ptype = INVALID_PACKET; 176 } 177 178 if (lcn == 0 && ptype != RESTART && ptype != RESTART_CONF) { 179 pk_message (0, pkp -> pk_xcp, "illegal ptype (%d, %s) on lcn 0", 180 ptype, pk_name[ptype / MAXSTATES]); 181 if (pk_bad_packet) 182 m_freem (pk_bad_packet); 183 pk_bad_packet = m; 184 return; 185 } 186 187 switch (ptype + lcdstate) { 188 /* 189 * Incoming Call packet received. 190 */ 191 case CALL + LISTEN: 192 incoming_call (pkp, xp, m -> m_len); 193 break; 194 195 /* 196 * Call collision: Just throw this "incoming call" away since 197 * the DCE will ignore it anyway. 198 */ 199 case CALL + SENT_CALL: 200 pk_message ((int)lcn, pkp -> pk_xcp, 201 "incoming call collision"); 202 break; 203 204 /* 205 * Call confirmation packet received. This usually means our 206 * previous connect request is now complete. 207 */ 208 case CALL_ACCEPTED + SENT_CALL: 209 call_accepted (lcp, xp, m -> m_len); 210 break; 211 212 /* 213 * This condition can only happen if the previous state was 214 * SENT_CALL. Just ignore the packet, eventually a clear 215 * confirmation should arrive. 216 */ 217 case CALL_ACCEPTED + SENT_CLEAR: 218 break; 219 220 /* 221 * Clear packet received. This requires a complete tear down 222 * of the virtual circuit. Free buffers and control blocks. 223 * and send a clear confirmation. 224 */ 225 case CLEAR + READY: 226 case CLEAR + RECEIVED_CALL: 227 case CLEAR + SENT_CALL: 228 case CLEAR + DATA_TRANSFER: 229 lcp -> lcd_state = RECEIVED_CLEAR; 230 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR_CONFIRM); 231 pk_output (lcp); 232 pk_clearcause (pkp, xp); 233 pk_close (lcp); 234 break; 235 236 /* 237 * Clear collision: Treat this clear packet as a confirmation. 238 */ 239 case CLEAR + SENT_CLEAR: 240 pk_close (lcp); 241 break; 242 243 /* 244 * Clear confirmation received. This usually means the virtual 245 * circuit is now completely removed. 246 */ 247 case CLEAR_CONF + SENT_CLEAR: 248 pk_close (lcp); 249 break; 250 251 /* 252 * A clear confirmation on an unassigned logical channel - just 253 * ignore it. Note: All other packets on an unassigned channel 254 * results in a clear. 255 */ 256 case CLEAR_CONF + READY: 257 break; 258 259 /* 260 * Data packet received. Pass on to next level. Move the Q and M 261 * bits into the data portion for the next level. 262 */ 263 case DATA + DATA_TRANSFER: 264 if (lcp -> lcd_reset_condition) { 265 ptype = DELETE_PACKET; 266 break; 267 } 268 269 /* 270 * Process the P(S) flow control information in this Data packet. 271 * Check that the packets arrive in the correct sequence and that 272 * they are within the "lcd_input_window". Input window rotation is 273 * initiated by the receive interface. 274 */ 275 276 if (PS(xp) != ((lcp -> lcd_rsn + 1) % MODULUS) || 277 PS(xp) == ((lcp -> lcd_input_window + lcp->lcd_windowsize) % MODULUS)) { 278 m_freem (m); 279 pk_procerror (RESET, lcp, "p(s) flow control error"); 280 break; 281 } 282 lcp -> lcd_rsn = PS(xp); 283 284 if (pk_ack (lcp, PR(xp)) != PACKET_OK) { 285 m_freem (m); 286 break; 287 } 288 if (so == 0) 289 break; 290 m -> m_data += PKHEADERLN; 291 m -> m_len -= PKHEADERLN; 292 if (lcp -> lcd_flags & X25_MQBIT) { 293 octet t = (xp -> q_bit) ? t = 0x80 : 0; 294 295 if (MBIT(xp)) 296 t |= 0x40; 297 m -> m_data -= 1; 298 m -> m_len += 1; 299 *mtod(m, octet *) = t; 300 } 301 302 /* 303 * Discard Q-BIT packets if the application 304 * doesn't want to be informed of M and Q bit status 305 */ 306 if (xp -> q_bit && (lcp -> lcd_flags & X25_MQBIT) == 0) { 307 m_freem (m); 308 lcp -> lcd_rxcnt++; 309 /* 310 * NB. This is dangerous: sending a RR here can 311 * cause sequence number errors if a previous data 312 * packet has not yet been passed up to the application 313 * (RR's are normally generated via PRU_RCVD). 314 */ 315 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR); 316 pk_output (lcp); 317 } else { 318 sbappendrecord (&so -> so_rcv, m); 319 sorwakeup (so); 320 } 321 break; 322 323 /* 324 * Interrupt packet received. 325 */ 326 case INTERRUPT + DATA_TRANSFER: 327 if (lcp -> lcd_reset_condition) 328 break; 329 lcp -> lcd_intrdata = xp -> packet_data; 330 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT_CONFIRM); 331 pk_output (lcp); 332 MCHTYPE(m, MT_OOBDATA); 333 if (so) 334 sohasoutofband (so); 335 break; 336 337 /* 338 * Interrupt confirmation packet received. 339 */ 340 case INTERRUPT_CONF + DATA_TRANSFER: 341 if (lcp -> lcd_reset_condition) 342 break; 343 if (lcp -> lcd_intrconf_pending == TRUE) 344 lcp -> lcd_intrconf_pending = FALSE; 345 else 346 pk_procerror (RESET, lcp, "unexpected packet"); 347 MCHTYPE(m, MT_CONTROL); 348 break; 349 350 /* 351 * Receiver ready received. Rotate the output window and output 352 * any data packets waiting transmission. 353 */ 354 case RR + DATA_TRANSFER: 355 if (lcp -> lcd_reset_condition || 356 pk_ack (lcp, PR(xp)) != PACKET_OK) { 357 ptype = DELETE_PACKET; 358 break; 359 } 360 if (lcp -> lcd_rnr_condition == TRUE) 361 lcp -> lcd_rnr_condition = FALSE; 362 pk_output (lcp); 363 MCHTYPE(m, MT_CONTROL); 364 break; 365 366 /* 367 * Receiver Not Ready received. Packets up to the P(R) can be 368 * be sent. Condition is cleared with a RR. 369 */ 370 case RNR + DATA_TRANSFER: 371 if (lcp -> lcd_reset_condition || 372 pk_ack (lcp, PR(xp)) != PACKET_OK) { 373 ptype = DELETE_PACKET; 374 break; 375 } 376 lcp -> lcd_rnr_condition = TRUE; 377 MCHTYPE(m, MT_CONTROL); 378 break; 379 380 /* 381 * Reset packet received. Set state to FLOW_OPEN. The Input and 382 * Output window edges ar set to zero. Both the send and receive 383 * numbers are reset. A confirmation is returned. 384 */ 385 case RESET + DATA_TRANSFER: 386 if (lcp -> lcd_reset_condition) 387 /* Reset collision. Just ignore packet. */ 388 break; 389 390 pk_resetcause (pkp, xp); 391 lcp -> lcd_window_condition = lcp -> lcd_rnr_condition = 392 lcp -> lcd_intrconf_pending = FALSE; 393 lcp -> lcd_output_window = lcp -> lcd_input_window = 394 lcp -> lcd_last_transmitted_pr = 0; 395 lcp -> lcd_ssn = 0; 396 lcp -> lcd_rsn = MODULUS - 1; 397 398 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET_CONFIRM); 399 pk_output (lcp); 400 401 MCHTYPE(m, MT_CONTROL); 402 if (so == 0) 403 break; 404 sbflush (&so -> so_snd); 405 sbflush (&so -> so_rcv); 406 wakeup ((caddr_t) & so -> so_timeo); 407 sorwakeup (so); 408 sowwakeup (so); 409 break; 410 411 /* 412 * Reset confirmation received. 413 */ 414 case RESET_CONF + DATA_TRANSFER: 415 if (lcp -> lcd_reset_condition) { 416 lcp -> lcd_reset_condition = FALSE; 417 pk_output (lcp); 418 } 419 else 420 pk_procerror (RESET, lcp, "unexpected packet"); 421 MCHTYPE(m, MT_CONTROL); 422 break; 423 424 case DATA + SENT_CLEAR: 425 ptype = DELETE_PACKET; 426 case RR + SENT_CLEAR: 427 case RNR + SENT_CLEAR: 428 case INTERRUPT + SENT_CLEAR: 429 case INTERRUPT_CONF + SENT_CLEAR: 430 case RESET + SENT_CLEAR: 431 case RESET_CONF + SENT_CLEAR: 432 /* Just ignore p if we have sent a CLEAR already. 433 */ 434 break; 435 436 /* 437 * Restart sets all the permanent virtual circuits to the "Data 438 * Transfer" stae and all the switched virtual circuits to the 439 * "Ready" state. 440 */ 441 case RESTART + READY: 442 switch (pkp -> pk_state) { 443 case DTE_SENT_RESTART: 444 /* Restart collision. */ 445 pkp -> pk_state = DTE_READY; 446 pk_message (0, pkp -> pk_xcp, 447 "Packet level operational"); 448 break; 449 450 default: 451 pk_restart (pkp, -1); 452 pk_restartcause (pkp, xp); 453 pkp -> pk_chan[0] -> lcd_template = pk_template (0, 454 X25_RESTART_CONFIRM); 455 pk_output (pkp -> pk_chan[0]); 456 } 457 break; 458 459 /* 460 * Restart confirmation received. All logical channels are set 461 * to READY. 462 */ 463 case RESTART_CONF + READY: 464 switch (pkp -> pk_state) { 465 case DTE_SENT_RESTART: 466 pkp -> pk_state = DTE_READY; 467 pk_message (0, pkp -> pk_xcp, 468 "Packet level operational"); 469 break; 470 471 default: 472 /* Restart local procedure error. */ 473 pk_restart (pkp, X25_RESTART_LOCAL_PROCEDURE_ERROR); 474 pkp -> pk_state = DTE_SENT_RESTART; 475 } 476 break; 477 478 default: 479 if (lcp) { 480 pk_procerror (CLEAR, lcp, "unknown packet error"); 481 pk_message (lcn, pkp -> pk_xcp, 482 "\"%s\" unexpected in \"%s\" state", 483 pk_name[ptype/MAXSTATES], pk_state[lcdstate]); 484 } 485 else /* Packets arrived on an unassigned channel. 486 */ 487 pk_message (lcn, pkp -> pk_xcp, 488 "packet arrived on unassigned lcn"); 489 break; 490 } 491 if (so == 0 && lcdstate == DATA_TRANSFER && lcp -> lcd_upper) 492 lcp -> lcd_upper (lcp, m); 493 else if (ptype != DATA) 494 m_freem (m); 495 } 496 497 498 /* 499 * This routine handles incoming call packets. It matches the protocol 500 * field on the Call User Data field (usually the first four bytes) with 501 * sockets awaiting connections. 502 */ 503 504 static 505 incoming_call (pkp, xp, len) 506 struct pkcb *pkp; 507 struct x25_packet *xp; 508 { 509 register struct pklcd *lcp = 0, *l; 510 register struct sockaddr_x25 *sa; 511 register struct x25_calladdr *a; 512 register struct socket *so = 0; 513 struct mbuf *m; 514 register int l1, l2; 515 char *e, *errstr = "server unavailable"; 516 octet *u; 517 int lcn = LCN(xp); 518 519 /* First, copy the data from the incoming call packet to a X25_socket 520 descriptor. */ 521 522 a = (struct x25_calladdr *) &xp -> packet_data; 523 l1 = a -> calling_addrlen; 524 l2 = a -> called_addrlen; 525 if ((m = m_getclr (M_DONTWAIT, MT_SONAME)) == 0) 526 return; 527 sa = mtod (m, struct sockaddr_x25 *); 528 u = (octet *) (a -> address_field + l2 / 2); 529 e = sa -> x25_addr; 530 if (l2 & 0x01) { 531 *e++ = *u++ & 0x0f; 532 l1--; 533 } 534 from_bcd (e, &u, l1); 535 if (l1 & 0x01) 536 u++; 537 538 parse_facilities (u, sa); 539 u += *u + 1; 540 sa -> x25_udlen = min (16, ((octet *)xp) + len - u); 541 if (sa -> x25_udlen < 0) 542 sa -> x25_udlen = 0; 543 bcopy ((caddr_t)u, sa -> x25_udata, (unsigned)sa -> x25_udlen); 544 545 /* 546 * Now, loop through the listen sockets looking for a match on the 547 * PID. That is the first four octets of the user data field. This 548 * is the closest thing to a port number for X.25 packets. What it 549 * does provide is away of multiplexing services at the user level. 550 */ 551 552 for (l = pk_listenhead; l; l = l -> lcd_listen) { 553 struct sockaddr_x25 *sxp = l -> lcd_ceaddr; 554 555 if (bcmp (sxp -> x25_udata, sa -> x25_udata, sxp->x25_udlen)) 556 continue; 557 if (sxp -> x25_net && 558 sxp -> x25_net != pkp->pk_xc.xc_addr.x25_net) 559 continue; 560 /* 561 * don't accept incoming collect calls unless 562 * the server sets the reverse charging option. 563 */ 564 if ((sxp -> x25_opts.op_flags & (X25_OLDSOCKADDR|X25_REVERSE_CHARGE)) == 0 && 565 sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) { 566 errstr = "incoming collect call refused"; 567 break; 568 } 569 /* 570 * don't accept incoming calls with the D-Bit on 571 * unless the server agrees 572 */ 573 if (xp -> d_bit && !(sxp -> x25_opts.op_flags & X25_DBIT)) { 574 errstr = "incoming D-Bit mismatch"; 575 break; 576 } 577 if (l -> lcd_so) { 578 if (so = sonewconn (l -> lcd_so, SS_ISCONNECTED)) 579 lcp = (struct pklcd *) so -> so_pcb; 580 } else 581 lcp = pk_attach((struct socket *) 0); 582 if (lcp == 0) { 583 /* 584 * Insufficient space or too many unaccepted 585 * connections. Just throw the call away. 586 */ 587 errstr = "server malfunction"; 588 break; 589 } 590 lcp -> lcd_upper = l -> lcd_upper; 591 lcp -> lcd_upnext = l -> lcd_upnext; 592 lcp -> lcd_lcn = lcn; 593 lcp -> lcd_state = RECEIVED_CALL; 594 lcp -> lcd_craddr = sa; 595 sa -> x25_opts.op_flags |= sxp -> x25_opts.op_flags & 596 ~X25_REVERSE_CHARGE; 597 pk_assoc (pkp, lcp, sa); 598 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL_ACCEPTED); 599 if (lcp -> lcd_flags & X25_DBIT) { 600 if (xp -> d_bit) 601 lcp -> lcd_template -> d_bit = 1; 602 else 603 lcp -> lcd_flags &= ~X25_DBIT; 604 } 605 if (so) { 606 pk_output (lcp); 607 soisconnected (so); 608 } else if (lcp->lcd_upper) 609 (*lcp->lcd_upper)(lcp, m); 610 return; 611 } 612 613 /* 614 * If the call fails for whatever reason, we still need to build a 615 * skeleton LCD in order to be able to properly receive the CLEAR 616 * CONFIRMATION. 617 */ 618 #ifdef WATERLOO /* be explicit */ 619 if (l == 0 && bcmp(sa->x25_udata, "ean", 3) == 0) 620 pk_message (lcn, pkp -> pk_xcp, "host=%s ean%c: %s", 621 sa->x25_addr, sa->x25_udata[3] & 0xff, errstr); 622 else if (l == 0 && bcmp(sa->x25_udata, "\1\0\0\0", 4) == 0) 623 pk_message (lcn, pkp -> pk_xcp, "host=%s x29d: %s", 624 sa->x25_addr, errstr); 625 else 626 #endif 627 pk_message (lcn, pkp -> pk_xcp, "host=%s pid=%x %x %x %x: %s", 628 sa -> x25_addr, sa -> x25_udata[0] & 0xff, 629 sa -> x25_udata[1] & 0xff, sa -> x25_udata[2] & 0xff, 630 sa -> x25_udata[3] & 0xff, errstr); 631 if ((lcp = pk_attach((struct socket *)0)) == 0) { 632 (void) m_free (m); 633 return; 634 } 635 lcp -> lcd_lcn = lcn; 636 lcp -> lcd_state = RECEIVED_CALL; 637 pk_assoc (pkp, lcp, sa); 638 (void) m_free (m); 639 pk_clear (lcp); 640 } 641 642 static 643 call_accepted (lcp, xp, len) 644 struct pklcd *lcp; 645 struct x25_packet *xp; 646 { 647 register struct x25_calladdr *ap; 648 register octet *fcp; 649 650 lcp -> lcd_state = DATA_TRANSFER; 651 if (lcp -> lcd_so) 652 soisconnected (lcp -> lcd_so); 653 if ((lcp -> lcd_flags & X25_DBIT) && (xp -> d_bit == 0)) 654 lcp -> lcd_flags &= ~X25_DBIT; 655 if (len > 3) { 656 ap = (struct x25_calladdr *) &xp -> packet_data; 657 fcp = (octet *) ap -> address_field + (ap -> calling_addrlen + 658 ap -> called_addrlen + 1) / 2; 659 if (fcp + *fcp <= ((octet *)xp) + len) 660 parse_facilities (fcp, lcp -> lcd_ceaddr); 661 } 662 pk_assoc (lcp -> lcd_pkp, lcp, lcp -> lcd_ceaddr); 663 } 664 665 static 666 parse_facilities (fcp, sa) 667 register octet *fcp; 668 register struct sockaddr_x25 *sa; 669 { 670 register octet *maxfcp; 671 672 maxfcp = fcp + *fcp; 673 fcp++; 674 while (fcp < maxfcp) { 675 /* 676 * Ignore national DCE or DTE facilities 677 */ 678 if (*fcp == 0 || *fcp == 0xff) 679 break; 680 switch (*fcp) { 681 case FACILITIES_WINDOWSIZE: 682 sa -> x25_opts.op_wsize = fcp[1]; 683 fcp += 3; 684 break; 685 686 case FACILITIES_PACKETSIZE: 687 sa -> x25_opts.op_psize = fcp[1]; 688 fcp += 3; 689 break; 690 691 case FACILITIES_THROUGHPUT: 692 sa -> x25_opts.op_speed = fcp[1]; 693 fcp += 2; 694 break; 695 696 case FACILITIES_REVERSE_CHARGE: 697 if (fcp[1] & 01) 698 sa -> x25_opts.op_flags |= X25_REVERSE_CHARGE; 699 /* 700 * Datapac specific: for a X.25(1976) DTE, bit 2 701 * indicates a "hi priority" (eg. international) call. 702 */ 703 if (fcp[1] & 02 && sa -> x25_opts.op_psize == 0) 704 sa -> x25_opts.op_psize = X25_PS128; 705 fcp += 2; 706 break; 707 708 default: 709 /*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/ 710 switch ((*fcp & 0xc0) >> 6) { 711 case 0: /* class A */ 712 fcp += 2; 713 break; 714 715 case 1: 716 fcp += 3; 717 break; 718 719 case 2: 720 fcp += 4; 721 break; 722 723 case 3: 724 fcp++; 725 fcp += *fcp; 726 } 727 } 728 } 729 } 730 731 from_bcd (a, x, len) 732 register char *a; 733 register octet **x; 734 register int len; 735 { 736 register int posn = 0; 737 738 while (--len >= 0) { 739 if (posn++ & 0x01) 740 *a = *(*x)++ & 0x0f; 741 else 742 *a = (**x >> 4) & 0x0F; 743 *a++ |= 0x30; 744 } 745 } 746