| 1 | /* $NetBSD: if_strip.c,v 1.107 2016/10/02 14:17:07 christos Exp $ */ |
| 2 | /* from: NetBSD: if_sl.c,v 1.38 1996/02/13 22:00:23 christos Exp $ */ |
| 3 | |
| 4 | /* |
| 5 | * Copyright 1996 The Board of Trustees of The Leland Stanford |
| 6 | * Junior University. All Rights Reserved. |
| 7 | * |
| 8 | * Permission to use, copy, modify, and distribute this |
| 9 | * software and its documentation for any purpose and without |
| 10 | * fee is hereby granted, provided that the above copyright |
| 11 | * notice appear in all copies. Stanford University |
| 12 | * makes no representations about the suitability of this |
| 13 | * software for any purpose. It is provided "as is" without |
| 14 | * express or implied warranty. |
| 15 | * |
| 16 | * |
| 17 | * This driver was contributed by Jonathan Stone. |
| 18 | * |
| 19 | * Starmode Radio IP interface (STRIP) for Metricom wireless radio. |
| 20 | * This STRIP driver assumes address resolution of IP addresses to |
| 21 | * Metricom MAC addresses is done via local link-level routes. |
| 22 | * The link-level addresses are entered as an 8-digit packed BCD number. |
| 23 | * To add a route for a radio at IP address 10.1.2.3, with radio |
| 24 | * address '1234-5678', reachable via interface strip0, use the command |
| 25 | * |
| 26 | * route add -host 10.1.2.3 -link strip0:12:34:56:78 |
| 27 | */ |
| 28 | |
| 29 | |
| 30 | /* |
| 31 | * Copyright (c) 1987, 1989, 1992, 1993 |
| 32 | * The Regents of the University of California. All rights reserved. |
| 33 | * |
| 34 | * Redistribution and use in source and binary forms, with or without |
| 35 | * modification, are permitted provided that the following conditions |
| 36 | * are met: |
| 37 | * 1. Redistributions of source code must retain the above copyright |
| 38 | * notice, this list of conditions and the following disclaimer. |
| 39 | * 2. Redistributions in binary form must reproduce the above copyright |
| 40 | * notice, this list of conditions and the following disclaimer in the |
| 41 | * documentation and/or other materials provided with the distribution. |
| 42 | * 3. Neither the name of the University nor the names of its contributors |
| 43 | * may be used to endorse or promote products derived from this software |
| 44 | * without specific prior written permission. |
| 45 | * |
| 46 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 47 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 48 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 49 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 50 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 51 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 52 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 53 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 54 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 55 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 56 | * SUCH DAMAGE. |
| 57 | * |
| 58 | * @(#)if_sl.c 8.6 (Berkeley) 2/1/94 |
| 59 | */ |
| 60 | |
| 61 | /* |
| 62 | * Derived from: Serial Line interface written by Rick Adams (rick@seismo.gov) |
| 63 | * |
| 64 | * Rick Adams |
| 65 | * Center for Seismic Studies |
| 66 | * 1300 N 17th Street, Suite 1450 |
| 67 | * Arlington, Virginia 22209 |
| 68 | * (703)276-7900 |
| 69 | * rick@seismo.ARPA |
| 70 | * seismo!rick |
| 71 | * |
| 72 | * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). |
| 73 | * N.B.: this belongs in netinet, not net, the way it stands now. |
| 74 | * Should have a link-layer type designation, but wouldn't be |
| 75 | * backwards-compatible. |
| 76 | * |
| 77 | * Converted to 4.3BSD Beta by Chris Torek. |
| 78 | * Other changes made at Berkeley, based in part on code by Kirk Smith. |
| 79 | * W. Jolitz added slip abort. |
| 80 | * |
| 81 | * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov). |
| 82 | * Added priority queuing for "interactive" traffic; hooks for TCP |
| 83 | * header compression; ICMP filtering (at 2400 baud, some cretin |
| 84 | * pinging you can use up all your bandwidth). Made low clist behavior |
| 85 | * more robust and slightly less likely to hang serial line. |
| 86 | * Sped up a bunch of things. |
| 87 | */ |
| 88 | |
| 89 | #include <sys/cdefs.h> |
| 90 | __KERNEL_RCSID(0, "$NetBSD: if_strip.c,v 1.107 2016/10/02 14:17:07 christos Exp $" ); |
| 91 | |
| 92 | #ifdef _KERNEL_OPT |
| 93 | #include "opt_inet.h" |
| 94 | #endif |
| 95 | |
| 96 | #include <sys/param.h> |
| 97 | #include <sys/proc.h> |
| 98 | #include <sys/mbuf.h> |
| 99 | #include <sys/buf.h> |
| 100 | #include <sys/dkstat.h> |
| 101 | #include <sys/socket.h> |
| 102 | #include <sys/ioctl.h> |
| 103 | #include <sys/file.h> |
| 104 | #include <sys/conf.h> |
| 105 | #include <sys/tty.h> |
| 106 | #include <sys/kernel.h> |
| 107 | #if __NetBSD__ |
| 108 | #include <sys/systm.h> |
| 109 | #include <sys/callout.h> |
| 110 | #include <sys/kauth.h> |
| 111 | #endif |
| 112 | #include <sys/syslog.h> |
| 113 | #include <sys/cpu.h> |
| 114 | #include <sys/intr.h> |
| 115 | #include <sys/socketvar.h> |
| 116 | #include <sys/device.h> |
| 117 | #include <sys/module.h> |
| 118 | |
| 119 | #include <net/if.h> |
| 120 | #include <net/if_dl.h> |
| 121 | #include <net/if_types.h> |
| 122 | #include <net/netisr.h> |
| 123 | #include <net/route.h> |
| 124 | |
| 125 | #ifdef INET |
| 126 | #include <netinet/in.h> |
| 127 | #include <netinet/in_systm.h> |
| 128 | #include <netinet/in_var.h> |
| 129 | #include <netinet/ip.h> |
| 130 | #endif |
| 131 | |
| 132 | #include <net/slcompress.h> |
| 133 | #include <net/if_stripvar.h> |
| 134 | #include <net/slip.h> |
| 135 | |
| 136 | #ifdef __NetBSD__ /* XXX -- jrs */ |
| 137 | typedef u_char ttychar_t; |
| 138 | #else |
| 139 | typedef char ttychar_t; |
| 140 | #endif |
| 141 | |
| 142 | #include <sys/time.h> |
| 143 | #include <net/bpf.h> |
| 144 | |
| 145 | /* |
| 146 | * SLMAX is a hard limit on input packet size. To simplify the code |
| 147 | * and improve performance, we require that packets fit in an mbuf |
| 148 | * cluster, and if we get a compressed packet, there's enough extra |
| 149 | * room to expand the header into a max length tcp/ip header (128 |
| 150 | * bytes). So, SLMAX can be at most |
| 151 | * MCLBYTES - 128 |
| 152 | * |
| 153 | * SLMTU is a hard limit on output packet size. To insure good |
| 154 | * interactive response, SLMTU wants to be the smallest size that |
| 155 | * amortizes the header cost. Remember that even with |
| 156 | * type-of-service queuing, we have to wait for any in-progress |
| 157 | * packet to finish. I.e., we wait, on the average, 1/2 * mtu / |
| 158 | * cps, where cps is the line speed in characters per second. |
| 159 | * E.g., 533ms wait for a 1024 byte MTU on a 9600 baud line. The |
| 160 | * average compressed header size is 6-8 bytes so any MTU > 90 |
| 161 | * bytes will give us 90% of the line bandwidth. A 100ms wait is |
| 162 | * tolerable (500ms is not), so want an MTU around 296. (Since TCP |
| 163 | * will send 256 byte segments (to allow for 40 byte headers), the |
| 164 | * typical packet size on the wire will be around 260 bytes). In |
| 165 | * 4.3tahoe+ systems, we can set an MTU in a route so we do that & |
| 166 | * leave the interface MTU relatively high (so we don't IP fragment |
| 167 | * when acting as a gateway to someone using a stupid MTU). |
| 168 | * |
| 169 | * Similar considerations apply to SLIP_HIWAT: It's the amount of |
| 170 | * data that will be queued 'downstream' of us (i.e., in clists |
| 171 | * waiting to be picked up by the tty output interrupt). If we |
| 172 | * queue a lot of data downstream, it's immune to our t.o.s. queuing. |
| 173 | * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed |
| 174 | * telnet/ftp will see a 1 sec wait, independent of the mtu (the |
| 175 | * wait is dependent on the ftp window size but that's typically |
| 176 | * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize |
| 177 | * the cost (in idle time on the wire) of the tty driver running |
| 178 | * off the end of its clists & having to call back slstart for a |
| 179 | * new packet. For a tty interface with any buffering at all, this |
| 180 | * cost will be zero. Even with a totally brain dead interface (like |
| 181 | * the one on a typical workstation), the cost will be <= 1 character |
| 182 | * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose |
| 183 | * at most 1% while maintaining good interactive response. |
| 184 | */ |
| 185 | #define BUFOFFSET (128+sizeof(struct ifnet **)+SLIP_HDRLEN) |
| 186 | #define SLMAX (MCLBYTES - BUFOFFSET) |
| 187 | #define SLBUFSIZE (SLMAX + BUFOFFSET) |
| 188 | #define SLMTU 1100 /* XXX -- appromaximated. 1024 may be safer. */ |
| 189 | |
| 190 | #define STRIP_MTU_ONWIRE (SLMTU + 20 + STRIP_HDRLEN) /* (2*SLMTU+2 in sl.c */ |
| 191 | |
| 192 | |
| 193 | #define SLIP_HIWAT roundup(50, TTROUND) |
| 194 | |
| 195 | /* This is a NetBSD-1.0 or later kernel. */ |
| 196 | #define CCOUNT(q) ((q)->c_cc) |
| 197 | |
| 198 | |
| 199 | #ifndef __NetBSD__ /* XXX - cgd */ |
| 200 | #define CLISTRESERVE 1024 /* Can't let clists get too low */ |
| 201 | #endif /* !__NetBSD__ */ |
| 202 | |
| 203 | /* |
| 204 | * SLIP ABORT ESCAPE MECHANISM: |
| 205 | * (inspired by HAYES modem escape arrangement) |
| 206 | * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape } |
| 207 | * within window time signals a "soft" exit from slip mode by remote end |
| 208 | * if the IFF_DEBUG flag is on. |
| 209 | */ |
| 210 | #define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/ |
| 211 | #define ABT_IDLE 1 /* in seconds - idle before an escape */ |
| 212 | #define ABT_COUNT 3 /* count of escapes for abort */ |
| 213 | #define ABT_WINDOW (ABT_COUNT*2+2) /* in seconds - time to count */ |
| 214 | |
| 215 | static int strip_clone_create(struct if_clone *, int); |
| 216 | static int strip_clone_destroy(struct ifnet *); |
| 217 | |
| 218 | static LIST_HEAD(, strip_softc) strip_softc_list; |
| 219 | |
| 220 | struct if_clone strip_cloner = |
| 221 | IF_CLONE_INITIALIZER("strip" , strip_clone_create, strip_clone_destroy); |
| 222 | |
| 223 | #define STRIP_FRAME_END 0x0D /* carriage return */ |
| 224 | |
| 225 | static void stripintr(void *); |
| 226 | |
| 227 | static int stripcreate(struct strip_softc *); |
| 228 | static struct mbuf *strip_btom(struct strip_softc *, int); |
| 229 | |
| 230 | /* |
| 231 | * STRIP header: '*' + modem address (dddd-dddd) + '*' + mactype ('SIP0') |
| 232 | * A Metricom packet looks like this: *<address>*<key><payload><CR> |
| 233 | * eg. *0000-1164*SIP0<payload><CR> |
| 234 | * |
| 235 | */ |
| 236 | |
| 237 | #define STRIP_ENCAP_SIZE(X) ((36) + (X)*65/64 + 2) |
| 238 | #define STRIP_HDRLEN 15 |
| 239 | #define STRIP_MAC_ADDR_LEN 9 |
| 240 | |
| 241 | /* |
| 242 | * Star mode packet header. |
| 243 | * (may be used for encapsulations other than STRIP.) |
| 244 | */ |
| 245 | #define STARMODE_ADDR_LEN 11 |
| 246 | struct { |
| 247 | u_char [STARMODE_ADDR_LEN]; |
| 248 | u_char [4]; |
| 249 | }; |
| 250 | |
| 251 | /* |
| 252 | * Forward declarations for Metricom-specific functions. |
| 253 | * Ideally, these would be in a library and shared across |
| 254 | * different STRIP implementations: *BSD, Linux, etc. |
| 255 | * |
| 256 | */ |
| 257 | static u_char* UnStuffData(u_char *src, u_char *end, u_char |
| 258 | *dest, u_long dest_length); |
| 259 | |
| 260 | static u_char* StuffData(u_char *src, u_long length, u_char *dest, |
| 261 | u_char **code_ptr_ptr); |
| 262 | |
| 263 | static void RecvErr(const char *msg, struct strip_softc *sc); |
| 264 | static void RecvErr_Message(struct strip_softc *strip_info, |
| 265 | u_char *sendername, const u_char *msg); |
| 266 | void strip_resetradio(struct strip_softc *sc, struct tty *tp); |
| 267 | void strip_proberadio(struct strip_softc *sc, struct tty *tp); |
| 268 | void strip_watchdog(struct ifnet *ifp); |
| 269 | void strip_sendbody(struct strip_softc *sc, struct mbuf *m); |
| 270 | int strip_newpacket(struct strip_softc *sc, u_char *ptr, u_char *end); |
| 271 | void strip_send(struct strip_softc *sc, struct mbuf *m0); |
| 272 | |
| 273 | void strip_timeout(void *x); |
| 274 | |
| 275 | #ifdef DEBUG |
| 276 | #define DPRINTF(x) printf x |
| 277 | #else |
| 278 | #define DPRINTF(x) |
| 279 | #endif |
| 280 | |
| 281 | |
| 282 | |
| 283 | /* |
| 284 | * Radio reset macros. |
| 285 | * The Metricom radios are not particularly well-designed for |
| 286 | * use in packet mode (starmode). There's no easy way to tell |
| 287 | * when the radio is in starmode. Worse, when the radios are reset |
| 288 | * or power-cycled, they come back up in Hayes AT-emulation mode, |
| 289 | * and there's no good way for this driver to tell. |
| 290 | * We deal with this by peridically tickling the radio |
| 291 | * with an invalid starmode command. If the radio doesn't |
| 292 | * respond with an error, the driver knows to reset the radio. |
| 293 | */ |
| 294 | |
| 295 | /* Radio-reset finite state machine (if_watchdog) callback rate, in seconds */ |
| 296 | #define STRIP_WATCHDOG_INTERVAL 5 |
| 297 | |
| 298 | /* Period between intrusive radio probes, in seconds */ |
| 299 | #define ST_PROBE_INTERVAL 10 |
| 300 | |
| 301 | /* Grace period for radio to answer probe, in seconds */ |
| 302 | #define ST_PROBERESPONSE_INTERVAL 2 |
| 303 | |
| 304 | /* Be less agressive about repeated resetting. */ |
| 305 | #define STRIP_RESET_INTERVAL 5 |
| 306 | |
| 307 | /* |
| 308 | * We received a response from the radio that indicates it's in |
| 309 | * star mode. Clear any pending probe or reset timer. |
| 310 | * Don't probe radio again for standard polling interval. |
| 311 | */ |
| 312 | #define CLEAR_RESET_TIMER(sc) \ |
| 313 | do {\ |
| 314 | (sc)->sc_state = ST_ALIVE; \ |
| 315 | (sc)->sc_statetimo = time_second + ST_PROBE_INTERVAL; \ |
| 316 | } while (/*CONSTCOND*/ 0) |
| 317 | |
| 318 | /* |
| 319 | * we received a response from the radio that indicates it's crashed |
| 320 | * out of starmode into Hayse mode. Reset it ASAP. |
| 321 | */ |
| 322 | #define FORCE_RESET(sc) \ |
| 323 | do {\ |
| 324 | (sc)->sc_statetimo = time_second - 1; \ |
| 325 | (sc)->sc_state = ST_DEAD; \ |
| 326 | /*(sc)->sc_if.if_timer = 0;*/ \ |
| 327 | } while (/*CONSTCOND*/ 0) |
| 328 | |
| 329 | #define RADIO_PROBE_TIMEOUT(sc) \ |
| 330 | ((sc)-> sc_statetimo > time_second) |
| 331 | |
| 332 | static int stripclose(struct tty *, int); |
| 333 | static int stripinput(int, struct tty *); |
| 334 | static int stripioctl(struct ifnet *, u_long, void *); |
| 335 | static int stripopen(dev_t, struct tty *); |
| 336 | static int stripoutput(struct ifnet *, |
| 337 | struct mbuf *, const struct sockaddr *, |
| 338 | const struct rtentry *); |
| 339 | static int stripstart(struct tty *); |
| 340 | static int striptioctl(struct tty *, u_long, void *, int, struct lwp *); |
| 341 | |
| 342 | static struct linesw strip_disc = { |
| 343 | .l_name = "strip" , |
| 344 | .l_open = stripopen, |
| 345 | .l_close = stripclose, |
| 346 | .l_read = ttyerrio, |
| 347 | .l_write = ttyerrio, |
| 348 | .l_ioctl = striptioctl, |
| 349 | .l_rint = stripinput, |
| 350 | .l_start = stripstart, |
| 351 | .l_modem = nullmodem, |
| 352 | .l_poll = ttyerrpoll |
| 353 | }; |
| 354 | |
| 355 | void |
| 356 | stripattach(void) |
| 357 | { |
| 358 | /* |
| 359 | * Nothing to do here, initialization is handled by the |
| 360 | * module initialization code in slinit() below). |
| 361 | */ |
| 362 | } |
| 363 | |
| 364 | static void |
| 365 | stripinit(void) |
| 366 | { |
| 367 | |
| 368 | if (ttyldisc_attach(&strip_disc) != 0) |
| 369 | panic("%s" , __func__); |
| 370 | LIST_INIT(&strip_softc_list); |
| 371 | if_clone_attach(&strip_cloner); |
| 372 | } |
| 373 | |
| 374 | static int |
| 375 | stripdetach(void) |
| 376 | { |
| 377 | int error = 0; |
| 378 | |
| 379 | if (!LIST_EMPTY(&strip_softc_list)) |
| 380 | error = EBUSY; |
| 381 | |
| 382 | if (error == 0) |
| 383 | error = ttyldisc_detach(&strip_disc); |
| 384 | |
| 385 | if (error == 0) |
| 386 | if_clone_detach(&strip_cloner); |
| 387 | |
| 388 | return error; |
| 389 | } |
| 390 | |
| 391 | static int |
| 392 | strip_clone_create(struct if_clone *ifc, int unit) |
| 393 | { |
| 394 | struct strip_softc *sc; |
| 395 | |
| 396 | sc = malloc(sizeof(*sc), M_DEVBUF, M_WAIT|M_ZERO); |
| 397 | sc->sc_unit = unit; |
| 398 | if_initname(&sc->sc_if, ifc->ifc_name, unit); |
| 399 | callout_init(&sc->sc_timo_ch, 0); |
| 400 | sc->sc_if.if_softc = sc; |
| 401 | sc->sc_if.if_mtu = SLMTU; |
| 402 | sc->sc_if.if_flags = 0; |
| 403 | #if 0 |
| 404 | sc->sc_if.if_flags |= SC_AUTOCOMP /* | IFF_POINTOPOINT | IFF_MULTICAST*/; |
| 405 | #endif |
| 406 | sc->sc_if.if_type = IFT_SLIP; |
| 407 | sc->sc_if.if_ioctl = stripioctl; |
| 408 | sc->sc_if.if_output = stripoutput; |
| 409 | sc->sc_if.if_dlt = DLT_SLIP; |
| 410 | sc->sc_fastq.ifq_maxlen = 32; |
| 411 | IFQ_SET_READY(&sc->sc_if.if_snd); |
| 412 | |
| 413 | sc->sc_if.if_watchdog = strip_watchdog; |
| 414 | if_attach(&sc->sc_if); |
| 415 | if_alloc_sadl(&sc->sc_if); |
| 416 | bpf_attach(&sc->sc_if, DLT_SLIP, SLIP_HDRLEN); |
| 417 | LIST_INSERT_HEAD(&strip_softc_list, sc, sc_iflist); |
| 418 | return 0; |
| 419 | } |
| 420 | |
| 421 | static int |
| 422 | strip_clone_destroy(struct ifnet *ifp) |
| 423 | { |
| 424 | struct strip_softc *sc = (struct strip_softc *)ifp->if_softc; |
| 425 | |
| 426 | if (sc->sc_ttyp != NULL) |
| 427 | return EBUSY; /* Not removing it */ |
| 428 | |
| 429 | LIST_REMOVE(sc, sc_iflist); |
| 430 | |
| 431 | bpf_detach(ifp); |
| 432 | if_detach(ifp); |
| 433 | |
| 434 | free(sc, M_DEVBUF); |
| 435 | return 0; |
| 436 | } |
| 437 | |
| 438 | static int |
| 439 | stripcreate(struct strip_softc *sc) |
| 440 | { |
| 441 | u_char *p; |
| 442 | |
| 443 | if (sc->sc_mbuf == NULL) { |
| 444 | sc->sc_mbuf = m_get(M_WAIT, MT_DATA); |
| 445 | m_clget(sc->sc_mbuf, M_WAIT); |
| 446 | } |
| 447 | sc->sc_ep = (u_char *) sc->sc_mbuf->m_ext.ext_buf + |
| 448 | sc->sc_mbuf->m_ext.ext_size; |
| 449 | sc->sc_mp = sc->sc_pktstart = (u_char *) sc->sc_mbuf->m_ext.ext_buf + |
| 450 | BUFOFFSET; |
| 451 | |
| 452 | /* Get contiguous buffer in which to de-bytestuff/rll-decode input */ |
| 453 | if (sc->sc_rxbuf == NULL) { |
| 454 | p = (u_char *)malloc(MCLBYTES, M_DEVBUF, M_WAITOK); |
| 455 | if (p) |
| 456 | sc->sc_rxbuf = p + SLBUFSIZE - SLMAX; |
| 457 | else { |
| 458 | printf("%s: can't allocate input buffer\n" , |
| 459 | sc->sc_if.if_xname); |
| 460 | sc->sc_if.if_flags &= ~IFF_UP; |
| 461 | return (0); |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | /* Get contiguous buffer in which to bytestuff/rll-encode output */ |
| 466 | if (sc->sc_txbuf == NULL) { |
| 467 | p = (u_char *)malloc(MCLBYTES, M_DEVBUF, M_WAITOK); |
| 468 | if (p) |
| 469 | sc->sc_txbuf = (u_char *)p + SLBUFSIZE - SLMAX; |
| 470 | else { |
| 471 | printf("%s: can't allocate buffer\n" , |
| 472 | sc->sc_if.if_xname); |
| 473 | |
| 474 | sc->sc_if.if_flags &= ~IFF_UP; |
| 475 | return (0); |
| 476 | } |
| 477 | } |
| 478 | |
| 479 | #ifdef INET |
| 480 | sl_compress_init(&sc->sc_comp); |
| 481 | #endif |
| 482 | |
| 483 | /* Initialize radio probe/reset state machine */ |
| 484 | sc->sc_state = ST_DEAD; /* assumet the worst. */ |
| 485 | sc->sc_statetimo = time_second; /* do reset immediately */ |
| 486 | |
| 487 | return (1); |
| 488 | } |
| 489 | |
| 490 | /* |
| 491 | * Line specific open routine. |
| 492 | * Attach the given tty to the first available sl unit. |
| 493 | */ |
| 494 | /* ARGSUSED */ |
| 495 | int |
| 496 | stripopen(dev_t dev, struct tty *tp) |
| 497 | { |
| 498 | struct lwp *l = curlwp; /* XXX */ |
| 499 | struct strip_softc *sc; |
| 500 | int error; |
| 501 | |
| 502 | error = kauth_authorize_network(l->l_cred, |
| 503 | KAUTH_NETWORK_INTERFACE_STRIP, |
| 504 | KAUTH_REQ_NETWORK_INTERFACE_STRIP_ADD, NULL, NULL, NULL); |
| 505 | if (error) |
| 506 | return (error); |
| 507 | |
| 508 | if (tp->t_linesw == &strip_disc) |
| 509 | return (0); |
| 510 | |
| 511 | LIST_FOREACH(sc, &strip_softc_list, sc_iflist) { |
| 512 | if (sc->sc_ttyp == NULL) { |
| 513 | sc->sc_si = softint_establish(SOFTINT_NET, |
| 514 | stripintr, sc); |
| 515 | if (stripcreate(sc) == 0) { |
| 516 | softint_disestablish(sc->sc_si); |
| 517 | return (ENOBUFS); |
| 518 | } |
| 519 | mutex_spin_enter(&tty_lock); |
| 520 | tp->t_sc = (void *)sc; |
| 521 | sc->sc_ttyp = tp; |
| 522 | sc->sc_if.if_baudrate = tp->t_ospeed; |
| 523 | ttyflush(tp, FREAD | FWRITE); |
| 524 | /* |
| 525 | * Make sure tty output queue is large enough |
| 526 | * to hold a full-sized packet (including frame |
| 527 | * end, and a possible extra frame end). |
| 528 | * A full-sized of 65/64) *SLMTU bytes (because |
| 529 | * of escapes and clever RLL bytestuffing), |
| 530 | * plus frame header, and add two on for frame ends. |
| 531 | */ |
| 532 | if (tp->t_outq.c_cn < STRIP_MTU_ONWIRE) { |
| 533 | sc->sc_oldbufsize = tp->t_outq.c_cn; |
| 534 | sc->sc_oldbufquot = tp->t_outq.c_cq != 0; |
| 535 | |
| 536 | mutex_spin_exit(&tty_lock); |
| 537 | clfree(&tp->t_outq); |
| 538 | error = clalloc(&tp->t_outq, 3*SLMTU, 0); |
| 539 | if (error) { |
| 540 | softint_disestablish(sc->sc_si); |
| 541 | /* |
| 542 | * clalloc() might return -1 which |
| 543 | * is no good, so we need to return |
| 544 | * something else. |
| 545 | */ |
| 546 | return (ENOMEM); |
| 547 | } |
| 548 | mutex_spin_enter(&tty_lock); |
| 549 | } else |
| 550 | sc->sc_oldbufsize = sc->sc_oldbufquot = 0; |
| 551 | strip_resetradio(sc, tp); |
| 552 | mutex_spin_exit(&tty_lock); |
| 553 | |
| 554 | /* |
| 555 | * Start the watchdog timer to get the radio |
| 556 | * "probe-for-death"/reset machine going. |
| 557 | */ |
| 558 | sc->sc_if.if_timer = STRIP_WATCHDOG_INTERVAL; |
| 559 | |
| 560 | return (0); |
| 561 | } |
| 562 | } |
| 563 | return (ENXIO); |
| 564 | } |
| 565 | |
| 566 | /* |
| 567 | * Line specific close routine. |
| 568 | * Detach the tty from the strip unit. |
| 569 | */ |
| 570 | static int |
| 571 | stripclose(struct tty *tp, int flag) |
| 572 | { |
| 573 | struct strip_softc *sc; |
| 574 | int s; |
| 575 | |
| 576 | ttywflush(tp); |
| 577 | sc = tp->t_sc; |
| 578 | |
| 579 | if (sc != NULL) { |
| 580 | softint_disestablish(sc->sc_si); |
| 581 | s = splnet(); |
| 582 | /* |
| 583 | * Cancel watchdog timer, which stops the "probe-for-death"/ |
| 584 | * reset machine. |
| 585 | */ |
| 586 | sc->sc_if.if_timer = 0; |
| 587 | if_down(&sc->sc_if); |
| 588 | IF_PURGE(&sc->sc_fastq); |
| 589 | splx(s); |
| 590 | |
| 591 | s = spltty(); |
| 592 | ttyldisc_release(tp->t_linesw); |
| 593 | tp->t_linesw = ttyldisc_default(); |
| 594 | tp->t_state = 0; |
| 595 | |
| 596 | sc->sc_ttyp = NULL; |
| 597 | tp->t_sc = NULL; |
| 598 | |
| 599 | m_freem(sc->sc_mbuf); |
| 600 | sc->sc_mbuf = NULL; |
| 601 | sc->sc_ep = sc->sc_mp = sc->sc_pktstart = NULL; |
| 602 | IF_PURGE(&sc->sc_inq); |
| 603 | |
| 604 | /* XXX */ |
| 605 | free((void *)(sc->sc_rxbuf - SLBUFSIZE + SLMAX), M_DEVBUF); |
| 606 | sc->sc_rxbuf = NULL; |
| 607 | |
| 608 | /* XXX */ |
| 609 | free((void *)(sc->sc_txbuf - SLBUFSIZE + SLMAX), M_DEVBUF); |
| 610 | sc->sc_txbuf = NULL; |
| 611 | |
| 612 | if (sc->sc_flags & SC_TIMEOUT) { |
| 613 | callout_stop(&sc->sc_timo_ch); |
| 614 | sc->sc_flags &= ~SC_TIMEOUT; |
| 615 | } |
| 616 | |
| 617 | /* |
| 618 | * If necessary, install a new outq buffer of the |
| 619 | * appropriate size. |
| 620 | */ |
| 621 | if (sc->sc_oldbufsize != 0) { |
| 622 | clfree(&tp->t_outq); |
| 623 | clalloc(&tp->t_outq, sc->sc_oldbufsize, |
| 624 | sc->sc_oldbufquot); |
| 625 | } |
| 626 | splx(s); |
| 627 | } |
| 628 | |
| 629 | return (0); |
| 630 | } |
| 631 | |
| 632 | /* |
| 633 | * Line specific (tty) ioctl routine. |
| 634 | * Provide a way to get the sl unit number. |
| 635 | */ |
| 636 | /* ARGSUSED */ |
| 637 | int |
| 638 | striptioctl(struct tty *tp, u_long cmd, void *data, int flag, |
| 639 | struct lwp *l) |
| 640 | { |
| 641 | struct strip_softc *sc = (struct strip_softc *)tp->t_sc; |
| 642 | |
| 643 | switch (cmd) { |
| 644 | case SLIOCGUNIT: |
| 645 | *(int *)data = sc->sc_unit; |
| 646 | break; |
| 647 | |
| 648 | default: |
| 649 | return (EPASSTHROUGH); |
| 650 | } |
| 651 | return (0); |
| 652 | } |
| 653 | |
| 654 | /* |
| 655 | * Take an mbuf chain containing a STRIP packet (no link-level header), |
| 656 | * byte-stuff (escape) it, and enqueue it on the tty send queue. |
| 657 | */ |
| 658 | void |
| 659 | strip_sendbody(struct strip_softc *sc, struct mbuf *m) |
| 660 | { |
| 661 | struct tty *tp = sc->sc_ttyp; |
| 662 | u_char *dp = sc->sc_txbuf; |
| 663 | struct mbuf *m2; |
| 664 | int len; |
| 665 | u_char *rllstate_ptr = NULL; |
| 666 | |
| 667 | while (m) { |
| 668 | if (m->m_len != 0) { |
| 669 | /* |
| 670 | * Byte-stuff/run-length encode this mbuf's data |
| 671 | * into the output buffer. |
| 672 | * XXX Note that chained calls to stuffdata() |
| 673 | * require that the stuffed data be left in the |
| 674 | * output buffer until the entire packet is encoded. |
| 675 | */ |
| 676 | dp = StuffData(mtod(m, u_char *), m->m_len, dp, |
| 677 | &rllstate_ptr); |
| 678 | } |
| 679 | m = m2 = m_free(m); |
| 680 | } |
| 681 | |
| 682 | /* |
| 683 | * Put the entire stuffed packet into the tty output queue. |
| 684 | */ |
| 685 | len = dp - sc->sc_txbuf; |
| 686 | if (b_to_q((ttychar_t *)sc->sc_txbuf, len, &tp->t_outq)) { |
| 687 | if (sc->sc_if.if_flags & IFF_DEBUG) |
| 688 | addlog("%s: tty output overflow\n" , |
| 689 | sc->sc_if.if_xname); |
| 690 | return; |
| 691 | } |
| 692 | sc->sc_if.if_obytes += len; |
| 693 | } |
| 694 | |
| 695 | /* |
| 696 | * Send a STRIP packet. Must be called at spltty(). |
| 697 | */ |
| 698 | void |
| 699 | strip_send(struct strip_softc *sc, struct mbuf *m0) |
| 700 | { |
| 701 | struct tty *tp = sc->sc_ttyp; |
| 702 | struct st_header *hdr; |
| 703 | |
| 704 | /* |
| 705 | * Send starmode header (unstuffed). |
| 706 | */ |
| 707 | hdr = mtod(m0, struct st_header *); |
| 708 | if (b_to_q((ttychar_t *)hdr, STRIP_HDRLEN, &tp->t_outq)) { |
| 709 | if (sc->sc_if.if_flags & IFF_DEBUG) |
| 710 | addlog("%s: outq overflow writing header\n" , |
| 711 | sc->sc_if.if_xname); |
| 712 | m_freem(m0); |
| 713 | return; |
| 714 | } |
| 715 | |
| 716 | m_adj(m0, sizeof(struct st_header)); |
| 717 | |
| 718 | /* Byte-stuff and run-length encode the remainder of the packet. */ |
| 719 | strip_sendbody(sc, m0); |
| 720 | |
| 721 | if (putc(STRIP_FRAME_END, &tp->t_outq)) { |
| 722 | /* |
| 723 | * Not enough room. Remove a char to make room |
| 724 | * and end the packet normally. |
| 725 | * If you get many collisions (more than one or two |
| 726 | * a day) you probably do not have enough clists |
| 727 | * and you should increase "nclist" in param.c. |
| 728 | */ |
| 729 | (void) unputc(&tp->t_outq); |
| 730 | (void) putc(STRIP_FRAME_END, &tp->t_outq); |
| 731 | sc->sc_if.if_collisions++; |
| 732 | } else { |
| 733 | ++sc->sc_if.if_obytes; |
| 734 | sc->sc_if.if_opackets++; |
| 735 | } |
| 736 | |
| 737 | /* |
| 738 | * If a radio probe is due now, append it to this packet rather |
| 739 | * than waiting until the watchdog routine next runs. |
| 740 | */ |
| 741 | if (time_second >= sc->sc_statetimo && sc->sc_state == ST_ALIVE) |
| 742 | strip_proberadio(sc, tp); |
| 743 | } |
| 744 | |
| 745 | /* |
| 746 | * Queue a packet. Start transmission if not active. |
| 747 | * Compression happens in stripintr(); if we do it here, IP TOS |
| 748 | * will cause us to not compress "background" packets, because |
| 749 | * ordering gets trashed. It can be done for all packets in stripintr(). |
| 750 | */ |
| 751 | int |
| 752 | stripoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, |
| 753 | const struct rtentry *rt) |
| 754 | { |
| 755 | struct strip_softc *sc = ifp->if_softc; |
| 756 | struct ip *ip; |
| 757 | struct st_header *shp; |
| 758 | const u_char *dldst; /* link-level next-hop */ |
| 759 | struct ifqueue *ifq; |
| 760 | int s, error; |
| 761 | u_char dl_addrbuf[STARMODE_ADDR_LEN+1]; |
| 762 | |
| 763 | /* |
| 764 | * Verify tty line is up and alive. |
| 765 | */ |
| 766 | if (sc->sc_ttyp == NULL) { |
| 767 | m_freem(m); |
| 768 | return (ENETDOWN); /* sort of */ |
| 769 | } |
| 770 | if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0 && |
| 771 | (sc->sc_ttyp->t_cflag & CLOCAL) == 0) { |
| 772 | m_freem(m); |
| 773 | return (EHOSTUNREACH); |
| 774 | } |
| 775 | |
| 776 | #ifdef DEBUG |
| 777 | if (rt) { |
| 778 | printf("stripout, rt: dst af%d gw af%d" , |
| 779 | rt_getkey(rt)->sa_family, rt->rt_gateway->sa_family); |
| 780 | if (rt_getkey(rt)->sa_family == AF_INET) |
| 781 | printf(" dst %x" , |
| 782 | satocsin(rt_getkey(rt))->sin_addr.s_addr); |
| 783 | printf("\n" ); |
| 784 | } |
| 785 | #endif |
| 786 | switch (dst->sa_family) { |
| 787 | case AF_INET: |
| 788 | /* assume rt is never NULL */ |
| 789 | if (rt == NULL || rt->rt_gateway->sa_family != AF_LINK || |
| 790 | satocsdl(rt->rt_gateway)->sdl_alen != ifp->if_addrlen) { |
| 791 | DPRINTF(("strip: could not arp starmode addr %x\n" , |
| 792 | satocsin(dst)->sin_addr.s_addr)); |
| 793 | m_freem(m); |
| 794 | return (EHOSTUNREACH); |
| 795 | } |
| 796 | dldst = CLLADDR(satocsdl(rt->rt_gateway)); |
| 797 | break; |
| 798 | |
| 799 | case AF_LINK: |
| 800 | dldst = CLLADDR(satocsdl(dst)); |
| 801 | break; |
| 802 | |
| 803 | default: |
| 804 | /* |
| 805 | * `Cannot happen' (see stripioctl). Someday we will extend |
| 806 | * the line protocol to support other address families. |
| 807 | */ |
| 808 | printf("%s: af %d not supported\n" , sc->sc_if.if_xname, |
| 809 | dst->sa_family); |
| 810 | m_freem(m); |
| 811 | sc->sc_if.if_noproto++; |
| 812 | return (EAFNOSUPPORT); |
| 813 | } |
| 814 | |
| 815 | ip = mtod(m, struct ip *); |
| 816 | #ifdef INET |
| 817 | if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { |
| 818 | m_freem(m); |
| 819 | return (ENETRESET); /* XXX ? */ |
| 820 | } |
| 821 | if ((ip->ip_tos & IPTOS_LOWDELAY) != 0 |
| 822 | #ifdef ALTQ |
| 823 | && ALTQ_IS_ENABLED(&ifp->if_snd) == 0 |
| 824 | #endif |
| 825 | ) |
| 826 | ifq = &sc->sc_fastq; |
| 827 | else |
| 828 | #endif |
| 829 | ifq = NULL; |
| 830 | |
| 831 | /* |
| 832 | * Add local net header. If no space in first mbuf, |
| 833 | * add another. |
| 834 | */ |
| 835 | M_PREPEND(m, sizeof(struct st_header), M_DONTWAIT); |
| 836 | if (m == 0) { |
| 837 | DPRINTF(("strip: could not prepend starmode header\n" )); |
| 838 | return (ENOBUFS); |
| 839 | } |
| 840 | |
| 841 | /* |
| 842 | * Unpack BCD route entry into an ASCII starmode address. |
| 843 | */ |
| 844 | dl_addrbuf[0] = '*'; |
| 845 | |
| 846 | dl_addrbuf[1] = ((dldst[0] >> 4) & 0x0f) + '0'; |
| 847 | dl_addrbuf[2] = ((dldst[0] ) & 0x0f) + '0'; |
| 848 | |
| 849 | dl_addrbuf[3] = ((dldst[1] >> 4) & 0x0f) + '0'; |
| 850 | dl_addrbuf[4] = ((dldst[1] ) & 0x0f) + '0'; |
| 851 | |
| 852 | dl_addrbuf[5] = '-'; |
| 853 | |
| 854 | dl_addrbuf[6] = ((dldst[2] >> 4) & 0x0f) + '0'; |
| 855 | dl_addrbuf[7] = ((dldst[2] ) & 0x0f) + '0'; |
| 856 | |
| 857 | dl_addrbuf[8] = ((dldst[3] >> 4) & 0x0f) + '0'; |
| 858 | dl_addrbuf[9] = ((dldst[3] ) & 0x0f) + '0'; |
| 859 | |
| 860 | dl_addrbuf[10] = '*'; |
| 861 | dl_addrbuf[11] = 0; |
| 862 | dldst = dl_addrbuf; |
| 863 | |
| 864 | shp = mtod(m, struct st_header *); |
| 865 | memcpy(&shp->starmode_type, "SIP0" , sizeof(shp->starmode_type)); |
| 866 | |
| 867 | memcpy(shp->starmode_addr, dldst, sizeof(shp->starmode_addr)); |
| 868 | |
| 869 | s = spltty(); |
| 870 | if (sc->sc_oqlen && sc->sc_ttyp->t_outq.c_cc == sc->sc_oqlen) { |
| 871 | struct bintime bt; |
| 872 | |
| 873 | /* if output's been stalled for too long, and restart */ |
| 874 | getbinuptime(&bt); |
| 875 | bintime_sub(&bt, &sc->sc_lastpacket); |
| 876 | if (bt.sec > 0) { |
| 877 | DPRINTF(("stripoutput: stalled, resetting\n" )); |
| 878 | sc->sc_otimeout++; |
| 879 | stripstart(sc->sc_ttyp); |
| 880 | } |
| 881 | } |
| 882 | splx(s); |
| 883 | |
| 884 | s = splnet(); |
| 885 | if ((error = ifq_enqueue2(ifp, ifq, m)) != 0) { |
| 886 | splx(s); |
| 887 | return error; |
| 888 | } |
| 889 | getbinuptime(&sc->sc_lastpacket); |
| 890 | splx(s); |
| 891 | |
| 892 | s = spltty(); |
| 893 | stripstart(sc->sc_ttyp); |
| 894 | splx(s); |
| 895 | |
| 896 | return (0); |
| 897 | } |
| 898 | |
| 899 | |
| 900 | /* |
| 901 | * Start output on interface. Get another datagram |
| 902 | * to send from the interface queue and map it to |
| 903 | * the interface before starting output. |
| 904 | * |
| 905 | */ |
| 906 | int |
| 907 | stripstart(struct tty *tp) |
| 908 | { |
| 909 | struct strip_softc *sc = tp->t_sc; |
| 910 | |
| 911 | /* |
| 912 | * If there is more in the output queue, just send it now. |
| 913 | * We are being called in lieu of ttstart and must do what |
| 914 | * it would. |
| 915 | */ |
| 916 | if (tp->t_outq.c_cc != 0) { |
| 917 | (*tp->t_oproc)(tp); |
| 918 | if (tp->t_outq.c_cc > SLIP_HIWAT) |
| 919 | return (0); |
| 920 | } |
| 921 | |
| 922 | /* |
| 923 | * This happens briefly when the line shuts down. |
| 924 | */ |
| 925 | if (sc == NULL) |
| 926 | return (0); |
| 927 | softint_schedule(sc->sc_si); |
| 928 | return (0); |
| 929 | } |
| 930 | |
| 931 | /* |
| 932 | * Copy data buffer to mbuf chain; add ifnet pointer. |
| 933 | */ |
| 934 | static struct mbuf * |
| 935 | strip_btom(struct strip_softc *sc, int len) |
| 936 | { |
| 937 | struct mbuf *m; |
| 938 | |
| 939 | /* |
| 940 | * Allocate a new input buffer and swap. |
| 941 | */ |
| 942 | m = sc->sc_mbuf; |
| 943 | MGETHDR(sc->sc_mbuf, M_DONTWAIT, MT_DATA); |
| 944 | if (sc->sc_mbuf == NULL) { |
| 945 | sc->sc_mbuf = m; |
| 946 | return (NULL); |
| 947 | } |
| 948 | MCLGET(sc->sc_mbuf, M_DONTWAIT); |
| 949 | if ((sc->sc_mbuf->m_flags & M_EXT) == 0) { |
| 950 | m_freem(sc->sc_mbuf); |
| 951 | sc->sc_mbuf = m; |
| 952 | return (NULL); |
| 953 | } |
| 954 | sc->sc_ep = (u_char *) sc->sc_mbuf->m_ext.ext_buf + |
| 955 | sc->sc_mbuf->m_ext.ext_size; |
| 956 | |
| 957 | m->m_data = sc->sc_pktstart; |
| 958 | |
| 959 | m->m_pkthdr.len = m->m_len = len; |
| 960 | m_set_rcvif(m, &sc->sc_if); |
| 961 | return (m); |
| 962 | } |
| 963 | |
| 964 | /* |
| 965 | * tty interface receiver interrupt. |
| 966 | * |
| 967 | * Called with a single char from the tty receiver interrupt; put |
| 968 | * the char into the buffer containing a partial packet. If the |
| 969 | * char is a packet delimiter, decapsulate the packet, wrap it in |
| 970 | * an mbuf, and put it on the protocol input queue. |
| 971 | */ |
| 972 | int |
| 973 | stripinput(int c, struct tty *tp) |
| 974 | { |
| 975 | struct strip_softc *sc; |
| 976 | struct mbuf *m; |
| 977 | int len; |
| 978 | |
| 979 | tk_nin++; |
| 980 | sc = (struct strip_softc *)tp->t_sc; |
| 981 | if (sc == NULL) |
| 982 | return (0); |
| 983 | if (c & TTY_ERRORMASK || ((tp->t_state & TS_CARR_ON) == 0 && |
| 984 | (tp->t_cflag & CLOCAL) == 0)) { |
| 985 | sc->sc_flags |= SC_ERROR; |
| 986 | DPRINTF(("strip: input, error %x\n" , c)); /* XXX */ |
| 987 | return (0); |
| 988 | } |
| 989 | c &= TTY_CHARMASK; |
| 990 | |
| 991 | ++sc->sc_if.if_ibytes; |
| 992 | |
| 993 | /* |
| 994 | * Accumulate characters until we see a frame terminator (\r). |
| 995 | */ |
| 996 | switch (c) { |
| 997 | |
| 998 | case '\n': |
| 999 | /* |
| 1000 | * Error message strings from the modem are terminated with |
| 1001 | * \r\n. This driver interprets the \r as a packet terminator. |
| 1002 | * If the first character in a packet is a \n, drop it. |
| 1003 | * (it can never be the first char of a vaild frame). |
| 1004 | */ |
| 1005 | if (sc->sc_mp - sc->sc_pktstart == 0) |
| 1006 | break; |
| 1007 | |
| 1008 | /* Fall through to */ |
| 1009 | |
| 1010 | default: |
| 1011 | if (sc->sc_mp < sc->sc_ep) { |
| 1012 | *sc->sc_mp++ = c; |
| 1013 | } else { |
| 1014 | sc->sc_flags |= SC_ERROR; |
| 1015 | goto error; |
| 1016 | } |
| 1017 | return (0); |
| 1018 | |
| 1019 | case STRIP_FRAME_END: |
| 1020 | break; |
| 1021 | } |
| 1022 | |
| 1023 | |
| 1024 | /* |
| 1025 | * We only reach here if we see a CR delimiting a packet. |
| 1026 | */ |
| 1027 | |
| 1028 | |
| 1029 | len = sc->sc_mp - sc->sc_pktstart; |
| 1030 | |
| 1031 | #ifdef XDEBUG |
| 1032 | if (len < 15 || sc->sc_flags & SC_ERROR) |
| 1033 | printf("stripinput: end of pkt, len %d, err %d\n" , |
| 1034 | len, sc->sc_flags & SC_ERROR); /*XXX*/ |
| 1035 | #endif |
| 1036 | if(sc->sc_flags & SC_ERROR) { |
| 1037 | sc->sc_flags &= ~SC_ERROR; |
| 1038 | addlog("%s: sc error flag set. terminating packet\n" , |
| 1039 | sc->sc_if.if_xname); |
| 1040 | goto newpack; |
| 1041 | } |
| 1042 | |
| 1043 | /* |
| 1044 | * We have a frame. |
| 1045 | * Process an IP packet, ARP packet, AppleTalk packet, |
| 1046 | * AT command resposne, or Starmode error. |
| 1047 | */ |
| 1048 | len = strip_newpacket(sc, sc->sc_pktstart, sc->sc_mp); |
| 1049 | if (len <= 1) |
| 1050 | /* less than min length packet - ignore */ |
| 1051 | goto newpack; |
| 1052 | |
| 1053 | m = strip_btom(sc, len); |
| 1054 | if (m == NULL) |
| 1055 | goto error; |
| 1056 | |
| 1057 | IF_ENQUEUE(&sc->sc_inq, m); |
| 1058 | softint_schedule(sc->sc_si); |
| 1059 | goto newpack; |
| 1060 | |
| 1061 | error: |
| 1062 | sc->sc_if.if_ierrors++; |
| 1063 | |
| 1064 | newpack: |
| 1065 | sc->sc_mp = sc->sc_pktstart = (u_char *) sc->sc_mbuf->m_ext.ext_buf + |
| 1066 | BUFOFFSET; |
| 1067 | |
| 1068 | return (0); |
| 1069 | } |
| 1070 | |
| 1071 | static void |
| 1072 | stripintr(void *arg) |
| 1073 | { |
| 1074 | struct strip_softc *sc = arg; |
| 1075 | struct tty *tp = sc->sc_ttyp; |
| 1076 | struct mbuf *m; |
| 1077 | int s, len; |
| 1078 | u_char *pktstart; |
| 1079 | #ifdef INET |
| 1080 | u_char c; |
| 1081 | #endif |
| 1082 | u_char chdr[CHDR_LEN]; |
| 1083 | |
| 1084 | KASSERT(tp != NULL); |
| 1085 | |
| 1086 | /* |
| 1087 | * Output processing loop. |
| 1088 | */ |
| 1089 | mutex_enter(softnet_lock); |
| 1090 | for (;;) { |
| 1091 | #ifdef INET |
| 1092 | struct ip *ip; |
| 1093 | #endif |
| 1094 | struct mbuf *bpf_m; |
| 1095 | |
| 1096 | /* |
| 1097 | * Do not remove the packet from the queue if it |
| 1098 | * doesn't look like it will fit into the current |
| 1099 | * serial output queue (STRIP_MTU_ONWIRE, or |
| 1100 | * Starmode header + 20 bytes + 4 bytes in case we |
| 1101 | * have to probe the radio). |
| 1102 | */ |
| 1103 | s = spltty(); |
| 1104 | if (tp->t_outq.c_cn - tp->t_outq.c_cc < |
| 1105 | STRIP_MTU_ONWIRE + 4) { |
| 1106 | splx(s); |
| 1107 | break; |
| 1108 | } |
| 1109 | splx(s); |
| 1110 | |
| 1111 | /* |
| 1112 | * Get a packet and send it to the radio. |
| 1113 | */ |
| 1114 | s = splnet(); |
| 1115 | IF_DEQUEUE(&sc->sc_fastq, m); |
| 1116 | if (m) |
| 1117 | sc->sc_if.if_omcasts++; /* XXX */ |
| 1118 | else |
| 1119 | IFQ_DEQUEUE(&sc->sc_if.if_snd, m); |
| 1120 | splx(s); |
| 1121 | |
| 1122 | if (m == NULL) |
| 1123 | break; |
| 1124 | |
| 1125 | /* |
| 1126 | * We do the header compression here rather than in |
| 1127 | * stripoutput() because the packets will be out of |
| 1128 | * order if we are using TOS queueing, and the |
| 1129 | * connection ID compression will get munged when |
| 1130 | * this happens. |
| 1131 | */ |
| 1132 | if (sc->sc_if.if_bpf) { |
| 1133 | /* |
| 1134 | * We need to save the TCP/IP header before |
| 1135 | * it's compressed. To avoid complicated |
| 1136 | * code, we just make a deep copy of the |
| 1137 | * entire packet (since this is a serial |
| 1138 | * line, packets should be short and/or the |
| 1139 | * copy should be negligible cost compared |
| 1140 | * to the packet transmission time). |
| 1141 | */ |
| 1142 | bpf_m = m_dup(m, 0, M_COPYALL, M_DONTWAIT); |
| 1143 | } else |
| 1144 | bpf_m = NULL; |
| 1145 | #ifdef INET |
| 1146 | if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) { |
| 1147 | if (sc->sc_if.if_flags & SC_COMPRESS) |
| 1148 | *mtod(m, u_char *) |= |
| 1149 | sl_compress_tcp(m, ip, |
| 1150 | &sc->sc_comp, 1); |
| 1151 | } |
| 1152 | #endif |
| 1153 | if (bpf_m != NULL) |
| 1154 | bpf_mtap_sl_out(&sc->sc_if, mtod(m, u_char *), bpf_m); |
| 1155 | getbinuptime(&sc->sc_lastpacket); |
| 1156 | |
| 1157 | s = spltty(); |
| 1158 | strip_send(sc, m); |
| 1159 | |
| 1160 | /* |
| 1161 | * We now have characters in the output queue, |
| 1162 | * kick the serial port. |
| 1163 | */ |
| 1164 | if (tp->t_outq.c_cc != 0) |
| 1165 | (*tp->t_oproc)(tp); |
| 1166 | splx(s); |
| 1167 | } |
| 1168 | |
| 1169 | /* |
| 1170 | * Input processing loop. |
| 1171 | */ |
| 1172 | for (;;) { |
| 1173 | s = spltty(); |
| 1174 | IF_DEQUEUE(&sc->sc_inq, m); |
| 1175 | splx(s); |
| 1176 | if (m == NULL) |
| 1177 | break; |
| 1178 | pktstart = mtod(m, u_char *); |
| 1179 | len = m->m_pkthdr.len; |
| 1180 | if (sc->sc_if.if_bpf) { |
| 1181 | /* |
| 1182 | * Save the compressed header, so we |
| 1183 | * can tack it on later. Note that we |
| 1184 | * will end up copying garbage in come |
| 1185 | * cases but this is okay. We remember |
| 1186 | * where the buffer started so we can |
| 1187 | * compute the new header length. |
| 1188 | */ |
| 1189 | memcpy(chdr, pktstart, CHDR_LEN); |
| 1190 | } |
| 1191 | #ifdef INET |
| 1192 | if ((c = (*pktstart & 0xf0)) != (IPVERSION << 4)) { |
| 1193 | if (c & 0x80) |
| 1194 | c = TYPE_COMPRESSED_TCP; |
| 1195 | else if (c == TYPE_UNCOMPRESSED_TCP) |
| 1196 | *pktstart &= 0x4f; /* XXX */ |
| 1197 | /* |
| 1198 | * We've got something that's not an IP |
| 1199 | * packet. If compression is enabled, |
| 1200 | * try to decompress it. Otherwise, if |
| 1201 | * `auto-enable' compression is on and |
| 1202 | * it's a reasonable packet, decompress |
| 1203 | * it and then enable compression. |
| 1204 | * Otherwise, drop it. |
| 1205 | */ |
| 1206 | if (sc->sc_if.if_flags & SC_COMPRESS) { |
| 1207 | len = sl_uncompress_tcp(&pktstart, len, |
| 1208 | (u_int)c, &sc->sc_comp); |
| 1209 | if (len <= 0) { |
| 1210 | m_freem(m); |
| 1211 | continue; |
| 1212 | } |
| 1213 | } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) && |
| 1214 | c == TYPE_UNCOMPRESSED_TCP && len >= 40) { |
| 1215 | len = sl_uncompress_tcp(&pktstart, len, |
| 1216 | (u_int)c, &sc->sc_comp); |
| 1217 | if (len <= 0) { |
| 1218 | m_freem(m); |
| 1219 | continue; |
| 1220 | } |
| 1221 | sc->sc_if.if_flags |= SC_COMPRESS; |
| 1222 | } else { |
| 1223 | m_freem(m); |
| 1224 | continue; |
| 1225 | } |
| 1226 | } |
| 1227 | #endif |
| 1228 | m->m_data = (void *) pktstart; |
| 1229 | m->m_pkthdr.len = m->m_len = len; |
| 1230 | if (sc->sc_if.if_bpf) { |
| 1231 | bpf_mtap_sl_in(&sc->sc_if, chdr, &m); |
| 1232 | if (m == NULL) |
| 1233 | continue; |
| 1234 | } |
| 1235 | /* |
| 1236 | * If the packet will fit into a single |
| 1237 | * header mbuf, copy it into one, to save |
| 1238 | * memory. |
| 1239 | */ |
| 1240 | if (m->m_pkthdr.len < MHLEN) { |
| 1241 | struct mbuf *n; |
| 1242 | int pktlen; |
| 1243 | |
| 1244 | MGETHDR(n, M_DONTWAIT, MT_DATA); |
| 1245 | pktlen = m->m_pkthdr.len; |
| 1246 | M_MOVE_PKTHDR(n, m); |
| 1247 | memcpy(mtod(n, void *), mtod(m, void *), pktlen); |
| 1248 | n->m_len = m->m_len; |
| 1249 | m_freem(m); |
| 1250 | m = n; |
| 1251 | } |
| 1252 | |
| 1253 | sc->sc_if.if_ipackets++; |
| 1254 | getbinuptime(&sc->sc_lastpacket); |
| 1255 | |
| 1256 | #ifdef INET |
| 1257 | s = splnet(); |
| 1258 | if (__predict_false(!pktq_enqueue(ip_pktq, m, 0))) { |
| 1259 | sc->sc_if.if_ierrors++; |
| 1260 | sc->sc_if.if_iqdrops++; |
| 1261 | m_freem(m); |
| 1262 | } |
| 1263 | splx(s); |
| 1264 | #endif |
| 1265 | } |
| 1266 | mutex_exit(softnet_lock); |
| 1267 | } |
| 1268 | |
| 1269 | /* |
| 1270 | * Process an ioctl request. |
| 1271 | */ |
| 1272 | int |
| 1273 | stripioctl(struct ifnet *ifp, u_long cmd, void *data) |
| 1274 | { |
| 1275 | struct ifaddr *ifa = (struct ifaddr *)data; |
| 1276 | struct ifreq *ifr; |
| 1277 | int s, error = 0; |
| 1278 | |
| 1279 | s = splnet(); |
| 1280 | |
| 1281 | switch (cmd) { |
| 1282 | |
| 1283 | case SIOCINITIFADDR: |
| 1284 | if (ifa->ifa_addr->sa_family == AF_INET) |
| 1285 | ifp->if_flags |= IFF_UP; |
| 1286 | else |
| 1287 | error = EAFNOSUPPORT; |
| 1288 | break; |
| 1289 | |
| 1290 | case SIOCSIFDSTADDR: |
| 1291 | if (ifa->ifa_addr->sa_family != AF_INET) |
| 1292 | error = EAFNOSUPPORT; |
| 1293 | break; |
| 1294 | |
| 1295 | case SIOCADDMULTI: |
| 1296 | case SIOCDELMULTI: |
| 1297 | ifr = (struct ifreq *)data; |
| 1298 | if (ifr == 0) { |
| 1299 | error = EAFNOSUPPORT; /* XXX */ |
| 1300 | break; |
| 1301 | } |
| 1302 | switch (ifreq_getaddr(cmd, ifr)->sa_family) { |
| 1303 | |
| 1304 | #ifdef INET |
| 1305 | case AF_INET: |
| 1306 | break; |
| 1307 | #endif |
| 1308 | |
| 1309 | default: |
| 1310 | error = EAFNOSUPPORT; |
| 1311 | break; |
| 1312 | } |
| 1313 | break; |
| 1314 | |
| 1315 | default: |
| 1316 | error = ifioctl_common(ifp, cmd, data); |
| 1317 | } |
| 1318 | splx(s); |
| 1319 | return (error); |
| 1320 | } |
| 1321 | |
| 1322 | |
| 1323 | /* |
| 1324 | * Strip subroutines |
| 1325 | */ |
| 1326 | |
| 1327 | /* |
| 1328 | * Set a radio into starmode. |
| 1329 | * Must be called at spltty(). |
| 1330 | */ |
| 1331 | void |
| 1332 | strip_resetradio(struct strip_softc *sc, struct tty *tp) |
| 1333 | { |
| 1334 | #if 0 |
| 1335 | static ttychar_t InitString[] = |
| 1336 | "\r\n\r\n\r\nat\r\n\r\n\r\nate0dt**starmode\r\n**\r\n" ; |
| 1337 | #else |
| 1338 | static ttychar_t InitString[] = |
| 1339 | "\r\rat\r\r\rate0q1dt**starmode\r**\r" ; |
| 1340 | #endif |
| 1341 | int i; |
| 1342 | |
| 1343 | /* |
| 1344 | * XXX Perhaps flush tty output queue? |
| 1345 | */ |
| 1346 | |
| 1347 | if ((i = b_to_q(InitString, sizeof(InitString) - 1, &tp->t_outq))) { |
| 1348 | printf("resetradio: %d chars didn't fit in tty queue\n" , i); |
| 1349 | return; |
| 1350 | } |
| 1351 | sc->sc_if.if_obytes += sizeof(InitString) - 1; |
| 1352 | |
| 1353 | /* |
| 1354 | * Assume the radio is still dead, so we can detect repeated |
| 1355 | * resets (perhaps the radio is disconnected, powered off, or |
| 1356 | * is so badlyhung it needs powercycling. |
| 1357 | */ |
| 1358 | sc->sc_state = ST_DEAD; |
| 1359 | getbinuptime(&sc->sc_lastpacket); |
| 1360 | sc->sc_statetimo = time_second + STRIP_RESET_INTERVAL; |
| 1361 | |
| 1362 | /* |
| 1363 | * XXX Does calling the tty output routine now help resets? |
| 1364 | */ |
| 1365 | (*sc->sc_ttyp->t_oproc)(tp); |
| 1366 | } |
| 1367 | |
| 1368 | |
| 1369 | /* |
| 1370 | * Send an invalid starmode packet to the radio, to induce an error message |
| 1371 | * indicating the radio is in starmode. |
| 1372 | * Update the state machine to indicate a response is expected. |
| 1373 | * Either the radio answers, which will be caught by the parser, |
| 1374 | * or the watchdog will start resetting. |
| 1375 | * |
| 1376 | * NOTE: drops chars directly on the tty output queue. |
| 1377 | * should be caled at spl >= spltty. |
| 1378 | */ |
| 1379 | void |
| 1380 | strip_proberadio(struct strip_softc *sc, struct tty *tp) |
| 1381 | { |
| 1382 | |
| 1383 | int overflow; |
| 1384 | const char *strip_probestr = "**" ; |
| 1385 | |
| 1386 | if (sc->sc_if.if_flags & IFF_DEBUG) |
| 1387 | addlog("%s: attempting to probe radio\n" , sc->sc_if.if_xname); |
| 1388 | |
| 1389 | overflow = b_to_q((const ttychar_t *)strip_probestr, 2, &tp->t_outq); |
| 1390 | if (overflow == 0) { |
| 1391 | if (sc->sc_if.if_flags & IFF_DEBUG) |
| 1392 | addlog("%s:: sent probe to radio\n" , |
| 1393 | sc->sc_if.if_xname); |
| 1394 | /* Go to probe-sent state, set timeout accordingly. */ |
| 1395 | sc->sc_state = ST_PROBE_SENT; |
| 1396 | sc->sc_statetimo = time_second + ST_PROBERESPONSE_INTERVAL; |
| 1397 | } else { |
| 1398 | addlog("%s: incomplete probe, tty queue %d bytes overfull\n" , |
| 1399 | sc->sc_if.if_xname, overflow); |
| 1400 | } |
| 1401 | } |
| 1402 | |
| 1403 | |
| 1404 | #ifdef DEBUG |
| 1405 | static const char *strip_statenames[] = { |
| 1406 | "Alive" , |
| 1407 | "Probe sent, awaiting answer" , |
| 1408 | "Probe not answered, resetting" |
| 1409 | }; |
| 1410 | #endif |
| 1411 | |
| 1412 | |
| 1413 | /* |
| 1414 | * Timeout routine -- try to start more output. |
| 1415 | * Will be needed to make strip work on ptys. |
| 1416 | */ |
| 1417 | void |
| 1418 | strip_timeout(void *x) |
| 1419 | { |
| 1420 | struct strip_softc *sc = (struct strip_softc *) x; |
| 1421 | struct tty *tp = sc->sc_ttyp; |
| 1422 | int s; |
| 1423 | |
| 1424 | s = spltty(); |
| 1425 | sc->sc_flags &= ~SC_TIMEOUT; |
| 1426 | stripstart(tp); |
| 1427 | splx(s); |
| 1428 | } |
| 1429 | |
| 1430 | |
| 1431 | /* |
| 1432 | * Strip watchdog routine. |
| 1433 | * The radio hardware is balky. When sent long packets or bursts of small |
| 1434 | * packets, the radios crash and reboots into Hayes-emulation mode. |
| 1435 | * The transmit-side machinery, the error parser, and strip_watchdog() |
| 1436 | * implement a simple finite state machine. |
| 1437 | * |
| 1438 | * We attempt to send a probe to the radio every ST_PROBE seconds. There |
| 1439 | * is no direct way to tell if the radio is in starmode, so we send it a |
| 1440 | * malformed starmode packet -- a frame with no destination address -- |
| 1441 | * and expect to an "name missing" error response from the radio within |
| 1442 | * 1 second. If we hear such a response, we assume the radio is alive |
| 1443 | * for the next ST_PROBE seconds. |
| 1444 | * If we don't hear a starmode-error response from the radio, we reset it. |
| 1445 | * |
| 1446 | * Probes, and parsing of error responses, are normally done inside the send |
| 1447 | * and receive side respectively. This watchdog routine examines the |
| 1448 | * state-machine variables. If there are no packets to send to the radio |
| 1449 | * during an entire probe interval, strip_output will not be called, |
| 1450 | * so we send a probe on its behalf. |
| 1451 | */ |
| 1452 | void |
| 1453 | strip_watchdog(struct ifnet *ifp) |
| 1454 | { |
| 1455 | struct strip_softc *sc = ifp->if_softc; |
| 1456 | struct tty *tp = sc->sc_ttyp; |
| 1457 | |
| 1458 | /* |
| 1459 | * Just punt if the line has been closed. |
| 1460 | */ |
| 1461 | if (tp == NULL) |
| 1462 | return; |
| 1463 | |
| 1464 | #ifdef DEBUG |
| 1465 | if (ifp->if_flags & IFF_DEBUG) |
| 1466 | addlog("\n%s: in watchdog, state %s timeout %lld\n" , |
| 1467 | ifp->if_xname, |
| 1468 | ((unsigned) sc->sc_state < 3) ? |
| 1469 | strip_statenames[sc->sc_state] : "<<illegal state>>" , |
| 1470 | (long long)(sc->sc_statetimo - time_second)); |
| 1471 | #endif |
| 1472 | |
| 1473 | /* |
| 1474 | * If time in this state hasn't yet expired, return. |
| 1475 | */ |
| 1476 | if ((ifp->if_flags & IFF_UP) == 0 || sc->sc_statetimo > time_second) { |
| 1477 | goto done; |
| 1478 | } |
| 1479 | |
| 1480 | /* |
| 1481 | * The time in the current state has expired. |
| 1482 | * Take appropriate action and advance FSA to the next state. |
| 1483 | */ |
| 1484 | switch (sc->sc_state) { |
| 1485 | case ST_ALIVE: |
| 1486 | /* |
| 1487 | * A probe is due but we haven't piggybacked one on a packet. |
| 1488 | * Send a probe now. |
| 1489 | */ |
| 1490 | strip_proberadio(sc, sc->sc_ttyp); |
| 1491 | (*tp->t_oproc)(tp); |
| 1492 | break; |
| 1493 | |
| 1494 | case ST_PROBE_SENT: |
| 1495 | /* |
| 1496 | * Probe sent but no response within timeout. Reset. |
| 1497 | */ |
| 1498 | addlog("%s: no answer to probe, resetting radio\n" , |
| 1499 | ifp->if_xname); |
| 1500 | strip_resetradio(sc, sc->sc_ttyp); |
| 1501 | ifp->if_oerrors++; |
| 1502 | break; |
| 1503 | |
| 1504 | case ST_DEAD: |
| 1505 | /* |
| 1506 | * The radio has been sent a reset but didn't respond. |
| 1507 | * XXX warn user to remove AC adaptor and battery, |
| 1508 | * wait 5 secs, and replace. |
| 1509 | */ |
| 1510 | addlog("%s: radio reset but not responding, Trying again\n" , |
| 1511 | ifp->if_xname); |
| 1512 | strip_resetradio(sc, sc->sc_ttyp); |
| 1513 | ifp->if_oerrors++; |
| 1514 | break; |
| 1515 | |
| 1516 | default: |
| 1517 | /* Cannot happen. To be safe, do a reset. */ |
| 1518 | addlog("%s: %s %d, resetting\n" , |
| 1519 | sc->sc_if.if_xname, |
| 1520 | "radio-reset finite-state machine in invalid state" , |
| 1521 | sc->sc_state); |
| 1522 | strip_resetradio(sc, sc->sc_ttyp); |
| 1523 | sc->sc_state = ST_DEAD; |
| 1524 | break; |
| 1525 | } |
| 1526 | |
| 1527 | done: |
| 1528 | ifp->if_timer = STRIP_WATCHDOG_INTERVAL; |
| 1529 | return; |
| 1530 | } |
| 1531 | |
| 1532 | |
| 1533 | /* |
| 1534 | * The following bytestuffing and run-length encoding/decoding |
| 1535 | * functions are taken, with permission from Stuart Cheshire, |
| 1536 | * from the MosquitonNet strip driver for Linux. |
| 1537 | * XXX Linux style left intact, to ease folding in updates from |
| 1538 | * the Mosquitonet group. |
| 1539 | */ |
| 1540 | |
| 1541 | |
| 1542 | /* |
| 1543 | * Process a received packet. |
| 1544 | */ |
| 1545 | int |
| 1546 | strip_newpacket(struct strip_softc *sc, u_char *ptr, u_char *end) |
| 1547 | { |
| 1548 | int len = ptr - end; |
| 1549 | u_char *name, *name_end; |
| 1550 | u_int packetlen; |
| 1551 | |
| 1552 | /* Ignore empty lines */ |
| 1553 | if (len == 0) return 0; |
| 1554 | |
| 1555 | /* Catch 'OK' responses which show radio has fallen out of starmode */ |
| 1556 | if (len >= 2 && ptr[0] == 'O' && ptr[1] == 'K') { |
| 1557 | printf("%s: Radio is back in AT command mode: will reset\n" , |
| 1558 | sc->sc_if.if_xname); |
| 1559 | FORCE_RESET(sc); /* Do reset ASAP */ |
| 1560 | return 0; |
| 1561 | } |
| 1562 | |
| 1563 | /* Check for start of address marker, and then skip over it */ |
| 1564 | if (*ptr != '*') { |
| 1565 | /* Catch other error messages */ |
| 1566 | if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_') |
| 1567 | RecvErr_Message(sc, NULL, ptr+4); |
| 1568 | /* XXX what should the message above be? */ |
| 1569 | else { |
| 1570 | RecvErr("No initial *" , sc); |
| 1571 | addlog("(len = %d)\n" , len); |
| 1572 | } |
| 1573 | return 0; |
| 1574 | } |
| 1575 | |
| 1576 | /* skip the '*' */ |
| 1577 | ptr++; |
| 1578 | |
| 1579 | /* Skip the return address */ |
| 1580 | name = ptr; |
| 1581 | while (ptr < end && *ptr != '*') |
| 1582 | ptr++; |
| 1583 | |
| 1584 | /* Check for end of address marker, and skip over it */ |
| 1585 | if (ptr == end) { |
| 1586 | RecvErr("No second *" , sc); |
| 1587 | return 0; |
| 1588 | } |
| 1589 | name_end = ptr++; |
| 1590 | |
| 1591 | /* Check for SRIP key, and skip over it */ |
| 1592 | if (ptr[0] != 'S' || ptr[1] != 'I' || ptr[2] != 'P' || ptr[3] != '0') { |
| 1593 | if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && |
| 1594 | ptr[3] == '_') { |
| 1595 | *name_end = 0; |
| 1596 | RecvErr_Message(sc, name, ptr+4); |
| 1597 | } |
| 1598 | else RecvErr("No SRIP key" , sc); |
| 1599 | return 0; |
| 1600 | } |
| 1601 | ptr += 4; |
| 1602 | |
| 1603 | /* Decode start of the IP packet header */ |
| 1604 | ptr = UnStuffData(ptr, end, sc->sc_rxbuf, 4); |
| 1605 | if (ptr == 0) { |
| 1606 | RecvErr("Runt packet (hdr)" , sc); |
| 1607 | return 0; |
| 1608 | } |
| 1609 | |
| 1610 | /* |
| 1611 | * The STRIP bytestuff/RLL encoding has no explicit length |
| 1612 | * of the decoded packet. Decode start of IP header, get the |
| 1613 | * IP header length and decode that many bytes in total. |
| 1614 | */ |
| 1615 | packetlen = ((uint16_t)sc->sc_rxbuf[2] << 8) | sc->sc_rxbuf[3]; |
| 1616 | |
| 1617 | #ifdef DIAGNOSTIC |
| 1618 | #if 0 |
| 1619 | printf("Packet %02x.%02x.%02x.%02x\n" , |
| 1620 | sc->sc_rxbuf[0], sc->sc_rxbuf[1], |
| 1621 | sc->sc_rxbuf[2], sc->sc_rxbuf[3]); |
| 1622 | printf("Got %d byte packet\n" , packetlen); |
| 1623 | #endif |
| 1624 | #endif |
| 1625 | |
| 1626 | /* Decode remainder of the IP packer */ |
| 1627 | ptr = UnStuffData(ptr, end, sc->sc_rxbuf+4, packetlen-4); |
| 1628 | if (ptr == 0) { |
| 1629 | RecvErr("Short packet" , sc); |
| 1630 | return 0; |
| 1631 | } |
| 1632 | |
| 1633 | /* XXX redundant copy */ |
| 1634 | memcpy(sc->sc_pktstart, sc->sc_rxbuf, packetlen ); |
| 1635 | return (packetlen); |
| 1636 | } |
| 1637 | |
| 1638 | |
| 1639 | /* |
| 1640 | * Stuffing scheme: |
| 1641 | * 00 Unused (reserved character) |
| 1642 | * 01-3F Run of 2-64 different characters |
| 1643 | * 40-7F Run of 1-64 different characters plus a single zero at the end |
| 1644 | * 80-BF Run of 1-64 of the same character |
| 1645 | * C0-FF Run of 1-64 zeroes (ASCII 0) |
| 1646 | */ |
| 1647 | typedef enum |
| 1648 | { |
| 1649 | Stuff_Diff = 0x00, |
| 1650 | Stuff_DiffZero = 0x40, |
| 1651 | Stuff_Same = 0x80, |
| 1652 | Stuff_Zero = 0xC0, |
| 1653 | Stuff_NoCode = 0xFF, /* Special code, meaning no code selected */ |
| 1654 | |
| 1655 | Stuff_CodeMask = 0xC0, |
| 1656 | Stuff_CountMask = 0x3F, |
| 1657 | Stuff_MaxCount = 0x3F, |
| 1658 | Stuff_Magic = 0x0D /* The value we are eliminating */ |
| 1659 | } StuffingCode; |
| 1660 | |
| 1661 | /* |
| 1662 | * StuffData encodes the data starting at "src" for "length" bytes. |
| 1663 | * It writes it to the buffer pointed to by "dest" (which must be at least |
| 1664 | * as long as 1 + 65/64 of the input length). The output may be up to 1.6% |
| 1665 | * larger than the input for pathological input, but will usually be smaller. |
| 1666 | * StuffData returns the new value of the dest pointer as its result. |
| 1667 | * |
| 1668 | * "code_ptr_ptr" points to a "u_char *" which is used to hold |
| 1669 | * encoding state between calls, allowing an encoded packet to be |
| 1670 | * incrementally built up from small parts. |
| 1671 | * On the first call, the "u_char *" pointed to should be initialized |
| 1672 | * to NULL; between subsequent calls the calling routine should leave |
| 1673 | * the value alone and simply pass it back unchanged so that the |
| 1674 | * encoder can recover its current state. |
| 1675 | */ |
| 1676 | |
| 1677 | #define StuffData_FinishBlock(X) \ |
| 1678 | (*code_ptr = (X) ^ Stuff_Magic, code = Stuff_NoCode) |
| 1679 | |
| 1680 | static u_char* |
| 1681 | StuffData(u_char *src, u_long length, u_char *dest, u_char **code_ptr_ptr) |
| 1682 | { |
| 1683 | u_char *end = src + length; |
| 1684 | u_char *code_ptr = *code_ptr_ptr; |
| 1685 | u_char code = Stuff_NoCode, count = 0; |
| 1686 | |
| 1687 | if (!length) return (dest); |
| 1688 | |
| 1689 | if (code_ptr) { /* Recover state from last call, if applicable */ |
| 1690 | code = (*code_ptr ^ Stuff_Magic) & Stuff_CodeMask; |
| 1691 | count = (*code_ptr ^ Stuff_Magic) & Stuff_CountMask; |
| 1692 | } |
| 1693 | |
| 1694 | while (src < end) { |
| 1695 | switch (code) { |
| 1696 | /* |
| 1697 | * Stuff_NoCode: If no current code, select one |
| 1698 | */ |
| 1699 | case Stuff_NoCode: |
| 1700 | code_ptr = dest++; /* Record where we're going to put this code */ |
| 1701 | count = 0; /* Reset the count (zero means one instance) */ |
| 1702 | /* Tentatively start a new block */ |
| 1703 | if (*src == 0) { |
| 1704 | code = Stuff_Zero; |
| 1705 | src++; |
| 1706 | } else { |
| 1707 | code = Stuff_Same; |
| 1708 | *dest++ = *src++ ^ Stuff_Magic; |
| 1709 | } |
| 1710 | /* Note: We optimistically assume run of same -- which will be */ |
| 1711 | /* fixed later in Stuff_Same if it turns out not to be true. */ |
| 1712 | break; |
| 1713 | |
| 1714 | /* |
| 1715 | * Stuff_Zero: We already have at least one zero encoded |
| 1716 | */ |
| 1717 | case Stuff_Zero: |
| 1718 | |
| 1719 | /* If another zero, count it, else finish this code block */ |
| 1720 | if (*src == 0) { |
| 1721 | count++; |
| 1722 | src++; |
| 1723 | } else |
| 1724 | StuffData_FinishBlock(Stuff_Zero + count); |
| 1725 | break; |
| 1726 | |
| 1727 | /* |
| 1728 | * Stuff_Same: We already have at least one byte encoded |
| 1729 | */ |
| 1730 | case Stuff_Same: |
| 1731 | /* If another one the same, count it */ |
| 1732 | if ((*src ^ Stuff_Magic) == code_ptr[1]) { |
| 1733 | count++; |
| 1734 | src++; |
| 1735 | break; |
| 1736 | } |
| 1737 | /* else, this byte does not match this block. */ |
| 1738 | /* If we already have two or more bytes encoded, finish this code block */ |
| 1739 | if (count) { |
| 1740 | StuffData_FinishBlock(Stuff_Same + count); |
| 1741 | break; |
| 1742 | } |
| 1743 | /* else, we only have one so far, so switch to Stuff_Diff code */ |
| 1744 | code = Stuff_Diff; /* and fall through to Stuff_Diff case below */ |
| 1745 | |
| 1746 | case Stuff_Diff: /* Stuff_Diff: We have at least two *different* bytes encoded */ |
| 1747 | /* If this is a zero, must encode a Stuff_DiffZero, and begin a new block */ |
| 1748 | if (*src == 0) |
| 1749 | StuffData_FinishBlock(Stuff_DiffZero + count); |
| 1750 | /* else, if we have three in a row, it is worth starting a Stuff_Same block */ |
| 1751 | else if ((*src ^ Stuff_Magic) == dest[-1] && dest[-1] == dest[-2]) |
| 1752 | { |
| 1753 | code += count-2; |
| 1754 | if (code == Stuff_Diff) |
| 1755 | code = Stuff_Same; |
| 1756 | StuffData_FinishBlock(code); |
| 1757 | code_ptr = dest-2; |
| 1758 | /* dest[-1] already holds the correct value */ |
| 1759 | count = 2; /* 2 means three bytes encoded */ |
| 1760 | code = Stuff_Same; |
| 1761 | } |
| 1762 | /* else, another different byte, so add it to the block */ |
| 1763 | else { |
| 1764 | *dest++ = *src ^ Stuff_Magic; |
| 1765 | count++; |
| 1766 | } |
| 1767 | src++; /* Consume the byte */ |
| 1768 | break; |
| 1769 | } |
| 1770 | |
| 1771 | if (count == Stuff_MaxCount) |
| 1772 | StuffData_FinishBlock(code + count); |
| 1773 | } |
| 1774 | if (code == Stuff_NoCode) |
| 1775 | *code_ptr_ptr = NULL; |
| 1776 | else { |
| 1777 | *code_ptr_ptr = code_ptr; |
| 1778 | StuffData_FinishBlock(code + count); |
| 1779 | } |
| 1780 | |
| 1781 | return (dest); |
| 1782 | } |
| 1783 | |
| 1784 | |
| 1785 | |
| 1786 | /* |
| 1787 | * UnStuffData decodes the data at "src", up to (but not including) |
| 1788 | * "end". It writes the decoded data into the buffer pointed to by |
| 1789 | * "dst", up to a maximum of "dst_length", and returns the new |
| 1790 | * value of "src" so that a follow-on call can read more data, |
| 1791 | * continuing from where the first left off. |
| 1792 | * |
| 1793 | * There are three types of results: |
| 1794 | * 1. The source data runs out before extracting "dst_length" bytes: |
| 1795 | * UnStuffData returns NULL to indicate failure. |
| 1796 | * 2. The source data produces exactly "dst_length" bytes: |
| 1797 | * UnStuffData returns new_src = end to indicate that all bytes |
| 1798 | * were consumed. |
| 1799 | * 3. "dst_length" bytes are extracted, with more |
| 1800 | * remaining. UnStuffData returns new_src < end to indicate that |
| 1801 | * there are more bytes to be read. |
| 1802 | * |
| 1803 | * Note: The decoding may be dstructive, in that it may alter the |
| 1804 | * source data in the process of decoding it (this is necessary to |
| 1805 | * allow a follow-on call to resume correctly). |
| 1806 | */ |
| 1807 | |
| 1808 | static u_char* |
| 1809 | UnStuffData(u_char *src, u_char *end, u_char *dst, u_long dst_length) |
| 1810 | { |
| 1811 | u_char *dst_end = dst + dst_length; |
| 1812 | |
| 1813 | /* Sanity check */ |
| 1814 | if (!src || !end || !dst || !dst_length) |
| 1815 | return (NULL); |
| 1816 | |
| 1817 | while (src < end && dst < dst_end) |
| 1818 | { |
| 1819 | int count = (*src ^ Stuff_Magic) & Stuff_CountMask; |
| 1820 | switch ((*src ^ Stuff_Magic) & Stuff_CodeMask) |
| 1821 | { |
| 1822 | case Stuff_Diff: |
| 1823 | if (src+1+count >= end) |
| 1824 | return (NULL); |
| 1825 | do |
| 1826 | { |
| 1827 | *dst++ = *++src ^ Stuff_Magic; |
| 1828 | } |
| 1829 | while(--count >= 0 && dst < dst_end); |
| 1830 | if (count < 0) |
| 1831 | src += 1; |
| 1832 | else |
| 1833 | if (count == 0) |
| 1834 | *src = Stuff_Same ^ Stuff_Magic; |
| 1835 | else |
| 1836 | *src = (Stuff_Diff + count) ^ Stuff_Magic; |
| 1837 | break; |
| 1838 | case Stuff_DiffZero: |
| 1839 | if (src+1+count >= end) |
| 1840 | return (NULL); |
| 1841 | do |
| 1842 | { |
| 1843 | *dst++ = *++src ^ Stuff_Magic; |
| 1844 | } |
| 1845 | while(--count >= 0 && dst < dst_end); |
| 1846 | if (count < 0) |
| 1847 | *src = Stuff_Zero ^ Stuff_Magic; |
| 1848 | else |
| 1849 | *src = (Stuff_DiffZero + count) ^ Stuff_Magic; |
| 1850 | break; |
| 1851 | case Stuff_Same: |
| 1852 | if (src+1 >= end) |
| 1853 | return (NULL); |
| 1854 | do |
| 1855 | { |
| 1856 | *dst++ = src[1] ^ Stuff_Magic; |
| 1857 | } |
| 1858 | while(--count >= 0 && dst < dst_end); |
| 1859 | if (count < 0) |
| 1860 | src += 2; |
| 1861 | else |
| 1862 | *src = (Stuff_Same + count) ^ Stuff_Magic; |
| 1863 | break; |
| 1864 | case Stuff_Zero: |
| 1865 | do |
| 1866 | { |
| 1867 | *dst++ = 0; |
| 1868 | } |
| 1869 | while(--count >= 0 && dst < dst_end); |
| 1870 | if (count < 0) |
| 1871 | src += 1; |
| 1872 | else |
| 1873 | *src = (Stuff_Zero + count) ^ Stuff_Magic; |
| 1874 | break; |
| 1875 | } |
| 1876 | } |
| 1877 | |
| 1878 | if (dst < dst_end) |
| 1879 | return (NULL); |
| 1880 | else |
| 1881 | return (src); |
| 1882 | } |
| 1883 | |
| 1884 | |
| 1885 | |
| 1886 | /* |
| 1887 | * Log an error mesesage (for a packet received with errors?) |
| 1888 | * from the STRIP driver. |
| 1889 | */ |
| 1890 | static void |
| 1891 | RecvErr(const char *msg, struct strip_softc *sc) |
| 1892 | { |
| 1893 | #define MAX_RecErr 80 |
| 1894 | u_char *ptr = sc->sc_pktstart; |
| 1895 | u_char *end = sc->sc_mp; |
| 1896 | u_char pkt_text[MAX_RecErr], *p = pkt_text; |
| 1897 | *p++ = '\"'; |
| 1898 | while (ptr < end && p < &pkt_text[MAX_RecErr-4]) { |
| 1899 | if (*ptr == '\\') { |
| 1900 | *p++ = '\\'; |
| 1901 | *p++ = '\\'; |
| 1902 | } else if (*ptr >= 32 && *ptr <= 126) |
| 1903 | *p++ = *ptr; |
| 1904 | else { |
| 1905 | snprintf(p, sizeof(pkt_text) - (p - pkt_text), |
| 1906 | "\\%02x" , *ptr); |
| 1907 | p += 3; |
| 1908 | } |
| 1909 | ptr++; |
| 1910 | } |
| 1911 | |
| 1912 | if (ptr == end) *p++ = '\"'; |
| 1913 | *p++ = 0; |
| 1914 | addlog("%s: %13s : %s\n" , sc->sc_if.if_xname, msg, pkt_text); |
| 1915 | |
| 1916 | sc->sc_if.if_ierrors++; |
| 1917 | } |
| 1918 | |
| 1919 | |
| 1920 | /* |
| 1921 | * Parse an error message from the radio. |
| 1922 | */ |
| 1923 | static void |
| 1924 | RecvErr_Message(struct strip_softc *strip_info, u_char *sendername, |
| 1925 | const u_char *msg) |
| 1926 | { |
| 1927 | static const char ERR_001[] = "001" ; /* Not in StarMode! */ |
| 1928 | static const char ERR_002[] = "002" ; /* Remap handle */ |
| 1929 | static const char ERR_003[] = "003" ; /* Can't resolve name */ |
| 1930 | static const char ERR_004[] = "004" ; /* Name too small or missing */ |
| 1931 | static const char ERR_005[] = "005" ; /* Bad count specification */ |
| 1932 | static const char ERR_006[] = "006" ; /* Header too big */ |
| 1933 | static const char ERR_007[] = "007" ; /* Body too big */ |
| 1934 | static const char ERR_008[] = "008" ; /* Bad character in name */ |
| 1935 | static const char ERR_009[] = "009" ; /* No count or line terminator */ |
| 1936 | |
| 1937 | char * if_name; |
| 1938 | |
| 1939 | if_name = strip_info->sc_if.if_xname; |
| 1940 | |
| 1941 | if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1)) |
| 1942 | { |
| 1943 | RecvErr("radio error message:" , strip_info); |
| 1944 | addlog("%s: Radio %s is not in StarMode\n" , |
| 1945 | if_name, sendername); |
| 1946 | } |
| 1947 | else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1)) |
| 1948 | { |
| 1949 | RecvErr("radio error message:" , strip_info); |
| 1950 | #ifdef notyet /*Kernel doesn't have scanf!*/ |
| 1951 | int handle; |
| 1952 | u_char newname[64]; |
| 1953 | sscanf(msg, "ERR_002 Remap handle &%d to name %s" , &handle, newname); |
| 1954 | addlog("%s: Radio name %s is handle %d\n" , |
| 1955 | if_name, newname, handle); |
| 1956 | #endif |
| 1957 | } |
| 1958 | else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1)) |
| 1959 | { |
| 1960 | RecvErr("radio error message:" , strip_info); |
| 1961 | addlog("%s: Destination radio name is unknown\n" , if_name); |
| 1962 | } |
| 1963 | else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1)) { |
| 1964 | /* |
| 1965 | * The radio reports it got a badly-framed starmode packet |
| 1966 | * from us; so it must me in starmode. |
| 1967 | */ |
| 1968 | if (strip_info->sc_if.if_flags & IFF_DEBUG) |
| 1969 | addlog("%s: radio responded to probe\n" , if_name); |
| 1970 | if (strip_info->sc_state == ST_DEAD) { |
| 1971 | /* A successful reset... */ |
| 1972 | addlog("%s: Radio back in starmode\n" , if_name); |
| 1973 | } |
| 1974 | CLEAR_RESET_TIMER(strip_info); |
| 1975 | } |
| 1976 | else if (!strncmp(msg, ERR_005, sizeof(ERR_005)-1)) |
| 1977 | RecvErr("radio error message:" , strip_info); |
| 1978 | else if (!strncmp(msg, ERR_006, sizeof(ERR_006)-1)) |
| 1979 | RecvErr("radio error message:" , strip_info); |
| 1980 | else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1)) |
| 1981 | { |
| 1982 | /* |
| 1983 | * Note: This error knocks the radio back into |
| 1984 | * command mode. |
| 1985 | */ |
| 1986 | RecvErr("radio error message:" , strip_info); |
| 1987 | printf("%s: Error! Packet size too big for radio." , |
| 1988 | if_name); |
| 1989 | FORCE_RESET(strip_info); |
| 1990 | } |
| 1991 | else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1)) |
| 1992 | { |
| 1993 | RecvErr("radio error message:" , strip_info); |
| 1994 | printf("%s: Radio name contains illegal character\n" , |
| 1995 | if_name); |
| 1996 | } |
| 1997 | else if (!strncmp(msg, ERR_009, sizeof(ERR_009)-1)) |
| 1998 | RecvErr("radio error message:" , strip_info); |
| 1999 | else { |
| 2000 | addlog("failed to parse ]%3s[\n" , msg); |
| 2001 | RecvErr("unparsed radio error message:" , strip_info); |
| 2002 | } |
| 2003 | } |
| 2004 | |
| 2005 | /* |
| 2006 | * Module infrastructure |
| 2007 | */ |
| 2008 | #include "if_module.h" |
| 2009 | |
| 2010 | IF_MODULE(MODULE_CLASS_DRIVER, strip, "slcompress" ); |
| 2011 | |