1 /* $NetBSD: ppp_tty.c,v 1.67 2020/01/29 04:28:27 thorpej Exp $ */ 2 /* Id: ppp_tty.c,v 1.3 1996/07/01 01:04:11 paulus Exp */ 3 4 /* 5 * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous 6 * tty devices. 7 * 8 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * 3. The name "Carnegie Mellon University" must not be used to 23 * endorse or promote products derived from this software without 24 * prior written permission. For permission or any legal 25 * details, please contact 26 * Office of Technology Transfer 27 * Carnegie Mellon University 28 * 5000 Forbes Avenue 29 * Pittsburgh, PA 15213-3890 30 * (412) 268-4387, fax: (412) 268-7395 31 * tech-transfer@andrew.cmu.edu 32 * 33 * 4. Redistributions of any form whatsoever must retain the following 34 * acknowledgment: 35 * "This product includes software developed by Computing Services 36 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 37 * 38 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 39 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 40 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 41 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 42 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 43 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 44 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 45 * 46 * Based on: 47 * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 48 * 49 * Copyright (c) 1987 Regents of the University of California. 50 * All rights reserved. 51 * 52 * Redistribution and use in source and binary forms are permitted 53 * provided that the above copyright notice and this paragraph are 54 * duplicated in all such forms and that any documentation, 55 * advertising materials, and other materials related to such 56 * distribution and use acknowledge that the software was developed 57 * by the University of California, Berkeley. The name of the 58 * University may not be used to endorse or promote products derived 59 * from this software without specific prior written permission. 60 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 61 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 62 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 63 * 64 * Serial Line interface 65 * 66 * Rick Adams 67 * Center for Seismic Studies 68 * 1300 N 17th Street, Suite 1450 69 * Arlington, Virginia 22209 70 * (703)276-7900 71 * rick@seismo.ARPA 72 * seismo!rick 73 * 74 * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). 75 * Converted to 4.3BSD Beta by Chris Torek. 76 * Other changes made at Berkeley, based in part on code by Kirk Smith. 77 * 78 * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com) 79 * Added VJ tcp header compression; more unified ioctls 80 * 81 * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au). 82 * Cleaned up a lot of the mbuf-related code to fix bugs that 83 * caused system crashes and packet corruption. Changed pppstart 84 * so that it doesn't just give up with a "collision" if the whole 85 * packet doesn't fit in the output ring buffer. 86 * 87 * Added priority queueing for interactive IP packets, following 88 * the model of if_sl.c, plus hooks for bpf. 89 * Paul Mackerras (paulus@cs.anu.edu.au). 90 */ 91 92 /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ 93 /* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */ 94 95 #include <sys/cdefs.h> 96 __KERNEL_RCSID(0, "$NetBSD: ppp_tty.c,v 1.67 2020/01/29 04:28:27 thorpej Exp $"); 97 98 #ifdef _KERNEL_OPT 99 #include "ppp.h" 100 #include "opt_ppp.h" 101 #endif 102 #define VJC 103 #define PPP_COMPRESS 104 105 #include <sys/param.h> 106 #include <sys/proc.h> 107 #include <sys/mbuf.h> 108 #include <sys/dkstat.h> 109 #include <sys/socket.h> 110 #include <sys/ioctl.h> 111 #include <sys/file.h> 112 #include <sys/tty.h> 113 #include <sys/kernel.h> 114 #include <sys/conf.h> 115 #include <sys/vnode.h> 116 #include <sys/systm.h> 117 #include <sys/kauth.h> 118 119 #include <net/if.h> 120 #include <net/if_types.h> 121 122 #ifdef VJC 123 #include <netinet/in.h> 124 #include <netinet/in_systm.h> 125 #include <netinet/ip.h> 126 #include <net/slcompress.h> 127 #endif 128 129 #include <net/bpf.h> 130 #include <net/ppp_defs.h> 131 #include <net/if_ppp.h> 132 #include <net/if_pppvar.h> 133 134 static int pppopen(dev_t dev, struct tty *tp); 135 static int pppclose(struct tty *tp, int flag); 136 static int pppread(struct tty *tp, struct uio *uio, int flag); 137 static int pppwrite(struct tty *tp, struct uio *uio, int flag); 138 static int ppptioctl(struct tty *tp, u_long cmd, void *data, int flag, 139 struct lwp *); 140 static int pppinput(int c, struct tty *tp); 141 static int pppstart(struct tty *tp); 142 143 struct linesw ppp_disc = { /* XXX should be static */ 144 .l_name = "ppp", 145 .l_open = pppopen, 146 .l_close = pppclose, 147 .l_read = pppread, 148 .l_write = pppwrite, 149 .l_ioctl = ppptioctl, 150 .l_rint = pppinput, 151 .l_start = pppstart, 152 .l_modem = ttymodem, 153 .l_poll = ttpoll 154 }; 155 156 static void ppprcvframe(struct ppp_softc *sc, struct mbuf *m); 157 static uint16_t pppfcs(uint16_t fcs, const uint8_t *cp, int len); 158 static void pppsyncstart(struct ppp_softc *sc); 159 static void pppasyncstart(struct ppp_softc *); 160 static void pppasyncctlp(struct ppp_softc *); 161 static void pppasyncrelinq(struct ppp_softc *); 162 static void ppp_timeout(void *); 163 static void pppgetm(struct ppp_softc *sc); 164 static void pppdumpb(u_char *b, int l); 165 static void ppplogchar(struct ppp_softc *, int); 166 static void pppdumpframe(struct ppp_softc *sc, struct mbuf* m, int xmit); 167 168 /* 169 * Does c need to be escaped? 170 */ 171 #define ESCAPE_P(c) (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F))) 172 173 /* 174 * Procedures for using an async tty interface for PPP. 175 */ 176 177 /* This is a NetBSD-1.0 or later kernel. */ 178 #define CCOUNT(q) ((q)->c_cc) 179 180 #define PPP_LOWAT 100 /* Process more output when < LOWAT on queue */ 181 #define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */ 182 183 /* 184 * Line specific open routine for async tty devices. 185 * Attach the given tty to the first available ppp unit. 186 * Called from device open routine or ttioctl. 187 */ 188 /* ARGSUSED */ 189 static int 190 pppopen(dev_t dev, struct tty *tp) 191 { 192 struct lwp *l = curlwp; /* XXX */ 193 struct ppp_softc *sc; 194 int error, s; 195 196 error = kauth_authorize_network(l->l_cred, KAUTH_NETWORK_INTERFACE_PPP, 197 KAUTH_REQ_NETWORK_INTERFACE_PPP_ADD, NULL, NULL, NULL); 198 if (error) 199 return (error); 200 201 s = spltty(); 202 203 if (tp->t_linesw == &ppp_disc) { 204 sc = (struct ppp_softc *) tp->t_sc; 205 if (sc != NULL && sc->sc_devp == (void *) tp) { 206 splx(s); 207 return (0); 208 } 209 } 210 211 if ((sc = pppalloc(l->l_proc->p_pid)) == NULL) { 212 splx(s); 213 return ENXIO; 214 } 215 216 if (sc->sc_relinq) 217 (*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */ 218 219 /* Switch DLT to PPP-over-serial. */ 220 bpf_change_type(&sc->sc_if, DLT_PPP_SERIAL, PPP_HDRLEN); 221 222 sc->sc_ilen = 0; 223 sc->sc_m = NULL; 224 memset(sc->sc_asyncmap, 0, sizeof(sc->sc_asyncmap)); 225 sc->sc_asyncmap[0] = 0xffffffff; 226 sc->sc_asyncmap[3] = 0x60000000; 227 sc->sc_rasyncmap = 0; 228 sc->sc_devp = (void *) tp; 229 sc->sc_start = pppasyncstart; 230 sc->sc_ctlp = pppasyncctlp; 231 sc->sc_relinq = pppasyncrelinq; 232 sc->sc_outm = NULL; 233 pppgetm(sc); 234 sc->sc_if.if_flags |= IFF_RUNNING; 235 sc->sc_if.if_baudrate = tp->t_ospeed; 236 237 tp->t_sc = (void *) sc; 238 mutex_spin_enter(&tty_lock); 239 ttyflush(tp, FREAD | FWRITE); 240 mutex_spin_exit(&tty_lock); 241 242 splx(s); 243 return (0); 244 } 245 246 /* 247 * Line specific close routine, called from device close routine 248 * and from ttioctl. 249 * Detach the tty from the ppp unit. 250 * Mimics part of ttyclose(). 251 */ 252 static int 253 pppclose(struct tty *tp, int flag) 254 { 255 struct ppp_softc *sc; 256 int s; 257 258 s = spltty(); 259 mutex_spin_enter(&tty_lock); 260 ttyflush(tp, FREAD|FWRITE); 261 mutex_spin_exit(&tty_lock); /* XXX */ 262 ttyldisc_release(tp->t_linesw); 263 tp->t_linesw = ttyldisc_default(); 264 sc = (struct ppp_softc *) tp->t_sc; 265 if (sc != NULL) { 266 tp->t_sc = NULL; 267 if (tp == (struct tty *) sc->sc_devp) { 268 pppasyncrelinq(sc); 269 pppdealloc(sc); 270 } 271 } 272 splx(s); 273 return 0; 274 } 275 276 /* 277 * Relinquish the interface unit to another device. 278 */ 279 static void 280 pppasyncrelinq(struct ppp_softc *sc) 281 { 282 int s; 283 284 /* Change DLT to back none. */ 285 bpf_change_type(&sc->sc_if, DLT_NULL, 0); 286 287 s = spltty(); 288 if (sc->sc_outm) { 289 m_freem(sc->sc_outm); 290 sc->sc_outm = NULL; 291 } 292 if (sc->sc_m) { 293 m_freem(sc->sc_m); 294 sc->sc_m = NULL; 295 } 296 if (sc->sc_flags & SC_TIMEOUT) { 297 callout_stop(&sc->sc_timo_ch); 298 sc->sc_flags &= ~SC_TIMEOUT; 299 } 300 splx(s); 301 } 302 303 /* 304 * Line specific (tty) read routine. 305 */ 306 static int 307 pppread(struct tty *tp, struct uio *uio, int flag) 308 { 309 struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; 310 struct mbuf *m, *m0; 311 int error = 0; 312 313 if (sc == NULL) 314 return 0; 315 /* 316 * Loop waiting for input, checking that nothing disasterous 317 * happens in the meantime. 318 */ 319 mutex_spin_enter(&tty_lock); 320 for (;;) { 321 if (tp != (struct tty *) sc->sc_devp || 322 tp->t_linesw != &ppp_disc) { 323 mutex_spin_exit(&tty_lock); 324 return 0; 325 } 326 if (sc->sc_inq.ifq_head != NULL) 327 break; 328 if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0 329 && (tp->t_state & TS_ISOPEN)) { 330 mutex_spin_exit(&tty_lock); 331 return 0; /* end of file */ 332 } 333 if (tp->t_state & TS_ASYNC || flag & IO_NDELAY) { 334 mutex_spin_exit(&tty_lock); 335 return (EWOULDBLOCK); 336 } 337 error = ttysleep(tp, &tp->t_rawcv, true, 0); 338 if (error) { 339 mutex_spin_exit(&tty_lock); 340 return error; 341 } 342 } 343 344 /* Pull place-holder byte out of canonical queue */ 345 getc(&tp->t_canq); 346 347 /* Get the packet from the input queue */ 348 IF_DEQUEUE(&sc->sc_inq, m0); 349 mutex_spin_exit(&tty_lock); 350 351 for (m = m0; m && uio->uio_resid; m = m->m_next) 352 if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0) 353 break; 354 m_freem(m0); 355 return (error); 356 } 357 358 /* 359 * Line specific (tty) write routine. 360 */ 361 static int 362 pppwrite(struct tty *tp, struct uio *uio, int flag) 363 { 364 struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; 365 struct mbuf *m, *m0; 366 struct sockaddr dst; 367 int len, error; 368 369 if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0) 370 return 0; /* wrote 0 bytes */ 371 if (tp->t_linesw != &ppp_disc) 372 return (EINVAL); 373 if (sc == NULL || tp != (struct tty *) sc->sc_devp) 374 return EIO; 375 if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN || 376 uio->uio_resid < PPP_HDRLEN) 377 return (EMSGSIZE); 378 379 MGETHDR(m0, M_WAIT, MT_DATA); 380 if (m0 == NULL) 381 return ENOBUFS; 382 383 m0->m_len = 0; 384 m0->m_pkthdr.len = uio->uio_resid; 385 m_reset_rcvif(m0); 386 387 if (uio->uio_resid >= MCLBYTES / 2) 388 MCLGET(m0, M_DONTWAIT); 389 390 for (m = m0; uio->uio_resid;) { 391 len = M_TRAILINGSPACE(m); 392 if (len > uio->uio_resid) 393 len = uio->uio_resid; 394 if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) { 395 m_freem(m0); 396 return (error); 397 } 398 m->m_len = len; 399 400 if (uio->uio_resid == 0) 401 break; 402 403 MGET(m->m_next, M_WAIT, MT_DATA); 404 if (m->m_next == NULL) { 405 m_freem(m0); 406 return ENOBUFS; 407 } 408 m = m->m_next; 409 m->m_len = 0; 410 } 411 dst.sa_family = AF_UNSPEC; 412 bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN); 413 m_adj(m0, PPP_HDRLEN); 414 return if_output_lock(&sc->sc_if, &sc->sc_if, m0, &dst, (struct rtentry *)0); 415 } 416 417 /* 418 * Line specific (tty) ioctl routine. 419 * This discipline requires that tty device drivers call 420 * the line specific l_ioctl routine from their ioctl routines. 421 */ 422 /* ARGSUSED */ 423 static int 424 ppptioctl(struct tty *tp, u_long cmd, void *data, int flag, struct lwp *l) 425 { 426 struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc; 427 int error, s; 428 429 if (sc == NULL) 430 return (EPASSTHROUGH); 431 432 KERNEL_LOCK(1, NULL); 433 434 if (tp != (struct tty *) sc->sc_devp) { 435 error = EPASSTHROUGH; 436 goto out; 437 } 438 439 error = 0; 440 switch (cmd) { 441 case TIOCRCVFRAME: 442 ppprcvframe(sc,*((struct mbuf **)data)); 443 break; 444 445 case PPPIOCSASYNCMAP: 446 if ((error = kauth_authorize_device_tty(l->l_cred, 447 KAUTH_DEVICE_TTY_PRIVSET, tp)) != 0) 448 break; 449 sc->sc_asyncmap[0] = *(u_int *)data; 450 break; 451 452 case PPPIOCGASYNCMAP: 453 *(u_int *)data = sc->sc_asyncmap[0]; 454 break; 455 456 case PPPIOCSRASYNCMAP: 457 if ((error = kauth_authorize_device_tty(l->l_cred, 458 KAUTH_DEVICE_TTY_PRIVSET, tp)) != 0) 459 break; 460 sc->sc_rasyncmap = *(u_int *)data; 461 break; 462 463 case PPPIOCGRASYNCMAP: 464 *(u_int *)data = sc->sc_rasyncmap; 465 break; 466 467 case PPPIOCSXASYNCMAP: 468 if ((error = kauth_authorize_device_tty(l->l_cred, 469 KAUTH_DEVICE_TTY_PRIVSET, tp)) != 0) 470 break; 471 s = spltty(); 472 bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap)); 473 sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */ 474 sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */ 475 sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */ 476 splx(s); 477 break; 478 479 case PPPIOCGXASYNCMAP: 480 bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap)); 481 break; 482 483 default: 484 error = pppioctl(sc, cmd, data, flag, l); 485 if (error == 0 && cmd == PPPIOCSMRU) 486 pppgetm(sc); 487 } 488 489 out: 490 KERNEL_UNLOCK_ONE(NULL); 491 return error; 492 } 493 494 /* receive a complete ppp frame from device in synchronous 495 * hdlc mode. caller gives up ownership of mbuf 496 */ 497 static void 498 ppprcvframe(struct ppp_softc *sc, struct mbuf *m) 499 { 500 int len, s; 501 struct mbuf *n; 502 u_char hdr[4]; 503 int hlen,count; 504 505 for (n=m,len=0;n != NULL;n = n->m_next) 506 len += n->m_len; 507 if (len==0) { 508 m_freem(m); 509 return; 510 } 511 512 /* extract PPP header from mbuf chain (1 to 4 bytes) */ 513 for (n=m,hlen=0;n!=NULL && hlen<sizeof(hdr);n=n->m_next) { 514 count = (sizeof(hdr)-hlen) < n->m_len ? 515 sizeof(hdr)-hlen : n->m_len; 516 bcopy(mtod(n,u_char*),&hdr[hlen],count); 517 hlen+=count; 518 } 519 520 s = spltty(); 521 522 /* if AFCF compressed then prepend AFCF */ 523 if (hdr[0] != PPP_ALLSTATIONS) { 524 if (sc->sc_flags & SC_REJ_COMP_AC) { 525 if (sc->sc_flags & SC_DEBUG) 526 printf( 527 "%s: garbage received: 0x%x (need 0xFF)\n", 528 sc->sc_if.if_xname, hdr[0]); 529 goto bail; 530 } 531 M_PREPEND(m,2,M_DONTWAIT); 532 if (m==NULL) { 533 splx(s); 534 return; 535 } 536 hdr[3] = hdr[1]; 537 hdr[2] = hdr[0]; 538 hdr[0] = PPP_ALLSTATIONS; 539 hdr[1] = PPP_UI; 540 len += 2; 541 } 542 543 /* if protocol field compressed, add MSB of protocol field = 0 */ 544 if (hdr[2] & 1) { 545 /* a compressed protocol */ 546 M_PREPEND(m,1,M_DONTWAIT); 547 if (m==NULL) { 548 splx(s); 549 return; 550 } 551 hdr[3] = hdr[2]; 552 hdr[2] = 0; 553 len++; 554 } 555 556 /* valid LSB of protocol field has bit0 set */ 557 if (!(hdr[3] & 1)) { 558 if (sc->sc_flags & SC_DEBUG) 559 printf("%s: bad protocol %x\n", sc->sc_if.if_xname, 560 (hdr[2] << 8) + hdr[3]); 561 goto bail; 562 } 563 564 /* packet beyond configured mru? */ 565 if (len > sc->sc_mru + PPP_HDRLEN) { 566 if (sc->sc_flags & SC_DEBUG) 567 printf("%s: packet too big\n", sc->sc_if.if_xname); 568 goto bail; 569 } 570 571 /* add expanded 4 byte header to mbuf chain */ 572 for (n=m,hlen=0;n!=NULL && hlen<sizeof(hdr);n=n->m_next) { 573 count = (sizeof(hdr)-hlen) < n->m_len ? 574 sizeof(hdr)-hlen : n->m_len; 575 bcopy(&hdr[hlen],mtod(n,u_char*),count); 576 hlen+=count; 577 } 578 579 /* if_ppp.c requires the PPP header and IP header */ 580 /* to be contiguous */ 581 count = len < MHLEN ? len : MHLEN; 582 if (m->m_len < count) { 583 m = m_pullup(m,count); 584 if (m==NULL) 585 goto bail; 586 } 587 588 sc->sc_stats.ppp_ibytes += len; 589 590 if (sc->sc_flags & SC_LOG_RAWIN) 591 pppdumpframe(sc,m,0); 592 593 ppppktin(sc, m, 0); 594 splx(s); 595 return; 596 bail: 597 m_freem(m); 598 splx(s); 599 } 600 601 /* 602 * FCS lookup table as calculated by genfcstab. 603 */ 604 static const uint16_t fcstab[256] = { 605 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 606 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 607 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 608 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 609 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 610 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 611 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 612 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 613 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 614 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 615 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 616 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 617 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 618 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 619 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 620 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 621 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 622 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 623 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 624 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 625 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 626 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 627 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 628 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 629 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 630 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 631 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 632 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 633 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 634 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 635 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 636 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 637 }; 638 639 /* 640 * Calculate a new FCS given the current FCS and the new data. 641 */ 642 static uint16_t 643 pppfcs(uint16_t fcs, const uint8_t *cp, int len) 644 { 645 while (len--) 646 fcs = PPP_FCS(fcs, *cp++); 647 return (fcs); 648 } 649 650 /* This gets called at splsoftnet from pppasyncstart at various times 651 * when there is data ready to be sent. 652 */ 653 static void 654 pppsyncstart(struct ppp_softc *sc) 655 { 656 struct tty *tp = (struct tty *) sc->sc_devp; 657 struct mbuf *m, *n; 658 const struct cdevsw *cdev; 659 int len; 660 661 for(m = sc->sc_outm;;) { 662 if (m == NULL) { 663 m = ppp_dequeue(sc); /* get new packet */ 664 if (m == NULL) 665 break; /* no more packets */ 666 if (sc->sc_flags & SC_DEBUG) 667 pppdumpframe(sc,m,1); 668 } 669 for(n=m,len=0;n!=NULL;n=n->m_next) 670 len += n->m_len; 671 672 /* call device driver IOCTL to transmit a frame */ 673 cdev = cdevsw_lookup(tp->t_dev); 674 if (cdev == NULL || 675 (*cdev->d_ioctl)(tp->t_dev, TIOCXMTFRAME, (void *)&m, 676 0, 0)) { 677 /* busy or error, set as current packet */ 678 sc->sc_outm = m; 679 break; 680 } 681 sc->sc_outm = m = NULL; 682 sc->sc_stats.ppp_obytes += len; 683 } 684 } 685 686 /* 687 * This gets called at splsoftnet from if_ppp.c at various times 688 * when there is data ready to be sent. 689 */ 690 static void 691 pppasyncstart(struct ppp_softc *sc) 692 { 693 struct tty *tp = (struct tty *) sc->sc_devp; 694 struct mbuf *m; 695 int len; 696 u_char *start, *stop, *cp; 697 int n, ndone, done, idle; 698 struct mbuf *m2; 699 700 if (sc->sc_flags & SC_SYNC){ 701 pppsyncstart(sc); 702 return; 703 } 704 705 mutex_spin_enter(&tty_lock); 706 707 idle = 0; 708 while (CCOUNT(&tp->t_outq) < PPP_HIWAT) { 709 /* 710 * See if we have an existing packet partly sent. 711 * If not, get a new packet and start sending it. 712 */ 713 m = sc->sc_outm; 714 if (m == NULL) { 715 /* 716 * Get another packet to be sent. 717 */ 718 m = ppp_dequeue(sc); 719 if (m == NULL) { 720 idle = 1; 721 break; 722 } 723 724 /* 725 * The extra PPP_FLAG will start up a new packet, and thus 726 * will flush any accumulated garbage. We do this whenever 727 * the line may have been idle for some time. 728 */ 729 if (CCOUNT(&tp->t_outq) == 0) { 730 ++sc->sc_stats.ppp_obytes; 731 (void) putc(PPP_FLAG, &tp->t_outq); 732 } 733 734 /* Calculate the FCS for the first mbuf's worth. */ 735 sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, uint8_t *), m->m_len); 736 } 737 738 for (;;) { 739 start = mtod(m, u_char *); 740 len = m->m_len; 741 stop = start + len; 742 while (len > 0) { 743 /* 744 * Find out how many bytes in the string we can 745 * handle without doing something special. 746 */ 747 for (cp = start; cp < stop; cp++) 748 if (ESCAPE_P(*cp)) 749 break; 750 n = cp - start; 751 if (n) { 752 /* NetBSD (0.9 or later), 4.3-Reno or similar. */ 753 ndone = n - b_to_q(start, n, &tp->t_outq); 754 len -= ndone; 755 start += ndone; 756 sc->sc_stats.ppp_obytes += ndone; 757 758 if (ndone < n) 759 break; /* packet doesn't fit */ 760 } 761 /* 762 * If there are characters left in the mbuf, 763 * the first one must be special. 764 * Put it out in a different form. 765 */ 766 if (len) { 767 if (putc(PPP_ESCAPE, &tp->t_outq)) 768 break; 769 if (putc(*start ^ PPP_TRANS, &tp->t_outq)) { 770 (void) unputc(&tp->t_outq); 771 break; 772 } 773 sc->sc_stats.ppp_obytes += 2; 774 start++; 775 len--; 776 } 777 } 778 779 /* 780 * If we didn't empty this mbuf, remember where we're up to. 781 * If we emptied the last mbuf, try to add the FCS and closing 782 * flag, and if we can't, leave sc_outm pointing to m, but with 783 * m->m_len == 0, to remind us to output the FCS and flag later. 784 */ 785 done = len == 0; 786 if (done && m->m_next == NULL) { 787 u_char *p, *q; 788 int c; 789 u_char endseq[8]; 790 791 /* 792 * We may have to escape the bytes in the FCS. 793 */ 794 p = endseq; 795 c = ~sc->sc_outfcs & 0xFF; 796 if (ESCAPE_P(c)) { 797 *p++ = PPP_ESCAPE; 798 *p++ = c ^ PPP_TRANS; 799 } else 800 *p++ = c; 801 c = (~sc->sc_outfcs >> 8) & 0xFF; 802 if (ESCAPE_P(c)) { 803 *p++ = PPP_ESCAPE; 804 *p++ = c ^ PPP_TRANS; 805 } else 806 *p++ = c; 807 *p++ = PPP_FLAG; 808 809 /* 810 * Try to output the FCS and flag. If the bytes 811 * don't all fit, back out. 812 */ 813 for (q = endseq; q < p; ++q) 814 if (putc(*q, &tp->t_outq)) { 815 done = 0; 816 for (; q > endseq; --q) 817 unputc(&tp->t_outq); 818 break; 819 } 820 if (done) 821 sc->sc_stats.ppp_obytes += q - endseq; 822 } 823 824 if (!done) { 825 /* remember where we got to */ 826 m->m_data = start; 827 m->m_len = len; 828 break; 829 } 830 831 /* Finished with this mbuf; free it and move on. */ 832 m = m2 = m_free(m); 833 if (m == NULL) { 834 /* Finished a packet */ 835 break; 836 } 837 sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, uint8_t *), m->m_len); 838 } 839 840 /* 841 * If m == NULL, we have finished a packet. 842 * If m != NULL, we've either done as much work this time 843 * as we need to, or else we've filled up the output queue. 844 */ 845 sc->sc_outm = m; 846 if (m) 847 break; 848 } 849 850 /* Call pppstart to start output again if necessary. */ 851 pppstart(tp); 852 853 /* 854 * This timeout is needed for operation on a pseudo-tty, 855 * because the pty code doesn't call pppstart after it has 856 * drained the t_outq. 857 */ 858 if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) { 859 callout_reset(&sc->sc_timo_ch, 1, ppp_timeout, sc); 860 sc->sc_flags |= SC_TIMEOUT; 861 } 862 863 mutex_spin_exit(&tty_lock); 864 } 865 866 /* 867 * This gets called when a received packet is placed on 868 * the inq, at splsoftnet. 869 */ 870 static void 871 pppasyncctlp(struct ppp_softc *sc) 872 { 873 struct tty *tp; 874 875 /* Put a placeholder byte in canq for ttselect()/ttnread(). */ 876 mutex_spin_enter(&tty_lock); 877 tp = (struct tty *) sc->sc_devp; 878 putc(0, &tp->t_canq); 879 ttwakeup(tp); 880 mutex_spin_exit(&tty_lock); 881 } 882 883 /* 884 * Start output on async tty interface. If the transmit queue 885 * has drained sufficiently, arrange for pppasyncstart to be 886 * called later at splsoftnet. 887 * Called at spltty or higher. 888 */ 889 static int 890 pppstart(struct tty *tp) 891 { 892 struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc; 893 894 /* 895 * If there is stuff in the output queue, send it now. 896 * We are being called in lieu of ttstart and must do what it would. 897 */ 898 if (tp->t_oproc != NULL) 899 (*tp->t_oproc)(tp); 900 901 /* 902 * If the transmit queue has drained and the tty has not hung up 903 * or been disconnected from the ppp unit, then tell if_ppp.c that 904 * we need more output. 905 */ 906 if ((CCOUNT(&tp->t_outq) >= PPP_LOWAT) 907 && ((sc == NULL) || (sc->sc_flags & SC_TIMEOUT))) 908 return 0; 909 #ifdef ALTQ 910 /* 911 * if ALTQ is enabled, don't invoke NETISR_PPP. 912 * pppintr() could loop without doing anything useful 913 * under rate-limiting. 914 */ 915 if (ALTQ_IS_ENABLED(&sc->sc_if.if_snd)) 916 return 0; 917 #endif 918 if (!((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0) 919 && sc != NULL && tp == (struct tty *) sc->sc_devp) { 920 ppp_restart(sc); 921 } 922 923 return 0; 924 } 925 926 /* 927 * Timeout routine - try to start some more output. 928 */ 929 static void 930 ppp_timeout(void *x) 931 { 932 struct ppp_softc *sc = (struct ppp_softc *) x; 933 struct tty *tp = (struct tty *) sc->sc_devp; 934 935 mutex_spin_enter(&tty_lock); 936 sc->sc_flags &= ~SC_TIMEOUT; 937 pppstart(tp); 938 mutex_spin_exit(&tty_lock); 939 } 940 941 /* 942 * Allocate enough mbuf to handle current MRU. 943 */ 944 static void 945 pppgetm(struct ppp_softc *sc) 946 { 947 struct mbuf *m, **mp; 948 int len; 949 950 mp = &sc->sc_m; 951 for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){ 952 if ((m = *mp) == NULL) { 953 MGETHDR(m, M_DONTWAIT, MT_DATA); 954 if (m == NULL) 955 break; 956 *mp = m; 957 MCLGET(m, M_DONTWAIT); 958 } 959 len -= M_BUFSIZE(m); 960 mp = &m->m_next; 961 } 962 } 963 964 /* 965 * tty interface receiver interrupt. 966 */ 967 static const unsigned paritytab[8] = { 968 0x96696996, 0x69969669, 0x69969669, 0x96696996, 969 0x69969669, 0x96696996, 0x96696996, 0x69969669 970 }; 971 972 static int 973 pppinput(int c, struct tty *tp) 974 { 975 struct ppp_softc *sc; 976 struct mbuf *m; 977 int ilen, s; 978 int result; 979 980 sc = (struct ppp_softc *) tp->t_sc; 981 if (sc == NULL || tp != (struct tty *) sc->sc_devp) 982 return 0; 983 984 ++tk_nin; 985 ++sc->sc_stats.ppp_ibytes; 986 987 if (c & TTY_FE) { 988 /* framing error or overrun on this char - abort packet */ 989 if (sc->sc_flags & SC_DEBUG) 990 printf("%s: bad char %x\n", sc->sc_if.if_xname, c); 991 goto flush; 992 } 993 994 c &= 0xff; 995 996 /* 997 * Handle software flow control of output. 998 */ 999 result = tty_try_xonxoff(tp, c); 1000 if (result == 0) { 1001 /* Character was recognized and consumed. */ 1002 return 0; 1003 } 1004 /* Character wasn't consumed, continue processing it. */ 1005 1006 s = spltty(); 1007 if (c & 0x80) 1008 sc->sc_flags |= SC_RCV_B7_1; 1009 else 1010 sc->sc_flags |= SC_RCV_B7_0; 1011 if (paritytab[c >> 5] & (1 << (c & 0x1F))) 1012 sc->sc_flags |= SC_RCV_ODDP; 1013 else 1014 sc->sc_flags |= SC_RCV_EVNP; 1015 splx(s); 1016 1017 ppplogchar(sc, c); 1018 1019 if (c == PPP_FLAG) { 1020 ilen = sc->sc_ilen; 1021 sc->sc_ilen = 0; 1022 1023 if ((sc->sc_flags & SC_LOG_RAWIN) && sc->sc_rawin.count > 0) 1024 ppplogchar(sc, -1); 1025 1026 /* 1027 * If SC_ESCAPED is set, then we've seen the packet 1028 * abort sequence "}~". 1029 */ 1030 if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED) 1031 || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) { 1032 s = spltty(); 1033 sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */ 1034 if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){ 1035 if (sc->sc_flags & SC_DEBUG) 1036 printf("%s: bad fcs %x\n", sc->sc_if.if_xname, 1037 sc->sc_fcs); 1038 if_statinc(&sc->sc_if, if_ierrors); 1039 sc->sc_stats.ppp_ierrors++; 1040 } else 1041 sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED); 1042 splx(s); 1043 return 0; 1044 } 1045 1046 if (ilen < PPP_HDRLEN + PPP_FCSLEN) { 1047 if (ilen) { 1048 if (sc->sc_flags & SC_DEBUG) 1049 printf("%s: too short (%d)\n", sc->sc_if.if_xname, ilen); 1050 s = spltty(); 1051 if_statinc(&sc->sc_if, if_ierrors); 1052 sc->sc_stats.ppp_ierrors++; 1053 sc->sc_flags |= SC_PKTLOST; 1054 splx(s); 1055 } 1056 return 0; 1057 } 1058 1059 /* 1060 * Remove FCS trailer. Somewhat painful... 1061 */ 1062 ilen -= 2; 1063 if (--sc->sc_mc->m_len == 0) { 1064 for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next) 1065 ; 1066 sc->sc_mc = m; 1067 } 1068 sc->sc_mc->m_len--; 1069 1070 /* excise this mbuf chain */ 1071 m = sc->sc_m; 1072 sc->sc_m = sc->sc_mc->m_next; 1073 sc->sc_mc->m_next = NULL; 1074 1075 ppppktin(sc, m, sc->sc_flags & SC_PKTLOST); 1076 if (sc->sc_flags & SC_PKTLOST) { 1077 s = spltty(); 1078 sc->sc_flags &= ~SC_PKTLOST; 1079 splx(s); 1080 } 1081 1082 pppgetm(sc); 1083 return 0; 1084 } 1085 1086 if (sc->sc_flags & SC_FLUSH) { 1087 if (sc->sc_flags & SC_LOG_FLUSH) 1088 ppplogchar(sc, c); 1089 return 0; 1090 } 1091 1092 if (c < 0x20 && (sc->sc_rasyncmap & (1 << c))) 1093 return 0; 1094 1095 s = spltty(); 1096 if (sc->sc_flags & SC_ESCAPED) { 1097 sc->sc_flags &= ~SC_ESCAPED; 1098 c ^= PPP_TRANS; 1099 } else if (c == PPP_ESCAPE) { 1100 sc->sc_flags |= SC_ESCAPED; 1101 splx(s); 1102 return 0; 1103 } 1104 splx(s); 1105 1106 /* 1107 * Initialize buffer on first octet received. 1108 * First octet could be address or protocol (when compressing 1109 * address/control). 1110 * Second octet is control. 1111 * Third octet is first or second (when compressing protocol) 1112 * octet of protocol. 1113 * Fourth octet is second octet of protocol. 1114 */ 1115 if (sc->sc_ilen == 0) { 1116 /* reset the first input mbuf */ 1117 if (sc->sc_m == NULL) { 1118 pppgetm(sc); 1119 if (sc->sc_m == NULL) { 1120 if (sc->sc_flags & SC_DEBUG) 1121 printf("%s: no input mbufs!\n", sc->sc_if.if_xname); 1122 goto flush; 1123 } 1124 } 1125 m = sc->sc_m; 1126 m->m_len = 0; 1127 MRESETDATA(m); 1128 sc->sc_mc = m; 1129 sc->sc_mp = mtod(m, char *); 1130 sc->sc_fcs = PPP_INITFCS; 1131 if (c != PPP_ALLSTATIONS) { 1132 if (sc->sc_flags & SC_REJ_COMP_AC) { 1133 if (sc->sc_flags & SC_DEBUG) 1134 printf("%s: garbage received: 0x%x (need 0xFF)\n", 1135 sc->sc_if.if_xname, c); 1136 goto flush; 1137 } 1138 *sc->sc_mp++ = PPP_ALLSTATIONS; 1139 *sc->sc_mp++ = PPP_UI; 1140 sc->sc_ilen += 2; 1141 m->m_len += 2; 1142 } 1143 } 1144 if (sc->sc_ilen == 1 && c != PPP_UI) { 1145 if (sc->sc_flags & SC_DEBUG) 1146 printf("%s: missing UI (0x3), got 0x%x\n", 1147 sc->sc_if.if_xname, c); 1148 goto flush; 1149 } 1150 if (sc->sc_ilen == 2 && (c & 1) == 1) { 1151 /* a compressed protocol */ 1152 *sc->sc_mp++ = 0; 1153 sc->sc_ilen++; 1154 sc->sc_mc->m_len++; 1155 } 1156 if (sc->sc_ilen == 3 && (c & 1) == 0) { 1157 if (sc->sc_flags & SC_DEBUG) 1158 printf("%s: bad protocol %x\n", sc->sc_if.if_xname, 1159 (sc->sc_mp[-1] << 8) + c); 1160 goto flush; 1161 } 1162 1163 /* packet beyond configured mru? */ 1164 if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) { 1165 if (sc->sc_flags & SC_DEBUG) 1166 printf("%s: packet too big\n", sc->sc_if.if_xname); 1167 goto flush; 1168 } 1169 1170 /* is this mbuf full? */ 1171 m = sc->sc_mc; 1172 if (M_TRAILINGSPACE(m) <= 0) { 1173 if (m->m_next == NULL) { 1174 pppgetm(sc); 1175 if (m->m_next == NULL) { 1176 if (sc->sc_flags & SC_DEBUG) 1177 printf("%s: too few input mbufs!\n", sc->sc_if.if_xname); 1178 goto flush; 1179 } 1180 } 1181 sc->sc_mc = m = m->m_next; 1182 m->m_len = 0; 1183 MRESETDATA(m); 1184 sc->sc_mp = mtod(m, char *); 1185 } 1186 1187 ++m->m_len; 1188 *sc->sc_mp++ = c; 1189 sc->sc_fcs = PPP_FCS(sc->sc_fcs, c); 1190 return 0; 1191 1192 flush: 1193 if (!(sc->sc_flags & SC_FLUSH)) { 1194 s = spltty(); 1195 if_statinc(&sc->sc_if, if_ierrors); 1196 sc->sc_stats.ppp_ierrors++; 1197 sc->sc_flags |= SC_FLUSH; 1198 splx(s); 1199 if (sc->sc_flags & SC_LOG_FLUSH) 1200 ppplogchar(sc, c); 1201 } 1202 return 0; 1203 } 1204 1205 #define MAX_DUMP_BYTES 128 1206 1207 static void 1208 ppplogchar(struct ppp_softc *sc, int c) 1209 { 1210 if (c >= 0) { 1211 sc->sc_rawin.buf[sc->sc_rawin_start++] = c; 1212 if (sc->sc_rawin.count < sizeof(sc->sc_rawin.buf)) 1213 sc->sc_rawin.count++; 1214 } 1215 if (sc->sc_rawin_start >= sizeof(sc->sc_rawin.buf) 1216 || (c < 0 && sc->sc_rawin_start > 0)) { 1217 if (sc->sc_flags & (SC_LOG_FLUSH|SC_LOG_RAWIN)) { 1218 printf("%s input: ", sc->sc_if.if_xname); 1219 pppdumpb(sc->sc_rawin.buf, sc->sc_rawin_start); 1220 } 1221 if (c < 0) 1222 sc->sc_rawin.count = 0; 1223 sc->sc_rawin_start = 0; 1224 } 1225 } 1226 1227 static void 1228 pppdumpb(u_char *b, int l) 1229 { 1230 char bf[3*MAX_DUMP_BYTES+4]; 1231 char *bp = bf; 1232 1233 while (l--) { 1234 if (bp >= bf + sizeof(bf) - 3) { 1235 *bp++ = '>'; 1236 break; 1237 } 1238 *bp++ = hexdigits[*b >> 4]; /* convert byte to ascii hex */ 1239 *bp++ = hexdigits[*b++ & 0xf]; 1240 *bp++ = ' '; 1241 } 1242 1243 *bp = 0; 1244 printf("%s\n", bf); 1245 } 1246 1247 static void 1248 pppdumpframe(struct ppp_softc *sc, struct mbuf *m, int xmit) 1249 { 1250 int i,lcount,copycount,count; 1251 char lbuf[16]; 1252 char *data; 1253 1254 if (m == NULL) 1255 return; 1256 1257 for(count=m->m_len,data=mtod(m,char*);m != NULL;) { 1258 /* build a line of output */ 1259 for(lcount=0;lcount < sizeof(lbuf);lcount += copycount) { 1260 if (!count) { 1261 m = m->m_next; 1262 if (m == NULL) 1263 break; 1264 count = m->m_len; 1265 data = mtod(m,char*); 1266 } 1267 copycount = (count > sizeof(lbuf)-lcount) ? 1268 sizeof(lbuf)-lcount : count; 1269 bcopy(data,&lbuf[lcount],copycount); 1270 data += copycount; 1271 count -= copycount; 1272 } 1273 1274 /* output line (hex 1st, then ascii) */ 1275 printf("%s %s:", sc->sc_if.if_xname, 1276 xmit ? "output" : "input "); 1277 for(i=0;i<lcount;i++) 1278 printf("%02x ",(u_char)lbuf[i]); 1279 for(;i<sizeof(lbuf);i++) 1280 printf(" "); 1281 for(i=0;i<lcount;i++) 1282 printf("%c",(lbuf[i] >= 040 && 1283 lbuf[i] <= 0176) ? lbuf[i] : '.'); 1284 printf("\n"); 1285 } 1286 } 1287