1 /* 2 * Copyright (c) 1987, 1989 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)if_sl.c 7.21 (Berkeley) 06/28/90 8 */ 9 10 /* 11 * Serial Line interface 12 * 13 * Rick Adams 14 * Center for Seismic Studies 15 * 1300 N 17th Street, Suite 1450 16 * Arlington, Virginia 22209 17 * (703)276-7900 18 * rick@seismo.ARPA 19 * seismo!rick 20 * 21 * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). 22 * N.B.: this belongs in netinet, not net, the way it stands now. 23 * Should have a link-layer type designation, but wouldn't be 24 * backwards-compatible. 25 * 26 * Converted to 4.3BSD Beta by Chris Torek. 27 * Other changes made at Berkeley, based in part on code by Kirk Smith. 28 * W. Jolitz added slip abort. 29 * 30 * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov). 31 * Added priority queuing for "interactive" traffic; hooks for TCP 32 * header compression; ICMP filtering (at 2400 baud, some cretin 33 * pinging you can use up all your bandwidth). Made low clist behavior 34 * more robust and slightly less likely to hang serial line. 35 * Sped up a bunch of things. 36 * 37 * Note that splimp() is used throughout to block both (tty) input 38 * interrupts and network activity; thus, splimp must be >= spltty. 39 */ 40 41 /* $Header: if_sl.c,v 1.7 89/05/31 02:24:52 van Exp $ */ 42 /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ 43 44 #include "sl.h" 45 #if NSL > 0 46 47 #include "param.h" 48 #include "user.h" 49 #include "mbuf.h" 50 #include "buf.h" 51 #include "dkstat.h" 52 #include "socket.h" 53 #include "ioctl.h" 54 #include "file.h" 55 #include "tty.h" 56 #include "kernel.h" 57 #include "conf.h" 58 #include "errno.h" 59 60 #include "if.h" 61 #include "if_types.h" 62 #include "netisr.h" 63 #include "route.h" 64 #if INET 65 #include "../netinet/in.h" 66 #include "../netinet/in_systm.h" 67 #include "../netinet/in_var.h" 68 #include "../netinet/ip.h" 69 #endif 70 71 #include "machine/mtpr.h" 72 73 #include "slcompress.h" 74 #include "if_slvar.h" 75 76 /* 77 * SLMAX is a hard limit on input packet size. To simplify the code 78 * and improve performance, we require that packets fit in an mbuf 79 * cluster, and if we get a compressed packet, there's enough extra 80 * room to expand the header into a max length tcp/ip header (128 81 * bytes). So, SLMAX can be at most 82 * MCLBYTES - 128 83 * 84 * SLMTU is a hard limit on output packet size. To insure good 85 * interactive response, SLMTU wants to be the smallest size that 86 * amortizes the header cost. (Remember that even with 87 * type-of-service queuing, we have to wait for any in-progress 88 * packet to finish. I.e., we wait, on the average, 1/2 * mtu / 89 * cps, where cps is the line speed in characters per second. 90 * E.g., 533ms wait for a 1024 byte MTU on a 9600 baud line. The 91 * average compressed header size is 6-8 bytes so any MTU > 90 92 * bytes will give us 90% of the line bandwidth. A 100ms wait is 93 * tolerable (500ms is not), so want an MTU around 296. (Since TCP 94 * will send 256 byte segments (to allow for 40 byte headers), the 95 * typical packet size on the wire will be around 260 bytes). In 96 * 4.3tahoe+ systems, we can set an MTU in a route so we do that & 97 * leave the interface MTU relatively high (so we don't IP fragment 98 * when acting as a gateway to someone using a stupid MTU). 99 * 100 * Similar considerations apply to SLIP_HIWAT: It's the amount of 101 * data that will be queued 'downstream' of us (i.e., in clists 102 * waiting to be picked up by the tty output interrupt). If we 103 * queue a lot of data downstream, it's immune to our t.o.s. queuing. 104 * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed 105 * telnet/ftp will see a 1 sec wait, independent of the mtu (the 106 * wait is dependent on the ftp window size but that's typically 107 * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize 108 * the cost (in idle time on the wire) of the tty driver running 109 * off the end of its clists & having to call back slstart for a 110 * new packet. For a tty interface with any buffering at all, this 111 * cost will be zero. Even with a totally brain dead interface (like 112 * the one on a typical workstation), the cost will be <= 1 character 113 * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose 114 * at most 1% while maintaining good interactive response. 115 */ 116 #define BUFOFFSET 128 117 #define SLMAX (MCLBYTES - BUFOFFSET) 118 #define SLBUFSIZE (SLMAX + BUFOFFSET) 119 #define SLMTU 296 120 #define SLIP_HIWAT roundup(50,CBSIZE) 121 #define CLISTRESERVE 1024 /* Can't let clists get too low */ 122 123 /* 124 * SLIP ABORT ESCAPE MECHANISM: 125 * (inspired by HAYES modem escape arrangement) 126 * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape } 127 * signals a "soft" exit from slip mode by usermode process 128 */ 129 130 #define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/ 131 #define ABT_WAIT 1 /* in seconds - idle before an escape & after */ 132 #define ABT_RECYCLE (5*2+2) /* in seconds - time window processing abort */ 133 134 #define ABT_SOFT 3 /* count of escapes */ 135 136 /* 137 * The following disgusting hack gets around the problem that IP TOS 138 * can't be set yet. We want to put "interactive" traffic on a high 139 * priority queue. To decide if traffic is interactive, we check that 140 * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. 141 */ 142 static u_short interactive_ports[8] = { 143 0, 513, 0, 0, 144 0, 21, 0, 23, 145 }; 146 #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) 147 148 struct sl_softc sl_softc[NSL]; 149 150 #define FRAME_END 0xc0 /* Frame End */ 151 #define FRAME_ESCAPE 0xdb /* Frame Esc */ 152 #define TRANS_FRAME_END 0xdc /* transposed frame end */ 153 #define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */ 154 155 #define t_sc T_LINEP 156 157 int sloutput(), slioctl(), ttrstrt(); 158 extern struct timeval time; 159 160 /* 161 * Called from boot code to establish sl interfaces. 162 */ 163 slattach() 164 { 165 register struct sl_softc *sc; 166 register int i = 0; 167 168 for (sc = sl_softc; i < NSL; sc++) { 169 sc->sc_if.if_name = "sl"; 170 sc->sc_if.if_unit = i++; 171 sc->sc_if.if_mtu = SLMTU; 172 sc->sc_if.if_flags = IFF_POINTOPOINT; 173 sc->sc_if.if_type = IFT_SLIP; 174 sc->sc_if.if_ioctl = slioctl; 175 sc->sc_if.if_output = sloutput; 176 sc->sc_if.if_snd.ifq_maxlen = 50; 177 sc->sc_fastq.ifq_maxlen = 32; 178 if_attach(&sc->sc_if); 179 } 180 } 181 182 static int 183 slinit(sc) 184 register struct sl_softc *sc; 185 { 186 register caddr_t p; 187 188 if (sc->sc_ep == (u_char *) 0) { 189 MCLALLOC(p, M_WAIT); 190 if (p) 191 sc->sc_ep = (u_char *)p + SLBUFSIZE; 192 else { 193 printf("sl%d: can't allocate buffer\n", sc - sl_softc); 194 sc->sc_if.if_flags &= ~IFF_UP; 195 return (0); 196 } 197 } 198 sc->sc_buf = sc->sc_ep - SLMAX; 199 sc->sc_mp = sc->sc_buf; 200 sl_compress_init(&sc->sc_comp); 201 return (1); 202 } 203 204 /* 205 * Line specific open routine. 206 * Attach the given tty to the first available sl unit. 207 */ 208 /* ARGSUSED */ 209 slopen(dev, tp) 210 dev_t dev; 211 register struct tty *tp; 212 { 213 register struct sl_softc *sc; 214 register int nsl; 215 int error; 216 217 if (error = suser(u.u_cred, &u.u_acflag)) 218 return (error); 219 220 if (tp->t_line == SLIPDISC) 221 return (0); 222 223 for (nsl = NSL, sc = sl_softc; --nsl >= 0; sc++) 224 if (sc->sc_ttyp == NULL) { 225 if (slinit(sc) == 0) 226 return (ENOBUFS); 227 tp->t_sc = (caddr_t)sc; 228 sc->sc_ttyp = tp; 229 sc->sc_if.if_baudrate = tp->t_ospeed; 230 ttyflush(tp, FREAD | FWRITE); 231 return (0); 232 } 233 return (ENXIO); 234 } 235 236 /* 237 * Line specific close routine. 238 * Detach the tty from the sl unit. 239 * Mimics part of ttyclose(). 240 */ 241 slclose(tp) 242 struct tty *tp; 243 { 244 register struct sl_softc *sc; 245 int s; 246 247 ttywflush(tp); 248 s = splimp(); /* actually, max(spltty, splnet) */ 249 tp->t_line = 0; 250 sc = (struct sl_softc *)tp->t_sc; 251 if (sc != NULL) { 252 if_down(&sc->sc_if); 253 sc->sc_ttyp = NULL; 254 tp->t_sc = NULL; 255 MCLFREE((caddr_t)(sc->sc_ep - SLBUFSIZE)); 256 sc->sc_ep = 0; 257 sc->sc_mp = 0; 258 sc->sc_buf = 0; 259 } 260 splx(s); 261 } 262 263 /* 264 * Line specific (tty) ioctl routine. 265 * Provide a way to get the sl unit number. 266 */ 267 /* ARGSUSED */ 268 sltioctl(tp, cmd, data, flag) 269 struct tty *tp; 270 caddr_t data; 271 { 272 struct sl_softc *sc = (struct sl_softc *)tp->t_sc; 273 int s; 274 275 switch (cmd) { 276 case TIOCGETD: /* XXX */ 277 case SLIOGUNIT: 278 *(int *)data = sc->sc_if.if_unit; 279 break; 280 281 case SLIOCGFLAGS: 282 *(int *)data = sc->sc_flags; 283 break; 284 285 case SLIOCSFLAGS: 286 #define SC_MASK 0xffff 287 s = splimp(); 288 sc->sc_flags = 289 (sc->sc_flags &~ SC_MASK) | ((*(int *)data) & SC_MASK); 290 splx(s); 291 break; 292 293 default: 294 return (-1); 295 } 296 return (0); 297 } 298 299 /* 300 * Queue a packet. Start transmission if not active. 301 */ 302 sloutput(ifp, m, dst) 303 struct ifnet *ifp; 304 register struct mbuf *m; 305 struct sockaddr *dst; 306 { 307 register struct sl_softc *sc = &sl_softc[ifp->if_unit]; 308 register struct ip *ip; 309 register struct ifqueue *ifq; 310 int s; 311 312 /* 313 * `Cannot happen' (see slioctl). Someday we will extend 314 * the line protocol to support other address families. 315 */ 316 if (dst->sa_family != AF_INET) { 317 printf("sl%d: af%d not supported\n", sc->sc_if.if_unit, 318 dst->sa_family); 319 m_freem(m); 320 return (EAFNOSUPPORT); 321 } 322 323 if (sc->sc_ttyp == NULL) { 324 m_freem(m); 325 return (ENETDOWN); /* sort of */ 326 } 327 if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { 328 m_freem(m); 329 return (EHOSTUNREACH); 330 } 331 ifq = &sc->sc_if.if_snd; 332 if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) { 333 register int p = ((int *)ip)[ip->ip_hl]; 334 335 if (INTERACTIVE(p & 0xffff) || INTERACTIVE(p >> 16)) { 336 ifq = &sc->sc_fastq; 337 p = 1; 338 } else 339 p = 0; 340 341 if (sc->sc_flags & SC_COMPRESS) { 342 /* 343 * The last parameter turns off connection id 344 * compression for background traffic: Since 345 * fastq traffic can jump ahead of the background 346 * traffic, we don't know what order packets will 347 * go on the line. 348 */ 349 p = sl_compress_tcp(m, ip, &sc->sc_comp, p); 350 *mtod(m, u_char *) |= p; 351 } 352 } else if (sc->sc_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { 353 m_freem(m); 354 return (0); 355 } 356 s = splimp(); 357 if (IF_QFULL(ifq)) { 358 IF_DROP(ifq); 359 m_freem(m); 360 splx(s); 361 sc->sc_if.if_oerrors++; 362 return (ENOBUFS); 363 } 364 IF_ENQUEUE(ifq, m); 365 sc->sc_if.if_lastchange = time; 366 if (sc->sc_ttyp->t_outq.c_cc == 0) 367 slstart(sc->sc_ttyp); 368 splx(s); 369 return (0); 370 } 371 372 /* 373 * Start output on interface. Get another datagram 374 * to send from the interface queue and map it to 375 * the interface before starting output. 376 */ 377 slstart(tp) 378 register struct tty *tp; 379 { 380 register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; 381 register struct mbuf *m; 382 register u_char *cp; 383 int s; 384 struct mbuf *m2; 385 extern int cfreecount; 386 387 for (;;) { 388 /* 389 * If there is more in the output queue, just send it now. 390 * We are being called in lieu of ttstart and must do what 391 * it would. 392 */ 393 if (tp->t_outq.c_cc != 0) { 394 (*tp->t_oproc)(tp); 395 if (tp->t_outq.c_cc > SLIP_HIWAT) 396 return; 397 } 398 /* 399 * This happens briefly when the line shuts down. 400 */ 401 if (sc == NULL) 402 return; 403 404 /* 405 * Get a packet and send it to the interface. 406 */ 407 s = splimp(); 408 IF_DEQUEUE(&sc->sc_fastq, m); 409 if (m == NULL) 410 IF_DEQUEUE(&sc->sc_if.if_snd, m); 411 splx(s); 412 if (m == NULL) 413 return; 414 sc->sc_if.if_lastchange = time; 415 /* 416 * If system is getting low on clists, just flush our 417 * output queue (if the stuff was important, it'll get 418 * retransmitted). 419 */ 420 if (cfreecount < CLISTRESERVE + SLMTU) { 421 m_freem(m); 422 sc->sc_if.if_collisions++; 423 continue; 424 } 425 426 /* 427 * The extra FRAME_END will start up a new packet, and thus 428 * will flush any accumulated garbage. We do this whenever 429 * the line may have been idle for some time. 430 */ 431 if (tp->t_outq.c_cc == 0) { 432 ++sc->sc_bytessent; 433 (void) putc(FRAME_END, &tp->t_outq); 434 } 435 436 while (m) { 437 register u_char *ep; 438 439 cp = mtod(m, u_char *); ep = cp + m->m_len; 440 while (cp < ep) { 441 /* 442 * Find out how many bytes in the string we can 443 * handle without doing something special. 444 */ 445 register u_char *bp = cp; 446 447 while (cp < ep) { 448 switch (*cp++) { 449 case FRAME_ESCAPE: 450 case FRAME_END: 451 --cp; 452 goto out; 453 } 454 } 455 out: 456 if (cp > bp) { 457 /* 458 * Put n characters at once 459 * into the tty output queue. 460 */ 461 if (b_to_q((char *)bp, cp - bp, &tp->t_outq)) 462 break; 463 sc->sc_bytessent += cp - bp; 464 } 465 /* 466 * If there are characters left in the mbuf, 467 * the first one must be special.. 468 * Put it out in a different form. 469 */ 470 if (cp < ep) { 471 if (putc(FRAME_ESCAPE, &tp->t_outq)) 472 break; 473 if (putc(*cp++ == FRAME_ESCAPE ? 474 TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 475 &tp->t_outq)) { 476 (void) unputc(&tp->t_outq); 477 break; 478 } 479 sc->sc_bytessent += 2; 480 } 481 } 482 MFREE(m, m2); 483 m = m2; 484 } 485 486 if (putc(FRAME_END, &tp->t_outq)) { 487 /* 488 * Not enough room. Remove a char to make room 489 * and end the packet normally. 490 * If you get many collisions (more than one or two 491 * a day) you probably do not have enough clists 492 * and you should increase "nclist" in param.c. 493 */ 494 (void) unputc(&tp->t_outq); 495 (void) putc(FRAME_END, &tp->t_outq); 496 sc->sc_if.if_collisions++; 497 } else { 498 ++sc->sc_bytessent; 499 sc->sc_if.if_opackets++; 500 } 501 sc->sc_if.if_obytes = sc->sc_bytessent; 502 } 503 } 504 505 /* 506 * Copy data buffer to mbuf chain; add ifnet pointer. 507 */ 508 static struct mbuf * 509 sl_btom(sc, len) 510 register struct sl_softc *sc; 511 register int len; 512 { 513 register struct mbuf *m; 514 515 MGETHDR(m, M_DONTWAIT, MT_DATA); 516 if (m == NULL) 517 return (NULL); 518 519 /* 520 * If we have more than MHLEN bytes, it's cheaper to 521 * queue the cluster we just filled & allocate a new one 522 * for the input buffer. Otherwise, fill the mbuf we 523 * allocated above. Note that code in the input routine 524 * guarantees that packet will fit in a cluster. 525 */ 526 if (len >= MHLEN) { 527 MCLGET(m, M_DONTWAIT); 528 if ((m->m_flags & M_EXT) == 0) { 529 /* 530 * we couldn't get a cluster - if memory's this 531 * low, it's time to start dropping packets. 532 */ 533 (void) m_free(m); 534 return (NULL); 535 } 536 sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE; 537 m->m_data = (caddr_t)sc->sc_buf; 538 m->m_ext.ext_buf = (caddr_t)((int)sc->sc_buf &~ MCLOFSET); 539 } else 540 bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len); 541 542 m->m_len = len; 543 m->m_pkthdr.len = len; 544 m->m_pkthdr.rcvif = &sc->sc_if; 545 return (m); 546 } 547 548 /* 549 * tty interface receiver interrupt. 550 */ 551 slinput(c, tp) 552 register int c; 553 register struct tty *tp; 554 { 555 register struct sl_softc *sc; 556 register struct mbuf *m; 557 register int len; 558 int s; 559 560 tk_nin++; 561 sc = (struct sl_softc *)tp->t_sc; 562 if (sc == NULL) 563 return; 564 if (!(tp->t_state&TS_CARR_ON)) /* XXX */ 565 return; 566 567 ++sc->sc_bytesrcvd; 568 ++sc->sc_if.if_ibytes; 569 c &= 0xff; /* XXX */ 570 571 #ifdef ABT_ESC 572 if (sc->sc_flags & SC_ABORT) { 573 /* if we see an abort after "idle" time, count it */ 574 if (c == ABT_ESC && time.tv_sec >= sc->sc_lasttime + ABT_WAIT) { 575 sc->sc_abortcount++; 576 /* record when the first abort escape arrived */ 577 if (sc->sc_abortcount == 1) 578 sc->sc_starttime = time.tv_sec; 579 } 580 /* 581 * if we have an abort, see that we have not run out of time, 582 * or that we have an "idle" time after the complete escape 583 * sequence 584 */ 585 if (sc->sc_abortcount) { 586 if (time.tv_sec >= sc->sc_starttime + ABT_RECYCLE) 587 sc->sc_abortcount = 0; 588 if (sc->sc_abortcount >= ABT_SOFT && 589 time.tv_sec >= sc->sc_lasttime + ABT_WAIT) { 590 slclose(tp); 591 return; 592 } 593 } 594 sc->sc_lasttime = time.tv_sec; 595 } 596 #endif 597 598 switch (c) { 599 600 case TRANS_FRAME_ESCAPE: 601 if (sc->sc_escape) 602 c = FRAME_ESCAPE; 603 break; 604 605 case TRANS_FRAME_END: 606 if (sc->sc_escape) 607 c = FRAME_END; 608 break; 609 610 case FRAME_ESCAPE: 611 sc->sc_escape = 1; 612 return; 613 614 case FRAME_END: 615 len = sc->sc_mp - sc->sc_buf; 616 if (len < 3) 617 /* less than min length packet - ignore */ 618 goto newpack; 619 620 if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) { 621 if (c & 0x80) 622 c = TYPE_COMPRESSED_TCP; 623 else if (c == TYPE_UNCOMPRESSED_TCP) 624 *sc->sc_buf &= 0x4f; /* XXX */ 625 /* 626 * We've got something that's not an IP packet. 627 * If compression is enabled, try to decompress it. 628 * Otherwise, if `auto-enable' compression is on and 629 * it's a reasonable packet, decompress it and then 630 * enable compression. Otherwise, drop it. 631 */ 632 if (sc->sc_flags & SC_COMPRESS) { 633 len = sl_uncompress_tcp(&sc->sc_buf, len, 634 (u_int)c, &sc->sc_comp); 635 if (len <= 0) 636 goto error; 637 } else if ((sc->sc_flags & SC_AUTOCOMP) && 638 c == TYPE_UNCOMPRESSED_TCP && len >= 40) { 639 len = sl_uncompress_tcp(&sc->sc_buf, len, 640 (u_int)c, &sc->sc_comp); 641 if (len <= 0) 642 goto error; 643 sc->sc_flags |= SC_COMPRESS; 644 } else 645 goto error; 646 } 647 m = sl_btom(sc, len); 648 if (m == NULL) 649 goto error; 650 651 sc->sc_if.if_ipackets++; 652 sc->sc_if.if_lastchange = time; 653 s = splimp(); 654 if (IF_QFULL(&ipintrq)) { 655 IF_DROP(&ipintrq); 656 sc->sc_if.if_ierrors++; 657 sc->sc_if.if_iqdrops++; 658 m_freem(m); 659 } else { 660 IF_ENQUEUE(&ipintrq, m); 661 schednetisr(NETISR_IP); 662 } 663 splx(s); 664 goto newpack; 665 } 666 if (sc->sc_mp < sc->sc_ep) { 667 *sc->sc_mp++ = c; 668 sc->sc_escape = 0; 669 return; 670 } 671 error: 672 sc->sc_if.if_ierrors++; 673 newpack: 674 sc->sc_mp = sc->sc_buf = sc->sc_ep - SLMAX; 675 sc->sc_escape = 0; 676 } 677 678 /* 679 * Process an ioctl request. 680 */ 681 slioctl(ifp, cmd, data) 682 register struct ifnet *ifp; 683 int cmd; 684 caddr_t data; 685 { 686 register struct ifaddr *ifa = (struct ifaddr *)data; 687 int s = splimp(), error = 0; 688 689 switch (cmd) { 690 691 case SIOCSIFADDR: 692 if (ifa->ifa_addr->sa_family == AF_INET) 693 ifp->if_flags |= IFF_UP; 694 else 695 error = EAFNOSUPPORT; 696 break; 697 698 case SIOCSIFDSTADDR: 699 if (ifa->ifa_addr->sa_family != AF_INET) 700 error = EAFNOSUPPORT; 701 break; 702 703 default: 704 error = EINVAL; 705 } 706 splx(s); 707 return (error); 708 } 709 #endif 710