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