| 1 | /* |
| 2 | * Copyright (c) 1987, 1989, 1992, 1993 |
| 3 | * The Regents of the University of California. All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. All advertising materials mentioning features or use of this software |
| 14 | * must display the following acknowledgement: |
| 15 | * This product includes software developed by the University of |
| 16 | * California, Berkeley and its contributors. |
| 17 | * 4. Neither the name of the University nor the names of its contributors |
| 18 | * may be used to endorse or promote products derived from this software |
| 19 | * without specific prior written permission. |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 31 | * SUCH DAMAGE. |
| 32 | * |
| 33 | * @(#)if_sl.c 8.6 (Berkeley) 2/1/94 |
| 34 | * $FreeBSD: src/sys/net/if_sl.c,v 1.84.2.2 2002/02/13 00:43:10 dillon Exp $ |
| 35 | * $DragonFly: src/sys/net/sl/if_sl.c,v 1.4 2003/06/25 03:56:02 dillon Exp $ |
| 36 | */ |
| 37 | |
| 38 | /* |
| 39 | * Serial Line interface |
| 40 | * |
| 41 | * Rick Adams |
| 42 | * Center for Seismic Studies |
| 43 | * 1300 N 17th Street, Suite 1450 |
| 44 | * Arlington, Virginia 22209 |
| 45 | * (703)276-7900 |
| 46 | * rick@seismo.ARPA |
| 47 | * seismo!rick |
| 48 | * |
| 49 | * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). |
| 50 | * N.B.: this belongs in netinet, not net, the way it stands now. |
| 51 | * Should have a link-layer type designation, but wouldn't be |
| 52 | * backwards-compatible. |
| 53 | * |
| 54 | * Converted to 4.3BSD Beta by Chris Torek. |
| 55 | * Other changes made at Berkeley, based in part on code by Kirk Smith. |
| 56 | * W. Jolitz added slip abort. |
| 57 | * |
| 58 | * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov). |
| 59 | * Added priority queuing for "interactive" traffic; hooks for TCP |
| 60 | * header compression; ICMP filtering (at 2400 baud, some cretin |
| 61 | * pinging you can use up all your bandwidth). Made low clist behavior |
| 62 | * more robust and slightly less likely to hang serial line. |
| 63 | * Sped up a bunch of things. |
| 64 | * |
| 65 | * Note that splimp() is used throughout to block both (tty) input |
| 66 | * interrupts and network activity; thus, splimp must be >= spltty. |
| 67 | */ |
| 68 | |
| 69 | #include "sl.h" |
| 70 | |
| 71 | #include "opt_inet.h" |
| 72 | #if !defined(KLD_MODULE) |
| 73 | #include "opt_slip.h" |
| 74 | #endif |
| 75 | #include <sys/param.h> |
| 76 | #include <sys/systm.h> |
| 77 | #include <sys/proc.h> |
| 78 | #include <sys/malloc.h> |
| 79 | #include <sys/mbuf.h> |
| 80 | #include <sys/dkstat.h> |
| 81 | #include <sys/socket.h> |
| 82 | #include <sys/sockio.h> |
| 83 | #include <sys/fcntl.h> |
| 84 | #include <sys/signalvar.h> |
| 85 | #include <sys/tty.h> |
| 86 | #include <sys/clist.h> |
| 87 | #include <sys/kernel.h> |
| 88 | #include <sys/conf.h> |
| 89 | |
| 90 | #include <net/if.h> |
| 91 | #include <net/if_types.h> |
| 92 | #include <net/netisr.h> |
| 93 | |
| 94 | #if INET |
| 95 | #include <netinet/in.h> |
| 96 | #include <netinet/in_systm.h> |
| 97 | #include <netinet/in_var.h> |
| 98 | #include <netinet/ip.h> |
| 99 | #else |
| 100 | #error "Huh? Slip without inet?" |
| 101 | #endif |
| 102 | |
| 103 | #include <net/slcompress.h> |
| 104 | #include <net/if_slvar.h> |
| 105 | #include <net/slip.h> |
| 106 | |
| 107 | #include <net/bpf.h> |
| 108 | |
| 109 | #ifdef __i386__ |
| 110 | #include <i386/isa/intr_machdep.h> |
| 111 | #endif |
| 112 | |
| 113 | static void slattach __P((void *)); |
| 114 | PSEUDO_SET(slattach, if_sl); |
| 115 | |
| 116 | /* |
| 117 | * SLRMAX is a hard limit on input packet size. To simplify the code |
| 118 | * and improve performance, we require that packets fit in an mbuf |
| 119 | * cluster, and if we get a compressed packet, there's enough extra |
| 120 | * room to expand the header into a max length tcp/ip header (128 |
| 121 | * bytes). So, SLRMAX can be at most |
| 122 | * MCLBYTES - 128 |
| 123 | * |
| 124 | * SLMTU is the default transmit MTU. The transmit MTU should be kept |
| 125 | * small enough so that interactive use doesn't suffer, but large |
| 126 | * enough to provide good performance. 552 is a good choice for SLMTU |
| 127 | * because it is high enough to not fragment TCP packets being routed |
| 128 | * through this host. Packet fragmentation is bad with SLIP because |
| 129 | * fragment headers aren't compressed. The previous assumptions about |
| 130 | * the best MTU value don't really hold when using modern modems with |
| 131 | * BTLZ data compression because the modem buffers play a much larger |
| 132 | * role in interactive performance than the MTU. The MTU can be changed |
| 133 | * at any time to suit the specific environment with ifconfig(8), and |
| 134 | * its maximum value is defined as SLTMAX. SLTMAX must not be so large |
| 135 | * that it would overflow the stack if BPF is configured (XXX; if_ppp.c |
| 136 | * handles this better). |
| 137 | * |
| 138 | * SLIP_HIWAT is the amount of data that will be queued 'downstream' |
| 139 | * of us (i.e., in clists waiting to be picked up by the tty output |
| 140 | * interrupt). If we queue a lot of data downstream, it's immune to |
| 141 | * our t.o.s. queuing. |
| 142 | * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed |
| 143 | * telnet/ftp will see a 1 sec wait, independent of the mtu (the |
| 144 | * wait is dependent on the ftp window size but that's typically |
| 145 | * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize |
| 146 | * the cost (in idle time on the wire) of the tty driver running |
| 147 | * off the end of its clists & having to call back slstart for a |
| 148 | * new packet. For a tty interface with any buffering at all, this |
| 149 | * cost will be zero. Even with a totally brain dead interface (like |
| 150 | * the one on a typical workstation), the cost will be <= 1 character |
| 151 | * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose |
| 152 | * at most 1% while maintaining good interactive response. |
| 153 | */ |
| 154 | #define BUFOFFSET (128+sizeof(struct ifnet **)+SLIP_HDRLEN) |
| 155 | #define SLRMAX (MCLBYTES - BUFOFFSET) |
| 156 | #define SLBUFSIZE (SLRMAX + BUFOFFSET) |
| 157 | #ifndef SLMTU |
| 158 | #define SLMTU 552 /* default MTU */ |
| 159 | #endif |
| 160 | #define SLTMAX 1500 /* maximum MTU */ |
| 161 | #define SLIP_HIWAT roundup(50,CBSIZE) |
| 162 | #define CLISTRESERVE 1024 /* Can't let clists get too low */ |
| 163 | |
| 164 | /* |
| 165 | * SLIP ABORT ESCAPE MECHANISM: |
| 166 | * (inspired by HAYES modem escape arrangement) |
| 167 | * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape } |
| 168 | * within window time signals a "soft" exit from slip mode by remote end |
| 169 | * if the IFF_DEBUG flag is on. |
| 170 | */ |
| 171 | #define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/ |
| 172 | #define ABT_IDLE 1 /* in seconds - idle before an escape */ |
| 173 | #define ABT_COUNT 3 /* count of escapes for abort */ |
| 174 | #define ABT_WINDOW (ABT_COUNT*2+2) /* in seconds - time to count */ |
| 175 | |
| 176 | static struct sl_softc sl_softc[NSL]; |
| 177 | |
| 178 | #define FRAME_END 0xc0 /* Frame End */ |
| 179 | #define FRAME_ESCAPE 0xdb /* Frame Esc */ |
| 180 | #define TRANS_FRAME_END 0xdc /* transposed frame end */ |
| 181 | #define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */ |
| 182 | |
| 183 | static int slinit __P((struct sl_softc *)); |
| 184 | static struct mbuf *sl_btom __P((struct sl_softc *, int)); |
| 185 | static timeout_t sl_keepalive; |
| 186 | static timeout_t sl_outfill; |
| 187 | static int slclose __P((struct tty *,int)); |
| 188 | static int slinput __P((int, struct tty *)); |
| 189 | static int slioctl __P((struct ifnet *, u_long, caddr_t)); |
| 190 | static int sltioctl __P((struct tty *, u_long, caddr_t, int, struct thread *)); |
| 191 | static int slopen __P((dev_t, struct tty *)); |
| 192 | static int sloutput __P((struct ifnet *, |
| 193 | struct mbuf *, struct sockaddr *, struct rtentry *)); |
| 194 | static int slstart __P((struct tty *)); |
| 195 | |
| 196 | static struct linesw slipdisc = { |
| 197 | slopen, slclose, l_noread, l_nowrite, |
| 198 | sltioctl, slinput, slstart, ttymodem, |
| 199 | FRAME_END |
| 200 | }; |
| 201 | |
| 202 | /* |
| 203 | * Called from boot code to establish sl interfaces. |
| 204 | */ |
| 205 | static void |
| 206 | slattach(dummy) |
| 207 | void *dummy; |
| 208 | { |
| 209 | register struct sl_softc *sc; |
| 210 | register int i = 0; |
| 211 | |
| 212 | linesw[SLIPDISC] = slipdisc; |
| 213 | |
| 214 | for (sc = sl_softc; i < NSL; sc++) { |
| 215 | sc->sc_if.if_name = "sl"; |
| 216 | sc->sc_if.if_unit = i++; |
| 217 | sc->sc_if.if_mtu = SLMTU; |
| 218 | sc->sc_if.if_flags = |
| 219 | #ifdef SLIP_IFF_OPTS |
| 220 | SLIP_IFF_OPTS; |
| 221 | #else |
| 222 | IFF_POINTOPOINT | SC_AUTOCOMP | IFF_MULTICAST; |
| 223 | #endif |
| 224 | sc->sc_if.if_type = IFT_SLIP; |
| 225 | sc->sc_if.if_ioctl = slioctl; |
| 226 | sc->sc_if.if_output = sloutput; |
| 227 | sc->sc_if.if_snd.ifq_maxlen = 50; |
| 228 | sc->sc_fastq.ifq_maxlen = 32; |
| 229 | sc->sc_if.if_linkmib = sc; |
| 230 | sc->sc_if.if_linkmiblen = sizeof *sc; |
| 231 | if_attach(&sc->sc_if); |
| 232 | bpfattach(&sc->sc_if, DLT_SLIP, SLIP_HDRLEN); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | static int |
| 237 | slinit(sc) |
| 238 | register struct sl_softc *sc; |
| 239 | { |
| 240 | register caddr_t p; |
| 241 | |
| 242 | #ifdef __i386__ |
| 243 | int s; |
| 244 | |
| 245 | s = splhigh(); |
| 246 | tty_imask |= net_imask; |
| 247 | net_imask = tty_imask; |
| 248 | update_intr_masks(); |
| 249 | splx(s); |
| 250 | if (bootverbose) |
| 251 | printf("new imasks: bio %x, tty %x, net %x\n", |
| 252 | bio_imask, tty_imask, net_imask); |
| 253 | #endif |
| 254 | if (sc->sc_ep == (u_char *) 0) { |
| 255 | MCLALLOC(p, M_WAIT); |
| 256 | if (p) |
| 257 | sc->sc_ep = (u_char *)p + SLBUFSIZE; |
| 258 | else { |
| 259 | printf("sl%ld: can't allocate buffer\n", |
| 260 | (long)(sc - sl_softc)); |
| 261 | return (0); |
| 262 | } |
| 263 | } |
| 264 | sc->sc_buf = sc->sc_ep - SLRMAX; |
| 265 | sc->sc_mp = sc->sc_buf; |
| 266 | sl_compress_init(&sc->sc_comp, -1); |
| 267 | return (1); |
| 268 | } |
| 269 | |
| 270 | /* |
| 271 | * Line specific open routine. |
| 272 | * Attach the given tty to the first available sl unit. |
| 273 | */ |
| 274 | /* ARGSUSED */ |
| 275 | static int |
| 276 | slopen(dev_t dev, struct tty *tp) |
| 277 | { |
| 278 | struct sl_softc *sc; |
| 279 | int nsl; |
| 280 | int s, error; |
| 281 | struct thread *td = curthread; /* XXX */ |
| 282 | |
| 283 | error = suser(td); |
| 284 | if (error) |
| 285 | return (error); |
| 286 | |
| 287 | if (tp->t_line == SLIPDISC) |
| 288 | return (0); |
| 289 | |
| 290 | for (nsl = NSL, sc = sl_softc; --nsl >= 0; sc++) |
| 291 | if (sc->sc_ttyp == NULL && !(sc->sc_flags & SC_STATIC)) { |
| 292 | if (slinit(sc) == 0) |
| 293 | return (ENOBUFS); |
| 294 | tp->t_sc = (caddr_t)sc; |
| 295 | sc->sc_ttyp = tp; |
| 296 | sc->sc_if.if_baudrate = tp->t_ospeed; |
| 297 | ttyflush(tp, FREAD | FWRITE); |
| 298 | |
| 299 | tp->t_line = SLIPDISC; |
| 300 | /* |
| 301 | * We don't use t_canq or t_rawq, so reduce their |
| 302 | * cblock resources to 0. Reserve enough cblocks |
| 303 | * for t_outq to guarantee that we can fit a full |
| 304 | * packet if the SLIP_HIWAT check allows slstart() |
| 305 | * to loop. Use the same value for the cblock |
| 306 | * limit since the reserved blocks should always |
| 307 | * be enough. Reserving cblocks probably makes |
| 308 | * the CLISTRESERVE check unnecessary and wasteful. |
| 309 | */ |
| 310 | clist_alloc_cblocks(&tp->t_canq, 0, 0); |
| 311 | clist_alloc_cblocks(&tp->t_outq, |
| 312 | SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1, |
| 313 | SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1); |
| 314 | clist_alloc_cblocks(&tp->t_rawq, 0, 0); |
| 315 | |
| 316 | s = splnet(); |
| 317 | if_up(&sc->sc_if); |
| 318 | splx(s); |
| 319 | return (0); |
| 320 | } |
| 321 | return (ENXIO); |
| 322 | } |
| 323 | |
| 324 | /* |
| 325 | * Line specific close routine. |
| 326 | * Detach the tty from the sl unit. |
| 327 | */ |
| 328 | static int |
| 329 | slclose(tp,flag) |
| 330 | struct tty *tp; |
| 331 | int flag; |
| 332 | { |
| 333 | register struct sl_softc *sc; |
| 334 | int s; |
| 335 | |
| 336 | ttyflush(tp, FREAD | FWRITE); |
| 337 | /* |
| 338 | * XXX the placement of the following spl is misleading. tty |
| 339 | * interrupts must be blocked across line discipline switches |
| 340 | * and throughout closes to avoid races. |
| 341 | */ |
| 342 | s = splimp(); /* actually, max(spltty, splnet) */ |
| 343 | clist_free_cblocks(&tp->t_outq); |
| 344 | tp->t_line = 0; |
| 345 | sc = (struct sl_softc *)tp->t_sc; |
| 346 | if (sc != NULL) { |
| 347 | if (sc->sc_outfill) { |
| 348 | sc->sc_outfill = 0; |
| 349 | untimeout(sl_outfill, sc, sc->sc_ofhandle); |
| 350 | } |
| 351 | if (sc->sc_keepalive) { |
| 352 | sc->sc_keepalive = 0; |
| 353 | untimeout(sl_keepalive, sc, sc->sc_kahandle); |
| 354 | } |
| 355 | if_down(&sc->sc_if); |
| 356 | sc->sc_flags &= SC_STATIC; |
| 357 | sc->sc_ttyp = NULL; |
| 358 | tp->t_sc = NULL; |
| 359 | MCLFREE((caddr_t)(sc->sc_ep - SLBUFSIZE)); |
| 360 | sc->sc_ep = 0; |
| 361 | sc->sc_mp = 0; |
| 362 | sc->sc_buf = 0; |
| 363 | } |
| 364 | splx(s); |
| 365 | return 0; |
| 366 | } |
| 367 | |
| 368 | /* |
| 369 | * Line specific (tty) ioctl routine. |
| 370 | * Provide a way to get the sl unit number. |
| 371 | */ |
| 372 | /* ARGSUSED */ |
| 373 | static int |
| 374 | sltioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *p) |
| 375 | { |
| 376 | struct sl_softc *sc = (struct sl_softc *)tp->t_sc, *nc, *tmpnc; |
| 377 | int s, nsl; |
| 378 | |
| 379 | s = splimp(); |
| 380 | switch (cmd) { |
| 381 | case SLIOCGUNIT: |
| 382 | *(int *)data = sc->sc_if.if_unit; |
| 383 | break; |
| 384 | |
| 385 | case SLIOCSUNIT: |
| 386 | if (sc->sc_if.if_unit != *(u_int *)data) { |
| 387 | for (nsl = NSL, nc = sl_softc; --nsl >= 0; nc++) { |
| 388 | if ( nc->sc_if.if_unit == *(u_int *)data |
| 389 | && nc->sc_ttyp == NULL |
| 390 | ) { |
| 391 | tmpnc = malloc(sizeof *tmpnc, M_TEMP, |
| 392 | M_NOWAIT); |
| 393 | if (tmpnc == NULL) { |
| 394 | splx(s); |
| 395 | return (ENOMEM); |
| 396 | } |
| 397 | *tmpnc = *nc; |
| 398 | *nc = *sc; |
| 399 | nc->sc_if = tmpnc->sc_if; |
| 400 | tmpnc->sc_if = sc->sc_if; |
| 401 | *sc = *tmpnc; |
| 402 | free(tmpnc, M_TEMP); |
| 403 | if (sc->sc_if.if_flags & IFF_UP) { |
| 404 | if_down(&sc->sc_if); |
| 405 | if (!(nc->sc_if.if_flags & IFF_UP)) |
| 406 | if_up(&nc->sc_if); |
| 407 | } else if (nc->sc_if.if_flags & IFF_UP) |
| 408 | if_down(&nc->sc_if); |
| 409 | sc->sc_flags &= ~SC_STATIC; |
| 410 | sc->sc_flags |= (nc->sc_flags & SC_STATIC); |
| 411 | tp->t_sc = sc = nc; |
| 412 | clist_alloc_cblocks(&tp->t_outq, |
| 413 | SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1, |
| 414 | SLIP_HIWAT + 2 * sc->sc_if.if_mtu + 1); |
| 415 | sl_compress_init(&sc->sc_comp, -1); |
| 416 | goto slfound; |
| 417 | } |
| 418 | } |
| 419 | splx(s); |
| 420 | return (ENXIO); |
| 421 | } |
| 422 | slfound: |
| 423 | sc->sc_flags |= SC_STATIC; |
| 424 | break; |
| 425 | |
| 426 | case SLIOCSKEEPAL: |
| 427 | sc->sc_keepalive = *(u_int *)data * hz; |
| 428 | if (sc->sc_keepalive) { |
| 429 | sc->sc_flags |= SC_KEEPALIVE; |
| 430 | sc->sc_kahandle = timeout(sl_keepalive, sc, |
| 431 | sc->sc_keepalive); |
| 432 | } else { |
| 433 | if ((sc->sc_flags & SC_KEEPALIVE) != 0) { |
| 434 | untimeout(sl_keepalive, sc, sc->sc_kahandle); |
| 435 | sc->sc_flags &= ~SC_KEEPALIVE; |
| 436 | } |
| 437 | } |
| 438 | break; |
| 439 | |
| 440 | case SLIOCGKEEPAL: |
| 441 | *(int *)data = sc->sc_keepalive / hz; |
| 442 | break; |
| 443 | |
| 444 | case SLIOCSOUTFILL: |
| 445 | sc->sc_outfill = *(u_int *)data * hz; |
| 446 | if (sc->sc_outfill) { |
| 447 | sc->sc_flags |= SC_OUTWAIT; |
| 448 | sc->sc_ofhandle = timeout(sl_outfill, sc, |
| 449 | sc->sc_outfill); |
| 450 | } else { |
| 451 | if ((sc->sc_flags & SC_OUTWAIT) != 0) { |
| 452 | untimeout(sl_outfill, sc, sc->sc_ofhandle); |
| 453 | sc->sc_flags &= ~SC_OUTWAIT; |
| 454 | } |
| 455 | } |
| 456 | break; |
| 457 | |
| 458 | case SLIOCGOUTFILL: |
| 459 | *(int *)data = sc->sc_outfill / hz; |
| 460 | break; |
| 461 | |
| 462 | default: |
| 463 | splx(s); |
| 464 | return (ENOIOCTL); |
| 465 | } |
| 466 | splx(s); |
| 467 | return (0); |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | * Queue a packet. Start transmission if not active. |
| 472 | * Compression happens in slstart; if we do it here, IP TOS |
| 473 | * will cause us to not compress "background" packets, because |
| 474 | * ordering gets trashed. It can be done for all packets in slstart. |
| 475 | */ |
| 476 | static int |
| 477 | sloutput(ifp, m, dst, rtp) |
| 478 | struct ifnet *ifp; |
| 479 | register struct mbuf *m; |
| 480 | struct sockaddr *dst; |
| 481 | struct rtentry *rtp; |
| 482 | { |
| 483 | register struct sl_softc *sc = &sl_softc[ifp->if_unit]; |
| 484 | register struct ip *ip; |
| 485 | register struct ifqueue *ifq; |
| 486 | int s; |
| 487 | |
| 488 | /* |
| 489 | * `Cannot happen' (see slioctl). Someday we will extend |
| 490 | * the line protocol to support other address families. |
| 491 | */ |
| 492 | if (dst->sa_family != AF_INET) { |
| 493 | printf("sl%d: af%d not supported\n", sc->sc_if.if_unit, |
| 494 | dst->sa_family); |
| 495 | m_freem(m); |
| 496 | sc->sc_if.if_noproto++; |
| 497 | return (EAFNOSUPPORT); |
| 498 | } |
| 499 | |
| 500 | if (sc->sc_ttyp == NULL || !(ifp->if_flags & IFF_UP)) { |
| 501 | m_freem(m); |
| 502 | return (ENETDOWN); |
| 503 | } |
| 504 | if ((sc->sc_ttyp->t_state & TS_CONNECTED) == 0) { |
| 505 | m_freem(m); |
| 506 | return (EHOSTUNREACH); |
| 507 | } |
| 508 | ifq = &sc->sc_if.if_snd; |
| 509 | ip = mtod(m, struct ip *); |
| 510 | if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { |
| 511 | m_freem(m); |
| 512 | return (ENETRESET); /* XXX ? */ |
| 513 | } |
| 514 | if (ip->ip_tos & IPTOS_LOWDELAY) |
| 515 | ifq = &sc->sc_fastq; |
| 516 | s = splimp(); |
| 517 | if (IF_QFULL(ifq)) { |
| 518 | IF_DROP(ifq); |
| 519 | m_freem(m); |
| 520 | splx(s); |
| 521 | sc->sc_if.if_oerrors++; |
| 522 | return (ENOBUFS); |
| 523 | } |
| 524 | IF_ENQUEUE(ifq, m); |
| 525 | if (sc->sc_ttyp->t_outq.c_cc == 0) |
| 526 | slstart(sc->sc_ttyp); |
| 527 | splx(s); |
| 528 | return (0); |
| 529 | } |
| 530 | |
| 531 | /* |
| 532 | * Start output on interface. Get another datagram |
| 533 | * to send from the interface queue and map it to |
| 534 | * the interface before starting output. |
| 535 | */ |
| 536 | static int |
| 537 | slstart(tp) |
| 538 | register struct tty *tp; |
| 539 | { |
| 540 | register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; |
| 541 | register struct mbuf *m; |
| 542 | register u_char *cp; |
| 543 | register struct ip *ip; |
| 544 | int s; |
| 545 | u_char bpfbuf[SLTMAX + SLIP_HDRLEN]; |
| 546 | register int len = 0; |
| 547 | |
| 548 | for (;;) { |
| 549 | /* |
| 550 | * Call output process whether or not there is more in the |
| 551 | * output queue. We are being called in lieu of ttstart |
| 552 | * and must do what it would. |
| 553 | */ |
| 554 | (*tp->t_oproc)(tp); |
| 555 | |
| 556 | if (tp->t_outq.c_cc != 0) { |
| 557 | if (sc != NULL) |
| 558 | sc->sc_flags &= ~SC_OUTWAIT; |
| 559 | if (tp->t_outq.c_cc > SLIP_HIWAT) |
| 560 | return 0; |
| 561 | } |
| 562 | |
| 563 | /* |
| 564 | * This happens briefly when the line shuts down. |
| 565 | */ |
| 566 | if (sc == NULL) |
| 567 | return 0; |
| 568 | |
| 569 | /* |
| 570 | * Get a packet and send it to the interface. |
| 571 | */ |
| 572 | s = splimp(); |
| 573 | IF_DEQUEUE(&sc->sc_fastq, m); |
| 574 | if (m) |
| 575 | sc->sc_if.if_omcasts++; /* XXX */ |
| 576 | else |
| 577 | IF_DEQUEUE(&sc->sc_if.if_snd, m); |
| 578 | splx(s); |
| 579 | if (m == NULL) |
| 580 | return 0; |
| 581 | |
| 582 | /* |
| 583 | * We do the header compression here rather than in sloutput |
| 584 | * because the packets will be out of order if we are using TOS |
| 585 | * queueing, and the connection id compression will get |
| 586 | * munged when this happens. |
| 587 | */ |
| 588 | if (sc->sc_if.if_bpf) { |
| 589 | /* |
| 590 | * We need to save the TCP/IP header before it's |
| 591 | * compressed. To avoid complicated code, we just |
| 592 | * copy the entire packet into a stack buffer (since |
| 593 | * this is a serial line, packets should be short |
| 594 | * and/or the copy should be negligible cost compared |
| 595 | * to the packet transmission time). |
| 596 | */ |
| 597 | register struct mbuf *m1 = m; |
| 598 | register u_char *cp = bpfbuf + SLIP_HDRLEN; |
| 599 | |
| 600 | len = 0; |
| 601 | do { |
| 602 | register int mlen = m1->m_len; |
| 603 | |
| 604 | bcopy(mtod(m1, caddr_t), cp, mlen); |
| 605 | cp += mlen; |
| 606 | len += mlen; |
| 607 | } while ((m1 = m1->m_next) != NULL); |
| 608 | } |
| 609 | ip = mtod(m, struct ip *); |
| 610 | if (ip->ip_v == IPVERSION && ip->ip_p == IPPROTO_TCP) { |
| 611 | if (sc->sc_if.if_flags & SC_COMPRESS) |
| 612 | *mtod(m, u_char *) |= sl_compress_tcp(m, ip, |
| 613 | &sc->sc_comp, 1); |
| 614 | } |
| 615 | if (sc->sc_if.if_bpf) { |
| 616 | /* |
| 617 | * Put the SLIP pseudo-"link header" in place. The |
| 618 | * compressed header is now at the beginning of the |
| 619 | * mbuf. |
| 620 | */ |
| 621 | bpfbuf[SLX_DIR] = SLIPDIR_OUT; |
| 622 | bcopy(mtod(m, caddr_t), &bpfbuf[SLX_CHDR], CHDR_LEN); |
| 623 | bpf_tap(&sc->sc_if, bpfbuf, len + SLIP_HDRLEN); |
| 624 | } |
| 625 | |
| 626 | /* |
| 627 | * If system is getting low on clists, just flush our |
| 628 | * output queue (if the stuff was important, it'll get |
| 629 | * retransmitted). Note that SLTMAX is used instead of |
| 630 | * the current if_mtu setting because connections that |
| 631 | * have already been established still use the original |
| 632 | * (possibly larger) mss. |
| 633 | */ |
| 634 | if (cfreecount < CLISTRESERVE + SLTMAX) { |
| 635 | m_freem(m); |
| 636 | sc->sc_if.if_collisions++; |
| 637 | continue; |
| 638 | } |
| 639 | |
| 640 | sc->sc_flags &= ~SC_OUTWAIT; |
| 641 | /* |
| 642 | * The extra FRAME_END will start up a new packet, and thus |
| 643 | * will flush any accumulated garbage. We do this whenever |
| 644 | * the line may have been idle for some time. |
| 645 | */ |
| 646 | if (tp->t_outq.c_cc == 0) { |
| 647 | ++sc->sc_if.if_obytes; |
| 648 | (void) putc(FRAME_END, &tp->t_outq); |
| 649 | } |
| 650 | |
| 651 | while (m) { |
| 652 | register u_char *ep; |
| 653 | |
| 654 | cp = mtod(m, u_char *); ep = cp + m->m_len; |
| 655 | while (cp < ep) { |
| 656 | /* |
| 657 | * Find out how many bytes in the string we can |
| 658 | * handle without doing something special. |
| 659 | */ |
| 660 | register u_char *bp = cp; |
| 661 | |
| 662 | while (cp < ep) { |
| 663 | switch (*cp++) { |
| 664 | case FRAME_ESCAPE: |
| 665 | case FRAME_END: |
| 666 | --cp; |
| 667 | goto out; |
| 668 | } |
| 669 | } |
| 670 | out: |
| 671 | if (cp > bp) { |
| 672 | /* |
| 673 | * Put n characters at once |
| 674 | * into the tty output queue. |
| 675 | */ |
| 676 | if (b_to_q((char *)bp, cp - bp, |
| 677 | &tp->t_outq)) |
| 678 | break; |
| 679 | sc->sc_if.if_obytes += cp - bp; |
| 680 | } |
| 681 | /* |
| 682 | * If there are characters left in the mbuf, |
| 683 | * the first one must be special.. |
| 684 | * Put it out in a different form. |
| 685 | */ |
| 686 | if (cp < ep) { |
| 687 | if (putc(FRAME_ESCAPE, &tp->t_outq)) |
| 688 | break; |
| 689 | if (putc(*cp++ == FRAME_ESCAPE ? |
| 690 | TRANS_FRAME_ESCAPE : TRANS_FRAME_END, |
| 691 | &tp->t_outq)) { |
| 692 | (void) unputc(&tp->t_outq); |
| 693 | break; |
| 694 | } |
| 695 | sc->sc_if.if_obytes += 2; |
| 696 | } |
| 697 | } |
| 698 | m = m_free(m); |
| 699 | } |
| 700 | |
| 701 | if (putc(FRAME_END, &tp->t_outq)) { |
| 702 | /* |
| 703 | * Not enough room. Remove a char to make room |
| 704 | * and end the packet normally. |
| 705 | * If you get many collisions (more than one or two |
| 706 | * a day) you probably do not have enough clists |
| 707 | * and you should increase "nclist" in param.c. |
| 708 | */ |
| 709 | (void) unputc(&tp->t_outq); |
| 710 | (void) putc(FRAME_END, &tp->t_outq); |
| 711 | sc->sc_if.if_collisions++; |
| 712 | } else { |
| 713 | ++sc->sc_if.if_obytes; |
| 714 | sc->sc_if.if_opackets++; |
| 715 | } |
| 716 | } |
| 717 | return 0; |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | * Copy data buffer to mbuf chain; add ifnet pointer. |
| 722 | */ |
| 723 | static struct mbuf * |
| 724 | sl_btom(sc, len) |
| 725 | register struct sl_softc *sc; |
| 726 | register int len; |
| 727 | { |
| 728 | register struct mbuf *m; |
| 729 | |
| 730 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
| 731 | if (m == NULL) |
| 732 | return (NULL); |
| 733 | |
| 734 | /* |
| 735 | * If we have more than MHLEN bytes, it's cheaper to |
| 736 | * queue the cluster we just filled & allocate a new one |
| 737 | * for the input buffer. Otherwise, fill the mbuf we |
| 738 | * allocated above. Note that code in the input routine |
| 739 | * guarantees that packet will fit in a cluster. |
| 740 | */ |
| 741 | if (len >= MHLEN) { |
| 742 | MCLGET(m, M_DONTWAIT); |
| 743 | if ((m->m_flags & M_EXT) == 0) { |
| 744 | /* |
| 745 | * we couldn't get a cluster - if memory's this |
| 746 | * low, it's time to start dropping packets. |
| 747 | */ |
| 748 | (void) m_free(m); |
| 749 | return (NULL); |
| 750 | } |
| 751 | sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE; |
| 752 | m->m_data = (caddr_t)sc->sc_buf; |
| 753 | m->m_ext.ext_buf = (caddr_t)((intptr_t)sc->sc_buf &~ MCLOFSET); |
| 754 | } else |
| 755 | bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len); |
| 756 | |
| 757 | m->m_len = len; |
| 758 | m->m_pkthdr.len = len; |
| 759 | m->m_pkthdr.rcvif = &sc->sc_if; |
| 760 | return (m); |
| 761 | } |
| 762 | |
| 763 | /* |
| 764 | * tty interface receiver interrupt. |
| 765 | */ |
| 766 | static int |
| 767 | slinput(c, tp) |
| 768 | register int c; |
| 769 | register struct tty *tp; |
| 770 | { |
| 771 | register struct sl_softc *sc; |
| 772 | register struct mbuf *m; |
| 773 | register int len; |
| 774 | int s; |
| 775 | u_char chdr[CHDR_LEN]; |
| 776 | |
| 777 | tk_nin++; |
| 778 | sc = (struct sl_softc *)tp->t_sc; |
| 779 | if (sc == NULL) |
| 780 | return 0; |
| 781 | if (c & TTY_ERRORMASK || (tp->t_state & TS_CONNECTED) == 0) { |
| 782 | sc->sc_flags |= SC_ERROR; |
| 783 | return 0; |
| 784 | } |
| 785 | c &= TTY_CHARMASK; |
| 786 | |
| 787 | ++sc->sc_if.if_ibytes; |
| 788 | |
| 789 | if (sc->sc_if.if_flags & IFF_DEBUG) { |
| 790 | if (c == ABT_ESC) { |
| 791 | /* |
| 792 | * If we have a previous abort, see whether |
| 793 | * this one is within the time limit. |
| 794 | */ |
| 795 | if (sc->sc_abortcount && |
| 796 | time_second >= sc->sc_starttime + ABT_WINDOW) |
| 797 | sc->sc_abortcount = 0; |
| 798 | /* |
| 799 | * If we see an abort after "idle" time, count it; |
| 800 | * record when the first abort escape arrived. |
| 801 | */ |
| 802 | if (time_second >= sc->sc_lasttime + ABT_IDLE) { |
| 803 | if (++sc->sc_abortcount == 1) |
| 804 | sc->sc_starttime = time_second; |
| 805 | if (sc->sc_abortcount >= ABT_COUNT) { |
| 806 | slclose(tp,0); |
| 807 | return 0; |
| 808 | } |
| 809 | } |
| 810 | } else |
| 811 | sc->sc_abortcount = 0; |
| 812 | sc->sc_lasttime = time_second; |
| 813 | } |
| 814 | |
| 815 | switch (c) { |
| 816 | |
| 817 | case TRANS_FRAME_ESCAPE: |
| 818 | if (sc->sc_escape) |
| 819 | c = FRAME_ESCAPE; |
| 820 | break; |
| 821 | |
| 822 | case TRANS_FRAME_END: |
| 823 | if (sc->sc_escape) |
| 824 | c = FRAME_END; |
| 825 | break; |
| 826 | |
| 827 | case FRAME_ESCAPE: |
| 828 | sc->sc_escape = 1; |
| 829 | return 0; |
| 830 | |
| 831 | case FRAME_END: |
| 832 | sc->sc_flags &= ~SC_KEEPALIVE; |
| 833 | if(sc->sc_flags & SC_ERROR) { |
| 834 | sc->sc_flags &= ~SC_ERROR; |
| 835 | goto newpack; |
| 836 | } |
| 837 | len = sc->sc_mp - sc->sc_buf; |
| 838 | if (len < 3) |
| 839 | /* less than min length packet - ignore */ |
| 840 | goto newpack; |
| 841 | |
| 842 | if (sc->sc_if.if_bpf) { |
| 843 | /* |
| 844 | * Save the compressed header, so we |
| 845 | * can tack it on later. Note that we |
| 846 | * will end up copying garbage in some |
| 847 | * cases but this is okay. We remember |
| 848 | * where the buffer started so we can |
| 849 | * compute the new header length. |
| 850 | */ |
| 851 | bcopy(sc->sc_buf, chdr, CHDR_LEN); |
| 852 | } |
| 853 | |
| 854 | if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) { |
| 855 | if (c & 0x80) |
| 856 | c = TYPE_COMPRESSED_TCP; |
| 857 | else if (c == TYPE_UNCOMPRESSED_TCP) |
| 858 | *sc->sc_buf &= 0x4f; /* XXX */ |
| 859 | /* |
| 860 | * We've got something that's not an IP packet. |
| 861 | * If compression is enabled, try to decompress it. |
| 862 | * Otherwise, if `auto-enable' compression is on and |
| 863 | * it's a reasonable packet, decompress it and then |
| 864 | * enable compression. Otherwise, drop it. |
| 865 | */ |
| 866 | if (sc->sc_if.if_flags & SC_COMPRESS) { |
| 867 | len = sl_uncompress_tcp(&sc->sc_buf, len, |
| 868 | (u_int)c, &sc->sc_comp); |
| 869 | if (len <= 0) |
| 870 | goto error; |
| 871 | } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) && |
| 872 | c == TYPE_UNCOMPRESSED_TCP && len >= 40) { |
| 873 | len = sl_uncompress_tcp(&sc->sc_buf, len, |
| 874 | (u_int)c, &sc->sc_comp); |
| 875 | if (len <= 0) |
| 876 | goto error; |
| 877 | sc->sc_if.if_flags |= SC_COMPRESS; |
| 878 | } else |
| 879 | goto error; |
| 880 | } |
| 881 | if (sc->sc_if.if_bpf) { |
| 882 | /* |
| 883 | * Put the SLIP pseudo-"link header" in place. |
| 884 | * We couldn't do this any earlier since |
| 885 | * decompression probably moved the buffer |
| 886 | * pointer. Then, invoke BPF. |
| 887 | */ |
| 888 | register u_char *hp = sc->sc_buf - SLIP_HDRLEN; |
| 889 | |
| 890 | hp[SLX_DIR] = SLIPDIR_IN; |
| 891 | bcopy(chdr, &hp[SLX_CHDR], CHDR_LEN); |
| 892 | bpf_tap(&sc->sc_if, hp, len + SLIP_HDRLEN); |
| 893 | } |
| 894 | m = sl_btom(sc, len); |
| 895 | if (m == NULL) |
| 896 | goto error; |
| 897 | |
| 898 | sc->sc_if.if_ipackets++; |
| 899 | |
| 900 | if ((sc->sc_if.if_flags & IFF_UP) == 0) { |
| 901 | m_freem(m); |
| 902 | goto newpack; |
| 903 | } |
| 904 | |
| 905 | s = splimp(); |
| 906 | if (IF_QFULL(&ipintrq)) { |
| 907 | IF_DROP(&ipintrq); |
| 908 | sc->sc_if.if_ierrors++; |
| 909 | sc->sc_if.if_iqdrops++; |
| 910 | m_freem(m); |
| 911 | } else { |
| 912 | IF_ENQUEUE(&ipintrq, m); |
| 913 | schednetisr(NETISR_IP); |
| 914 | } |
| 915 | splx(s); |
| 916 | goto newpack; |
| 917 | } |
| 918 | if (sc->sc_mp < sc->sc_ep) { |
| 919 | *sc->sc_mp++ = c; |
| 920 | sc->sc_escape = 0; |
| 921 | return 0; |
| 922 | } |
| 923 | |
| 924 | /* can't put lower; would miss an extra frame */ |
| 925 | sc->sc_flags |= SC_ERROR; |
| 926 | |
| 927 | error: |
| 928 | sc->sc_if.if_ierrors++; |
| 929 | newpack: |
| 930 | sc->sc_mp = sc->sc_buf = sc->sc_ep - SLRMAX; |
| 931 | sc->sc_escape = 0; |
| 932 | return 0; |
| 933 | } |
| 934 | |
| 935 | /* |
| 936 | * Process an ioctl request. |
| 937 | */ |
| 938 | static int |
| 939 | slioctl(ifp, cmd, data) |
| 940 | register struct ifnet *ifp; |
| 941 | u_long cmd; |
| 942 | caddr_t data; |
| 943 | { |
| 944 | register struct ifaddr *ifa = (struct ifaddr *)data; |
| 945 | register struct ifreq *ifr = (struct ifreq *)data; |
| 946 | register int s, error = 0; |
| 947 | |
| 948 | s = splimp(); |
| 949 | |
| 950 | switch (cmd) { |
| 951 | |
| 952 | case SIOCSIFFLAGS: |
| 953 | /* |
| 954 | * if.c will set the interface up even if we |
| 955 | * don't want it to. |
| 956 | */ |
| 957 | if (sl_softc[ifp->if_unit].sc_ttyp == NULL) { |
| 958 | ifp->if_flags &= ~IFF_UP; |
| 959 | } |
| 960 | break; |
| 961 | case SIOCSIFADDR: |
| 962 | /* |
| 963 | * This is "historical" - set the interface up when |
| 964 | * setting the address. |
| 965 | */ |
| 966 | if (ifa->ifa_addr->sa_family == AF_INET) { |
| 967 | if (sl_softc[ifp->if_unit].sc_ttyp != NULL) |
| 968 | ifp->if_flags |= IFF_UP; |
| 969 | } else { |
| 970 | error = EAFNOSUPPORT; |
| 971 | } |
| 972 | break; |
| 973 | |
| 974 | case SIOCSIFDSTADDR: |
| 975 | if (ifa->ifa_addr->sa_family != AF_INET) |
| 976 | error = EAFNOSUPPORT; |
| 977 | break; |
| 978 | |
| 979 | case SIOCADDMULTI: |
| 980 | case SIOCDELMULTI: |
| 981 | break; |
| 982 | |
| 983 | case SIOCSIFMTU: |
| 984 | /* |
| 985 | * Set the interface MTU. |
| 986 | */ |
| 987 | if (ifr->ifr_mtu > SLTMAX) |
| 988 | error = EINVAL; |
| 989 | else { |
| 990 | struct tty *tp; |
| 991 | |
| 992 | ifp->if_mtu = ifr->ifr_mtu; |
| 993 | tp = sl_softc[ifp->if_unit].sc_ttyp; |
| 994 | if (tp != NULL) |
| 995 | clist_alloc_cblocks(&tp->t_outq, |
| 996 | SLIP_HIWAT + 2 * ifp->if_mtu + 1, |
| 997 | SLIP_HIWAT + 2 * ifp->if_mtu + 1); |
| 998 | } |
| 999 | break; |
| 1000 | |
| 1001 | default: |
| 1002 | error = EINVAL; |
| 1003 | } |
| 1004 | splx(s); |
| 1005 | return (error); |
| 1006 | } |
| 1007 | |
| 1008 | static void |
| 1009 | sl_keepalive(chan) |
| 1010 | void *chan; |
| 1011 | { |
| 1012 | struct sl_softc *sc = chan; |
| 1013 | |
| 1014 | if (sc->sc_keepalive) { |
| 1015 | if (sc->sc_flags & SC_KEEPALIVE) |
| 1016 | pgsignal (sc->sc_ttyp->t_pgrp, SIGURG, 1); |
| 1017 | else |
| 1018 | sc->sc_flags |= SC_KEEPALIVE; |
| 1019 | sc->sc_kahandle = timeout(sl_keepalive, sc, sc->sc_keepalive); |
| 1020 | } else { |
| 1021 | sc->sc_flags &= ~SC_KEEPALIVE; |
| 1022 | } |
| 1023 | } |
| 1024 | |
| 1025 | static void |
| 1026 | sl_outfill(chan) |
| 1027 | void *chan; |
| 1028 | { |
| 1029 | struct sl_softc *sc = chan; |
| 1030 | register struct tty *tp = sc->sc_ttyp; |
| 1031 | int s; |
| 1032 | |
| 1033 | if (sc->sc_outfill && tp != NULL) { |
| 1034 | if (sc->sc_flags & SC_OUTWAIT) { |
| 1035 | s = splimp (); |
| 1036 | ++sc->sc_if.if_obytes; |
| 1037 | (void) putc(FRAME_END, &tp->t_outq); |
| 1038 | (*tp->t_oproc)(tp); |
| 1039 | splx (s); |
| 1040 | } else |
| 1041 | sc->sc_flags |= SC_OUTWAIT; |
| 1042 | sc->sc_ofhandle = timeout(sl_outfill, sc, sc->sc_outfill); |
| 1043 | } else { |
| 1044 | sc->sc_flags &= ~SC_OUTWAIT; |
| 1045 | } |
| 1046 | } |