summaryrefslogtreecommitdiff
path: root/sys/net/if_strip.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1996-05-22 11:48:26 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1996-05-22 11:48:26 +0000
commit18762c94577cb8e91781bad751b969bc8052998f (patch)
tree61a64e353d6702a02c48a723781074ad40a36799 /sys/net/if_strip.c
parentb38ed5ae00295759a4ccd7ba7032cd145339ce9c (diff)
sync
Diffstat (limited to 'sys/net/if_strip.c')
-rw-r--r--sys/net/if_strip.c1714
1 files changed, 1714 insertions, 0 deletions
diff --git a/sys/net/if_strip.c b/sys/net/if_strip.c
new file mode 100644
index 00000000000..7256fdcc4ef
--- /dev/null
+++ b/sys/net/if_strip.c
@@ -0,0 +1,1714 @@
+/* $NetBSD: if_strip.c,v 1.2 1996/05/19 22:09:36 jonathan Exp $ */
+/* from: NetBSD: if_sl.c,v 1.38 1996/02/13 22:00:23 christos Exp $ */
+
+/*
+ * Copyright 1996 The Board of Trustees of The Leland Stanford
+ * Junior University. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. Stanford University
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ *
+ *
+ * This driver was contributed by Jonathan Stone.
+ *
+ * Starmode Radio IP interface (STRIP) for Metricom wireless radio.
+ * This STRIP driver assumes address resolution of IP addresses to
+ * Metricom MAC addresses is done via local link-level routes.
+ * The link-level addresses are entered as an 8-digit packed BCD number.
+ * To add a route for a radio at IP address 10.1.2.3, with radio
+ * address '1234-5678', reachable via interface st0, use the command
+ *
+ * route add -host 10.1.2.3 -link st0:12:34:56:78
+ */
+
+
+/*
+ * Copyright (c) 1987, 1989, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_sl.c 8.6 (Berkeley) 2/1/94
+ */
+
+/*
+ * Derived from: Serial Line interface written by Rick Adams (rick@seismo.gov)
+ *
+ * Rick Adams
+ * Center for Seismic Studies
+ * 1300 N 17th Street, Suite 1450
+ * Arlington, Virginia 22209
+ * (703)276-7900
+ * rick@seismo.ARPA
+ * seismo!rick
+ *
+ * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
+ * N.B.: this belongs in netinet, not net, the way it stands now.
+ * Should have a link-layer type designation, but wouldn't be
+ * backwards-compatible.
+ *
+ * Converted to 4.3BSD Beta by Chris Torek.
+ * Other changes made at Berkeley, based in part on code by Kirk Smith.
+ * W. Jolitz added slip abort.
+ *
+ * Hacked almost beyond recognition by Van Jacobson (van@helios.ee.lbl.gov).
+ * Added priority queuing for "interactive" traffic; hooks for TCP
+ * header compression; ICMP filtering (at 2400 baud, some cretin
+ * pinging you can use up all your bandwidth). Made low clist behavior
+ * more robust and slightly less likely to hang serial line.
+ * Sped up a bunch of things.
+ *
+ * Note that splimp() is used throughout to block both (tty) input
+ * interrupts and network activity; thus, splimp must be >= spltty.
+ */
+
+#include "strip.h"
+#if NSTRIP > 0
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/dkstat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/tty.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#if __NetBSD__
+#include <sys/systm.h>
+#endif
+#include <sys/syslog.h>
+
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#if INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#else
+#error Starmode Radio IP configured without configuring inet?
+#endif
+
+#include <net/slcompress.h>
+#include <net/if_stripvar.h>
+#include <net/slip.h>
+
+#ifdef __NetBSD__ /* XXX -- jrs */
+typedef u_char ttychar_t;
+#else
+typedef char ttychar_t;
+#endif
+
+#if NBPFILTER > 0
+#include <sys/time.h>
+#include <net/bpf.h>
+#endif
+
+/*
+ * SLMAX is a hard limit on input packet size. To simplify the code
+ * and improve performance, we require that packets fit in an mbuf
+ * cluster, and if we get a compressed packet, there's enough extra
+ * room to expand the header into a max length tcp/ip header (128
+ * bytes). So, SLMAX can be at most
+ * MCLBYTES - 128
+ *
+ * SLMTU is a hard limit on output packet size. To insure good
+ * interactive response, SLMTU wants to be the smallest size that
+ * amortizes the header cost. (Remember that even with
+ * type-of-service queuing, we have to wait for any in-progress
+ * packet to finish. I.e., we wait, on the average, 1/2 * mtu /
+ * cps, where cps is the line speed in characters per second.
+ * E.g., 533ms wait for a 1024 byte MTU on a 9600 baud line. The
+ * average compressed header size is 6-8 bytes so any MTU > 90
+ * bytes will give us 90% of the line bandwidth. A 100ms wait is
+ * tolerable (500ms is not), so want an MTU around 296. (Since TCP
+ * will send 256 byte segments (to allow for 40 byte headers), the
+ * typical packet size on the wire will be around 260 bytes). In
+ * 4.3tahoe+ systems, we can set an MTU in a route so we do that &
+ * leave the interface MTU relatively high (so we don't IP fragment
+ * when acting as a gateway to someone using a stupid MTU).
+ *
+ * Similar considerations apply to SLIP_HIWAT: It's the amount of
+ * data that will be queued 'downstream' of us (i.e., in clists
+ * waiting to be picked up by the tty output interrupt). If we
+ * queue a lot of data downstream, it's immune to our t.o.s. queuing.
+ * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed
+ * telnet/ftp will see a 1 sec wait, independent of the mtu (the
+ * wait is dependent on the ftp window size but that's typically
+ * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize
+ * the cost (in idle time on the wire) of the tty driver running
+ * off the end of its clists & having to call back slstart for a
+ * new packet. For a tty interface with any buffering at all, this
+ * cost will be zero. Even with a totally brain dead interface (like
+ * the one on a typical workstation), the cost will be <= 1 character
+ * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose
+ * at most 1% while maintaining good interactive response.
+ */
+#if NBPFILTER > 0
+#define BUFOFFSET (128+sizeof(struct ifnet **)+SLIP_HDRLEN)
+#else
+#define BUFOFFSET (128+sizeof(struct ifnet **))
+#endif
+#define SLMAX (MCLBYTES - BUFOFFSET)
+#define SLBUFSIZE (SLMAX + BUFOFFSET)
+#define SLMTU 1200 /*XXX*/
+
+#define SLIP_HIWAT roundup(50,CBSIZE)
+
+#ifndef __NetBSD__ /* XXX - cgd */
+#define CLISTRESERVE 1024 /* Can't let clists get too low */
+#endif /* !__NetBSD__ */
+
+/*
+ * SLIP ABORT ESCAPE MECHANISM:
+ * (inspired by HAYES modem escape arrangement)
+ * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape }
+ * within window time signals a "soft" exit from slip mode by remote end
+ * if the IFF_DEBUG flag is on.
+ */
+#define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/
+#define ABT_IDLE 1 /* in seconds - idle before an escape */
+#define ABT_COUNT 3 /* count of escapes for abort */
+#define ABT_WINDOW (ABT_COUNT*2+2) /* in seconds - time to count */
+
+struct st_softc st_softc[NSTRIP];
+
+#define STRIP_FRAME_END 0x0D /* carriage return */
+
+
+static int stripinit __P((struct st_softc *));
+static struct mbuf *strip_btom __P((struct st_softc *, int));
+
+/*
+ * STRIP header: '*' + modem address (dddd-dddd) + '*' + mactype ('SIP0')
+ * A Metricom packet looks like this: *<address>*<key><payload><CR>
+ * eg. *0000-1164*SIP0<payload><CR>
+ *
+ */
+
+#define STRIP_ENCAP_SIZE(X) ((36) + (X)*65/64 + 2)
+#define STRIP_HDRLEN 15
+#define STRIP_MAC_ADDR_LEN 9
+
+/*
+ * Star mode packet header.
+ * (may be used for encapsulations other than STRIP.)
+ */
+#define STARMODE_ADDR_LEN 11
+struct st_header {
+ u_char starmode_addr[STARMODE_ADDR_LEN];
+ u_char starmode_type[4];
+};
+
+/*
+ * Forward declarations for Metricom-specific functions.
+ * Ideally, these would be in a library and shared across
+ * different STRIP implementations: *BSD, Linux, etc.
+ *
+ */
+static u_char* UnStuffData __P((u_char *src, u_char *end, u_char
+ *dest, u_long dest_length));
+
+static u_char* StuffData __P((u_char *src, u_long length, u_char *dest,
+ u_char **code_ptr_ptr));
+
+static void RecErr __P((char *msg, struct st_softc *sc));
+static void RecERR_Message __P((struct st_softc *strip_info,
+ u_char *sendername, u_char *msg));
+void resetradio __P((struct st_softc *sc, struct tty *tp));
+void strip_esc __P((struct st_softc *sc, struct mbuf *m));
+int strip_newpacket __P((struct st_softc *sc, u_char *ptr, u_char *end));
+struct mbuf * strip_send __P((struct st_softc *sc, struct mbuf *m0));
+
+
+#ifdef DEBUG
+void ipdump __P((const char *msg, u_char *p, int len));
+void stripdump __P((const char *msg, u_char *p, int len));
+void stripdumpm __P((const char *msg, struct mbuf *m, int len));
+#define DPRINTF(x) printf x
+#define TXPRINTF(x) printf x/* causes outrageous delays */
+#define RXPRINTF(x) printf x /* causes outrageous delays */
+#else
+#define DPRINTF(x)
+#define TXPRINTF(x)
+#define RXPRINTF(x)
+#endif
+
+#define XDPRINTF(x) /* really verbose debugging */
+
+
+/*
+ * Radio reset macros.
+ * The Metricom radios are not particularly well-designed for
+ * use in packet mode (starmode). There's no easy way to tell
+ * when the radio is in starmode. Worse, when the radios are reset
+ * or power-cycled, they come back up in Hayes AT-emulation mode,
+ * and there's no good way for this driver to tell.
+ * We deal with this by peridically tickling the radio
+ * with an invalid starmode command. If the radio doesn't
+ * respond with an error, the driver knows to reset the radio.
+ */
+
+#define FORCE_RESET(sc) \
+ do {\
+ printf("strip: XXX: reset state-machine not yet implemented in *BSD\n"); \
+ } while (0)
+
+#define CLEAR_RESET_TIMER(sc) \
+ do {\
+ printf("strip: clearing reset timeout: not yet implemented in *BSD\n"); \
+ } while (0)
+
+
+
+/*
+ * Called from boot code to establish sl interfaces.
+ */
+void
+stripattach(n)
+ int n;
+{
+ register struct st_softc *sc;
+ register int i = 0;
+
+ for (sc = st_softc; i < NSTRIP; sc++) {
+ sprintf(sc->sc_if.if_xname, "st%d", i++);
+ sc->sc_if.if_softc = sc;
+ sc->sc_unit = i; /* XXX */
+ sc->sc_if.if_mtu = SLMTU;
+ sc->sc_if.if_flags = 0;
+ sc->sc_if.if_type = IFT_OTHER;
+#if 0
+ sc->sc_if.if_flags |= SC_AUTOCOMP /* | IFF_POINTOPOINT | IFF_MULTICAST*/;
+#endif
+ sc->sc_if.if_type = IFT_SLIP;
+ sc->sc_if.if_ioctl = stripioctl;
+ sc->sc_if.if_output = stripoutput;
+ sc->sc_if.if_snd.ifq_maxlen = 50;
+ sc->sc_fastq.ifq_maxlen = 32;
+ if_attach(&sc->sc_if);
+#if NBPFILTER > 0
+ bpfattach(&sc->sc_bpf, &sc->sc_if, DLT_SLIP, SLIP_HDRLEN);
+#endif
+ }
+}
+
+static int
+stripinit(sc)
+ register struct st_softc *sc;
+{
+ register caddr_t p;
+
+ if (sc->sc_ep == (u_char *) 0) {
+ MCLALLOC(p, M_WAIT);
+ if (p)
+ sc->sc_ep = (u_char *)p + SLBUFSIZE;
+ else {
+ printf("sl%d: can't allocate buffer\n", sc - st_softc);
+ sc->sc_if.if_flags &= ~IFF_UP;
+ return (0);
+ }
+ }
+
+ /* get buffer in which to unstuff input */
+ if (sc->sc_rxbuf == (u_char *) 0) {
+ MCLALLOC(p, M_WAIT);
+ if (p)
+ sc->sc_rxbuf = (u_char *)p + SLBUFSIZE - SLMAX;
+ else {
+ printf("sl%d: can't allocate buffer\n", sc - st_softc);
+ sc->sc_if.if_flags &= ~IFF_UP;
+ return (0);
+ }
+ }
+
+ /* get buffer in which to stuff output */
+ if (sc->sc_txbuf == (u_char *) 0) {
+ MCLALLOC(p, M_WAIT);
+ if (p)
+ sc->sc_txbuf = (u_char *)p + SLBUFSIZE - SLMAX;
+ else {
+ printf("sl%d: can't allocate buffer\n", sc - st_softc);
+
+ sc->sc_if.if_flags &= ~IFF_UP;
+ return (0);
+ }
+ }
+
+ sc->sc_buf = sc->sc_ep - SLMAX;
+ sc->sc_mp = sc->sc_buf;
+ sl_compress_init(&sc->sc_comp, -1);
+
+ return (1);
+}
+
+/*
+ * Line specific open routine.
+ * Attach the given tty to the first available sl unit.
+ */
+/* ARGSUSED */
+int
+stripopen(dev, tp)
+ dev_t dev;
+ register struct tty *tp;
+{
+ struct proc *p = curproc; /* XXX */
+ register struct st_softc *sc;
+ register int nstrip;
+ int error;
+#ifdef __NetBSD__
+ int s;
+#endif
+
+ if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
+ return (error);
+
+ if (tp->t_line == STRIPDISC)
+ return (0);
+
+ for (nstrip = NSTRIP, sc = st_softc; --nstrip >= 0; sc++)
+ if (sc->sc_ttyp == NULL) {
+ if (stripinit(sc) == 0)
+ return (ENOBUFS);
+ tp->t_sc = (caddr_t)sc;
+ sc->sc_ttyp = tp;
+ sc->sc_if.if_baudrate = tp->t_ospeed;
+ ttyflush(tp, FREAD | FWRITE);
+#ifdef __NetBSD__
+ /*
+ * make sure tty output queue is large enough
+ * to hold a full-sized packet (including frame
+ * end, and a possible extra frame end). full-sized
+ * packet occupies a max of 2*SLMTU bytes (because
+ * of possible escapes), and add two on for frame
+ * ends.
+ */
+ s = spltty();
+ if (tp->t_outq.c_cn < 2*SLMTU+2) {
+ sc->sc_oldbufsize = tp->t_outq.c_cn;
+ sc->sc_oldbufquot = tp->t_outq.c_cq != 0;
+
+ clfree(&tp->t_outq);
+ error = clalloc(&tp->t_outq, 3*SLMTU, 0);
+ if (error) {
+ splx(s);
+ return(error);
+ }
+ } else
+ sc->sc_oldbufsize = sc->sc_oldbufquot = 0;
+ splx(s);
+#endif /* __NetBSD__ */
+ s = spltty();
+ resetradio(sc, tp);
+ splx(s);
+
+ return (0);
+ }
+ return (ENXIO);
+}
+
+/*
+ * Line specific close routine.
+ * Detach the tty from the sl unit.
+ */
+void
+stripclose(tp)
+ struct tty *tp;
+{
+ register struct st_softc *sc;
+ int s;
+
+ ttywflush(tp);
+
+ DPRINTF(("stripclose: closing\n"));
+ s = splimp(); /* actually, max(spltty, splsoftnet) */
+ tp->t_line = 0;
+ sc = (struct st_softc *)tp->t_sc;
+ if (sc != NULL) {
+ if_down(&sc->sc_if);
+ sc->sc_ttyp = NULL;
+ tp->t_sc = NULL;
+ MCLFREE((caddr_t)(sc->sc_ep - SLBUFSIZE));
+ MCLFREE((caddr_t)(sc->sc_rxbuf - SLBUFSIZE + SLMAX)); /* XXX */
+ MCLFREE((caddr_t)(sc->sc_txbuf - SLBUFSIZE + SLMAX)); /* XXX */
+ sc->sc_ep = 0;
+ sc->sc_mp = 0;
+ sc->sc_buf = 0;
+ sc->sc_rxbuf = 0;
+ sc->sc_txbuf = 0;
+ }
+#ifdef __NetBSD__
+ /* if necessary, install a new outq buffer of the appropriate size */
+ if (sc->sc_oldbufsize != 0) {
+ clfree(&tp->t_outq);
+ clalloc(&tp->t_outq, sc->sc_oldbufsize, sc->sc_oldbufquot);
+ }
+#endif
+ splx(s);
+}
+
+/*
+ * Line specific (tty) ioctl routine.
+ * Provide a way to get the sl unit number.
+ */
+/* ARGSUSED */
+int
+striptioctl(tp, cmd, data, flag)
+ struct tty *tp;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+{
+ struct st_softc *sc = (struct st_softc *)tp->t_sc;
+
+ switch (cmd) {
+ case SLIOCGUNIT:
+ *(int *)data = sc->sc_unit;
+ break;
+
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+/*XXX*/
+/*
+ * Take an mbuf chain containing a SLIP packet, byte-stuff (escape)
+ * the packet, and enqueue it on the tty send queue.
+ */
+void
+strip_esc(sc, m)
+ struct st_softc *sc;
+ struct mbuf *m;
+{
+ register struct tty *tp = sc->sc_ttyp;
+ register u_char *cp;
+ register u_char *ep;
+ struct mbuf *m2;
+ register int len;
+ u_char *stuffstate = NULL;
+
+ while (m) {
+ cp = mtod(m, u_char *); ep = cp + m->m_len;
+ /*
+ * Find out how many bytes in the string we can
+ * handle without doing something special.
+ */
+ ep = StuffData(cp, m->m_len, sc->sc_txbuf, &stuffstate);
+ len = ep - sc->sc_txbuf;
+
+ /*
+ * Put n characters at once
+ * into the tty output queue.
+ */
+ if (b_to_q((ttychar_t *)sc->sc_txbuf,
+ len, &tp->t_outq))
+ goto bad;
+ sc->sc_if.if_obytes += len;
+ MFREE(m, m2);
+ m = m2;
+ }
+ return;
+
+bad:
+ m_freem(m);
+ return;
+}
+
+/*
+ * Prepend a STRIP header to the packet.
+ * (based on 4.4bsd if_ppp)
+ */
+struct mbuf *
+strip_send(sc, m0)
+ struct st_softc *sc;
+ struct mbuf *m0;
+{
+ register struct tty *tp = sc->sc_ttyp;
+ struct st_header *hdr;
+
+ /*
+ * Send starmode header (unstuffed).
+ */
+ hdr = mtod(m0, struct st_header *);
+ if (b_to_q((ttychar_t *)hdr, STRIP_HDRLEN, &tp->t_outq)) {
+ TXPRINTF(("prepend: outq overflow\n"));
+ m_freem(m0);
+ }
+
+ /* XXX undo M_PREPEND() */
+ m0->m_data += sizeof(struct st_header);
+ m0->m_len -= sizeof(struct st_header);
+ if (m0 && m0->m_flags & M_PKTHDR)
+ m0->m_pkthdr.len -= sizeof(struct st_header);
+
+
+ strip_esc(sc, m0);
+
+ if (putc(STRIP_FRAME_END, &tp->t_outq)) {
+ /*
+ * Not enough room. Remove a char to make room
+ * and end the packet normally.
+ * If you get many collisions (more than one or two
+ * a day) you probably do not have enough clists
+ * and you should increase "nclist" in param.c.
+ */
+ (void) unputc(&tp->t_outq);
+ (void) putc(STRIP_FRAME_END, &tp->t_outq);
+ sc->sc_if.if_collisions++;
+ } else {
+ ++sc->sc_if.if_obytes;
+ sc->sc_if.if_opackets++;
+ }
+ return(m0);
+}
+
+
+
+/*
+ * Queue a packet. Start transmission if not active.
+ * Compression happens in slstart; if we do it here, IP TOS
+ * will cause us to not compress "background" packets, because
+ * ordering gets trashed. It can be done for all packets in slstart.
+ */
+int
+stripoutput(ifp, m, dst, rt)
+ struct ifnet *ifp;
+ register struct mbuf *m;
+ struct sockaddr *dst;
+ struct rtentry *rt;
+{
+ register struct st_softc *sc = ifp->if_softc;
+ register struct ip *ip;
+ register struct ifqueue *ifq;
+ register struct st_header *shp;
+ register const u_char *dldst; /* link-level next-hop */
+ int s;
+ u_char dl_addrbuf[STARMODE_ADDR_LEN+1];
+
+
+ /*
+ * Verify tty line is up and alive.
+ */
+ if (sc->sc_ttyp == NULL) {
+ m_freem(m);
+ return (ENETDOWN); /* sort of */
+ }
+ if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0 &&
+ (sc->sc_ttyp->t_cflag & CLOCAL) == 0) {
+ m_freem(m);
+ return (EHOSTUNREACH);
+ }
+
+#define SDL(a) ((struct sockaddr_dl *) (a))
+
+#ifdef DEBUG
+ if (rt) {
+ printf("stripout, rt: dst af%d gw af%d",
+ rt_key(rt)->sa_family,
+ rt->rt_gateway->sa_family);
+ if (rt_key(rt)->sa_family == AF_INET)
+ printf(" dst %x",
+ ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr);
+ printf("\n");
+ }
+#endif
+ switch (dst->sa_family) {
+
+ case AF_INET:
+ /* XXX untested */
+
+ if (rt != NULL && rt->rt_gwroute != NULL)
+ rt = rt->rt_gwroute;
+
+ /* assume rt is never NULL */
+ if (rt == NULL || rt->rt_gateway->sa_family != AF_LINK
+ || SDL(rt->rt_gateway)->sdl_alen != ifp->if_addrlen) {
+ DPRINTF(("strip: could not arp starmode addr %x\n",
+ ((struct sockaddr_in *)dst)->sin_addr.s_addr));
+ m_freem(m);
+ return(EHOSTUNREACH);
+ }
+ /*bcopy(LLADDR(SDL(rt->rt_gateway)), dldst, ifp->if_addrlen);*/
+ dldst = LLADDR(SDL(rt->rt_gateway));
+ break;
+
+ case AF_LINK:
+ /*bcopy(LLADDR(SDL(rt->rt_gateway)), dldst, ifp->if_addrlen);*/
+ dldst = LLADDR(SDL(dst));
+ break;
+
+ default:
+ /*
+ * `Cannot happen' (see stripioctl). Someday we will extend
+ * the line protocol to support other address families.
+ */
+ printf("%s: af %d not supported\n", sc->sc_if.if_xname,
+ dst->sa_family);
+ m_freem(m);
+ sc->sc_if.if_noproto++;
+ return (EAFNOSUPPORT);
+ }
+
+ ifq = &sc->sc_if.if_snd;
+ ip = mtod(m, struct ip *);
+ if (sc->sc_if.if_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) {
+ m_freem(m);
+ return (ENETRESET); /* XXX ? */
+ }
+ if (ip->ip_tos & IPTOS_LOWDELAY)
+ ifq = &sc->sc_fastq;
+
+ /*
+ * Add local net header. If no space in first mbuf,
+ * add another.
+ */
+ M_PREPEND(m, sizeof(struct st_header), M_DONTWAIT);
+ if (m == 0) {
+ DPRINTF(("strip: could not prepend starmode header\n"));
+ return(ENOBUFS);
+ }
+
+
+ /*
+ * Unpack BCD route entry into an ASCII starmode address.
+ */
+
+ dl_addrbuf[0] = '*';
+
+ dl_addrbuf[1] = ((dldst[0] >> 4) & 0x0f) + '0';
+ dl_addrbuf[2] = ((dldst[0] ) & 0x0f) + '0';
+
+ dl_addrbuf[3] = ((dldst[1] >> 4) & 0x0f) + '0';
+ dl_addrbuf[4] = ((dldst[1] ) & 0x0f) + '0';
+
+ dl_addrbuf[5] = '-';
+
+ dl_addrbuf[6] = ((dldst[2] >> 4) & 0x0f) + '0';
+ dl_addrbuf[7] = ((dldst[2] ) & 0x0f) + '0';
+
+ dl_addrbuf[8] = ((dldst[3] >> 4) & 0x0f) + '0';
+ dl_addrbuf[9] = ((dldst[3] ) & 0x0f) + '0';
+
+ dl_addrbuf[10] = '*';
+ dl_addrbuf[11] = 0;
+ dldst = dl_addrbuf;
+
+ shp = mtod(m, struct st_header *);
+ bcopy((caddr_t)"SIP0", (caddr_t)&shp->starmode_type,
+ sizeof(shp->starmode_type));
+
+ bcopy((caddr_t)dldst, (caddr_t)shp->starmode_addr,
+ sizeof (shp->starmode_addr));
+
+ TXPRINTF(("strip address is %16s\n", (char *)shp));
+
+ s = splimp();
+ if (sc->sc_oqlen && sc->sc_ttyp->t_outq.c_cc == sc->sc_oqlen) {
+ struct timeval tv;
+
+ /* if output's been stalled for too long, and restart */
+ timersub(&time, &sc->sc_if.if_lastchange, &tv);
+ if (tv.tv_sec > 0) {
+ /*XXX*/ DPRINTF(("stripoutput: stalled, resetting\n"));
+ sc->sc_otimeout++;
+ stripstart(sc->sc_ttyp);
+ }
+ }
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ splx(s);
+ sc->sc_if.if_oerrors++;
+ /*XXX*/ TXPRINTF(("stripoutput: ifq full\n"));
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(ifq, m);
+ sc->sc_if.if_lastchange = time;
+ if ((sc->sc_oqlen = sc->sc_ttyp->t_outq.c_cc) == 0) {
+ /*XXX*/ TXPRINTF(("stripoutput: enqueued pkt, restarting\n"));
+ stripstart(sc->sc_ttyp);
+ }
+/* XXX FIXME */
+ stripstart(sc->sc_ttyp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Start output on interface. Get another datagram
+ * to send from the interface queue and map it to
+ * the interface before starting output.
+ */
+void
+stripstart(tp)
+ register struct tty *tp;
+{
+ register struct st_softc *sc = (struct st_softc *)tp->t_sc;
+ register struct mbuf *m;
+ register struct ip *ip;
+ int s;
+#if NBPFILTER > 0
+ u_char bpfbuf[SLMTU + SLIP_HDRLEN];
+ register int len = 0;
+#endif
+#ifndef __NetBSD__ /* XXX - cgd */
+ extern int cfreecount;
+#endif
+ for (;;) {
+/*XXX*/ TXPRINTF(("stripstart\n"));
+ /*
+ * If there is more in the output queue, just send it now.
+ * We are being called in lieu of ttstart and must do what
+ * it would.
+ */
+ if (tp->t_outq.c_cc != 0) {
+ (*tp->t_oproc)(tp);
+ if (tp->t_outq.c_cc > SLIP_HIWAT) {
+ TXPRINTF(("stripstart: outq past SLIP_HIWAT\n"));
+#if 0
+ /* XXX can't just stop output on
+ framed packet-radio links!
+ */
+ return;
+#endif
+ }
+ }
+ /*
+ * This happens briefly when the line shuts down.
+ */
+ if (sc == NULL) {
+ TXPRINTF(("(shutdown)\n"));
+ return;
+ }
+
+#if 0 /* XXX - jrs*/
+#if defined(__NetBSD__) /* XXX - cgd */
+ /*
+ * Do not remove the packet from the IP queue if it
+ * doesn't look like the packet will fit into the
+ * current serial output queue, with a packet full of
+ * escapes this could be as bad as SLMTU*2+2.
+ */
+ if (tp->t_outq.c_cn - tp->t_outq.c_cc < 2*SLMTU+2)
+ return;
+#endif /* __NetBSD__ */
+#endif
+ /*
+ * Get a packet and send it to the interface.
+ */
+ s = splimp();
+ IF_DEQUEUE(&sc->sc_fastq, m);
+ if (m)
+ sc->sc_if.if_omcasts++; /* XXX */
+ else
+ IF_DEQUEUE(&sc->sc_if.if_snd, m);
+ splx(s);
+ if (m == NULL) {
+/*XXX*/ TXPRINTF(("(empty q)\n"));
+ return;
+ }
+ /*
+ * We do the header compression here rather than in stripoutput
+ * because the packets will be out of order if we are using TOS
+ * queueing, and the connection id compression will get
+ * munged when this happens.
+ */
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * We need to save the TCP/IP header before it's
+ * compressed. To avoid complicated code, we just
+ * copy the entire packet into a stack buffer (since
+ * this is a serial line, packets should be short
+ * and/or the copy should be negligible cost compared
+ * to the packet transmission time).
+ */
+ register struct mbuf *m1 = m;
+ register u_char *cp = bpfbuf + SLIP_HDRLEN;
+
+ len = 0;
+ do {
+ register int mlen = m1->m_len;
+
+ bcopy(mtod(m1, caddr_t), cp, mlen);
+ cp += mlen;
+ len += mlen;
+ } while ((m1 = m1->m_next) != NULL);
+ }
+#endif
+ if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) {
+ if (sc->sc_if.if_flags & SC_COMPRESS)
+ *mtod(m, u_char *) |= sl_compress_tcp(m, ip,
+ &sc->sc_comp, 1);
+ }
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * Put the SLIP pseudo-"link header" in place. The
+ * compressed header is now at the beginning of the
+ * mbuf.
+ */
+ bpfbuf[SLX_DIR] = SLIPDIR_OUT;
+ bcopy(mtod(m, caddr_t), &bpfbuf[SLX_CHDR], CHDR_LEN);
+ bpf_tap(sc->sc_bpf, bpfbuf, len + SLIP_HDRLEN);
+ }
+#endif
+ sc->sc_if.if_lastchange = time;
+
+#ifndef __NetBSD__ /* XXX - cgd */
+ /*
+ * If system is getting low on clists, just flush our
+ * output queue (if the stuff was important, it'll get
+ * retransmitted).
+ */
+ if (cfreecount < CLISTRESERVE + SLMTU) {
+ m_freem(m);
+ sc->sc_if.if_collisions++;
+ continue;
+ }
+#endif /* !__NetBSD__ */
+
+
+ if (strip_send(sc, m) == NULL) {
+/*XXX*/ DPRINTF(("stripsend: failed to send pkt\n"));
+ }
+ }
+}
+
+
+
+/*
+ * Copy data buffer to mbuf chain; add ifnet pointer.
+ */
+static struct mbuf *
+strip_btom(sc, len)
+ register struct st_softc *sc;
+ register int len;
+{
+ register struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (NULL);
+
+ /*
+ * If we have more than MHLEN bytes, it's cheaper to
+ * queue the cluster we just filled & allocate a new one
+ * for the input buffer. Otherwise, fill the mbuf we
+ * allocated above. Note that code in the input routine
+ * guarantees that packet will fit in a cluster.
+ */
+ if (len >= MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ /*
+ * we couldn't get a cluster - if memory's this
+ * low, it's time to start dropping packets.
+ */
+ (void) m_free(m);
+ return (NULL);
+ }
+ sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE;
+ m->m_data = (caddr_t)sc->sc_buf;
+ m->m_ext.ext_buf = (caddr_t)((long)sc->sc_buf &~ MCLOFSET);
+ TXPRINTF(("strip_btom: new cluster for sc_buf\n"));
+ XDPRINTF(("XXX 1: sc_buf %x end %x hardlim %x\n",
+ sc->sc_buf, sc->sc_mp, sc->sc_ep));
+ } else
+ bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len);
+
+ m->m_len = len;
+ m->m_pkthdr.len = len;
+ m->m_pkthdr.rcvif = &sc->sc_if;
+ return (m);
+}
+
+/*
+ * tty interface receiver interrupt.
+ *
+ * Called with a single char from the tty receiver interrupt; put
+ * the char into the buffer containing a partial packet. If the
+ * char is a packet delimiter, decapsulate the packet, wrap it in
+ * an mbuf, and put it on the protocol input queue.
+*/
+void
+stripinput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register struct st_softc *sc;
+ register struct mbuf *m;
+ register int len;
+ int s;
+#if NBPFILTER > 0
+ u_char chdr[CHDR_LEN];
+#endif
+
+ tk_nin++;
+ sc = (struct st_softc *)tp->t_sc;
+ if (sc == NULL)
+ return;
+ if (c & TTY_ERRORMASK || ((tp->t_state & TS_CARR_ON) == 0 &&
+ (tp->t_cflag & CLOCAL) == 0)) {
+ sc->sc_flags |= SC_ERROR;
+ DPRINTF(("strip: input, error %x\n", c)); /* XXX */
+ return;
+ }
+ c &= TTY_CHARMASK;
+
+ ++sc->sc_if.if_ibytes;
+
+ switch (c) {
+
+#ifdef notanymore
+ case 0x0a:
+ /* (leading newline characters are ignored) */
+ if (sc->sc_mp - sc->sc_buf == 0)
+ return;
+#endif
+
+
+ case STRIP_FRAME_END:
+ len = sc->sc_mp - sc->sc_buf;
+
+#ifdef XDEBUG
+ if (len < 15 || sc->sc_flags & SC_ERROR)
+ printf("stripinput: end of pkt, len %d, err %d\n",
+ len, sc->sc_flags & SC_ERROR); /*XXX*/
+#endif
+ if(sc->sc_flags & SC_ERROR) {
+ sc->sc_flags &= ~SC_ERROR;
+ goto newpack;
+ }
+
+ len = strip_newpacket(sc, sc->sc_buf, sc->sc_mp);
+ if (len <= 1)
+ /* less than min length packet - ignore */
+ goto newpack;
+#if DEBUG > 1
+ ipdump("after destuff", sc->sc_buf, len);
+#endif /* DEBUG */
+
+
+
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * Save the compressed header, so we
+ * can tack it on later. Note that we
+ * will end up copying garbage in some
+ * cases but this is okay. We remember
+ * where the buffer started so we can
+ * compute the new header length.
+ */
+ bcopy(sc->sc_buf, chdr, CHDR_LEN);
+ }
+#endif
+
+ if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) {
+ if (c & 0x80)
+ c = TYPE_COMPRESSED_TCP;
+ else if (c == TYPE_UNCOMPRESSED_TCP)
+ *sc->sc_buf &= 0x4f; /* XXX */
+ /*
+ * We've got something that's not an IP packet.
+ * If compression is enabled, try to decompress it.
+ * Otherwise, if `auto-enable' compression is on and
+ * it's a reasonable packet, decompress it and then
+ * enable compression. Otherwise, drop it.
+ */
+ if (sc->sc_if.if_flags & SC_COMPRESS) {
+ len = sl_uncompress_tcp(&sc->sc_buf, len,
+ (u_int)c, &sc->sc_comp);
+ if (len <= 0)
+ goto error;
+ } else if ((sc->sc_if.if_flags & SC_AUTOCOMP) &&
+ c == TYPE_UNCOMPRESSED_TCP && len >= 40) {
+ len = sl_uncompress_tcp(&sc->sc_buf, len,
+ (u_int)c, &sc->sc_comp);
+ if (len <= 0)
+ goto error;
+ sc->sc_if.if_flags |= SC_COMPRESS;
+ } else
+ goto error;
+ }
+#if NBPFILTER > 0
+ if (sc->sc_bpf) {
+ /*
+ * Put the SLIP pseudo-"link header" in place.
+ * We couldn't do this any earlier since
+ * decompression probably moved the buffer
+ * pointer. Then, invoke BPF.
+ */
+ register u_char *hp = sc->sc_buf - SLIP_HDRLEN;
+
+ hp[SLX_DIR] = SLIPDIR_IN;
+ bcopy(chdr, &hp[SLX_CHDR], CHDR_LEN);
+ bpf_tap(sc->sc_bpf, hp, len + SLIP_HDRLEN);
+ }
+#endif
+ m = strip_btom(sc, len);
+ if (m == NULL)
+ goto error;
+
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_lastchange = time;
+ s = splimp();
+ if (IF_QFULL(&ipintrq)) {
+ IF_DROP(&ipintrq);
+ DPRINTF(("stripinput: ipintrq full\n"));
+ sc->sc_if.if_ierrors++;
+ sc->sc_if.if_iqdrops++;
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(&ipintrq, m);
+ schednetisr(NETISR_IP);
+ }
+ splx(s);
+ goto newpack;
+ }
+
+ if (sc->sc_mp < sc->sc_ep) {
+ *sc->sc_mp++ = c;
+ /*sc->sc_escape = 0;*/
+ return;
+ }
+
+ /* can't put lower; would miss an extra frame */
+ sc->sc_flags |= SC_ERROR;
+ DPRINTF(("stripinput: overran buf\n"));
+ goto quiet_error;
+
+error:
+ RXPRINTF(("stripinput: error\n"));
+quiet_error:
+ sc->sc_if.if_ierrors++;
+ goto quiet_newpack;
+
+newpack:
+ /*DPRINTF(("stripinput: newpack\n"));*/ /* XXX */
+quiet_newpack:
+ sc->sc_mp = sc->sc_buf = sc->sc_ep - SLMAX;
+ /*sc->sc_escape = 0;*/
+}
+
+/*
+ * Process an ioctl request.
+ */
+int
+stripioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ u_long cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ register struct ifreq *ifr;
+ register int s = splimp(), error = 0;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ ifp->if_flags |= IFF_UP;
+ else
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCSIFDSTADDR:
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ if (ifr == 0) {
+ error = EAFNOSUPPORT; /* XXX */
+ break;
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+
+ default:
+ error = EAFNOSUPPORT;
+ break;
+ }
+ break;
+
+ default:
+
+#ifdef DEBUG
+ printf("stripioctl: unknown request 0x%lx\n", cmd);
+#endif
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+/*
+ * Strip subroutines
+ */
+
+/*
+ * Set a radio into starmode.
+ */
+void
+resetradio(sc, tp)
+ struct st_softc *sc;
+ struct tty *tp;
+{
+#if 0
+ static ttychar_t InitString[] =
+ "\r\n\r\n\r\nat\r\n\r\n\r\nate0dt**starmode\r\n**\r\n";
+#else
+ static ttychar_t InitString[] =
+ "\r\rat\r\r\rate0dt**starmode\r*\r";
+#endif
+ register int i;
+
+
+ DPRINTF(("strip: resetting radio\n"));
+ if ((i = b_to_q(InitString, sizeof(InitString) - 1, &tp->t_outq))) {
+ printf("resetradio: %d chars didn't fit in tty queue\n", i);
+ return;
+ }
+ sc->sc_if.if_obytes += sizeof(InitString) - 1;
+
+#ifdef linux /*XXX*/
+ /* reset the watchdog counter */
+ sc->watchdog_doprobe = jiffies + 10 * HZ;
+ sc->watchdog_doreset = jiffies + 1 * HZ;
+#endif
+ /*XXX jrs DANGEROUS - does this help? */
+ sc->sc_if.if_lastchange = time;
+
+ /*XXX jrs DANGEROUS - does this work? */
+ (*sc->sc_ttyp->t_oproc)(tp);
+}
+
+
+/*
+ * XXX
+ * The following is taken, with permisino of the author, from
+ * the LInux strip driver.
+ */
+
+
+/*
+ * Process a received packet.
+ */
+int
+strip_newpacket(sc, ptr, end)
+ struct st_softc *sc;
+ register u_char *ptr, *end;
+{
+ register int len = ptr - end;
+ register u_char *name, *name_end;
+ u_int packetlen;
+
+ /* Ignore empty lines */
+ if (len == 0) return 0;
+
+ /* Catch 'OK' responses which show radio has fallen out of starmode */
+ if (len >= 2 && ptr[0] == 'O' && ptr[1] == 'K') {
+ printf("%s: Radio is back in AT command mode: will reset\n",
+ sc->sc_if.if_xname);
+ FORCE_RESET(sc); /* Do reset ASAP */
+ return 0;
+ }
+
+ /* Check for start of address marker, and then skip over it */
+ if (*ptr != '*') {
+ /* Catch other error messages */
+ if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_')
+ RecERR_Message(sc, NULL, ptr); /* XXX stuart? */
+ else {
+ RecErr("No initial *", sc);
+ addlog("(len = %d\n", len);
+ }
+ return 0;
+ }
+
+ /* skip the '*' */
+ ptr++;
+
+ /* Skip the return address */
+ name = ptr;
+ while (ptr < end && *ptr != '*')
+ ptr++;
+
+ /* Check for end of address marker, and skip over it */
+ if (ptr == end) {
+ RecErr("No second *", sc);
+ XDPRINTF(("XXX 3: sc_buf %x ptr %x end %x mp %x hardlim %x\n",
+ sc->sc_buf, ptr, end, sc->sc_mp, sc->sc_ep));
+ return 0;
+ }
+ name_end = ptr++;
+
+ /* Check for SRIP key, and skip over it */
+ if (ptr[0] != 'S' || ptr[1] != 'I' || ptr[2] != 'P' || ptr[3] != '0') {
+ if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' &&
+ ptr[3] == '_') {
+ *name_end = 0;
+ RecERR_Message(sc, name, ptr);
+ }
+ else RecErr("No SRIP key", sc);
+ return 0;
+ }
+ ptr += 4;
+
+ /* Decode start of the IP packet header */
+ ptr = UnStuffData(ptr, end, sc->sc_rxbuf, 4);
+ if (!ptr) {
+ RecErr("Runt packet (hdr)", sc);
+ return 0;
+ }
+
+ /* XXX is this the IP header length, or what? */
+ packetlen = ((u_short)sc->sc_rxbuf[2] << 8) | sc->sc_rxbuf[3];
+/* printf("Packet %02x.%02x.%02x.%02x\n",
+ sc->sc_rxbuf[0], sc->sc_rxbuf[1],
+ sc->sc_rxbuf[2], sc->sc_rxbuf[3]);
+ printf("Got %d byte packet\n", packetlen); */
+
+ /* Decode remainder of the IP packer */
+ ptr = UnStuffData(ptr, end, sc->sc_rxbuf+4, packetlen-4);
+ if (!ptr) {
+ RecErr("Short packet", sc);
+ return 0;
+ }
+
+ /* XXX*/ bcopy(sc->sc_rxbuf, sc->sc_buf, packetlen );
+
+#ifdef linux
+ strip_bump(sc, packetlen);
+#endif
+ return(packetlen);
+}
+
+
+/*
+ * Stuffing scheme:
+ * 00 Unused (reserved character)
+ * 01-3F Run of 2-64 different characters
+ * 40-7F Run of 1-64 different characters plus a single zero at the end
+ * 80-BF Run of 1-64 of the same character
+ * C0-FF Run of 1-64 zeroes (ASCII 0)
+*/
+typedef enum
+{
+ Stuff_Diff = 0x00,
+ Stuff_DiffZero = 0x40,
+ Stuff_Same = 0x80,
+ Stuff_Zero = 0xC0,
+ Stuff_NoCode = 0xFF, /* Special code, meaning no code selected */
+
+ Stuff_CodeMask = 0xC0,
+ Stuff_CountMask = 0x3F,
+ Stuff_MaxCount = 0x3F,
+ Stuff_Magic = 0x0D /* The value we are eliminating */
+} StuffingCode;
+
+/*
+ * StuffData encodes the data starting at "src" for "length" bytes.
+ * It writes it to the buffer pointed to by "dest" (which must be at least
+ * as long as 1 + 65/64 of the input length). The output may be up to 1.6%
+ * larger than the input for pathological input, but will usually be smaller.
+ * StuffData returns the new value of the dest pointer as its result.
+ *
+ * "code_ptr_ptr" points to a "u_char *" which is used to hold
+ * encoding state between calls, allowing an encoded packet to be
+ * incrementally built up from small parts.
+ * On the first call, the "u_char *" pointed to should be initialized
+ * to NULL; between subsequent calls the calling routine should leave
+ * the value alone and simply pass it back unchanged so that the
+ * encoder can recover its current state.
+ */
+
+#define StuffData_FinishBlock(X) \
+ (*code_ptr = (X) ^ Stuff_Magic, code = Stuff_NoCode)
+
+static u_char*
+StuffData(u_char *src, u_long length, u_char *dest, u_char **code_ptr_ptr)
+{
+ u_char *end = src + length;
+ u_char *code_ptr = *code_ptr_ptr;
+ u_char code = Stuff_NoCode, count = 0;
+
+ if (!length) return(dest);
+
+ if (code_ptr) { /* Recover state from last call, if applicable */
+ code = *code_ptr & Stuff_CodeMask;
+ count = *code_ptr & Stuff_CountMask;
+ }
+
+ while (src < end) {
+ switch (code) {
+ /*
+ * Stuff_NoCode: If no current code, select one
+ */
+ case Stuff_NoCode:
+ code_ptr = dest++; /* Record where we're going to put this code */
+ count = 0; /* Reset the count (zero means one instance) */
+ /* Tentatively start a new block */
+ if (*src == 0) {
+ code = Stuff_Zero;
+ src++;
+ } else {
+ code = Stuff_Same;
+ *dest++ = *src++ ^ Stuff_Magic;
+ }
+ /* Note: We optimistically assume run of same -- which will be */
+ /* fixed later in Stuff_Same if it turns out not to be true. */
+ break;
+
+ /*
+ * Stuff_Zero: We already have at least one zero encoded
+ */
+ case Stuff_Zero:
+
+ /* If another zero, count it, else finish this code block */
+ if (*src == 0) {
+ count++;
+ src++;
+ } else
+ StuffData_FinishBlock(Stuff_Zero + count);
+ break;
+
+ /*
+ * Stuff_Same: We already have at least one byte encoded
+ */
+ case Stuff_Same:
+ /* If another one the same, count it */
+ if ((*src ^ Stuff_Magic) == code_ptr[1]) {
+ count++;
+ src++;
+ break;
+ }
+ /* else, this byte does not match this block. */
+ /* If we already have two or more bytes encoded, finish this code block */
+ if (count) {
+ StuffData_FinishBlock(Stuff_Same + count);
+ break;
+ }
+ /* else, we only have one so far, so switch to Stuff_Diff code */
+ code = Stuff_Diff; /* and fall through to Stuff_Diff case below */
+
+ case Stuff_Diff: /* Stuff_Diff: We have at least two *different* bytes encoded */
+ /* If this is a zero, must encode a Stuff_DiffZero, and begin a new block */
+ if (*src == 0)
+ StuffData_FinishBlock(Stuff_DiffZero + count);
+ /* else, if we have three in a row, it is worth starting a Stuff_Same block */
+ else if ((*src ^ Stuff_Magic) == dest[-1] && dest[-1] == dest[-2])
+ {
+ code += count-2;
+ if (code == Stuff_Diff)
+ code = Stuff_Same;
+ StuffData_FinishBlock(code);
+ code_ptr = dest-2;
+ /* dest[-1] already holds the correct value */
+ count = 2; /* 2 means three bytes encoded */
+ code = Stuff_Same;
+ }
+ /* else, another different byte, so add it to the block */
+ else {
+ *dest++ = *src ^ Stuff_Magic;
+ count++;
+ }
+ src++; /* Consume the byte */
+ break;
+ }
+
+ if (count == Stuff_MaxCount)
+ StuffData_FinishBlock(code + count);
+ }
+ if (code == Stuff_NoCode)
+ *code_ptr_ptr = NULL;
+ else {
+ *code_ptr_ptr = code_ptr;
+ StuffData_FinishBlock(code + count);
+ }
+
+ return(dest);
+}
+
+
+
+/*
+ * UnStuffData decodes the data at "src", up to (but not including)
+ * "end". It writes the decoded data into the buffer pointed to by
+ * "dest", up to a maximum of "dest_length", and returns the new
+ * value of "src" so that a follow-on call can read more data,
+ * continuing from where the first left off.
+ *
+ * There are three types of results:
+ * 1. The source data runs out before extracting "dest_length" bytes:
+ * UnStuffData returns NULL to indicate failure.
+ * 2. The source data produces exactly "dest_length" bytes:
+ * UnStuffData returns new_src = end to indicate that all bytes
+ * were consumed.
+ * 3. "dest_length" bytes are extracted, with more
+ * remaining. UnStuffData returns new_src < end to indicate that
+ * there are more bytes to be read.
+ *
+ * Note: The decoding may be destructive, in that it may alter the
+ * source data in the process of decoding it (this is necessary to
+ * allow a follow-on call to resume correctly).
+ */
+
+static u_char*
+UnStuffData(u_char *src, u_char *end, u_char *dest, u_long dest_length)
+{
+ u_char *dest_end = dest + dest_length;
+
+ if (!src || !end || !dest || !dest_length)
+ return(NULL); /* Sanity check */
+
+ while (src < end && dest < dest_end) {
+ int count = (*src ^ Stuff_Magic) & Stuff_CountMask;
+ switch ((*src ^ Stuff_Magic) & Stuff_CodeMask)
+ {
+ case Stuff_Diff:
+ if (src+1+count >= end)
+ return(NULL);
+ do
+ *dest++ = *++src ^ Stuff_Magic;
+ while(--count >= 0 && dest < dest_end);
+ if (count < 0)
+ src += 1;
+ else if (count == 0)
+ *src = Stuff_Same ^ Stuff_Magic;
+ else
+ *src = (Stuff_Diff + count) ^ Stuff_Magic;
+ break;
+ case Stuff_DiffZero:
+ if (src+1+count >= end)
+ return(NULL);
+ do
+ *dest++ = *++src ^ Stuff_Magic;
+ while(--count >= 0 && dest < dest_end);
+ if (count < 0)
+ *src = Stuff_Zero ^ Stuff_Magic;
+ else
+ *src = (Stuff_DiffZero + count) ^ Stuff_Magic;
+ break;
+ case Stuff_Same:
+ if (src+1 >= end)
+ return(NULL);
+ do
+ *dest++ = src[1] ^ Stuff_Magic;
+ while(--count >= 0 && dest < dest_end);
+ if (count < 0)
+ src += 2;
+ else
+ *src = (Stuff_Same + count) ^ Stuff_Magic;
+ break;
+ case Stuff_Zero:
+ do
+ *dest++ = 0;
+ while(--count >= 0 && dest < dest_end);
+ if (count < 0)
+ src += 1;
+ else
+ *src = (Stuff_Zero + count) ^ Stuff_Magic;
+ break;
+ }
+ }
+
+ if (dest < dest_end)
+ return(NULL);
+ else
+ return(src);
+}
+
+
+
+/*
+ * Log an error mesesage (for a packet received with errors?)
+ * rom the STRIP driver.
+ * XXX check with original author.
+ */
+static void
+RecErr(msg, sc)
+ char *msg;
+ struct st_softc *sc;
+{
+ static const int MAX_RecErr = 80;
+ u_char *ptr = sc->sc_buf;
+ u_char *end = sc->sc_mp;
+ u_char pkt_text[MAX_RecErr], *p = pkt_text;
+ *p++ = '\"';
+ while (ptr < end && p < &pkt_text[MAX_RecErr-4]) {
+ if (*ptr == '\\') {
+ *p++ = '\\';
+ *p++ = '\\';
+ } else if (*ptr >= 32 && *ptr <= 126)
+ *p++ = *ptr;
+ else {
+ sprintf(p, "\\%02x", *ptr);
+ p+= 3;
+ }
+ ptr++;
+ }
+
+ if (ptr == end) *p++ = '\"';
+ *p++ = 0;
+ addlog("%13s : %s\n", msg, pkt_text);
+
+#ifdef linux
+ set_bit(SLF_ERROR, &sc->flags);
+ sc->rx_errors++;
+#endif /* linux */
+ sc->sc_if.if_ierrors++;
+}
+
+/*
+ * Log an error message for a packet recieved from a remote Metricom
+ * radio. Update the radio-reset timer if the message is one that
+ * is generated only in starmode.
+ *
+ * We only call this function when we have an error message from
+ * the radio, which can only happen after seeing a frame delimeter
+ * in the input-side routine; so it's safe to call the output side
+ * to reset the radio.
+ */
+static void
+RecERR_Message(sc, sendername, msg)
+ struct st_softc *sc;
+ u_char *sendername;
+ u_char *msg;
+{
+ static const char ERR_001[] = "ERR_001 Not in StarMode!";
+ static const char ERR_002[] = "ERR_002 Remap handle";
+ static const char ERR_003[] = "ERR_003 Can't resolve name";
+ static const char ERR_004[] = "ERR_004 Name too small or missing";
+ static const char ERR_007[] = "ERR_007 Body too big";
+ static const char ERR_008[] = "ERR_008 Bad character in name";
+
+ if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1)) {
+ printf("Radio %s is not in StarMode\n", sendername);
+ }
+ else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1)) {
+#ifdef notyet /*Kernel doesn't have scanf!*/
+ int handle;
+ u_char newname[64];
+ sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname);
+ printf("Radio name %s is handle %d\n", newname, handle);
+#endif
+ }
+ else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1)) {
+ printf("Radio name <unspecified> is unknown (\"Can't resolve name\" error)\n");
+ }
+ else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1)) {
+ CLEAR_RESET_TIMER(sc);
+ /* printf("%s: Received tickle response; clearing watchdog_doreset timer.\n",
+ sc->sc_if.if_xname); */
+ }
+ else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1)) {
+ /* Note: This error knoks the radio back into command mode. */
+ printf("Error! Packet size <unspecified> is too big for radio.");
+ FORCE_RESET(sc); /* Do reset ASAP */
+ }
+ else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1)) {
+ printf("Name <unspecified> contains illegal character\n");
+ }
+ else RecErr("Error Msg:", sc);
+}
+
+#ifdef DEBUG
+void
+stripdumpm(msg, m, len)
+ const char *msg;
+ struct mbuf *m;
+{
+ stripdump(msg, mtod(m, u_char*), len);
+ /*XXX*/
+}
+
+void
+stripdump(msg, p, len)
+ const char *msg;
+ u_char *p;
+ int len;
+{
+ register int i;
+
+
+ printf(msg);
+ for (i = 0; i < STRIP_HDRLEN; i++)
+ printf("%c", p[i]);
+ printf("\n");
+
+ p += STRIP_HDRLEN;
+ for (i = 0; i < 32; i++) {
+ printf("%02x ", p[i]);
+ }
+ printf("\n");
+}
+
+void
+ipdump(msg, p, len)
+ const char *msg;
+ u_char *p;
+ int len;
+{
+ register int i;
+
+ printf(msg);
+ for (i = 0; i < 32; i++) {
+ printf("%02x ", p[i]);
+ }
+ printf("\n");
+}
+
+#endif /* DEBUG */
+#endif /* NSTRIP > 0 */