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