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