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_subr.c 7.5 (Berkeley) 06/21/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 #include "time.h" 23 #include "kernel.h" 24 25 #include "../net/if.h" 26 27 #include "x25.h" 28 #include "pk.h" 29 #include "pk_var.h" 30 #include "x25err.h" 31 32 int pk_sendspace = 1024 * 2 + 8; 33 int pk_recvspace = 1024 * 2 + 8; 34 35 struct x25_packet *pk_template (); 36 37 /* 38 * Attach X.25 protocol to socket, allocate logical channel descripter 39 * and buffer space, and enter LISTEN state if we are to accept 40 * IN-COMMING CALL packets. 41 * 42 */ 43 44 struct pklcd * 45 pk_attach (so) 46 struct socket *so; 47 { 48 register struct pklcd *lcp; 49 register int error = ENOBUFS; 50 51 MALLOC(lcp, struct pklcd *, sizeof(*lcp), M_PCB, M_NOWAIT); 52 if (lcp) { 53 bzero((caddr_t)lcp, sizeof(*lcp)); 54 if (so) { 55 error = soreserve (so, pk_sendspace, pk_recvspace); 56 lcp -> lcd_so = so; 57 if (so -> so_options & SO_ACCEPTCONN) 58 lcp -> lcd_state = LISTEN; 59 else 60 lcp -> lcd_state = READY; 61 } else 62 sbreserve (&lcp -> lcd_sb, pk_sendpace); 63 } 64 if (so) { 65 so -> so_pcb = (caddr_t) lcp; 66 so -> so_error = error; 67 } 68 return (lcp); 69 } 70 71 /* 72 * Disconnect X.25 protocol from socket. 73 */ 74 75 pk_disconnect (lcp) 76 register struct pklcd *lcp; 77 { 78 register struct socket *so = lcp -> lcd_so; 79 register struct pklcd *l, *p; 80 81 switch (lcp -> lcd_state) { 82 case LISTEN: 83 for (p = 0, l = pk_listenhead; l && l != lcp; p = l, l = l -> lcd_listen); 84 if (p == 0) { 85 if (l != 0) 86 pk_listenhead = l -> lcd_listen; 87 } 88 else 89 if (l != 0) 90 p -> lcd_listen = l -> lcd_listen; 91 pk_close (lcp); 92 break; 93 94 case READY: 95 pk_acct (lcp); 96 pk_close (lcp); 97 break; 98 99 case SENT_CLEAR: 100 case RECEIVED_CLEAR: 101 break; 102 103 default: 104 pk_acct (lcp); 105 if (so) { 106 soisdisconnecting (so); 107 sbflush (&so -> so_rcv); 108 } 109 pk_clear (lcp); 110 111 } 112 } 113 114 /* 115 * Close an X.25 Logical Channel. Discard all space held by the 116 * connection and internal descriptors. Wake up any sleepers. 117 */ 118 119 pk_close (lcp) 120 struct pklcd *lcp; 121 { 122 register struct socket *so = lcp -> lcd_so; 123 124 pk_freelcd (lcp); 125 126 if (so == NULL) 127 return; 128 129 so -> so_pcb = 0; 130 sbflush (&so -> so_snd); 131 sbflush (&so -> so_rcv); 132 soisdisconnected (so); 133 sofree (so); /* gak!!! you can't do that here */ 134 } 135 136 /* 137 * Create a template to be used to send X.25 packets on a logical 138 * channel. It allocates an mbuf and fills in a skeletal packet 139 * depending on its type. This packet is passed to pk_output where 140 * the remainer of the packet is filled in. 141 */ 142 143 struct x25_packet * 144 pk_template (lcn, type) 145 int lcn, type; 146 { 147 register struct mbuf *m; 148 register struct x25_packet *xp; 149 150 MGET (m, M_DONTWAIT, MT_HEADER); 151 if (m == 0) 152 panic ("pk_template"); 153 m -> m_act = 0; 154 155 /* 156 * Efficiency hack: leave a four byte gap at the beginning 157 * of the packet level header with the hope that this will 158 * be enough room for the link level to insert its header. 159 */ 160 m -> m_data += 4; 161 m -> m_len = PKHEADERLN; 162 163 xp = mtod (m, struct x25_packet *); 164 *(long *)xp = 0; /* ugly, but fast */ 165 /* xp -> q_bit = 0;*/ 166 xp -> fmt_identifier = 1; 167 /* xp -> lc_group_number = 0;*/ 168 169 xp -> logical_channel_number = lcn; 170 xp -> packet_type = type; 171 172 return (xp); 173 } 174 175 /* 176 * This routine restarts all the virtual circuits. Actually, 177 * the virtual circuits are not "restarted" as such. Instead, 178 * any active switched circuit is simply returned to READY 179 * state. 180 */ 181 182 pk_restart (pkp, restart_cause) 183 register struct pkcb *pkp; 184 int restart_cause; 185 { 186 register struct x25_packet *xp; 187 register struct pklcd *lcp; 188 register int i; 189 190 /* Restart all logical channels. */ 191 if (pkp->pk_chan == 0) 192 return; 193 for (i = 1; i <= pkp->pk_maxlcn; ++i) 194 if ((lcp = pkp->pk_chan[i]) != NULL) { 195 if (lcp -> lcd_so) 196 lcp->lcd_so -> so_error = ENETRESET; 197 pk_close (lcp); 198 } 199 200 if (restart_cause < 0) 201 return; 202 203 pkp->pk_state = DTE_SENT_RESTART; 204 lcp = pkp->pk_chan[0]; 205 xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESTART); 206 (dtom (xp)) -> m_len++; 207 xp -> packet_data = 0; /* DTE only */ 208 pk_output (lcp); 209 } 210 211 212 /* 213 * This procedure frees up the Logical Channel Descripter. 214 */ 215 216 pk_freelcd (lcp) 217 register struct pklcd *lcp; 218 { 219 if (lcp == NULL) 220 return; 221 222 if (lcp -> lcd_template) 223 m_freem (dtom (lcp -> lcd_template)); 224 225 if (lcp -> lcd_lcn > 0) 226 lcp -> lcd_pkp -> pk_chan[lcp -> lcd_lcn] = NULL; 227 228 free((caddr_t)lcp, M_PCB); 229 } 230 231 232 /* 233 * Bind a address and protocol value to a socket. The important 234 * part is the protocol value - the first four characters of the 235 * Call User Data field. 236 */ 237 238 pk_bind (lcp, nam) 239 struct pklcd *lcp; 240 struct mbuf *nam; 241 { 242 register struct pkcb *pkp; 243 register struct mbuf *m; 244 register struct pklcd *pp; 245 register struct sockaddr_x25 *sa; 246 247 if (nam == NULL) 248 return (EADDRNOTAVAIL); 249 if (lcp -> lcd_ceaddr) /* XXX */ 250 return (EADDRINUSE); 251 if (checksockaddr (nam)) 252 return (EINVAL); 253 sa = mtod (nam, struct sockaddr_x25 *); 254 255 /* 256 * If the user wishes to accept calls only from a particular 257 * net (net != 0), make sure the net is known 258 */ 259 260 if (sa -> x25_net) 261 for (pkp = pkcbhead; ; pkp = pkp -> pk_next) { 262 if (pkp == 0) 263 return (ENETUNREACH); 264 if (pkp -> pk_xcp -> xc_net == sa -> x25_net) 265 break; 266 } 267 268 for (pp = pk_listenhead; pp; pp = pp -> lcd_listen) 269 if (bcmp (pp -> lcd_ceaddr -> x25_udata, sa -> x25_udata, 270 min (pp->lcd_ceaddr->x25_udlen, sa->x25_udlen)) == 0) 271 return (EADDRINUSE); 272 273 lcp -> lcd_laddr = *sa; 274 lcp -> lcd_ceaddr = &lcp -> lcd_laddr; 275 return (0); 276 } 277 278 /* 279 * Associate a logical channel descriptor with a network. 280 * Fill in the default network specific parameters and then 281 * set any parameters explicitly specified by the user or 282 * by the remote DTE. 283 */ 284 285 pk_assoc (pkp, lcp, sa) 286 register struct pkcb *pkp; 287 register struct pklcd *lcp; 288 register struct sockaddr_x25 *sa; 289 { 290 291 lcp -> lcd_pkp = pkp; 292 lcp -> lcd_packetsize = pkp -> pk_xcp -> xc_psize; 293 lcp -> lcd_windowsize = pkp -> pk_xcp -> xc_pwsize; 294 lcp -> lcd_rsn = MODULUS - 1; 295 pkp -> pk_chan[lcp -> lcd_lcn] = lcp; 296 297 if (sa -> x25_opts.op_psize) 298 lcp -> lcd_packetsize = sa -> x25_opts.op_psize; 299 else 300 sa -> x25_opts.op_psize = lcp -> lcd_packetsize; 301 if (sa -> x25_opts.op_wsize) 302 lcp -> lcd_windowsize = sa -> x25_opts.op_wsize; 303 else 304 sa -> x25_opts.op_wsize = lcp -> lcd_windowsize; 305 sa -> x25_net = pkp -> pk_xcp -> xc_net; 306 lcp -> lcd_flags = sa -> x25_opts.op_flags; 307 lcp -> lcd_stime = time.tv_sec; 308 } 309 310 pk_connect (lcp, nam, sa) 311 register struct pklcd *lcp; 312 register struct sockaddr_x25 *sa; 313 struct mbuf *nam; 314 { 315 register struct pkcb *pkp; 316 register struct mbuf *m; 317 register struct ifnet *ifp; 318 319 if (sa == 0) { 320 if (checksockaddr (nam)) 321 return (EINVAL); 322 sa = mtod (nam, struct sockaddr_x25 *); 323 } 324 if (sa -> x25_addr[0] == '\0') 325 return (EDESTADDRREQ); 326 if (lcp->lcd_pkp == 0) 327 for (pkp = pkcbhead; ; pkp = pkp->pk_next) { 328 if (pkp == 0) 329 return (ENETUNREACH); 330 /* 331 * use first net configured (last in list 332 * headed by pkcbhead) if net is zero 333 */ 334 if (sa -> x25_net == 0 && pkp -> pk_next == 0) 335 break; 336 if (sa -> x25_net == pkp -> pk_xcp -> xc_net) 337 break; 338 } 339 340 if (pkp -> pk_state != DTE_READY) 341 return (ENETDOWN); 342 if ((lcp -> lcd_lcn = pk_getlcn (pkp)) == 0) 343 return (EMFILE); 344 lcp -> lcd_faddr = *sa; 345 lcp -> lcd_ceaddr = & lcp->lcd_faddr; 346 pk_assoc (pkp, lcp, lcp -> lcd_ceaddr); 347 if (lcp -> so) 348 soisconnecting (lcp -> lcd_so); 349 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL); 350 pk_callrequest (lcp, lcp -> lcd_ceaddr, pkp -> pk_xcp); 351 return (*pkp -> pk_start)(lcp); 352 } 353 354 /* 355 * Build the rest of the CALL REQUEST packet. Fill in calling 356 * address, facilities fields and the user data field. 357 */ 358 359 pk_callrequest (lcp, sa, xcp) 360 struct pklcd *lcp; 361 register struct sockaddr_x25 *sa; 362 register struct x25config *xcp; 363 { 364 register struct x25_calladdr *a; 365 register struct mbuf *m = dtom (lcp -> lcd_template); 366 unsigned posn = 0; 367 octet *cp; 368 char addr[sizeof (xcp -> xc_ntn) * 2]; 369 370 a = (struct x25_calladdr *) &lcp -> lcd_template -> packet_data; 371 a -> calling_addrlen = xcp -> xc_ntnlen; 372 cp = (octet *) xcp -> xc_ntn; 373 from_bcd (addr, &cp, xcp -> xc_ntnlen); 374 a -> called_addrlen = strlen (sa -> x25_addr); 375 cp = (octet *) a -> address_field; 376 to_bcd (&cp, (int)a -> called_addrlen, sa -> x25_addr, &posn); 377 to_bcd (&cp, (int)a -> calling_addrlen, addr, &posn); 378 if (posn & 0x01) 379 *cp++ &= 0xf0; 380 381 build_facilities (&cp, sa, (int)xcp -> xc_type); 382 383 bcopy (sa -> x25_udata, (caddr_t)cp, (unsigned)sa -> x25_udlen); 384 cp += sa -> x25_udlen; 385 386 m -> m_len += cp - (octet *) a; 387 388 #ifdef ANDREW 389 printf ("call: "); 390 for (cp = mtod (m, octet *), posn = 0; posn < m->m_len; ++posn) 391 printf ("%x ", *cp++); 392 printf ("\n"); 393 #endif 394 } 395 396 build_facilities (cp, sa, type) 397 register octet **cp; 398 struct sockaddr_x25 *sa; 399 { 400 register octet *fcp; 401 register int revcharge; 402 403 fcp = *cp + 1; 404 revcharge = sa -> x25_opts.op_flags & X25_REVERSE_CHARGE ? 1 : 0; 405 /* 406 * This is specific to Datapac X.25(1976) DTEs. International 407 * calls must have the "hi priority" bit on. 408 */ 409 if (type == X25_1976 && sa -> x25_opts.op_psize == X25_PS128) 410 revcharge |= 02; 411 if (revcharge) { 412 *fcp++ = FACILITIES_REVERSE_CHARGE; 413 *fcp++ = revcharge; 414 } 415 switch (type) { 416 case X25_1980: 417 case X25_1984: 418 *fcp++ = FACILITIES_PACKETSIZE; 419 *fcp++ = sa -> x25_opts.op_psize; 420 *fcp++ = sa -> x25_opts.op_psize; 421 422 *fcp++ = FACILITIES_WINDOWSIZE; 423 *fcp++ = sa -> x25_opts.op_wsize; 424 *fcp++ = sa -> x25_opts.op_wsize; 425 } 426 **cp = fcp - *cp - 1; 427 *cp = fcp; 428 } 429 430 to_bcd (a, len, x, posn) 431 register octet **a; 432 register char *x; 433 register int len; 434 register unsigned *posn; 435 { 436 while (--len >= 0) 437 if ((*posn)++ & 0x01) 438 *(*a)++ |= *x++ & 0x0F; 439 else 440 **a = *x++ << 4; 441 } 442 443 /* 444 * This routine gets the first available logical channel number. The 445 * search is from the highest number to lowest number (DTE). 446 */ 447 448 pk_getlcn (pkp) 449 register struct pkcb *pkp; 450 { 451 register int i; 452 453 if (pkp->pk_chan == 0) 454 return (0); 455 for (i = pkp -> pk_maxlcn; i > 0; --i) 456 if (pkp -> pk_chan[i] == NULL) 457 break; 458 return (i); 459 460 } 461 462 static 463 checksockaddr (m) 464 struct mbuf *m; 465 { 466 register struct sockaddr_x25 *sa = mtod (m, struct sockaddr_x25 *); 467 register char *cp; 468 469 if (m -> m_len != sizeof (struct sockaddr_x25)) 470 return (1); 471 if (sa -> x25_family != AF_CCITT || sa -> x25_udlen == 0 || 472 sa -> x25_udlen > sizeof (sa -> x25_udata)) 473 return (1); 474 for (cp = sa -> x25_addr; *cp; cp++) { 475 if (*cp < '0' || *cp > '9' || 476 cp >= &sa -> x25_addr[sizeof (sa -> x25_addr) - 1]) 477 return (1); 478 } 479 return (0); 480 } 481 482 /* 483 * This procedure sends a CLEAR request packet. The lc state is 484 * set to "SENT_CLEAR". 485 */ 486 487 pk_clear (lcp) 488 struct pklcd *lcp; 489 { 490 register struct x25_packet *xp; 491 492 xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR); 493 (dtom (xp)) -> m_len++; 494 xp -> packet_data = 0; 495 496 pk_output (lcp); 497 498 } 499 500 /* 501 * This procedure sends a RESET request packet. It re-intializes 502 * virtual circuit. 503 */ 504 505 static 506 pk_reset (lcp) 507 register struct pklcd *lcp; 508 { 509 register struct x25_packet *xp; 510 register struct socket *so; 511 512 if (lcp -> lcd_state != DATA_TRANSFER) 513 return; 514 515 lcp -> lcd_reset_condition = TRUE; 516 517 /* Reset all the control variables for the channel. */ 518 lcp -> lcd_window_condition = lcp -> lcd_rnr_condition = 519 lcp -> lcd_intrconf_pending = FALSE; 520 lcp -> lcd_rsn = MODULUS - 1; 521 lcp -> lcd_ssn = 0; 522 lcp -> lcd_output_window = lcp -> lcd_input_window = 523 lcp -> lcd_last_transmitted_pr = 0; 524 if (so = lcp -> lcd_so) { 525 so -> so_error = ECONNRESET; 526 sbflush (&so -> so_rcv); 527 sbflush (&so -> so_snd); 528 } 529 xp = lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET); 530 (dtom (xp)) -> m_len += 2; 531 xp -> packet_data = 0; 532 pk_output (lcp); 533 534 } 535 536 537 /* 538 * This procedure handles all local protocol procedure errors. 539 */ 540 541 pk_procerror (error, lcp, errstr) 542 register struct pklcd *lcp; 543 char *errstr; 544 { 545 546 pk_message (lcp -> lcd_lcn, lcp -> lcd_pkp -> pk_xcp, errstr); 547 548 switch (error) { 549 case CLEAR: 550 if (lcp->lcd_so) { 551 lcp->lcd_so -> so_error = ECONNABORTED; 552 soisdisconnecting (lcp->lcd_so); 553 } 554 pk_clear (lcp); 555 break; 556 557 case RESET: 558 pk_reset (lcp); 559 } 560 } 561 562 /* 563 * This procedure is called during the DATA TRANSFER state to check 564 * and process the P(R) values received in the DATA, RR OR RNR 565 * packets. 566 */ 567 568 pk_ack (lcp, pr) 569 struct pklcd *lcp; 570 unsigned pr; 571 { 572 register struct socket *so = lcp -> lcd_so; 573 574 if (lcp -> lcd_output_window == pr) 575 return (PACKET_OK); 576 if (lcp -> lcd_output_window < lcp -> lcd_ssn) { 577 if (pr < lcp -> lcd_output_window || pr > lcp -> lcd_ssn) { 578 pk_procerror (RESET, lcp, "p(r) flow control error"); 579 return (ERROR_PACKET); 580 } 581 } 582 else { 583 if (pr < lcp -> lcd_output_window && pr > lcp -> lcd_ssn) { 584 pk_procerror (RESET, lcp, "p(r) flow control error"); 585 return (ERROR_PACKET); 586 } 587 } 588 589 lcp -> lcd_output_window = pr; /* Rotate window. */ 590 if (lcp -> lcd_window_condition == TRUE) 591 lcp -> lcd_window_condition = FALSE; 592 593 if (so && ((so -> so_snd.sb_flags & SB_WAIT) || so -> so_snd.sb_sel)) 594 sowwakeup (so); 595 if (lcp -> lcd_upper) 596 (*lcp -> lcd_upper)(lcp, 0); 597 598 return (PACKET_OK); 599 } 600 601 /* 602 * This procedure decodes the X.25 level 3 packet returning a 603 * code to be used in switchs or arrays. 604 */ 605 606 pk_decode (xp) 607 register struct x25_packet *xp; 608 { 609 register int type; 610 611 if (xp -> fmt_identifier != 1) 612 return (INVALID_PACKET); 613 614 /* 615 * Make sure that the logical channel group number is 0. 616 * This restriction may be removed at some later date. 617 */ 618 if (xp -> lc_group_number != 0) 619 return (INVALID_PACKET); 620 621 /* 622 * Test for data packet first. 623 */ 624 if (!(xp -> packet_type & DATA_PACKET_DESIGNATOR)) 625 return (DATA); 626 627 /* 628 * Test if flow control packet (RR or RNR). 629 */ 630 if (!(xp -> packet_type & RR_OR_RNR_PACKET_DESIGNATOR)) 631 if (!(xp -> packet_type & RR_PACKET_DESIGNATOR)) 632 return (RR); 633 else 634 return (RNR); 635 636 /* 637 * Determine the rest of the packet types. 638 */ 639 switch (xp -> packet_type) { 640 case X25_CALL: 641 type = CALL; 642 break; 643 644 case X25_CALL_ACCEPTED: 645 type = CALL_ACCEPTED; 646 break; 647 648 case X25_CLEAR: 649 type = CLEAR; 650 break; 651 652 case X25_CLEAR_CONFIRM: 653 type = CLEAR_CONF; 654 break; 655 656 case X25_INTERRUPT: 657 type = INTERRUPT; 658 break; 659 660 case X25_INTERRUPT_CONFIRM: 661 type = INTERRUPT_CONF; 662 break; 663 664 case X25_RESET: 665 type = RESET; 666 break; 667 668 case X25_RESET_CONFIRM: 669 type = RESET_CONF; 670 break; 671 672 case X25_RESTART: 673 type = RESTART; 674 break; 675 676 case X25_RESTART_CONFIRM: 677 type = RESTART_CONF; 678 break; 679 680 default: 681 type = INVALID_PACKET; 682 } 683 return (type); 684 } 685 686 /* 687 * A restart packet has been received. Print out the reason 688 * for the restart. 689 */ 690 691 pk_restartcause (pkp, xp) 692 struct pkcb *pkp; 693 register struct x25_packet *xp; 694 { 695 register struct x25config *xcp = pkp -> pk_xcp; 696 register int lcn = xp -> logical_channel_number; 697 698 switch (xp -> packet_data) { 699 case X25_RESTART_LOCAL_PROCEDURE_ERROR: 700 pk_message (lcn, xcp, "restart: local procedure error"); 701 break; 702 703 case X25_RESTART_NETWORK_CONGESTION: 704 pk_message (lcn, xcp, "restart: network congestion"); 705 break; 706 707 case X25_RESTART_NETWORK_OPERATIONAL: 708 pk_message (lcn, xcp, "restart: network operational"); 709 break; 710 711 default: 712 pk_message (lcn, xcp, "restart: unknown cause"); 713 } 714 } 715 716 #define MAXRESETCAUSE 7 717 718 int Reset_cause[] = { 719 EXRESET, EXROUT, 0, EXRRPE, 0, EXRLPE, 0, EXRNCG 720 }; 721 722 /* 723 * A reset packet has arrived. Return the cause to the user. 724 */ 725 726 pk_resetcause (pkp, xp) 727 struct pkcb *pkp; 728 register struct x25_packet *xp; 729 { 730 register struct pklcd *lcp = pkp->pk_chan[xp -> logical_channel_number]; 731 register int code = xp -> packet_data; 732 733 if (code > MAXRESETCAUSE) 734 code = 7; /* EXRNCG */ 735 736 lcp->lcd_so -> so_error = Reset_cause[code]; 737 } 738 739 #define MAXCLEARCAUSE 25 740 741 int Clear_cause[] = { 742 EXCLEAR, EXCBUSY, 0, EXCINV, 0, EXCNCG, 0, 743 0, 0, EXCOUT, 0, EXCAB, 0, EXCNOB, 0, 0, 0, EXCRPE, 744 0, EXCLPE, 0, 0, 0, 0, 0, EXCRRC 745 }; 746 747 /* 748 * A clear packet has arrived. Return the cause to the user. 749 */ 750 751 pk_clearcause (pkp, xp) 752 struct pkcb *pkp; 753 register struct x25_packet *xp; 754 { 755 register struct pklcd *lcp = pkp->pk_chan[xp -> logical_channel_number]; 756 register int code = xp -> packet_data; 757 758 if (code > MAXCLEARCAUSE) 759 code = 5; /* EXRNCG */ 760 lcp->lcd_so -> so_error = Clear_cause[code]; 761 } 762 763 char * 764 format_ntn (xcp) 765 register struct x25config *xcp; 766 { 767 register int i; 768 register char *src, *dest; 769 static char ntn[12]; 770 771 src = xcp->xc_ntn; 772 dest = ntn; 773 for (i = 0; i < xcp->xc_ntnlen / 2; i++) { 774 *dest++ = ((*src & 0xf0) >> 4) + '0'; 775 *dest++ = (*src++ & 0xf) + '0'; 776 } 777 if (xcp->xc_ntnlen & 01) 778 dest[-1] = 0; 779 else 780 *dest = 0; 781 return (ntn); 782 } 783 784 /* VARARGS1 */ 785 pk_message (lcn, xcp, fmt, a1, a2, a3, a4, a5, a6) 786 struct x25config *xcp; 787 char *fmt; 788 { 789 790 if (lcn) 791 if (pkcbhead -> pk_next) 792 printf ("X.25(%s): lcn %d: ", format_ntn (xcp), lcn); 793 else 794 printf ("X.25: lcn %d: ", lcn); 795 else 796 if (pkcbhead -> pk_next) 797 printf ("X.25(%s): ", format_ntn (xcp)); 798 else 799 printf ("X.25: "); 800 801 printf (fmt, a1, a2, a3, a4, a5, a6); 802 printf ("\n"); 803 } 804