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