diff options
| author | svn2git <svn2git@FreeBSD.org> | 1993-11-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1993-11-01 08:00:00 +0000 |
| commit | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (patch) | |
| tree | c5b2ce776438e0a52b492a2ab6ab41360b8ba1f6 /sys/net | |
Release FreeBSD 1.0upstream/1.0.0_cvsrelease/1.0.0_cvs
This commit was manufactured to restore the state of the 1.0-RELEASE image.
Releases prior to 5.3-RELEASE are omitting the secure/ and crypto/ subdirs.
Diffstat (limited to 'sys/net')
| -rw-r--r-- | sys/net/af.c | 47 | ||||
| -rw-r--r-- | sys/net/af.h | 65 | ||||
| -rw-r--r-- | sys/net/bpf.c | 1289 | ||||
| -rw-r--r-- | sys/net/bpf.h | 243 | ||||
| -rw-r--r-- | sys/net/bpf_filter.c | 546 | ||||
| -rw-r--r-- | sys/net/bpfdesc.h | 88 | ||||
| -rw-r--r-- | sys/net/if.c | 646 | ||||
| -rw-r--r-- | sys/net/if.h | 255 | ||||
| -rw-r--r-- | sys/net/if_arp.h | 79 | ||||
| -rw-r--r-- | sys/net/if_dl.h | 81 | ||||
| -rw-r--r-- | sys/net/if_ethersubr.c | 421 | ||||
| -rw-r--r-- | sys/net/if_llc.h | 83 | ||||
| -rw-r--r-- | sys/net/if_loop.c | 218 | ||||
| -rw-r--r-- | sys/net/if_ppp.c | 1441 | ||||
| -rw-r--r-- | sys/net/if_ppp.h | 119 | ||||
| -rw-r--r-- | sys/net/if_sl.c | 828 | ||||
| -rw-r--r-- | sys/net/if_slvar.h | 74 | ||||
| -rw-r--r-- | sys/net/if_tun.c | 566 | ||||
| -rw-r--r-- | sys/net/if_types.h | 66 | ||||
| -rw-r--r-- | sys/net/netisr.h | 79 | ||||
| -rw-r--r-- | sys/net/ppp.h | 41 | ||||
| -rw-r--r-- | sys/net/radix.c | 641 | ||||
| -rw-r--r-- | sys/net/radix.h | 119 | ||||
| -rw-r--r-- | sys/net/raw_cb.c | 143 | ||||
| -rw-r--r-- | sys/net/raw_cb.h | 60 | ||||
| -rw-r--r-- | sys/net/raw_usrreq.c | 310 | ||||
| -rw-r--r-- | sys/net/route.c | 509 | ||||
| -rw-r--r-- | sys/net/route.h | 224 | ||||
| -rw-r--r-- | sys/net/rtsock.c | 660 | ||||
| -rw-r--r-- | sys/net/slcompress.c | 551 | ||||
| -rw-r--r-- | sys/net/slcompress.h | 166 | ||||
| -rw-r--r-- | sys/net/slip.h | 48 |
32 files changed, 10706 insertions, 0 deletions
diff --git a/sys/net/af.c b/sys/net/af.c new file mode 100644 index 000000000000..e7c7a4cd3c18 --- /dev/null +++ b/sys/net/af.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1983, 1986 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. + * + * from: @(#)af.c 7.6 (Berkeley) 6/28/90 + * $Id: af.c,v 1.2 1993/10/16 17:43:00 rgrimes Exp $ + */ + +#include "param.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" +#include "af.h" + +/* + * Nothing in the file should be needed anymore. + */ +int SocketSizeForInspection = sizeof (struct socket); diff --git a/sys/net/af.h b/sys/net/af.h new file mode 100644 index 000000000000..e280ef280cd9 --- /dev/null +++ b/sys/net/af.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1980, 1986 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. + * + * from: @(#)af.h 7.5 (Berkeley) 6/28/90 + * $Id: af.h,v 1.2 1993/10/16 17:43:01 rgrimes Exp $ + */ + +/* + * This file is obsolete, I think. kls 12/21/88 + + * Address family routines, + * used in handling generic sockaddr structures. + * + * Hash routine is called + * af_hash(addr, h); + * struct sockaddr *addr; struct afhash *h; + * producing an afhash structure for addr. + * + * Netmatch routine is called + * af_netmatch(addr1, addr2); + * where addr1 and addr2 are sockaddr *. Returns 1 if network + * values match, 0 otherwise. +struct afswitch { + int (*af_hash)(); + int (*af_netmatch)(); +}; + +struct afhash { + u_int afh_hosthash; + u_int afh_nethash; +}; + +#ifdef KERNEL +struct afswitch afswitch[]; +#endif + */ diff --git a/sys/net/bpf.c b/sys/net/bpf.c new file mode 100644 index 000000000000..981275b112fb --- /dev/null +++ b/sys/net/bpf.c @@ -0,0 +1,1289 @@ +/*- + * Copyright (c) 1990-1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. + * + * from: @(#)bpf.c 7.5 (Berkeley) 7/15/91 + * $Id: bpf.c,v 1.2 1993/10/16 17:43:03 rgrimes Exp $ + */ + +#include "bpfilter.h" + +#if NBPFILTER > 0 + +#ifdef notyet +#ifndef __GNUC__ +#define inline +#else +#define inline __inline__ +#endif /* __GNUC__ */ +#endif /* notyet */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/buf.h> +#include <sys/dir.h> +#include <sys/time.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/ioctl.h> +/* #include <sys/map.h> */ + +#include <sys/file.h> +#if defined(sparc) && BSD < 199103 +#include <sys/stream.h> +#endif +#include <sys/tty.h> +#include <sys/uio.h> + +#include <sys/protosw.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <net/bpf.h> +#include <net/bpfdesc.h> + +#include <sys/errno.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <sys/kernel.h> + +/* + * Older BSDs don't have kernel malloc. + */ +#if BSD < 199103 +extern bcopy(); +static caddr_t bpf_alloc(); +#include <net/bpf_compat.h> +#define BPF_BUFSIZE (MCLBYTES-8) +#define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio) +#else +#define BPF_BUFSIZE 4096 +#define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio) +#endif + +#define PRINET 26 /* interruptible */ + +/* + * The default read buffer size is patchable. + */ +int bpf_bufsize = BPF_BUFSIZE; + +/* + * bpf_iflist is the list of interfaces; each corresponds to an ifnet + * bpf_dtab holds the descriptors, indexed by minor device # + */ +struct bpf_if *bpf_iflist; +struct bpf_d bpf_dtab[NBPFILTER]; + +static void bpf_ifname(); +static void catchpacket(); +static void bpf_freed(); +static int bpf_setif(); +static int bpf_initd(); +static int bpf_allocbufs(); + +static int +bpf_movein(uio, linktype, mp, sockp) + register struct uio *uio; + int linktype; + register struct mbuf **mp; + register struct sockaddr *sockp; +{ + struct mbuf *m; + int error; + int len; + int hlen; + + /* + * Build a sockaddr based on the data link layer type. + * We do this at this level because the ethernet header + * is copied directly into the data field of the sockaddr. + * In the case of SLIP, there is no header and the packet + * is forwarded as is. + * Also, we are careful to leave room at the front of the mbuf + * for the link level header. + */ + switch (linktype) { + + case DLT_SLIP: + sockp->sa_family = AF_INET; + hlen = 0; + break; + + case DLT_EN10MB: + sockp->sa_family = AF_UNSPEC; + /* XXX Would MAXLINKHDR be better? */ + hlen = sizeof(struct ether_header); + break; + + case DLT_FDDI: + sockp->sa_family = AF_UNSPEC; + /* XXX 4(FORMAC)+6(dst)+6(src)+3(LLC)+5(SNAP) */ + hlen = 24; + break; + + case DLT_NULL: + sockp->sa_family = AF_UNSPEC; + hlen = 0; + break; + + default: + return (EIO); + } + + len = uio->uio_resid; + if ((unsigned)len > MCLBYTES) + return (EIO); + + MGET(m, M_WAIT, MT_DATA); + if (m == 0) + return (ENOBUFS); + if (len > MLEN) { +#if BSD >= 199103 + MCLGET(m, M_WAIT); + if ((m->m_flags & M_EXT) == 0) { +#else + MCLGET(m); + if (m->m_len != MCLBYTES) { +#endif + error = ENOBUFS; + goto bad; + } + } + m->m_len = len; + *mp = m; + /* + * Make room for link header. + */ + if (hlen != 0) { + m->m_len -= hlen; +#if BSD >= 199103 + m->m_data += hlen; /* XXX */ +#else + m->m_off += hlen; +#endif + error = UIOMOVE((caddr_t)sockp->sa_data, hlen, UIO_WRITE, uio); + if (error) + goto bad; + } + error = UIOMOVE(mtod(m, caddr_t), len - hlen, UIO_WRITE, uio); + if (!error) + return (0); + bad: + m_freem(m); + return (error); +} + +/* + * Attach file to the bpf interface, i.e. make d listen on bp. + * Must be called at splimp. + */ +static void +bpf_attachd(d, bp) + struct bpf_d *d; + struct bpf_if *bp; +{ + /* + * Point d at bp, and add d to the interface's list of listeners. + * Finally, point the driver's bpf cookie at the interface so + * it will divert packets to bpf. + */ + d->bd_bif = bp; + d->bd_next = bp->bif_dlist; + bp->bif_dlist = d; + + *bp->bif_driverp = bp; +} + +/* + * Detach a file from its interface. + */ +static void +bpf_detachd(d) + struct bpf_d *d; +{ + struct bpf_d **p; + struct bpf_if *bp; + + bp = d->bd_bif; + /* + * Check if this descriptor had requested promiscuous mode. + * If so, turn it off. + */ + if (d->bd_promisc) { + d->bd_promisc = 0; + if (ifpromisc(bp->bif_ifp, 0)) + /* + * Something is really wrong if we were able to put + * the driver into promiscuous mode, but can't + * take it out. + */ + panic("bpf: ifpromisc failed"); + } + /* Remove d from the interface's descriptor list. */ + p = &bp->bif_dlist; + while (*p != d) { + p = &(*p)->bd_next; + if (*p == 0) + panic("bpf_detachd: descriptor not in list"); + } + *p = (*p)->bd_next; + if (bp->bif_dlist == 0) + /* + * Let the driver know that there are no more listeners. + */ + *d->bd_bif->bif_driverp = 0; + d->bd_bif = 0; +} + + +/* + * Mark a descriptor free by making it point to itself. + * This is probably cheaper than marking with a constant since + * the address should be in a register anyway. + */ +#define D_ISFREE(d) ((d) == (d)->bd_next) +#define D_MARKFREE(d) ((d)->bd_next = (d)) +#define D_MARKUSED(d) ((d)->bd_next = 0) + +/* + * Open ethernet device. Returns ENXIO for illegal minor device number, + * EBUSY if file is open by another process. + */ +/* ARGSUSED */ +int +bpfopen(dev, flag) + dev_t dev; + int flag; +{ + register struct bpf_d *d; + + if (minor(dev) >= NBPFILTER) + return (ENXIO); + /* + * Each minor can be opened by only one process. If the requested + * minor is in use, return EBUSY. + */ + d = &bpf_dtab[minor(dev)]; + if (!D_ISFREE(d)) + return (EBUSY); + + /* Mark "free" and do most initialization. */ + bzero((char *)d, sizeof(*d)); + d->bd_bufsize = bpf_bufsize; + + return (0); +} + +/* + * Close the descriptor by detaching it from its interface, + * deallocating its buffers, and marking it free. + */ +/* ARGSUSED */ +int +bpfclose(dev, flag) + dev_t dev; + int flag; +{ + register struct bpf_d *d = &bpf_dtab[minor(dev)]; + register int s; + + s = splimp(); + if (d->bd_bif) + bpf_detachd(d); + splx(s); + bpf_freed(d); + + return (0); +} + +/* + * Support for SunOS, which does not have tsleep. + */ +#if BSD < 199103 +static +bpf_timeout(arg) + caddr_t arg; +{ + struct bpf_d *d = (struct bpf_d *)arg; + d->bd_timedout = 1; + wakeup(arg); +} + +#define BPF_SLEEP(chan, pri, s, t) bpf_sleep((struct bpf_d *)chan) + +int +bpf_sleep(d) + register struct bpf_d *d; +{ + register int rto = d->bd_rtout; + register int st; + + if (rto != 0) { + d->bd_timedout = 0; + timeout(bpf_timeout, (caddr_t)d, rto); + } + st = sleep((caddr_t)d, PRINET|PCATCH); + if (rto != 0) { + if (d->bd_timedout == 0) + untimeout(bpf_timeout, (caddr_t)d); + else if (st == 0) + return EWOULDBLOCK; + } + return (st != 0) ? EINTR : 0; +} +#else +#define BPF_SLEEP tsleep +#endif + +/* + * Rotate the packet buffers in descriptor d. Move the store buffer + * into the hold slot, and the free buffer into the store slot. + * Zero the length of the new store buffer. + */ +#define ROTATE_BUFFERS(d) \ + (d)->bd_hbuf = (d)->bd_sbuf; \ + (d)->bd_hlen = (d)->bd_slen; \ + (d)->bd_sbuf = (d)->bd_fbuf; \ + (d)->bd_slen = 0; \ + (d)->bd_fbuf = 0; +/* + * bpfread - read next chunk of packets from buffers + */ +int +bpfread(dev, uio) + dev_t dev; + register struct uio *uio; +{ + register struct bpf_d *d = &bpf_dtab[minor(dev)]; + int error; + int s; + + /* + * Restrict application to use a buffer the same size as + * as kernel buffers. + */ + if (uio->uio_resid != d->bd_bufsize) + return (EINVAL); + + s = splimp(); + /* + * If the hold buffer is empty, then do a timed sleep, which + * ends when the timeout expires or when enough packets + * have arrived to fill the store buffer. + */ + while (d->bd_hbuf == 0) { + if (d->bd_immediate && d->bd_slen != 0) { + /* + * A packet(s) either arrived since the previous + * read or arrived while we were asleep. + * Rotate the buffers and return what's here. + */ + ROTATE_BUFFERS(d); + break; + } + error = BPF_SLEEP((caddr_t)d, PRINET|PCATCH, "bpf", + d->bd_rtout); + if (error == EINTR || error == ERESTART) { + splx(s); + return (error); + } + if (error == EWOULDBLOCK) { + /* + * On a timeout, return what's in the buffer, + * which may be nothing. If there is something + * in the store buffer, we can rotate the buffers. + */ + if (d->bd_hbuf) + /* + * We filled up the buffer in between + * getting the timeout and arriving + * here, so we don't need to rotate. + */ + break; + + if (d->bd_slen == 0) { + splx(s); + return (0); + } + ROTATE_BUFFERS(d); + break; + } + } + /* + * At this point, we know we have something in the hold slot. + */ + splx(s); + + /* + * Move data from hold buffer into user space. + * We know the entire buffer is transferred since + * we checked above that the read buffer is bpf_bufsize bytes. + */ + error = UIOMOVE(d->bd_hbuf, d->bd_hlen, UIO_READ, uio); + + s = splimp(); + d->bd_fbuf = d->bd_hbuf; + d->bd_hbuf = 0; + d->bd_hlen = 0; + splx(s); + + return (error); +} + + +/* + * If there are processes sleeping on this descriptor, wake them up. + */ +static inline void +bpf_wakeup(d) + register struct bpf_d *d; +{ + wakeup((caddr_t)d); +#if 0 + selwakeup(&d->bd_sel); + /* XXX */ + d->bd_sel.si_pid = 0; +#else + if (d->bd_selproc) { + selwakeup(d->bd_selproc->p_pid, (int)d->bd_selcoll); + d->bd_selcoll = 0; + d->bd_selproc = 0; + } +#endif +} + +int +bpfwrite(dev, uio) + dev_t dev; + struct uio *uio; +{ + register struct bpf_d *d = &bpf_dtab[minor(dev)]; + struct ifnet *ifp; + struct mbuf *m; + int error, s; + static struct sockaddr dst; + + if (d->bd_bif == 0) + return (ENXIO); + + ifp = d->bd_bif->bif_ifp; + + if (uio->uio_resid == 0) + return (0); + if (uio->uio_resid > ifp->if_mtu) + return (EMSGSIZE); + + error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, &m, &dst); + if (error) + return (error); + + s = splnet(); +#if BSD >= 199103 + error = (*ifp->if_output)(ifp, m, &dst, (struct rtentry *)0); +#else + error = (*ifp->if_output)(ifp, m, &dst); +#endif + splx(s); + /* + * The driver frees the mbuf. + */ + return (error); +} + +/* + * Reset a descriptor by flushing its packet buffer and clearing the + * receive and drop counts. Should be called at splimp. + */ +static void +reset_d(d) + struct bpf_d *d; +{ + if (d->bd_hbuf) { + /* Free the hold buffer. */ + d->bd_fbuf = d->bd_hbuf; + d->bd_hbuf = 0; + } + d->bd_slen = 0; + d->bd_hlen = 0; + d->bd_rcount = 0; + d->bd_dcount = 0; +} + +/* + * FIONREAD Check for read packet available. + * SIOCGIFADDR Get interface address - convenient hook to driver. + * BIOCGBLEN Get buffer len [for read()]. + * BIOCSETF Set ethernet read filter. + * BIOCFLUSH Flush read packet buffer. + * BIOCPROMISC Put interface into promiscuous mode. + * BIOCGDLT Get link layer type. + * BIOCGETIF Get interface name. + * BIOCSETIF Set interface. + * BIOCSRTIMEOUT Set read timeout. + * BIOCGRTIMEOUT Get read timeout. + * BIOCGSTATS Get packet stats. + * BIOCIMMEDIATE Set immediate mode. + * BIOCVERSION Get filter language version. + */ +/* ARGSUSED */ +int +bpfioctl(dev, cmd, addr, flag) + dev_t dev; + int cmd; + caddr_t addr; + int flag; +{ + register struct bpf_d *d = &bpf_dtab[minor(dev)]; + int s, error = 0; + + switch (cmd) { + + default: + error = EINVAL; + break; + + /* + * Check for read packet available. + */ + case FIONREAD: + { + int n; + + s = splimp(); + n = d->bd_slen; + if (d->bd_hbuf) + n += d->bd_hlen; + splx(s); + + *(int *)addr = n; + break; + } + + case SIOCGIFADDR: + { + struct ifnet *ifp; + + if (d->bd_bif == 0) + error = EINVAL; + else { + ifp = d->bd_bif->bif_ifp; + error = (*ifp->if_ioctl)(ifp, cmd, addr); + } + break; + } + + /* + * Get buffer len [for read()]. + */ + case BIOCGBLEN: + *(u_int *)addr = d->bd_bufsize; + break; + + /* + * Set buffer length. + */ + case BIOCSBLEN: +#if BSD < 199103 + error = EINVAL; +#else + if (d->bd_bif != 0) + error = EINVAL; + else { + register u_int size = *(u_int *)addr; + + if (size > BPF_MAXBUFSIZE) + *(u_int *)addr = size = BPF_MAXBUFSIZE; + else if (size < BPF_MINBUFSIZE) + *(u_int *)addr = size = BPF_MINBUFSIZE; + d->bd_bufsize = size; + } +#endif + break; + + /* + * Set link layer read filter. + */ + case BIOCSETF: + error = bpf_setf(d, (struct bpf_program *)addr); + break; + + /* + * Flush read packet buffer. + */ + case BIOCFLUSH: + s = splimp(); + reset_d(d); + splx(s); + break; + + /* + * Put interface into promiscuous mode. + */ + case BIOCPROMISC: + if (d->bd_bif == 0) { + /* + * No interface attached yet. + */ + error = EINVAL; + break; + } + s = splimp(); + if (d->bd_promisc == 0) { + error = ifpromisc(d->bd_bif->bif_ifp, 1); + if (error == 0) + d->bd_promisc = 1; + } + splx(s); + break; + + /* + * Get device parameters. + */ + case BIOCGDLT: + if (d->bd_bif == 0) + error = EINVAL; + else + *(u_int *)addr = d->bd_bif->bif_dlt; + break; + + /* + * Set interface name. + */ + case BIOCGETIF: + if (d->bd_bif == 0) + error = EINVAL; + else + bpf_ifname(d->bd_bif->bif_ifp, (struct ifreq *)addr); + break; + + /* + * Set interface. + */ + case BIOCSETIF: + error = bpf_setif(d, (struct ifreq *)addr); + break; + + /* + * Set read timeout. + */ + case BIOCSRTIMEOUT: + { + struct timeval *tv = (struct timeval *)addr; + u_long msec; + + /* Compute number of milliseconds. */ + msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; + /* Scale milliseconds to ticks. Assume hard + clock has millisecond or greater resolution + (i.e. tick >= 1000). For 10ms hardclock, + tick/1000 = 10, so rtout<-msec/10. */ + d->bd_rtout = msec / (tick / 1000); + break; + } + + /* + * Get read timeout. + */ + case BIOCGRTIMEOUT: + { + struct timeval *tv = (struct timeval *)addr; + u_long msec = d->bd_rtout; + + msec *= tick / 1000; + tv->tv_sec = msec / 1000; + tv->tv_usec = msec % 1000; + break; + } + + /* + * Get packet stats. + */ + case BIOCGSTATS: + { + struct bpf_stat *bs = (struct bpf_stat *)addr; + + bs->bs_recv = d->bd_rcount; + bs->bs_drop = d->bd_dcount; + break; + } + + /* + * Set immediate mode. + */ + case BIOCIMMEDIATE: + d->bd_immediate = *(u_int *)addr; + break; + + case BIOCVERSION: + { + struct bpf_version *bv = (struct bpf_version *)addr; + + bv->bv_major = BPF_MAJOR_VERSION; + bv->bv_minor = BPF_MINOR_VERSION; + break; + } + } + return (error); +} + +/* + * Set d's packet filter program to fp. If this file already has a filter, + * free it and replace it. Returns EINVAL for bogus requests. + */ +int +bpf_setf(d, fp) + struct bpf_d *d; + struct bpf_program *fp; +{ + struct bpf_insn *fcode, *old; + u_int flen, size; + int s; + + old = d->bd_filter; + if (fp->bf_insns == 0) { + if (fp->bf_len != 0) + return (EINVAL); + s = splimp(); + d->bd_filter = 0; + reset_d(d); + splx(s); + if (old != 0) + free((caddr_t)old, M_DEVBUF); + return (0); + } + flen = fp->bf_len; + if (flen > BPF_MAXINSNS) + return (EINVAL); + + size = flen * sizeof(*fp->bf_insns); + fcode = (struct bpf_insn *)malloc(size, M_DEVBUF, M_WAITOK); + if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 && + bpf_validate(fcode, (int)flen)) { + s = splimp(); + d->bd_filter = fcode; + reset_d(d); + splx(s); + if (old != 0) + free((caddr_t)old, M_DEVBUF); + + return (0); + } + free((caddr_t)fcode, M_DEVBUF); + return (EINVAL); +} + +/* + * Detach a file from its current interface (if attached at all) and attach + * to the interface indicated by the name stored in ifr. + * Return an errno or 0. + */ +static int +bpf_setif(d, ifr) + struct bpf_d *d; + struct ifreq *ifr; +{ + struct bpf_if *bp; + char *cp; + int unit, s, error; + + /* + * Separate string into name part and unit number. Put a null + * byte at the end of the name part, and compute the number. + * If the a unit number is unspecified, the default is 0, + * as initialized above. XXX This should be common code. + */ + unit = 0; + cp = ifr->ifr_name; + cp[sizeof(ifr->ifr_name) - 1] = '\0'; + while (*cp++) { + if (*cp >= '0' && *cp <= '9') { + unit = *cp - '0'; + *cp++ = '\0'; + while (*cp) + unit = 10 * unit + *cp++ - '0'; + break; + } + } + /* + * Look through attached interfaces for the named one. + */ + for (bp = bpf_iflist; bp != 0; bp = bp->bif_next) { + struct ifnet *ifp = bp->bif_ifp; + + if (ifp == 0 || unit != ifp->if_unit + || strcmp(ifp->if_name, ifr->ifr_name) != 0) + continue; + /* + * We found the requested interface. + * If it's not up, return an error. + * Allocate the packet buffers if we need to. + * If we're already attached to requested interface, + * just flush the buffer. + */ + if ((ifp->if_flags & IFF_UP) == 0) + return (ENETDOWN); + + if (d->bd_sbuf == 0) { + error = bpf_allocbufs(d); + if (error != 0) + return (error); + } + s = splimp(); + if (bp != d->bd_bif) { + if (d->bd_bif) + /* + * Detach if attached to something else. + */ + bpf_detachd(d); + + bpf_attachd(d, bp); + } + reset_d(d); + splx(s); + return (0); + } + /* Not found. */ + return (ENXIO); +} + +/* + * Convert an interface name plus unit number of an ifp to a single + * name which is returned in the ifr. + */ +static void +bpf_ifname(ifp, ifr) + struct ifnet *ifp; + struct ifreq *ifr; +{ + char *s = ifp->if_name; + char *d = ifr->ifr_name; + + while (*d++ = *s++) + continue; + /* XXX Assume that unit number is less than 10. */ + *d++ = ifp->if_unit + '0'; + *d = '\0'; +} + +/* + * The new select interface passes down the proc pointer; the old select + * stubs had to grab it out of the user struct. This glue allows either case. + */ +#if BSD >= 199103 +#define bpf_select bpfselect +#else +int +bpfselect(dev, rw) + register dev_t dev; + int rw; +{ + return (bpf_select(dev, rw, u.u_procp)); +} +#endif + +/* + * Support for select() system call + * Inspired by the code in tty.c for the same purpose. + * + * Return true iff the specific operation will not block indefinitely. + * Otherwise, return false but make a note that a selwakeup() must be done. + */ +int +bpf_select(dev, rw, p) + register dev_t dev; + int rw; + struct proc *p; +{ + register struct bpf_d *d; + register int s; + + if (rw != FREAD) + return (0); + /* + * An imitation of the FIONREAD ioctl code. + */ + d = &bpf_dtab[minor(dev)]; + + s = splimp(); + if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0)) { + /* + * There is data waiting. + */ + splx(s); + return (1); + } +#if 0 + selrecord(p, &d->bd_sel); +#else + /* + * No data ready. If there's already a select() waiting on this + * minor device then this is a collision. This shouldn't happen + * because minors really should not be shared, but if a process + * forks while one of these is open, it is possible that both + * processes could select on the same descriptor. + */ + if (d->bd_selproc && d->bd_selproc->p_wchan == (caddr_t)&selwait) + d->bd_selcoll = 1; + else + d->bd_selproc = p; +#endif + splx(s); + return (0); +} + +/* + * Incoming linkage from device drivers. Process the packet pkt, of length + * pktlen, which is stored in a contiguous buffer. The packet is parsed + * by each process' filter, and if accepted, stashed into the corresponding + * buffer. + */ +void +bpf_tap(arg, pkt, pktlen) + caddr_t arg; + register u_char *pkt; + register u_int pktlen; +{ + struct bpf_if *bp; + register struct bpf_d *d; + register u_int slen; + /* + * Note that the ipl does not have to be raised at this point. + * The only problem that could arise here is that if two different + * interfaces shared any data. This is not the case. + */ + bp = (struct bpf_if *)arg; + for (d = bp->bif_dlist; d != 0; d = d->bd_next) { + ++d->bd_rcount; + slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen); + if (slen != 0) + catchpacket(d, pkt, pktlen, slen, bcopy); + } +} + +/* + * Copy data from an mbuf chain into a buffer. This code is derived + * from m_copydata in sys/uipc_mbuf.c. + */ +static void +bpf_mcopy(src, dst, len) + u_char *src; + u_char *dst; + register int len; +{ + register struct mbuf *m = (struct mbuf *)src; + register unsigned count; + + while (len > 0) { + if (m == 0) + panic("bpf_mcopy"); + count = MIN(m->m_len, len); + bcopy(mtod(m, caddr_t), (caddr_t)dst, count); + m = m->m_next; + dst += count; + len -= count; + } +} + +/* + * Incoming linkage from device drivers, when packet is in an mbuf chain. + */ +void +bpf_mtap(arg, m) + caddr_t arg; + struct mbuf *m; +{ + struct bpf_if *bp = (struct bpf_if *)arg; + struct bpf_d *d; + u_int pktlen, slen; + struct mbuf *m0; + + pktlen = 0; + for (m0 = m; m0 != 0; m0 = m0->m_next) + pktlen += m0->m_len; + + for (d = bp->bif_dlist; d != 0; d = d->bd_next) { + ++d->bd_rcount; + slen = bpf_filter(d->bd_filter, (u_char *)m, pktlen, 0); + if (slen != 0) + catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcopy); + } +} + +/* + * Move the packet data from interface memory (pkt) into the + * store buffer. Return 1 if it's time to wakeup a listener (buffer full), + * otherwise 0. "copy" is the routine called to do the actual data + * transfer. bcopy is passed in to copy contiguous chunks, while + * bpf_mcopy is passed in to copy mbuf chains. In the latter case, + * pkt is really an mbuf. + */ +static void +catchpacket(d, pkt, pktlen, snaplen, cpfn) + register struct bpf_d *d; + register u_char *pkt; + register u_int pktlen, snaplen; + register void (*cpfn)(); +{ + register struct bpf_hdr *hp; + register int totlen, curlen; + register int hdrlen = d->bd_bif->bif_hdrlen; + /* + * Figure out how many bytes to move. If the packet is + * greater or equal to the snapshot length, transfer that + * much. Otherwise, transfer the whole packet (unless + * we hit the buffer size limit). + */ + totlen = hdrlen + MIN(snaplen, pktlen); + if (totlen > d->bd_bufsize) + totlen = d->bd_bufsize; + + /* + * Round up the end of the previous packet to the next longword. + */ + curlen = BPF_WORDALIGN(d->bd_slen); + if (curlen + totlen > d->bd_bufsize) { + /* + * This packet will overflow the storage buffer. + * Rotate the buffers if we can, then wakeup any + * pending reads. + */ + if (d->bd_fbuf == 0) { + /* + * We haven't completed the previous read yet, + * so drop the packet. + */ + ++d->bd_dcount; + return; + } + ROTATE_BUFFERS(d); + bpf_wakeup(d); + curlen = 0; + } + else if (d->bd_immediate) + /* + * Immediate mode is set. A packet arrived so any + * reads should be woken up. + */ + bpf_wakeup(d); + + /* + * Append the bpf header. + */ + hp = (struct bpf_hdr *)(d->bd_sbuf + curlen); +#if BSD >= 199103 + microtime(&hp->bh_tstamp); +#elif defined(sun) + uniqtime(&hp->bh_tstamp); +#else + hp->bh_tstamp = time; +#endif + hp->bh_datalen = pktlen; + hp->bh_hdrlen = hdrlen; + /* + * Copy the packet data into the store buffer and update its length. + */ + (*cpfn)(pkt, (u_char *)hp + hdrlen, (hp->bh_caplen = totlen - hdrlen)); + d->bd_slen = curlen + totlen; +} + +/* + * Initialize all nonzero fields of a descriptor. + */ +static int +bpf_allocbufs(d) + register struct bpf_d *d; +{ + d->bd_fbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK); + if (d->bd_fbuf == 0) + return (ENOBUFS); + + d->bd_sbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_WAITOK); + if (d->bd_sbuf == 0) { + free(d->bd_fbuf, M_DEVBUF); + return (ENOBUFS); + } + d->bd_slen = 0; + d->bd_hlen = 0; + return (0); +} + +/* + * Free buffers currently in use by a descriptor. + * Called on close. + */ +static void +bpf_freed(d) + register struct bpf_d *d; +{ + /* + * We don't need to lock out interrupts since this descriptor has + * been detached from its interface and it yet hasn't been marked + * free. + */ + if (d->bd_sbuf != 0) { + free(d->bd_sbuf, M_DEVBUF); + if (d->bd_hbuf != 0) + free(d->bd_hbuf, M_DEVBUF); + if (d->bd_fbuf != 0) + free(d->bd_fbuf, M_DEVBUF); + } + if (d->bd_filter) + free((caddr_t)d->bd_filter, M_DEVBUF); + + D_MARKFREE(d); +} + +/* + * Attach an interface to bpf. driverp is a pointer to a (struct bpf_if *) + * in the driver's softc; dlt is the link layer type; hdrlen is the fixed + * size of the link header (variable length headers not yet supported). + */ +void +bpfattach(driverp, ifp, dlt, hdrlen) + caddr_t *driverp; + struct ifnet *ifp; + u_int dlt, hdrlen; +{ + struct bpf_if *bp; + int i; +#if BSD < 199103 + static struct bpf_if bpf_ifs[NBPFILTER]; + static int bpfifno; + + bp = (bpfifno < NBPFILTER) ? &bpf_ifs[bpfifno++] : 0; +#else + bp = (struct bpf_if *)malloc(sizeof(*bp), M_DEVBUF, M_DONTWAIT); +#endif + if (bp == 0) + panic("bpfattach"); + + bp->bif_dlist = 0; + bp->bif_driverp = (struct bpf_if **)driverp; + bp->bif_ifp = ifp; + bp->bif_dlt = dlt; + + bp->bif_next = bpf_iflist; + bpf_iflist = bp; + + *bp->bif_driverp = 0; + + /* + * Compute the length of the bpf header. This is not necessarily + * equal to SIZEOF_BPF_HDR because we want to insert spacing such + * that the network layer header begins on a longword boundary (for + * performance reasons and to alleviate alignment restrictions). + */ + bp->bif_hdrlen = BPF_WORDALIGN(hdrlen + SIZEOF_BPF_HDR) - hdrlen; + + /* + * Mark all the descriptors free if this hasn't been done. + */ + if (!D_ISFREE(&bpf_dtab[0])) + for (i = 0; i < NBPFILTER; ++i) + D_MARKFREE(&bpf_dtab[i]); + + printf("bpf: %s%d attached\n", ifp->if_name, ifp->if_unit); +} + +#if BSD >= 199103 +/* XXX This routine belongs in net/if.c. */ +/* + * Set/clear promiscuous mode on interface ifp based on the truth value + * of pswitch. The calls are reference counted so that only the first + * "on" request actually has an effect, as does the final "off" request. + * Results are undefined if the "off" and "on" requests are not matched. + */ +int +ifpromisc(ifp, pswitch) + struct ifnet *ifp; + int pswitch; +{ + struct ifreq ifr; + /* + * If the device is not configured up, we cannot put it in + * promiscuous mode. + */ + if ((ifp->if_flags & IFF_UP) == 0) + return (ENETDOWN); + + if (pswitch) { + if (ifp->if_pcount++ != 0) + return (0); + ifp->if_flags |= IFF_PROMISC; + } else { + if (--ifp->if_pcount > 0) + return (0); + ifp->if_flags &= ~IFF_PROMISC; + } + ifr.ifr_flags = ifp->if_flags; + return ((*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr)); +} +#endif + +#if BSD < 199103 +/* + * Allocate some memory for bpf. This is temporary SunOS support, and + * is admittedly a hack. + * If resources unavaiable, return 0. + */ +static caddr_t +bpf_alloc(size, canwait) + register int size; + register int canwait; +{ + register struct mbuf *m; + + if ((unsigned)size > (MCLBYTES-8)) + return 0; + + MGET(m, canwait, MT_DATA); + if (m == 0) + return 0; + if ((unsigned)size > (MLEN-8)) { + MCLGET(m); + if (m->m_len != MCLBYTES) { + m_freem(m); + return 0; + } + } + *mtod(m, struct mbuf **) = m; + return mtod(m, caddr_t) + 8; +} +#endif +#endif diff --git a/sys/net/bpf.h b/sys/net/bpf.h new file mode 100644 index 000000000000..2019f83d90f0 --- /dev/null +++ b/sys/net/bpf.h @@ -0,0 +1,243 @@ +/*- + * Copyright (c) 1990-1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. + * + * from: @(#)bpf.h 7.1 (Berkeley) 5/7/91 + * $Id: bpf.h,v 1.2 1993/10/16 17:43:05 rgrimes Exp $ + */ + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + */ +#define BPF_ALIGNMENT sizeof(long) +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +#define BPF_MAXINSNS 512 +#define BPF_MAXBUFSIZE 0x8000 +#define BPF_MINBUFSIZE 32 + +/* + * Structure for BIOCSETF. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +/* + * Struct returned by BIOCGSTATS. + */ +struct bpf_stat { + u_int bs_recv; /* number of packets received */ + u_int bs_drop; /* number of packets dropped */ +}; + +/* + * Struct return by BIOCVERSION. This represents the version number of + * the filter language described by the instruction encodings below. + * bpf understands a program iff kernel_major == filter_major && + * kernel_minor >= filter_minor, that is, if the value returned by the + * running kernel has the same major number and a minor number equal + * equal to or less than the filter being downloaded. Otherwise, the + * results are undefined, meaning an error may be returned or packets + * may be accepted haphazardly. + * It has nothing to do with the source code version. + */ +struct bpf_version { + u_short bv_major; + u_short bv_minor; +}; +/* Current version number. */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * BPF ioctls + * + * The first set is for compatibility with Sun's pcc style + * header files. If your using gcc, we assume that you + * have run fixincludes so the latter set should work. + */ +#if (defined(sun) || defined(ibm032)) && !defined(__GNUC__) +#define BIOCGBLEN _IOR(B,102, u_int) +#define BIOCSBLEN _IOWR(B,102, u_int) +#define BIOCSETF _IOW(B,103, struct bpf_program) +#define BIOCFLUSH _IO(B,104) +#define BIOCPROMISC _IO(B,105) +#define BIOCGDLT _IOR(B,106, u_int) +#define BIOCGETIF _IOR(B,107, struct ifreq) +#define BIOCSETIF _IOW(B,108, struct ifreq) +#define BIOCSRTIMEOUT _IOW(B,109, struct timeval) +#define BIOCGRTIMEOUT _IOR(B,110, struct timeval) +#define BIOCGSTATS _IOR(B,111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW(B,112, u_int) +#define BIOCVERSION _IOR(B,113, struct bpf_version) +#else +#define BIOCGBLEN _IOR('B',102, u_int) +#define BIOCSBLEN _IOWR('B',102, u_int) +#define BIOCSETF _IOW('B',103, struct bpf_program) +#define BIOCFLUSH _IO('B',104) +#define BIOCPROMISC _IO('B',105) +#define BIOCGDLT _IOR('B',106, u_int) +#define BIOCGETIF _IOR('B',107, struct ifreq) +#define BIOCSETIF _IOW('B',108, struct ifreq) +#define BIOCSRTIMEOUT _IOW('B',109, struct timeval) +#define BIOCGRTIMEOUT _IOR('B',110, struct timeval) +#define BIOCGSTATS _IOR('B',111, struct bpf_stat) +#define BIOCIMMEDIATE _IOW('B',112, u_int) +#define BIOCVERSION _IOR('B',113, struct bpf_version) +#endif + +/* + * Structure prepended to each packet. + */ +struct bpf_hdr { + struct timeval bh_tstamp; /* time stamp */ + u_long bh_caplen; /* length of captured portion */ + u_long bh_datalen; /* original length of packet */ + u_short bh_hdrlen; /* length of bpf header (this struct + plus alignment padding) */ +}; +/* + * Because the structure above is not a multiple of 4 bytes, some compilers + * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work. + * Only the kernel needs to know about it; applications use bh_hdrlen. + */ +#ifdef KERNEL +#define SIZEOF_BPF_HDR 18 +#endif + +/* + * Data-link level type codes. + * Currently, only DLT_EN10MB and DLT_SLIP are supported. + */ +#define DLT_NULL 0 /* no link-layer encapsulation */ +#define DLT_EN10MB 1 /* Ethernet (10Mb) */ +#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ +#define DLT_AX25 3 /* Amateur Radio AX.25 */ +#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ +#define DLT_CHAOS 5 /* Chaos */ +#define DLT_IEEE802 6 /* IEEE 802 Networks */ +#define DLT_ARCNET 7 /* ARCNET */ +#define DLT_SLIP 8 /* Serial Line IP */ +#define DLT_PPP 9 /* Point-to-point Protocol */ +#define DLT_FDDI 10 /* FDDI */ + +/* + * The instruction encondings. + */ +/* instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +/* + * The instruction data structure. + */ +struct bpf_insn { + u_short code; + u_char jt; + u_char jf; + long k; +}; + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } + +#ifdef KERNEL +extern u_int bpf_filter(); +extern void bpfattach(); +extern void bpf_tap(); +extern void bpf_mtap(); +#endif + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + diff --git a/sys/net/bpf_filter.c b/sys/net/bpf_filter.c new file mode 100644 index 000000000000..c8fb0bb06262 --- /dev/null +++ b/sys/net/bpf_filter.c @@ -0,0 +1,546 @@ +/*- + * Copyright (c) 1990-1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. + * + * from: @(#)bpf.c 7.5 (Berkeley) 7/15/91 + * $Id: bpf_filter.c,v 1.2 1993/10/16 17:43:07 rgrimes Exp $ + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <net/bpf.h> + +#ifdef sun +#include <netinet/in.h> +#endif + +#if defined(sparc) || defined(mips) || defined(ibm032) +#define BPF_ALIGN +#endif + +#ifndef BPF_ALIGN +#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) +#define EXTRACT_LONG(p) (ntohl(*(u_long *)p)) +#else +#define EXTRACT_SHORT(p)\ + ((u_short)\ + ((u_short)*((u_char *)p+0)<<8|\ + (u_short)*((u_char *)p+1)<<0)) +#define EXTRACT_LONG(p)\ + ((u_long)*((u_char *)p+0)<<24|\ + (u_long)*((u_char *)p+1)<<16|\ + (u_long)*((u_char *)p+2)<<8|\ + (u_long)*((u_char *)p+3)<<0) +#endif + +#ifdef KERNEL +#include <sys/mbuf.h> +#define MINDEX(m, k) \ +{ \ + register int len = m->m_len; \ + \ + while (k >= len) { \ + k -= len; \ + m = m->m_next; \ + if (m == 0) \ + return 0; \ + len = m->m_len; \ + } \ +} + +static int +m_xword(m, k, err) + register struct mbuf *m; + register int k, *err; +{ + register int len; + register u_char *cp, *np; + register struct mbuf *m0; + + len = m->m_len; + while (k >= len) { + k -= len; + m = m->m_next; + if (m == 0) + goto bad; + len = m->m_len; + } + cp = mtod(m, u_char *) + k; + if (len - k >= 4) { + *err = 0; + return EXTRACT_LONG(cp); + } + m0 = m->m_next; + if (m0 == 0 || m0->m_len + len - k < 4) + goto bad; + *err = 0; + np = mtod(m0, u_char *); + switch (len - k) { + + case 1: + return (cp[k] << 24) | (np[0] << 16) | (np[1] << 8) | np[2]; + + case 2: + return (cp[k] << 24) | (cp[k + 1] << 16) | (np[0] << 8) | + np[1]; + + default: + return (cp[k] << 24) | (cp[k + 1] << 16) | (cp[k + 2] << 8) | + np[0]; + } + bad: + *err = 1; + return 0; +} + +static int +m_xhalf(m, k, err) + register struct mbuf *m; + register int k, *err; +{ + register int len; + register u_char *cp; + register struct mbuf *m0; + + len = m->m_len; + while (k >= len) { + k -= len; + m = m->m_next; + if (m == 0) + goto bad; + len = m->m_len; + } + cp = mtod(m, u_char *) + k; + if (len - k >= 2) { + *err = 0; + return EXTRACT_SHORT(cp); + } + m0 = m->m_next; + if (m0 == 0) + goto bad; + *err = 0; + return (cp[k] << 8) | mtod(m0, u_char *)[0]; + bad: + *err = 1; + return 0; +} +#endif + +/* + * Execute the filter program starting at pc on the packet p + * wirelen is the length of the original packet + * buflen is the amount of data present + */ +u_int +bpf_filter(pc, p, wirelen, buflen) + register struct bpf_insn *pc; + register u_char *p; + u_int wirelen; + register u_int buflen; +{ + register u_long A, X; + register int k; + long mem[BPF_MEMWORDS]; + + if (pc == 0) + /* + * No filter means accept all. + */ + return (u_int)-1; +#ifdef lint + A = 0; + X = 0; +#endif + --pc; + while (1) { + ++pc; + switch (pc->code) { + + default: +#ifdef KERNEL + return 0; +#else + abort(); +#endif + case BPF_RET|BPF_K: + return (u_int)pc->k; + + case BPF_RET|BPF_A: + return (u_int)A; + + case BPF_LD|BPF_W|BPF_ABS: + k = pc->k; + if (k + sizeof(long) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xword((struct mbuf *)p, k, &merr); + if (merr != 0) + return 0; + continue; +#else + return 0; +#endif + } +#ifdef BPF_ALIGN + if (((int)(p + k) & 3) != 0) + A = EXTRACT_LONG(&p[k]); + else +#endif + A = ntohl(*(long *)(p + k)); + continue; + + case BPF_LD|BPF_H|BPF_ABS: + k = pc->k; + if (k + sizeof(short) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xhalf((struct mbuf *)p, k, &merr); + continue; +#else + return 0; +#endif + } + A = EXTRACT_SHORT(&p[k]); + continue; + + case BPF_LD|BPF_B|BPF_ABS: + k = pc->k; + if (k >= buflen) { +#ifdef KERNEL + register struct mbuf *m; + + if (buflen != 0) + return 0; + m = (struct mbuf *)p; + MINDEX(m, k); + A = mtod(m, u_char *)[k]; + continue; +#else + return 0; +#endif + } + A = p[k]; + continue; + + case BPF_LD|BPF_W|BPF_LEN: + A = wirelen; + continue; + + case BPF_LDX|BPF_W|BPF_LEN: + X = wirelen; + continue; + + case BPF_LD|BPF_W|BPF_IND: + k = X + pc->k; + if (k + sizeof(long) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xword((struct mbuf *)p, k, &merr); + if (merr != 0) + return 0; + continue; +#else + return 0; +#endif + } +#ifdef BPF_ALIGN + if (((int)(p + k) & 3) != 0) + A = EXTRACT_LONG(&p[k]); + else +#endif + A = ntohl(*(long *)(p + k)); + continue; + + case BPF_LD|BPF_H|BPF_IND: + k = X + pc->k; + if (k + sizeof(short) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xhalf((struct mbuf *)p, k, &merr); + if (merr != 0) + return 0; + continue; +#else + return 0; +#endif + } + A = EXTRACT_SHORT(&p[k]); + continue; + + case BPF_LD|BPF_B|BPF_IND: + k = X + pc->k; + if (k >= buflen) { +#ifdef KERNEL + register struct mbuf *m; + + if (buflen != 0) + return 0; + m = (struct mbuf *)p; + MINDEX(m, k); + A = mtod(m, char *)[k]; + continue; +#else + return 0; +#endif + } + A = p[k]; + continue; + + case BPF_LDX|BPF_MSH|BPF_B: + k = pc->k; + if (k >= buflen) { +#ifdef KERNEL + register struct mbuf *m; + + if (buflen != 0) + return 0; + m = (struct mbuf *)p; + MINDEX(m, k); + X = (mtod(m, char *)[k] & 0xf) << 2; + continue; +#else + return 0; +#endif + } + X = (p[pc->k] & 0xf) << 2; + continue; + + case BPF_LD|BPF_IMM: + A = pc->k; + continue; + + case BPF_LDX|BPF_IMM: + X = pc->k; + continue; + + case BPF_LD|BPF_MEM: + A = mem[pc->k]; + continue; + + case BPF_LDX|BPF_MEM: + X = mem[pc->k]; + continue; + + case BPF_ST: + mem[pc->k] = A; + continue; + + case BPF_STX: + mem[pc->k] = X; + continue; + + case BPF_JMP|BPF_JA: + pc += pc->k; + continue; + + case BPF_JMP|BPF_JGT|BPF_K: + pc += (A > pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_K: + pc += (A >= pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_K: + pc += (A == pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_K: + pc += (A & pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGT|BPF_X: + pc += (A > X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_X: + pc += (A >= X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_X: + pc += (A == X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_X: + pc += (A & X) ? pc->jt : pc->jf; + continue; + + case BPF_ALU|BPF_ADD|BPF_X: + A += X; + continue; + + case BPF_ALU|BPF_SUB|BPF_X: + A -= X; + continue; + + case BPF_ALU|BPF_MUL|BPF_X: + A *= X; + continue; + + case BPF_ALU|BPF_DIV|BPF_X: + if (X == 0) + return 0; + A /= X; + continue; + + case BPF_ALU|BPF_AND|BPF_X: + A &= X; + continue; + + case BPF_ALU|BPF_OR|BPF_X: + A |= X; + continue; + + case BPF_ALU|BPF_LSH|BPF_X: + A <<= X; + continue; + + case BPF_ALU|BPF_RSH|BPF_X: + A >>= X; + continue; + + case BPF_ALU|BPF_ADD|BPF_K: + A += pc->k; + continue; + + case BPF_ALU|BPF_SUB|BPF_K: + A -= pc->k; + continue; + + case BPF_ALU|BPF_MUL|BPF_K: + A *= pc->k; + continue; + + case BPF_ALU|BPF_DIV|BPF_K: + A /= pc->k; + continue; + + case BPF_ALU|BPF_AND|BPF_K: + A &= pc->k; + continue; + + case BPF_ALU|BPF_OR|BPF_K: + A |= pc->k; + continue; + + case BPF_ALU|BPF_LSH|BPF_K: + A <<= pc->k; + continue; + + case BPF_ALU|BPF_RSH|BPF_K: + A >>= pc->k; + continue; + + case BPF_ALU|BPF_NEG: + A = -A; + continue; + + case BPF_MISC|BPF_TAX: + X = A; + continue; + + case BPF_MISC|BPF_TXA: + A = X; + continue; + } + } +} + +#ifdef KERNEL +/* + * Return true if the 'fcode' is a valid filter program. + * The constraints are that each jump be forward and to a valid + * code. The code must terminate with either an accept or reject. + * 'valid' is an array for use by the routine (it must be at least + * 'len' bytes long). + * + * The kernel needs to be able to verify an application's filter code. + * Otherwise, a bogus program could easily crash the system. + */ +int +bpf_validate(f, len) + struct bpf_insn *f; + int len; +{ + register int i; + register struct bpf_insn *p; + + for (i = 0; i < len; ++i) { + /* + * Check that that jumps are forward, and within + * the code block. + */ + p = &f[i]; + if (BPF_CLASS(p->code) == BPF_JMP) { + register int from = i + 1; + + if (BPF_OP(p->code) == BPF_JA) { + if (from + p->k >= len) + return 0; + } + else if (from + p->jt >= len || from + p->jf >= len) + return 0; + } + /* + * Check that memory operations use valid addresses. + */ + if ((BPF_CLASS(p->code) == BPF_ST || + (BPF_CLASS(p->code) == BPF_LD && + (p->code & 0xe0) == BPF_MEM)) && + (p->k >= BPF_MEMWORDS || p->k < 0)) + return 0; + /* + * Check for constant division by 0. + */ + if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0) + return 0; + } + return BPF_CLASS(f[len - 1].code) == BPF_RET; +} +#endif diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h new file mode 100644 index 000000000000..9313b3208494 --- /dev/null +++ b/sys/net/bpfdesc.h @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. + * + * from: @(#)bpfdesc.h 7.1 (Berkeley) 5/7/91 + * $Id: bpfdesc.h,v 1.2 1993/10/16 17:43:09 rgrimes Exp $ + */ + +/* + * Descriptor associated with each open bpf file. + */ +struct bpf_d { + struct bpf_d *bd_next; /* Linked list of descriptors */ + /* + * Buffer slots: two mbuf clusters buffer the incoming packets. + * The model has three slots. Sbuf is always occupied. + * sbuf (store) - Receive interrupt puts packets here. + * hbuf (hold) - When sbuf is full, put cluster here and + * wakeup read (replace sbuf with fbuf). + * fbuf (free) - When read is done, put cluster here. + * On receiving, if sbuf is full and fbuf is 0, packet is dropped. + */ + caddr_t bd_sbuf; /* store slot */ + caddr_t bd_hbuf; /* hold slot */ + caddr_t bd_fbuf; /* free slot */ + int bd_slen; /* current length of store buffer */ + int bd_hlen; /* current length of hold buffer */ + + int bd_bufsize; /* absolute length of buffers */ + + struct bpf_if * bd_bif; /* interface descriptor */ + u_long bd_rtout; /* Read timeout in 'ticks' */ + struct bpf_insn *bd_filter; /* filter code */ + u_long bd_rcount; /* number of packets received */ + u_long bd_dcount; /* number of packets dropped */ + + u_char bd_promisc; /* true if listening promiscuously */ + u_char bd_state; /* idle, waiting, or timed out */ + u_char bd_immediate; /* true to return on packet arrival */ + u_char bd_selcoll; /* true if selects collide */ + int bd_timedout; + struct proc * bd_selproc; /* process that last selected us */ +}; + +/* + * Descriptor associated with each attached hardware interface. + */ +struct bpf_if { + struct bpf_if *bif_next; /* list of all interfaces */ + struct bpf_d *bif_dlist; /* descriptor list */ + struct bpf_if **bif_driverp; /* pointer into softc */ + u_int bif_dlt; /* link layer type */ + u_int bif_hdrlen; /* length of header (with padding) */ + struct ifnet *bif_ifp; /* correspoding interface */ +}; diff --git a/sys/net/if.c b/sys/net/if.c new file mode 100644 index 000000000000..fb2ed43d37a0 --- /dev/null +++ b/sys/net/if.c @@ -0,0 +1,646 @@ +/* + * Copyright (c) 1980, 1986 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. + * + * from: @(#)if.c 7.14 (Berkeley) 4/20/91 + * $Id: if.c,v 1.4 1993/10/16 17:43:10 rgrimes Exp $ + */ + +#include "param.h" +#include "mbuf.h" +#include "systm.h" +#include "socket.h" +#include "socketvar.h" +#include "protosw.h" +#include "proc.h" +#include "kernel.h" +#include "ioctl.h" + +#include "if.h" +#include "af.h" +#include "if_dl.h" +#include "if_types.h" + +#include "ether.h" + +int ifqmaxlen = IFQ_MAXLEN; + +/* + * Network interface utility routines. + * + * Routines with ifa_ifwith* names take sockaddr *'s as + * parameters. + */ + +ifinit() +{ + register struct ifnet *ifp; + + for (ifp = ifnet; ifp; ifp = ifp->if_next) + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + if_slowtimo(); +} + +#ifdef vax +/* + * Call each interface on a Unibus reset. + */ +ifubareset(uban) + int uban; +{ + register struct ifnet *ifp; + + for (ifp = ifnet; ifp; ifp = ifp->if_next) + if (ifp->if_reset) + (*ifp->if_reset)(ifp->if_unit, uban); +} +#endif + +int if_index = 0; +struct ifaddr **ifnet_addrs; +static char *sprint_d(); + +/* + * Attach an interface to the + * list of "active" interfaces. + */ +if_attach(ifp) + struct ifnet *ifp; +{ + unsigned socksize, ifasize; + int namelen, unitlen; + char workbuf[12], *unitname; + register struct ifnet **p = &ifnet; + register struct sockaddr_dl *sdl; + register struct ifaddr *ifa; + static int if_indexlim = 8; + extern link_rtrequest(), ether_output(); + + while (*p) + p = &((*p)->if_next); + *p = ifp; + ifp->if_index = ++if_index; + if (ifnet_addrs == 0 || if_index >= if_indexlim) { + unsigned n = (if_indexlim <<= 1) * sizeof(ifa); + struct ifaddr **q = (struct ifaddr **) + malloc(n, M_IFADDR, M_WAITOK); + if (ifnet_addrs) { + bcopy((caddr_t)ifnet_addrs, (caddr_t)q, n/2); + free((caddr_t)ifnet_addrs, M_IFADDR); + } + ifnet_addrs = q; + } +#if defined(INET) && NETHER > 0 + /* XXX -- Temporary fix before changing 10 ethernet drivers */ + if (ifp->if_output == ether_output) { + ifp->if_type = IFT_ETHER; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 14; + } +#endif + /* + * create a Link Level name for this device + */ + unitname = sprint_d((u_int)ifp->if_unit, workbuf, sizeof(workbuf)); + namelen = strlen(ifp->if_name); + unitlen = strlen(unitname); +#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m)) + socksize = _offsetof(struct sockaddr_dl, sdl_data[0]) + + unitlen + namelen + ifp->if_addrlen; +#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1))) + socksize = ROUNDUP(socksize); + if (socksize < sizeof(*sdl)) + socksize = sizeof(*sdl); + ifasize = sizeof(*ifa) + 2 * socksize; + ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK); + if (ifa == 0) + return; + ifnet_addrs[if_index - 1] = ifa; + bzero((caddr_t)ifa, ifasize); + sdl = (struct sockaddr_dl *)(ifa + 1); + ifa->ifa_addr = (struct sockaddr *)sdl; + ifa->ifa_ifp = ifp; + sdl->sdl_len = socksize; + sdl->sdl_family = AF_LINK; + bcopy(ifp->if_name, sdl->sdl_data, namelen); + bcopy(unitname, namelen + (caddr_t)sdl->sdl_data, unitlen); + sdl->sdl_nlen = (namelen += unitlen); + sdl->sdl_index = ifp->if_index; + sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl); + ifa->ifa_netmask = (struct sockaddr *)sdl; + sdl->sdl_len = socksize - ifp->if_addrlen; + while (namelen != 0) + sdl->sdl_data[--namelen] = 0xff; + ifa->ifa_next = ifp->if_addrlist; + ifa->ifa_rtrequest = link_rtrequest; + ifp->if_addrlist = ifa; +} +/* + * Locate an interface based on a complete address. + */ +/*ARGSUSED*/ +struct ifaddr * +ifa_ifwithaddr(addr) + register struct sockaddr *addr; +{ + register struct ifnet *ifp; + register struct ifaddr *ifa; + +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) + for (ifp = ifnet; ifp; ifp = ifp->if_next) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != addr->sa_family) + continue; + if (equal(addr, ifa->ifa_addr)) + return (ifa); + if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && + equal(ifa->ifa_broadaddr, addr)) + return (ifa); + } + return ((struct ifaddr *)0); +} +/* + * Locate the point to point interface with a given destination address. + */ +/*ARGSUSED*/ +struct ifaddr * +ifa_ifwithdstaddr(addr) + register struct sockaddr *addr; +{ + register struct ifnet *ifp; + register struct ifaddr *ifa; + + for (ifp = ifnet; ifp; ifp = ifp->if_next) + if (ifp->if_flags & IFF_POINTOPOINT) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != addr->sa_family) + continue; + if (equal(addr, ifa->ifa_dstaddr)) + return (ifa); + } + return ((struct ifaddr *)0); +} + +/* + * Find an interface on a specific network. If many, choice + * is first found. + */ +struct ifaddr * +ifa_ifwithnet(addr) + struct sockaddr *addr; +{ + register struct ifnet *ifp; + register struct ifaddr *ifa; + u_int af = addr->sa_family; + + if (af >= AF_MAX) + return (0); + if (af == AF_LINK) { + register struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr; + if (sdl->sdl_index && sdl->sdl_index <= if_index) + return (ifnet_addrs[sdl->sdl_index - 1]); + } + for (ifp = ifnet; ifp; ifp = ifp->if_next) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + register char *cp, *cp2, *cp3; + register char *cplim; + if (ifa->ifa_addr->sa_family != af || ifa->ifa_netmask == 0) + continue; + cp = addr->sa_data; + cp2 = ifa->ifa_addr->sa_data; + cp3 = ifa->ifa_netmask->sa_data; + cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; + for (; cp3 < cplim; cp3++) + if ((*cp++ ^ *cp2++) & *cp3) + break; + if (cp3 == cplim) + return (ifa); + } + return ((struct ifaddr *)0); +} + +/* + * Find an interface using a specific address family + */ +struct ifaddr * +ifa_ifwithaf(af) + register int af; +{ + register struct ifnet *ifp; + register struct ifaddr *ifa; + + for (ifp = ifnet; ifp; ifp = ifp->if_next) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == af) + return (ifa); + return ((struct ifaddr *)0); +} + +/* + * Find an interface address specific to an interface best matching + * a given address. + */ +struct ifaddr * +ifaof_ifpforaddr(addr, ifp) + struct sockaddr *addr; + register struct ifnet *ifp; +{ + register struct ifaddr *ifa; + register char *cp, *cp2, *cp3; + register char *cplim; + struct ifaddr *ifa_maybe = 0; + u_int af = addr->sa_family; + + if (af >= AF_MAX) + return (0); + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr->sa_family != af) + continue; + ifa_maybe = ifa; + if (ifa->ifa_netmask == 0) { + if (equal(addr, ifa->ifa_addr) || + (ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr))) + return (ifa); + continue; + } + cp = addr->sa_data; + cp2 = ifa->ifa_addr->sa_data; + cp3 = ifa->ifa_netmask->sa_data; + cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask; + for (; cp3 < cplim; cp3++) + if ((*cp++ ^ *cp2++) & *cp3) + break; + if (cp3 == cplim) + return (ifa); + } + return (ifa_maybe); +} +#include "route.h" +/* + * Default action when installing a route with a Link Level gateway. + * Lookup an appropriate real ifa to point to. + * This should be moved to /sys/net/link.c eventually. + */ +link_rtrequest(cmd, rt, sa) +register struct rtentry *rt; +struct sockaddr *sa; +{ + register struct ifaddr *ifa; + struct sockaddr *dst; + struct ifnet *ifp, *oldifnet = ifnet; + + if (cmd != RTM_ADD || ((ifa = rt->rt_ifa) == 0) || + ((ifp = ifa->ifa_ifp) == 0) || ((dst = rt_key(rt)) == 0)) + return; + if (ifa = ifaof_ifpforaddr(dst, ifp)) { + rt->rt_ifa = ifa; + if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) + ifa->ifa_rtrequest(cmd, rt, sa); + } +} + +/* + * Mark an interface down and notify protocols of + * the transition. + * NOTE: must be called at splnet or eqivalent. + */ +if_down(ifp) + register struct ifnet *ifp; +{ + register struct ifaddr *ifa; + + ifp->if_flags &= ~IFF_UP; + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + pfctlinput(PRC_IFDOWN, ifa->ifa_addr); + if_qflush(&ifp->if_snd); +} + +/* + * Flush an interface queue. + */ +if_qflush(ifq) + register struct ifqueue *ifq; +{ + register struct mbuf *m, *n; + + n = ifq->ifq_head; + while (m = n) { + n = m->m_act; + m_freem(m); + } + ifq->ifq_head = 0; + ifq->ifq_tail = 0; + ifq->ifq_len = 0; +} + +/* + * Handle interface watchdog timer routines. Called + * from softclock, we decrement timers (if set) and + * call the appropriate interface routine on expiration. + */ +if_slowtimo() +{ + register struct ifnet *ifp; + int s = splimp(); + + for (ifp = ifnet; ifp; ifp = ifp->if_next) { + if (ifp->if_timer == 0 || --ifp->if_timer) + continue; + if (ifp->if_watchdog) + (*ifp->if_watchdog)(ifp->if_unit); + } + splx(s); + timeout(if_slowtimo, (caddr_t)0, hz / IFNET_SLOWHZ); +} + +/* + * Map interface name to + * interface structure pointer. + */ +struct ifnet * +ifunit(name) + register char *name; +{ + register char *cp; + register struct ifnet *ifp; + int unit; + unsigned len; + char *ep, c; + + for (cp = name; cp < name + IFNAMSIZ && *cp; cp++) + if (*cp >= '0' && *cp <= '9') + break; + if (*cp == '\0' || cp == name + IFNAMSIZ) + return ((struct ifnet *)0); + /* + * Save first char of unit, and pointer to it, + * so we can put a null there to avoid matching + * initial substrings of interface names. + */ + len = cp - name + 1; + c = *cp; + ep = cp; + for (unit = 0; *cp >= '0' && *cp <= '9'; ) + unit = unit * 10 + *cp++ - '0'; + *ep = 0; + for (ifp = ifnet; ifp; ifp = ifp->if_next) { + if (bcmp(ifp->if_name, name, len)) + continue; + if (unit == ifp->if_unit) + break; + } + *ep = c; + return (ifp); +} + +/* + * Interface ioctls. + */ +ifioctl(so, cmd, data, p) + struct socket *so; + int cmd; + caddr_t data; + struct proc *p; +{ + register struct ifnet *ifp; + register struct ifreq *ifr; + int error; + + switch (cmd) { + + case SIOCGIFCONF: + case OSIOCGIFCONF: + return (ifconf(cmd, data)); + +#if defined(INET) && NETHER > 0 + case SIOCSARP: + case SIOCDARP: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + /* FALL THROUGH */ + case SIOCGARP: + case OSIOCGARP: + return (arpioctl(cmd, data)); +#endif + } + ifr = (struct ifreq *)data; + ifp = ifunit(ifr->ifr_name); + if (ifp == 0) + return (ENXIO); + switch (cmd) { + + case SIOCGIFFLAGS: + ifr->ifr_flags = ifp->if_flags; + break; + + case SIOCGIFMETRIC: + ifr->ifr_metric = ifp->if_metric; + break; + + case SIOCSIFFLAGS: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + if (ifp->if_flags & IFF_UP && (ifr->ifr_flags & IFF_UP) == 0) { + int s = splimp(); + if_down(ifp); + splx(s); + } + ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) | + (ifr->ifr_flags &~ IFF_CANTCHANGE); + if (ifp->if_ioctl) + (void) (*ifp->if_ioctl)(ifp, cmd, data); + break; + + case SIOCSIFMETRIC: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + ifp->if_metric = ifr->ifr_metric; + break; + + case SIOCSIFMTU: + case SIOCGIFMTU: + case SIOCSIFASYNCMAP: + case SIOCGIFASYNCMAP: + if (!ifp->if_ioctl) + return (EOPNOTSUPP); + return ((*ifp->if_ioctl)(ifp, cmd, data)); + break; + + default: + if (so->so_proto == 0) + return (EOPNOTSUPP); +#ifndef COMPAT_43 + return ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL, + cmd, data, ifp)); +#else + { + int ocmd = cmd; + + switch (cmd) { + + case SIOCSIFDSTADDR: + case SIOCSIFADDR: + case SIOCSIFBRDADDR: + case SIOCSIFNETMASK: +#if BYTE_ORDER != BIG_ENDIAN + if (ifr->ifr_addr.sa_family == 0 && + ifr->ifr_addr.sa_len < 16) { + ifr->ifr_addr.sa_family = ifr->ifr_addr.sa_len; + ifr->ifr_addr.sa_len = 16; + } +#else + if (ifr->ifr_addr.sa_len == 0) + ifr->ifr_addr.sa_len = 16; +#endif + break; + + case OSIOCGIFADDR: + cmd = SIOCGIFADDR; + break; + + case OSIOCGIFDSTADDR: + cmd = SIOCGIFDSTADDR; + break; + + case OSIOCGIFBRDADDR: + cmd = SIOCGIFBRDADDR; + break; + + case OSIOCGIFNETMASK: + cmd = SIOCGIFNETMASK; + } + error = ((*so->so_proto->pr_usrreq)(so, PRU_CONTROL, + cmd, data, ifp)); + switch (ocmd) { + + case OSIOCGIFADDR: + case OSIOCGIFDSTADDR: + case OSIOCGIFBRDADDR: + case OSIOCGIFNETMASK: + *(u_short *)&ifr->ifr_addr = ifr->ifr_addr.sa_family; + } + return (error); + + } +#endif + } + return (0); +} + +/* + * Return interface configuration + * of system. List may be used + * in later ioctl's (above) to get + * other information. + */ +/*ARGSUSED*/ +ifconf(cmd, data) + int cmd; + caddr_t data; +{ + register struct ifconf *ifc = (struct ifconf *)data; + register struct ifnet *ifp = ifnet; + register struct ifaddr *ifa; + register char *cp, *ep; + struct ifreq ifr, *ifrp; + int space = ifc->ifc_len, error = 0; + + ifrp = ifc->ifc_req; + ep = ifr.ifr_name + sizeof (ifr.ifr_name) - 2; + for (; space > sizeof (ifr) && ifp; ifp = ifp->if_next) { + bcopy(ifp->if_name, ifr.ifr_name, sizeof (ifr.ifr_name) - 2); + for (cp = ifr.ifr_name; cp < ep && *cp; cp++) + ; + *cp++ = '0' + ifp->if_unit; *cp = '\0'; + if ((ifa = ifp->if_addrlist) == 0) { + bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); + error = copyout((caddr_t)&ifr, (caddr_t)ifrp, sizeof (ifr)); + if (error) + break; + space -= sizeof (ifr), ifrp++; + } else + for ( ; space > sizeof (ifr) && ifa; ifa = ifa->ifa_next) { + register struct sockaddr *sa = ifa->ifa_addr; +#ifdef COMPAT_43 + if (cmd == OSIOCGIFCONF) { + struct osockaddr *osa = + (struct osockaddr *)&ifr.ifr_addr; + ifr.ifr_addr = *sa; + osa->sa_family = sa->sa_family; + error = copyout((caddr_t)&ifr, (caddr_t)ifrp, + sizeof (ifr)); + ifrp++; + } else +#endif + if (sa->sa_len <= sizeof(*sa)) { + ifr.ifr_addr = *sa; + error = copyout((caddr_t)&ifr, (caddr_t)ifrp, + sizeof (ifr)); + ifrp++; + } else { + space -= sa->sa_len - sizeof(*sa); + if (space < sizeof (ifr)) + break; + error = copyout((caddr_t)&ifr, (caddr_t)ifrp, + sizeof (ifr.ifr_name)); + if (error == 0) + error = copyout((caddr_t)sa, + (caddr_t)&ifrp->ifr_addr, sa->sa_len); + ifrp = (struct ifreq *) + (sa->sa_len + (caddr_t)&ifrp->ifr_addr); + } + if (error) + break; + space -= sizeof (ifr); + } + } + ifc->ifc_len -= space; + return (error); +} + +static char * +sprint_d(n, buf, buflen) + u_int n; + char *buf; + int buflen; +{ + register char *cp = buf + buflen - 1; + + *cp = 0; + do { + cp--; + *cp = "0123456789"[n % 10]; + n /= 10; + } while (n != 0); + return (cp); +} diff --git a/sys/net/if.h b/sys/net/if.h new file mode 100644 index 000000000000..310f9c069674 --- /dev/null +++ b/sys/net/if.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 1982, 1986, 1989 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. + * + * from: @(#)if.h 7.11 (Berkeley) 3/19/91 + * $Id: if.h,v 1.5 1993/10/16 17:43:12 rgrimes Exp $ + */ + +/* + * Structures defining a network interface, providing a packet + * transport mechanism (ala level 0 of the PUP protocols). + * + * Each interface accepts output datagrams of a specified maximum + * length, and provides higher level routines with input datagrams + * received from its medium. + * + * Output occurs when the routine if_output is called, with three parameters: + * (*ifp->if_output)(ifp, m, dst) + * Here m is the mbuf chain to be sent and dst is the destination address. + * The output routine encapsulates the supplied datagram if necessary, + * and then transmits it on its medium. + * + * On input, each interface unwraps the data received by it, and either + * places it on the input queue of a internetwork datagram routine + * and posts the associated software interrupt, or passes the datagram to a raw + * packet input routine. + * + * Routines exist for locating interfaces by their addresses + * or for locating a interface on a certain network, as well as more general + * routing and gateway routines maintaining information used to locate + * interfaces. These routines live in the files if.c and route.c + */ +#ifndef _TIME_ /* XXX fast fix for SNMP, going away soon */ +#ifdef KERNEL +#include "../sys/time.h" +#else +#include <sys/time.h> +#endif +#endif + +/* + * Structure defining a queue for a network interface. + * + * (Would like to call this struct ``if'', but C isn't PL/1.) + */ + +struct ifnet { + char *if_name; /* name, e.g. ``en'' or ``lo'' */ + short if_unit; /* sub-unit for lower level driver */ + short if_mtu; /* maximum transmission unit */ + short if_flags; /* up/down, broadcast, etc. */ + short if_timer; /* time 'til if_watchdog called */ + int if_metric; /* routing metric (external only) */ + struct ifaddr *if_addrlist; /* linked list of addresses per if */ + struct ifqueue { + struct mbuf *ifq_head; + struct mbuf *ifq_tail; + int ifq_len; + int ifq_maxlen; + int ifq_drops; + } if_snd; /* output queue */ +/* procedure handles */ + int (*if_init)(); /* init routine */ + int (*if_output)(); /* output routine (enqueue) */ + int (*if_start)(); /* initiate output routine */ + int (*if_done)(); /* output complete routine */ + int (*if_ioctl)(); /* ioctl routine */ + int (*if_reset)(); /* bus reset routine */ + int (*if_watchdog)(); /* timer routine */ +/* generic interface statistics */ + int if_ipackets; /* packets received on interface */ + int if_ierrors; /* input errors on interface */ + int if_opackets; /* packets sent on interface */ + int if_oerrors; /* output errors on interface */ + int if_collisions; /* collisions on csma interfaces */ +/* end statistics */ + struct ifnet *if_next; + u_char if_type; /* ethernet, tokenring, etc */ + u_char if_addrlen; /* media address length */ + u_char if_hdrlen; /* media header length */ + u_char if_index; /* numeric abbreviation for this if */ +/* more statistics here to avoid recompiling netstat */ + struct timeval if_lastchange; /* last updated */ + int if_ibytes; /* total number of octets received */ + int if_obytes; /* total number of octets sent */ + int if_imcasts; /* packets received via multicast */ + int if_omcasts; /* packets sent via multicast */ + int if_iqdrops; /* dropped on input, this interface */ + int if_noproto; /* destined for unsupported protocol */ + int if_baudrate; /* linespeed */ + int if_pcount; /* number of promiscuous listeners */ +}; + +#define IFF_UP 0x1 /* interface is up */ +#define IFF_BROADCAST 0x2 /* broadcast address valid */ +#define IFF_DEBUG 0x4 /* turn on debugging */ +#define IFF_LOOPBACK 0x8 /* is a loopback net */ +#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */ +#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */ +#define IFF_RUNNING 0x40 /* resources allocated */ +#define IFF_NOARP 0x80 /* no address resolution protocol */ +/* next two not supported now, but reserved: */ +#define IFF_PROMISC 0x100 /* receive all packets */ +#define IFF_ALLMULTI 0x200 /* receive all multicast packets */ +#define IFF_OACTIVE 0x400 /* transmission in progress */ +#define IFF_SIMPLEX 0x800 /* can't hear own transmissions */ +#define IFF_LLC0 0x1000 /* interface driver control/status */ +#define IFF_LLC1 0x2000 /* interface driver control/status */ +#define IFF_LLC2 0x4000 /* interface driver control/status */ + +/* flags set internally only: */ +#define IFF_CANTCHANGE \ + (IFF_BROADCAST|IFF_POINTOPOINT|IFF_RUNNING|IFF_OACTIVE|IFF_SIMPLEX) + +/* + * Output queues (ifp->if_snd) and internetwork datagram level (pup level 1) + * input routines have queues of messages stored on ifqueue structures + * (defined above). Entries are added to and deleted from these structures + * by these macros, which should be called with ipl raised to splimp(). + */ +#define IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) +#define IF_DROP(ifq) ((ifq)->ifq_drops++) +#define IF_ENQUEUE(ifq, m) { \ + (m)->m_nextpkt = 0; \ + if ((ifq)->ifq_tail == 0) \ + (ifq)->ifq_head = m; \ + else \ + (ifq)->ifq_tail->m_nextpkt = m; \ + (ifq)->ifq_tail = m; \ + (ifq)->ifq_len++; \ +} +#define IF_PREPEND(ifq, m) { \ + (m)->m_nextpkt = (ifq)->ifq_head; \ + if ((ifq)->ifq_tail == 0) \ + (ifq)->ifq_tail = (m); \ + (ifq)->ifq_head = (m); \ + (ifq)->ifq_len++; \ +} +#define IF_DEQUEUE(ifq, m) { \ + (m) = (ifq)->ifq_head; \ + if (m) { \ + if (((ifq)->ifq_head = (m)->m_nextpkt) == 0) \ + (ifq)->ifq_tail = 0; \ + (m)->m_nextpkt = 0; \ + (ifq)->ifq_len--; \ + } \ +} + +#define IFQ_MAXLEN 50 +#define IFNET_SLOWHZ 1 /* granularity is 1 second */ + +/* + * The ifaddr structure contains information about one address + * of an interface. They are maintained by the different address families, + * are allocated and attached when an address is set, and are linked + * together so all addresses for an interface can be located. + */ +struct ifaddr { + struct sockaddr *ifa_addr; /* address of interface */ + struct sockaddr *ifa_dstaddr; /* other end of p-to-p link */ +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ + struct sockaddr *ifa_netmask; /* used to determine subnet */ + struct ifnet *ifa_ifp; /* back-pointer to interface */ + struct ifaddr *ifa_next; /* next address for interface */ + int (*ifa_rtrequest)(); /* check or clean routes (+ or -)'d */ + struct rtentry *ifa_rt; /* ??? for ROUTETOIF */ + u_short ifa_flags; /* mostly rt_flags for cloning */ + u_short ifa_llinfolen; /* extra to malloc for link info */ +}; +#define IFA_ROUTE RTF_UP /* route installed */ +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ +struct ifreq { +#define IFNAMSIZ 16 + char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + short ifru_flags; + int ifru_metric; + caddr_t ifru_data; + } ifr_ifru; +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_metric /* metric */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +}; + +struct ifaliasreq { + char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + struct sockaddr ifra_addr; + struct sockaddr ifra_broadaddr; + struct sockaddr ifra_mask; +}; + +/* + * Structure used in SIOCGIFCONF request. + * Used to retrieve interface configuration + * for machine (useful for programs which + * must know all networks accessible). + */ +struct ifconf { + int ifc_len; /* size of associated buffer */ + union { + caddr_t ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ +}; + +#ifdef KERNEL +#include "../net/if_arp.h" +struct ifqueue rawintrq; /* raw packet input queue */ +struct ifnet *ifnet; +struct ifaddr *ifa_ifwithaddr(), *ifa_ifwithnet(); +struct ifaddr *ifa_ifwithdstaddr(); +#else KERNEL +#include <net/if_arp.h> +#endif KERNEL diff --git a/sys/net/if_arp.h b/sys/net/if_arp.h new file mode 100644 index 000000000000..37c3f8711174 --- /dev/null +++ b/sys/net/if_arp.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1986 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. + * + * from: @(#)if_arp.h 7.4 (Berkeley) 6/28/90 + * $Id: if_arp.h,v 1.2 1993/10/16 17:43:13 rgrimes Exp $ + */ + +/* + * Address Resolution Protocol. + * + * See RFC 826 for protocol description. ARP packets are variable + * in size; the arphdr structure defines the fixed-length portion. + * Protocol type values are the same as those for 10 Mb/s Ethernet. + * It is followed by the variable-sized fields ar_sha, arp_spa, + * arp_tha and arp_tpa in that order, according to the lengths + * specified. Field names used correspond to RFC 826. + */ +struct arphdr { + u_short ar_hrd; /* format of hardware address */ +#define ARPHRD_ETHER 1 /* ethernet hardware address */ + u_short ar_pro; /* format of protocol address */ + u_char ar_hln; /* length of hardware address */ + u_char ar_pln; /* length of protocol address */ + u_short ar_op; /* one of: */ +#define ARPOP_REQUEST 1 /* request to resolve address */ +#define ARPOP_REPLY 2 /* response to previous request */ +/* + * The remaining fields are variable in size, + * according to the sizes above. + */ +/* u_char ar_sha[]; /* sender hardware address */ +/* u_char ar_spa[]; /* sender protocol address */ +/* u_char ar_tha[]; /* target hardware address */ +/* u_char ar_tpa[]; /* target protocol address */ +}; + +/* + * ARP ioctl request + */ +struct arpreq { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ +}; +/* arp_flags and at_flags field values */ +#define ATF_INUSE 0x01 /* entry in use */ +#define ATF_COM 0x02 /* completed entry (enaddr valid) */ +#define ATF_PERM 0x04 /* permanent entry */ +#define ATF_PUBL 0x08 /* publish entry (respond for other host) */ +#define ATF_USETRAILERS 0x10 /* has requested trailers */ diff --git a/sys/net/if_dl.h b/sys/net/if_dl.h new file mode 100644 index 000000000000..4793bcb2a828 --- /dev/null +++ b/sys/net/if_dl.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1990 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. + * + * from: @(#)if_dl.h 7.2 (Berkeley) 2/22/91 + * $Id: if_dl.h,v 1.2 1993/10/16 17:43:15 rgrimes Exp $ + */ + +/* + * A Link-Level Sockaddr may specify the interface in one of two + * ways: either by means of a system-provided index number (computed + * anew and possibly differently on every reboot), or by a human-readable + * string such as "il0" (for managerial convenience). + * + * Census taking actions, such as something akin to SIOCGCONF would return + * both the index and the human name. + * + * High volume transactions (such as giving a link-level ``from'' address + * in a recvfrom or recvmsg call) may be likely only to provide the indexed + * form, (which requires fewer copy operations and less space). + * + * The form and interpretation of the link-level address is purely a matter + * of convention between the device driver and its consumers; however, it is + * expected that all drivers for an interface of a given if_type will agree. + */ + +/* + * Structure of a Link-Level sockaddr: + */ +struct sockaddr_dl { + u_char sdl_len; /* Total length of sockaddr */ + u_char sdl_family; /* AF_DLI */ + u_short sdl_index; /* if != 0, system given index for interface */ + u_char sdl_type; /* interface type */ + u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */ + u_char sdl_alen; /* link level address length */ + u_char sdl_slen; /* link layer selector length */ + char sdl_data[12]; /* minimum work area, can be larger; + contains both if name and ll address */ +}; + +#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen)) + +#ifndef KERNEL + +#include <sys/cdefs.h> + +__BEGIN_DECLS +void link_addr __P((const char *, struct sockaddr_dl *)); +char *link_ntoa __P((const struct sockaddr_dl *)); +__END_DECLS + +#endif /* !KERNEL */ diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c new file mode 100644 index 000000000000..a3f76dbfa7ce --- /dev/null +++ b/sys/net/if_ethersubr.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 1982, 1989 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. + * + * from: @(#)if_ethersubr.c 7.13 (Berkeley) 4/20/91 + * $Id: if_ethersubr.c,v 1.3 1993/10/16 17:43:16 rgrimes Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "kernel.h" +#include "malloc.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "ioctl.h" +#include "errno.h" +#include "syslog.h" + +#include "if.h" +#include "netisr.h" +#include "route.h" +#include "if_llc.h" +#include "if_dl.h" + +#include "machine/mtpr.h" + +#ifdef INET +#include "../netinet/in.h" +#include "../netinet/in_var.h" +#endif +#include "../netinet/if_ether.h" + +#ifdef NS +#include "../netns/ns.h" +#include "../netns/ns_if.h" +#endif + +#ifdef ISO +#include "../netiso/argo_debug.h" +#include "../netiso/iso.h" +#include "../netiso/iso_var.h" +#include "../netiso/iso_snpac.h" +#endif + +u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +extern struct ifnet loif; + +/* + * Ethernet output routine. + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + * Assumes that ifp is actually pointer to arpcom structure. + */ +ether_output(ifp, m0, dst, rt) + register struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; + struct rtentry *rt; +{ + short type; + int s, error = 0; + u_char edst[6]; + struct in_addr idst; + register struct mbuf *m = m0; + struct mbuf *mcopy = (struct mbuf *)0; + register struct ether_header *eh; + int usetrailers, off, len = m->m_pkthdr.len; +#define ac ((struct arpcom *)ifp) + + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { + error = ENETDOWN; + goto bad; + } + ifp->if_lastchange = time; + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + idst = ((struct sockaddr_in *)dst)->sin_addr; + if (!arpresolve(ac, m, &idst, edst, &usetrailers)) + return (0); /* if not yet resolved */ + if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1)) + mcopy = m_copy(m, 0, (int)M_COPYALL); + off = m->m_pkthdr.len - m->m_len; + if (usetrailers && off > 0 && (off & 0x1ff) == 0 && + (m->m_flags & M_EXT) == 0 && + m->m_data >= m->m_pktdat + 2 * sizeof (u_short)) { + type = ETHERTYPE_TRAIL + (off>>9); + m->m_data -= 2 * sizeof (u_short); + m->m_len += 2 * sizeof (u_short); + len += 2 * sizeof (u_short); + *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); + *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); + goto gottrailertype; + } + type = ETHERTYPE_IP; + goto gottype; +#endif +#ifdef NS + case AF_NS: + type = ETHERTYPE_NS; + bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), + (caddr_t)edst, sizeof (edst)); + if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst))) + return (looutput(ifp, m, dst, rt)); + if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1)) + mcopy = m_copy(m, 0, (int)M_COPYALL); + goto gottype; +#endif +#ifdef ISO + case AF_ISO: { + int snpalen; + struct llc *l; + + iso_again: + if (rt && rt->rt_gateway && (rt->rt_flags & RTF_UP)) { + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_llinfo) { + rt = (struct rtentry *)rt->rt_llinfo; + goto iso_again; + } + } else { + register struct sockaddr_dl *sdl = + (struct sockaddr_dl *)rt->rt_gateway; + if (sdl && sdl->sdl_family == AF_LINK + && sdl->sdl_alen > 0) { + bcopy(LLADDR(sdl), (char *)edst, + sizeof(edst)); + goto iso_resolved; + } + } + } + if ((error = iso_snparesolve(ifp, (struct sockaddr_iso *)dst, + (char *)edst, &snpalen)) > 0) + goto bad; /* Not Resolved */ + iso_resolved: + if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) && + (mcopy = m_copy(m, 0, (int)M_COPYALL))) { + M_PREPEND(mcopy, sizeof (*eh), M_DONTWAIT); + if (mcopy) { + eh = mtod(mcopy, struct ether_header *); + bcopy((caddr_t)edst, + (caddr_t)eh->ether_dhost, sizeof (edst)); + bcopy((caddr_t)ac->ac_enaddr, + (caddr_t)eh->ether_shost, sizeof (edst)); + } + } + M_PREPEND(m, 3, M_DONTWAIT); + if (m == NULL) + return (0); + type = m->m_pkthdr.len; + l = mtod(m, struct llc *); + l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP; + l->llc_control = LLC_UI; + len += 3; + IFDEBUG(D_ETHER) + int i; + printf("unoutput: sending pkt to: "); + for (i=0; i<6; i++) + printf("%x ", edst[i] & 0xff); + printf("\n"); + ENDDEBUG + } goto gottype; +#endif ISO +#ifdef RMP + case AF_RMP: + /* + * This is IEEE 802.3 -- the Ethernet `type' field is + * really a `length' field. + */ + type = m->m_len; + bcopy((caddr_t)dst->sa_data, (caddr_t)edst, sizeof(edst)); + break; +#endif + + case AF_UNSPEC: + eh = (struct ether_header *)dst->sa_data; + bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); + type = eh->ether_type; + goto gottype; + + default: + printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, + dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + +gottrailertype: + /* + * Packet to be sent as trailer: move first packet + * (control information) to end of chain. + */ + while (m->m_next) + m = m->m_next; + m->m_next = m0; + m = m0->m_next; + m0->m_next = 0; + +gottype: + if (mcopy) + (void) looutput(ifp, mcopy, dst, rt); + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT); + if (m == 0) { + error = ENOBUFS; + goto bad; + } + eh = mtod(m, struct ether_header *); + + eh->ether_type = htons((u_short) type); + + *(int *) eh->ether_dhost = *(int *) edst; + ((short *) eh->ether_dhost)[2] = ((short *) edst)[2]; + + *(int *) eh->ether_shost = *(int *) ac->ac_enaddr; + ((short *) eh->ether_shost)[2] = ((short *) ac->ac_enaddr)[2]; + + s = splimp(); + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + splx(s); + error = ENOBUFS; + goto bad; + } + IF_ENQUEUE(&ifp->if_snd, m); + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + ifp->if_obytes += len + sizeof (struct ether_header); + if (edst[0] & 1) + ifp->if_omcasts++; + return (error); + +bad: + if (m) + m_freem(m); + return (error); +} + +/* + * Process a received Ethernet packet; + * the packet is in the mbuf chain m without + * the ether header, which is provided separately. + */ +ether_input(ifp, eh, m) + struct ifnet *ifp; + register struct ether_header *eh; + struct mbuf *m; +{ + register struct ifqueue *inq; + register struct llc *l; + int s; + + ifp->if_lastchange = time; + ifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh); + if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, + sizeof(etherbroadcastaddr)) == 0) + m->m_flags |= M_BCAST; + else if (eh->ether_dhost[0] & 1) + m->m_flags |= M_MCAST; + if (m->m_flags & (M_BCAST|M_MCAST)) + ifp->if_imcasts++; + + switch (eh->ether_type) { +#ifdef INET + case ETHERTYPE_IP: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; + + case ETHERTYPE_ARP: + arpinput((struct arpcom *)ifp, m); + return; +#endif +#ifdef NS + case ETHERTYPE_NS: + schednetisr(NETISR_NS); + inq = &nsintrq; + break; + +#endif + default: +#ifdef ISO + if (eh->ether_type > ETHERMTU) + goto dropanyway; + l = mtod(m, struct llc *); + switch (l->llc_control) { + case LLC_UI: + /* LLC_UI_P forbidden in class 1 service */ + if ((l->llc_dsap == LLC_ISO_LSAP) && + (l->llc_ssap == LLC_ISO_LSAP)) { + /* LSAP for ISO */ + if (m->m_pkthdr.len > eh->ether_type) + m_adj(m, eh->ether_type - m->m_pkthdr.len); + m->m_data += 3; /* XXX */ + m->m_len -= 3; /* XXX */ + m->m_pkthdr.len -= 3; /* XXX */ + M_PREPEND(m, sizeof *eh, M_DONTWAIT); + if (m == 0) + return; + *mtod(m, struct ether_header *) = *eh; + IFDEBUG(D_ETHER) + printf("clnp packet"); + ENDDEBUG + schednetisr(NETISR_ISO); + inq = &clnlintrq; + break; + } + goto dropanyway; + + case LLC_XID: + case LLC_XID_P: + if(m->m_len < 6) + goto dropanyway; + l->llc_window = 0; + l->llc_fid = 9; + l->llc_class = 1; + l->llc_dsap = l->llc_ssap = 0; + /* Fall through to */ + case LLC_TEST: + case LLC_TEST_P: + { + struct sockaddr sa; + register struct ether_header *eh2; + int i; + u_char c = l->llc_dsap; + l->llc_dsap = l->llc_ssap; + l->llc_ssap = c; + if (m->m_flags & (M_BCAST | M_MCAST)) + bcopy((caddr_t)ac->ac_enaddr, + (caddr_t)eh->ether_dhost, 6); + sa.sa_family = AF_UNSPEC; + sa.sa_len = sizeof(sa); + eh2 = (struct ether_header *)sa.sa_data; + for (i = 0; i < 6; i++) { + eh2->ether_shost[i] = c = eh->ether_dhost[i]; + eh2->ether_dhost[i] = + eh->ether_dhost[i] = eh->ether_shost[i]; + eh->ether_shost[i] = c; + } + ifp->if_output(ifp, m, &sa); + return; + } + dropanyway: + default: + m_freem(m); + return; + } +#else + m_freem(m); + return; +#endif ISO + } + + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else + IF_ENQUEUE(inq, m); + splx(s); +} + +/* + * Convert Ethernet address to printable (loggable) representation. + */ +static char digits[] = "0123456789abcdef"; +char * +ether_sprintf(ap) + register u_char *ap; +{ + register i; + static char etherbuf[18]; + register char *cp = etherbuf; + + for (i = 0; i < 6; i++) { + *cp++ = digits[*ap >> 4]; + *cp++ = digits[*ap++ & 0xf]; + *cp++ = ':'; + } + *--cp = 0; + return (etherbuf); +} diff --git a/sys/net/if_llc.h b/sys/net/if_llc.h new file mode 100644 index 000000000000..a672be41a3d1 --- /dev/null +++ b/sys/net/if_llc.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1988 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. + * + * from: @(#)if_llc.h 7.2 (Berkeley) 6/28/90 + * $Id: if_llc.h,v 1.2 1993/10/16 17:43:17 rgrimes Exp $ + */ + +/* + * IEEE 802.2 Link Level Control headers, for use in conjunction with + * 802.{3,4,5} media access control methods. + * + * Headers here do not use bit fields due to shortcommings in many + * compilers. + */ + +struct llc { + u_char llc_dsap; + u_char llc_ssap; + union { + struct { + u_char control; + u_char format_id; + u_char class; + u_char window_x2; + } type_u; + struct { + u_char num_snd_x2; + u_char num_rcv_x2; + } type_i; + struct { + u_char control; + u_char num_rcv_x2; + } type_s; + struct { + u_char control; + u_char org_code[3]; + u_short ether_type; + } type_snap; + } llc_un; +}; +#define llc_control llc_un.type_u.control +#define llc_fid llc_un.type_u.format_id +#define llc_class llc_un.type_u.class +#define llc_window llc_un.type_u.window_x2 + +#define LLC_UI 0x3 +#define LLC_UI_P 0x13 +#define LLC_XID 0xaf +#define LLC_XID_P 0xbf +#define LLC_TEST 0xe3 +#define LLC_TEST_P 0xf3 + +#define LLC_ISO_LSAP 0xfe +#define LLC_SNAP_LSAP 0xaa diff --git a/sys/net/if_loop.c b/sys/net/if_loop.c new file mode 100644 index 000000000000..40492d421843 --- /dev/null +++ b/sys/net/if_loop.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1982, 1986 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. + * + * from: @(#)if_loop.c 7.13 (Berkeley) 4/26/91 + * $Id: if_loop.c,v 1.2 1993/10/16 17:43:19 rgrimes Exp $ + */ + +/* + * Loopback interface driver for protocol testing and timing. + */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "socket.h" +#include "errno.h" +#include "ioctl.h" + +#include "../net/if.h" +#include "../net/if_types.h" +#include "../net/netisr.h" +#include "../net/route.h" + +#include "machine/mtpr.h" + +#ifdef INET +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/in_var.h" +#include "../netinet/ip.h" +#endif + +#ifdef NS +#include "../netns/ns.h" +#include "../netns/ns_if.h" +#endif + +#ifdef ISO +#include "../netiso/iso.h" +#include "../netiso/iso_var.h" +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <sys/time.h> +#include <net/bpf.h> +static caddr_t lo_bpf; +#endif + +#define LOMTU (1024+512) + +struct ifnet loif; +int looutput(), loioctl(); + +loattach() +{ + register struct ifnet *ifp = &loif; + + ifp->if_name = "lo"; + ifp->if_mtu = LOMTU; + ifp->if_flags = IFF_LOOPBACK; + ifp->if_ioctl = loioctl; + ifp->if_output = looutput; + ifp->if_type = IFT_LOOP; + ifp->if_hdrlen = 0; + ifp->if_addrlen = 0; + if_attach(ifp); +#if NBPFILTER > 0 + bpfattach(&lo_bpf, ifp, DLT_NULL, sizeof(u_int)); +#endif +} + +looutput(ifp, m, dst, rt) + struct ifnet *ifp; + register struct mbuf *m; + struct sockaddr *dst; + register struct rtentry *rt; +{ + int s, isr; + register struct ifqueue *ifq = 0; + + if ((m->m_flags & M_PKTHDR) == 0) + panic("looutput no HDR"); +#if NBPFILTER > 0 + if (lo_bpf) { + /* + * We need to prepend the address family as + * a four byte field. Cons up a dummy header + * to pacify bpf. This is safe because bpf + * will only read from the mbuf (i.e., it won't + * try to free it or keep a pointer to it). + */ + struct mbuf m0; + u_int af = dst->sa_family; + + m0.m_next = m; + m0.m_len = 4; + m0.m_data = (char *)⁡ + + bpf_mtap(lo_bpf, &m0); + } +#endif + m->m_pkthdr.rcvif = ifp; + + if (rt && rt->rt_flags & RTF_REJECT) { + m_freem(m); + return (rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); + } + ifp->if_opackets++; + ifp->if_obytes += m->m_pkthdr.len; + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + ifq = &ipintrq; + isr = NETISR_IP; + break; +#endif +#ifdef NS + case AF_NS: + ifq = &nsintrq; + isr = NETISR_NS; + break; +#endif +#ifdef ISO + case AF_ISO: + ifq = &clnlintrq; + isr = NETISR_ISO; + break; +#endif + default: + printf("lo%d: can't handle af%d\n", ifp->if_unit, + dst->sa_family); + m_freem(m); + return (EAFNOSUPPORT); + } + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + m_freem(m); + splx(s); + return (ENOBUFS); + } + IF_ENQUEUE(ifq, m); + schednetisr(isr); + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + splx(s); + return (0); +} + +/* ARGSUSED */ +lortrequest(cmd, rt, sa) +struct rtentry *rt; +struct sockaddr *sa; +{ + if (rt) + rt->rt_rmx.rmx_mtu = LOMTU; +} + +/* + * Process an ioctl request. + */ +/* ARGSUSED */ +loioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifaddr *ifa; + int error = 0; + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + ifa = (struct ifaddr *)data; + if (ifa != 0 && ifa->ifa_addr->sa_family == AF_ISO) + ifa->ifa_rtrequest = lortrequest; + /* + * Everything else is done at a higher level. + */ + break; + + default: + error = EINVAL; + } + return (error); +} diff --git a/sys/net/if_ppp.c b/sys/net/if_ppp.c new file mode 100644 index 000000000000..1baa79659136 --- /dev/null +++ b/sys/net/if_ppp.c @@ -0,0 +1,1441 @@ +/* + * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Drew D. Perkins + * Carnegie Mellon University + * 4910 Forbes Ave. + * Pittsburgh, PA 15213 + * (412) 268-8576 + * ddp@andrew.cmu.edu + * + * Based on: + * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 + * + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Serial Line interface + * + * 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). + * Converted to 4.3BSD Beta by Chris Torek. + * Other changes made at Berkeley, based in part on code by Kirk Smith. + * + * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com) + * Added VJ tcp header compression; more unified ioctls + * + * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au). + * Cleaned up a lot of the mbuf-related code to fix bugs that + * caused system crashes and packet corruption. Changed pppstart + * so that it doesn't just give up with a collision if the whole + * packet doesn't fit in the output ring buffer. + * + * Added priority queueing for interactive IP packets, following + * the model of if_sl.c, plus hooks for bpf. + * Paul Mackerras (paulus@cs.anu.edu.au). + */ + +/* + * $Id: if_ppp.c,v 1.4 1993/10/07 02:19:37 rgrimes Exp $ + * From: if_ppp.c,v 1.22 1993/08/31 23:20:40 paulus Exp + * From: if_ppp.c,v 1.21 1993/08/29 11:22:37 paulus Exp + * From: if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp + */ + +#include "ppp.h" +#if NPPP > 0 + +#define VJC + +#include "param.h" +#include "systm.h" +#include "proc.h" +#include "mbuf.h" +#include "buf.h" +#include "dkstat.h" +#include "socket.h" +#include "ioctl.h" +#include "file.h" +#include "tty.h" +#include "kernel.h" +#include "conf.h" + +#include "if.h" +#include "if_types.h" +#include "netisr.h" +#include "route.h" +#if INET +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/in_var.h" +#include "../netinet/ip.h" +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include "time.h" +#include "bpf.h" +#endif + +/* + * Here we try to tell whether we are in a 386BSD kernel, or + * in a NetBSD/Net-2/4.3-Reno kernel. + */ +#ifndef RB_LEN +/* NetBSD, 4.3-Reno or similar */ +#define CCOUNT(q) ((q)->c_cc) + +#else +/* 386BSD, Jolitz-style ring buffers */ +#define t_outq t_out +#define t_rawq t_raw +#define t_canq t_can +#define CCOUNT(q) (RB_LEN(q)) +#endif + +#ifdef VJC +#include "slcompress.h" +#define HDROFF MAX_HDR +/* HDROFF should really be 128, but other parts of the system will + panic on TCP+IP headers bigger than MAX_HDR = MHLEN (100). */ + +#else +#define HDROFF (0) +#endif + +#include "if_ppp.h" +#include "machine/mtpr.h" + +struct ppp_softc ppp_softc[NPPP]; +int ppp_async_out_debug = 0; +int ppp_async_in_debug = 0; +int ppp_debug = 0; +int ppp_raw_in_debug = -1; +char ppp_rawin[32]; +int ppp_rawin_count; + +void pppattach __P((void)); +int pppopen __P((dev_t dev, struct tty *tp)); +void pppclose __P((struct tty *tp, int flag)); +int pppread __P((struct tty *tp, struct uio *uio, int flag)); +int pppwrite __P((struct tty *tp, struct uio *uio, int flag)); +int ppptioctl __P((struct tty *tp, int cmd, caddr_t data, int flag)); +int pppoutput __P((struct ifnet *ifp, struct mbuf *m0, + struct sockaddr *dst)); +void pppstart __P((struct tty *tp)); +void pppinput __P((int c, struct tty *tp)); +int pppioctl __P((struct ifnet *ifp, int cmd, caddr_t data)); + +static u_short pppfcs __P((u_short fcs, u_char *cp, int len)); +static int pppinit __P((struct ppp_softc *sc)); +static struct mbuf *ppp_btom __P((struct ppp_softc *sc)); +static void pppdumpm __P((struct mbuf *m0, int pktlen)); +static void pppdumpb __P((u_char *b, int l)); + +/* + * Some useful mbuf macros not in mbuf.h. + */ +#define M_DATASTART(m) \ + ((m)->m_flags & M_EXT ? (m)->m_ext.ext_buf : \ + (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat) + +#define M_DATASIZE(m) \ + ((m)->m_flags & M_EXT ? (m)->m_ext.ext_size : \ + (m)->m_flags & M_PKTHDR ? MHLEN: MLEN) + +/* + * The following disgusting hack gets around the problem that IP TOS + * can't be set yet. We want to put "interactive" traffic on a high + * priority queue. To decide if traffic is interactive, we check that + * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. + */ +static u_short interactive_ports[8] = { + 0, 513, 0, 0, + 0, 21, 0, 23, +}; +#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) + +/* + * Does c need to be escaped? + */ +#define ESCAPE_P(c) (((c) == PPP_FLAG) || ((c) == PPP_ESCAPE) || \ + (c) < 0x20 && (sc->sc_asyncmap & (1 << (c)))) + +/* + * Called from boot code to establish ppp interfaces. + */ +void +pppattach() +{ + register struct ppp_softc *sc; + register int i = 0; + + for (sc = ppp_softc; i < NPPP; sc++) { + sc->sc_if.if_name = "ppp"; + sc->sc_if.if_unit = i++; + sc->sc_if.if_mtu = PPP_MTU; + sc->sc_if.if_flags = IFF_POINTOPOINT; + sc->sc_if.if_type = IFT_PPP; + sc->sc_if.if_hdrlen = PPP_HEADER_LEN; + sc->sc_if.if_ioctl = pppioctl; + sc->sc_if.if_output = pppoutput; + sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; + sc->sc_inq.ifq_maxlen = IFQ_MAXLEN; + sc->sc_fastq.ifq_maxlen = IFQ_MAXLEN; + if_attach(&sc->sc_if); +#if NBPFILTER > 0 + bpfattach(&sc->sc_bpf, &sc->sc_if, DLT_PPP, PPP_HEADER_LEN); +#endif + } +} + +/* + * Line specific open routine. + * Attach the given tty to the first available ppp unit. + */ +/* ARGSUSED */ +int +pppopen(dev, tp) + dev_t dev; + register struct tty *tp; +{ + struct proc *p = curproc; /* XXX */ + register struct ppp_softc *sc; + register int nppp; + int error, s; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + + if (tp->t_line == PPPDISC) + return (0); + + for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++) + if (sc->sc_ttyp == NULL) + break; + if (nppp >= NPPP) + return ENXIO; + + sc->sc_flags = 0; + sc->sc_ilen = 0; + sc->sc_asyncmap = 0xffffffff; + sc->sc_rasyncmap = 0; + sc->sc_mru = PPP_MRU; +#ifdef VJC + sl_compress_init(&sc->sc_comp); +#endif + if (pppinit(sc) == 0) { + sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + return (ENOBUFS); + } + tp->t_sc = (caddr_t)sc; + sc->sc_ttyp = tp; + sc->sc_outm = NULL; + ttyflush(tp, FREAD | FWRITE); + sc->sc_if.if_flags |= IFF_RUNNING; + +#ifdef PPP_OUTQ_SIZE + /* N.B. this code is designed *only* for use in NetBSD */ + s = spltty(); + /* get rid of the default outq clist buffer */ + clfree(&tp->t_outq); + /* and get a new one, without quoting support, much larger */ + clalloc(&tp->t_outq, PPP_OUTQ_SIZE, 0); + splx (s); +#endif /* PPP_OUTQ_SIZE */ + + return (0); +} + +/* + * Line specific close routine. + * Detach the tty from the ppp unit. + * Mimics part of ttyclose(). + */ +void +pppclose(tp, flag) + struct tty *tp; + int flag; +{ + register struct ppp_softc *sc; + struct mbuf *m; + int s; + + ttywflush(tp); + s = splimp(); /* paranoid; splnet probably ok */ + tp->t_line = 0; + sc = (struct ppp_softc *)tp->t_sc; + if (sc != NULL) { + if_down(&sc->sc_if); + sc->sc_ttyp = NULL; + tp->t_sc = NULL; + m_freem(sc->sc_outm); + sc->sc_outm = NULL; + m_freem(sc->sc_m); + sc->sc_m = NULL; + for (;;) { + IF_DEQUEUE(&sc->sc_inq, m); + if (m == NULL) + break; + m_freem(m); + } + for (;;) { + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + break; + m_freem(m); + } + sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + +#ifdef PPP_OUTQ_SIZE + /* reinstall default clist-buffer for outq + XXXX should really remember old value and restore that!! */ + clfree(&tp->t_outq); + clalloc(&tp->t_outq, 1024, 0); +#endif /* PPP_OUTQ_SIZE */ + + } + splx(s); +} + +/* + * Line specific (tty) read routine. + */ +int +pppread(tp, uio, flag) + register struct tty *tp; + struct uio *uio; + int flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct mbuf *m, *m0; + register int s; + int error; + + if ((tp->t_state & TS_CARR_ON)==0) + return (EIO); + s = splimp(); + while (sc->sc_inq.ifq_head == NULL && tp->t_line == PPPDISC) { + if (tp->t_state & TS_ASYNC) { + splx(s); + return (EWOULDBLOCK); + } + error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0); + if (error) + return error; + } + if (tp->t_line != PPPDISC) { + splx(s); + return (-1); + } + + /* Pull place-holder byte out of canonical queue */ + getc(&tp->t_canq); + + /* Get the packet from the input queue */ + IF_DEQUEUE(&sc->sc_inq, m0); + splx(s); + + for (m = m0; m && uio->uio_resid; m = m->m_next) + if (error = uiomove(mtod(m, u_char *), m->m_len, uio)) + break; + m_freem(m0); + return (error); +} + +/* + * Line specific (tty) write routine. + */ +int +pppwrite(tp, uio, flag) + register struct tty *tp; + struct uio *uio; + int flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct mbuf *m, *m0, **mp; + struct sockaddr dst; + struct ppp_header *ph1, *ph2; + int len, error; + + if ((tp->t_state & TS_CARR_ON)==0) + return (EIO); + if (tp->t_line != PPPDISC) + return (EINVAL); + if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HEADER_LEN || + uio->uio_resid < PPP_HEADER_LEN) + return (EMSGSIZE); + for (mp = &m0; uio->uio_resid; mp = &m->m_next) { + MGET(m, M_WAIT, MT_DATA); + if ((*mp = m) == NULL) { + m_freem(m0); + return (ENOBUFS); + } + if (uio->uio_resid >= MCLBYTES / 2) + MCLGET(m, M_DONTWAIT); + len = MIN(M_TRAILINGSPACE(m), uio->uio_resid); + if (error = uiomove(mtod(m, u_char *), len, uio)) { + m_freem(m0); + return (error); + } + m->m_len = len; + } + dst.sa_family = AF_UNSPEC; + ph1 = (struct ppp_header *) &dst.sa_data; + ph2 = mtod(m0, struct ppp_header *); + *ph1 = *ph2; + m0->m_data += PPP_HEADER_LEN; + m0->m_len -= PPP_HEADER_LEN; + return (pppoutput(&sc->sc_if, m0, &dst)); +} + +/* + * Line specific (tty) ioctl routine. + * Provide a way to get the ppp unit number. + * This discipline requires that tty device drivers call + * the line specific l_ioctl routine from their ioctl routines. + */ +/* ARGSUSED */ +int +ppptioctl(tp, cmd, data, flag) + struct tty *tp; + caddr_t data; + int cmd, flag; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + struct proc *p = curproc; /* XXX */ + int s, error, flags, mru; + + switch (cmd) { +#if 0 /* this is handled (properly) by ttioctl */ + case TIOCGETD: + *(int *)data = sc->sc_if.if_unit; + break; +#endif + case FIONREAD: + *(int *)data = sc->sc_inq.ifq_len; + break; + + case PPPIOCGUNIT: + *(int *)data = sc->sc_if.if_unit; + break; + + case PPPIOCGFLAGS: + *(u_int *)data = sc->sc_flags; + break; + + case PPPIOCSFLAGS: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + flags = *(int *)data & SC_MASK; + s = splimp(); + sc->sc_flags = (sc->sc_flags & ~SC_MASK) | flags; + splx(s); + break; + + case PPPIOCSASYNCMAP: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + sc->sc_asyncmap = *(u_int *)data; + break; + + case PPPIOCGASYNCMAP: + *(u_int *)data = sc->sc_asyncmap; + break; + + case PPPIOCSRASYNCMAP: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + sc->sc_rasyncmap = *(u_int *)data; + break; + + case PPPIOCGRASYNCMAP: + *(u_int *)data = sc->sc_rasyncmap; + break; + + case PPPIOCSMRU: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + mru = *(int *)data; + if (mru >= PPP_MRU && mru <= PPP_MAXMRU) { + sc->sc_mru = mru; + if (pppinit(sc) == 0) { + error = ENOBUFS; + sc->sc_mru = PPP_MRU; + if (pppinit(sc) == 0) + sc->sc_if.if_flags &= ~IFF_UP; + } + } + break; + + case PPPIOCGMRU: + *(int *)data = sc->sc_mru; + break; + + default: + return (-1); + } + return (0); +} + +/* + * FCS lookup table as calculated by genfcstab. + */ +static u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* + * Calculate a new FCS given the current FCS and the new data. + */ +static u_short +pppfcs(fcs, cp, len) + register u_short fcs; + register u_char *cp; + register int len; +{ + while (len--) + fcs = PPP_FCS(fcs, *cp++); + return (fcs); +} + +/* + * Queue a packet. Start transmission if not active. + * Packet is placed in Information field of PPP frame. + */ +int +pppoutput(ifp, m0, dst) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; +{ + register struct ppp_softc *sc = &ppp_softc[ifp->if_unit]; + struct ppp_header *ph; + int protocol, address, control; + u_char *cp; + int s, error; + struct ip *ip; + struct ifqueue *ifq; + + if (sc->sc_ttyp == NULL || (ifp->if_flags & IFF_RUNNING) == 0 + || (ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC) { + error = ENETDOWN; /* sort of */ + goto bad; + } + if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { + error = EHOSTUNREACH; + goto bad; + } + + /* + * Compute PPP header. + */ + address = PPP_ALLSTATIONS; + control = PPP_UI; + ifq = &ifp->if_snd; + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + protocol = PPP_IP; + /* + * If this is a TCP packet to or from an "interactive" port, + * put the packet on the fastq instead. + */ + if ((ip = mtod(m0, struct ip *))->ip_p == IPPROTO_TCP) { + register int p = ((int *)ip)[ip->ip_hl]; + if (INTERACTIVE(p & 0xffff) || INTERACTIVE(p >> 16)) + ifq = &sc->sc_fastq; + } + break; +#endif +#ifdef NS + case AF_NS: + protocol = PPP_XNS; + break; +#endif + case AF_UNSPEC: + ph = (struct ppp_header *) dst->sa_data; + address = ph->ph_address; + control = ph->ph_control; + protocol = ntohs(ph->ph_protocol); + break; + default: + printf("ppp%d: af%d not supported\n", ifp->if_unit, dst->sa_family); + error = EAFNOSUPPORT; + goto bad; + } + + /* + * Add PPP header. If no space in first mbuf, allocate another. + * (This assumes M_LEADINGSPACE is always 0 for a cluster mbuf.) + */ + if (M_LEADINGSPACE(m0) < PPP_HEADER_LEN) { + m0 = m_prepend(m0, PPP_HEADER_LEN, M_DONTWAIT); + if (m0 == 0) { + error = ENOBUFS; + goto bad; + } + m0->m_len = 0; + } else + m0->m_data -= PPP_HEADER_LEN; + + cp = mtod(m0, u_char *); + *cp++ = address; + *cp++ = control; + *cp++ = protocol >> 8; + *cp++ = protocol & 0xff; + m0->m_len += PPP_HEADER_LEN; + + if (ppp_async_out_debug) { + printf("ppp%d output: ", ifp->if_unit); + pppdumpm(m0, -1); + } + +#if NBPFILTER > 0 + /* See if bpf wants to look at the packet. */ + if (sc->sc_bpf) + bpf_mtap(sc->sc_bpf, m0); +#endif + + /* + * Put the packet on the appropriate queue. + */ + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + splx(s); + sc->sc_if.if_oerrors++; + error = ENOBUFS; + goto bad; + } + IF_ENQUEUE(ifq, m0); + /* + * The next statement used to be subject to: + * if (CCOUNT(&sc->sc_ttyp->t_outq) == 0) + * which was removed so that we don't hang up completely + * if the serial transmitter loses an interrupt. + */ + pppstart(sc->sc_ttyp); + splx(s); + return (0); + +bad: + m_freem(m0); + return (error); +} + +/* + * Start output on interface. Get another datagram + * to send from the interface queue and map it to + * the interface before starting output. + */ +void +pppstart(tp) + register struct tty *tp; +{ + register struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc; + register struct mbuf *m; + register int len; + register u_char *start, *stop, *cp; + int n, s, ndone, done; + struct mbuf *m2; + int address, control, protocol; + int compac, compprot, nb; + + for (;;) { + /* + * 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 (CCOUNT(&tp->t_outq) != 0 && tp->t_oproc != NULL) { + (*tp->t_oproc)(tp); + if (CCOUNT(&tp->t_outq) > PPP_HIWAT) + return; + } + /* + * This happens briefly when the line shuts down. + */ + if (sc == NULL) + return; + + /* + * See if we have an existing packet partly sent. + * If not, get a new packet and start sending it. + * We take packets on the priority queue ahead of those + * on the normal queue. + */ + m = sc->sc_outm; + if (m == NULL) { + s = splimp(); + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + IF_DEQUEUE(&sc->sc_if.if_snd, m); + splx(s); + if (m == NULL) + return; + + /* + * Extract the ppp header of the new packet. + * The ppp header will be in one mbuf. + */ + cp = mtod(m, u_char *); + address = *cp++; + control = *cp++; + protocol = *cp++; + protocol = (protocol << 8) + *cp++; + m->m_data += PPP_HEADER_LEN; + m->m_len -= PPP_HEADER_LEN; + +#ifdef VJC + /* + * If the packet is a TCP/IP packet, see if we can compress it. + */ + if (protocol == PPP_IP && sc->sc_flags & SC_COMP_TCP) { + struct ip *ip; + int type; + struct mbuf *mp; + + mp = m; + if (mp->m_len <= 0) { + mp = mp->m_next; + cp = mtod(mp, u_char *); + } + ip = (struct ip *) cp; + if (ip->ip_p == IPPROTO_TCP) { + type = sl_compress_tcp(mp, ip, &sc->sc_comp, + !(sc->sc_flags & SC_NO_TCP_CCID)); + switch (type) { + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + } + } + } +#endif + + /* + * Compress the address/control and protocol, if possible. + */ + compac = sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS && + control == PPP_UI && protocol != PPP_ALLSTATIONS && + protocol != PPP_LCP; + compprot = sc->sc_flags & SC_COMP_PROT && protocol < 0x100; + nb = (compac ? 0 : 2) + (compprot ? 1 : 2); + m->m_data -= nb; + m->m_len += nb; + + cp = mtod(m, u_char *); + if (!compac) { + *cp++ = address; + *cp++ = control; + } + if (!compprot) + *cp++ = protocol >> 8; + *cp++ = protocol; + + /* + * The extra PPP_FLAG will start up a new packet, and thus + * will flush any accumulated garbage. We do this whenever + * the line may have been idle for some time. + */ + if (CCOUNT(&tp->t_outq) == 0) { + ++sc->sc_bytessent; + (void) putc(PPP_FLAG, &tp->t_outq); + } + + /* Calculate the FCS for the first mbuf's worth. */ + sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len); + } + + for (;;) { + start = mtod(m, u_char *); + len = m->m_len; + stop = start + len; + while (len > 0) { + /* + * Find out how many bytes in the string we can + * handle without doing something special. + */ + for (cp = start; cp < stop; cp++) + if (ESCAPE_P(*cp)) + break; + n = cp - start; + if (n) { +#ifndef RB_LEN + /* NetBSD (0.9 or later), 4.3-Reno or similar. */ + ndone = n - b_to_q(start, n, &tp->t_outq); +#else +#ifdef NetBSD + /* NetBSD with 2-byte ring buffer entries */ + ndone = rb_cwrite(&tp->t_out, start, n); +#else + /* 386BSD, FreeBSD */ + int cc, nleft; + for (nleft = n; nleft > 0; nleft -= cc) { + if ((cc = RB_CONTIGPUT(&tp->t_out)) == 0) + break; + cc = min (cc, nleft); + bcopy((char *)start + n - nleft, tp->t_out.rb_tl, cc); + tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out, + tp->t_out.rb_tl + cc); + } + ndone = n - nleft; +#endif /* NetBSD */ +#endif /* RB_LEN */ + len -= ndone; + start += ndone; + sc->sc_bytessent += ndone; + + if (ndone < n) + break; /* packet doesn't fit */ + } + /* + * If there are characters left in the mbuf, + * the first one must be special.. + * Put it out in a different form. + */ + if (len) { + if (putc(PPP_ESCAPE, &tp->t_outq)) + break; + if (putc(*start ^ PPP_TRANS, &tp->t_outq)) { + (void) unputc(&tp->t_outq); + break; + } + sc->sc_bytessent += 2; + start++; + len--; + } + } + /* + * If we didn't empty this mbuf, remember where we're up to. + * If we emptied the last mbuf, try to add the FCS and closing + * flag, and if we can't, leave sc_outm pointing to m, but with + * m->m_len == 0, to remind us to output the FCS and flag later. + */ + done = len == 0; + if (done && m->m_next == NULL) { + u_char *p, *q; + int c; + u_char endseq[8]; + + /* + * We may have to escape the bytes in the FCS. + */ + p = endseq; + c = ~sc->sc_outfcs & 0xFF; + if (ESCAPE_P(c)) { + *p++ = PPP_ESCAPE; + *p++ = c ^ PPP_TRANS; + } else + *p++ = c; + c = (~sc->sc_outfcs >> 8) & 0xFF; + if (ESCAPE_P(c)) { + *p++ = PPP_ESCAPE; + *p++ = c ^ PPP_TRANS; + } else + *p++ = c; + *p++ = PPP_FLAG; + + /* + * Try to output the FCS and flag. If the bytes + * don't all fit, back out. + */ + for (q = endseq; q < p; ++q) + if (putc(*q, &tp->t_outq)) { + done = 0; + for (; q > endseq; --q) + unputc(&tp->t_outq); + break; + } + } + + if (!done) { + m->m_data = start; + m->m_len = len; + sc->sc_outm = m; + if (tp->t_oproc != NULL) + (*tp->t_oproc)(tp); + return; /* can't do any more at the moment */ + } + + /* Finished with this mbuf; free it and move on. */ + MFREE(m, m2); + if (m2 == NULL) + break; + + m = m2; + sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len); + } + + /* Finished a packet */ + sc->sc_outm = NULL; + sc->sc_bytessent++; /* account for closing flag */ + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes = sc->sc_bytessent; + } +} + +/* + * Allocate enough mbuf to handle current MRU. + */ +static int +pppinit(sc) + register struct ppp_softc *sc; +{ + struct mbuf *m, **mp; + int len = HDROFF + sc->sc_mru + PPP_HEADER_LEN + PPP_FCS_LEN; + int s; + + s = splimp(); + for (mp = &sc->sc_m; (m = *mp) != NULL; mp = &m->m_next) + if ((len -= M_DATASIZE(m)) <= 0) { + splx(s); + return (1); + } + + for (;; mp = &m->m_next) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(sc->sc_m); + sc->sc_m = NULL; + splx(s); + printf("ppp%d: can't allocate mbuf\n", sc->sc_if.if_unit); + return (0); + } + *mp = m; + MCLGET(m, M_DONTWAIT); + if ((len -= M_DATASIZE(m)) <= 0) { + splx(s); + return (1); + } + } +} + +/* + * Copy mbuf chain. Would like to use m_copy(), but we need a real copy + * of the data, not just copies of pointers to the data. + */ +static struct mbuf * +ppp_btom(sc) + struct ppp_softc *sc; +{ + register struct mbuf *m, **mp; + struct mbuf *top = sc->sc_m; + + /* + * First check current mbuf. If we have more than a small mbuf, + * return the whole cluster and set beginning of buffer to the + * next mbuf. + * Else, copy the current bytes into a small mbuf, attach the new + * mbuf to the end of the chain and set beginning of buffer to the + * current mbuf. + */ + + if (sc->sc_mc->m_len > MHLEN) { + sc->sc_m = sc->sc_mc->m_next; + sc->sc_mc->m_next = NULL; + } + else { + /* rather than waste a whole cluster on <= MHLEN bytes, + alloc a small mbuf and copy to it */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + + bcopy(mtod(sc->sc_mc, caddr_t), mtod(m, caddr_t), sc->sc_mc->m_len); + m->m_len = sc->sc_mc->m_len; + for (mp = ⊤ *mp != sc->sc_mc; mp = &(*mp)->m_next) + ; + *mp = m; + sc->sc_m = sc->sc_mc; + } + + /* + * Try to allocate enough extra mbufs to handle the next packet. + */ + if (pppinit(sc) == 0) { + m_freem(top); + if (pppinit(sc) == 0) + sc->sc_if.if_flags &= ~IFF_UP; + return (NULL); + } + + return (top); +} + +/* + * tty interface receiver interrupt. + */ +#define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \ + TYPE_UNCOMPRESSED_TCP) + +void +pppinput(c, tp) + int c; + register struct tty *tp; +{ + register struct ppp_softc *sc; + struct mbuf *m; + struct ifqueue *inq; + int s, ilen, xlen, proto; + struct ppp_header hdr; + + tk_nin++; + sc = (struct ppp_softc *)tp->t_sc; + if (sc == NULL) + return; + + ++sc->sc_if.if_ibytes; + + if (c & TTY_FE) { + /* framing error or overrun on this char - abort packet */ + if (ppp_debug) + printf("ppp%d: bad char %x\n", sc->sc_if.if_unit, c); + goto flush; + } + + c &= 0xff; + + if (sc->sc_if.if_unit == ppp_raw_in_debug) { + ppp_rawin[ppp_rawin_count++] = c; + if (ppp_rawin_count >= sizeof(ppp_rawin)) { + printf("raw ppp%d: ", ppp_raw_in_debug); + pppdumpb(ppp_rawin, ppp_rawin_count); + ppp_rawin_count = 0; + } + } + + if (c == PPP_FLAG) { + ilen = sc->sc_ilen; + sc->sc_ilen = 0; + + if (sc->sc_flags & SC_FLUSH + || ilen > 0 && sc->sc_fcs != PPP_GOODFCS) { +#ifdef VJC + /* + * If we've missed a packet, we must toss subsequent compressed + * packets which don't have an explicit connection ID. + */ + sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &sc->sc_comp); +#endif + if ((sc->sc_flags & SC_FLUSH) == 0){ + if (ppp_debug) + printf("ppp%d: bad fcs\n", sc->sc_if.if_unit); + sc->sc_if.if_ierrors++; + } else + sc->sc_flags &= ~SC_FLUSH; + return; + } + + if (ilen < PPP_HEADER_LEN + PPP_FCS_LEN) { + if (ilen) { + if (ppp_debug) + printf("ppp%d: too short (%d)\n", sc->sc_if.if_unit, ilen); + sc->sc_if.if_ierrors++; + } + return; + } + + /* + * Remove FCS trailer. Somewhat painful... + */ + ilen -= 2; + if (--sc->sc_mc->m_len == 0) { + for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next) + ; + sc->sc_mc = m; + } + sc->sc_mc->m_len--; + + sc->sc_if.if_ipackets++; + m = sc->sc_m; + + if (ppp_async_in_debug) { + printf("ppp%d: got %d bytes\n", sc->sc_if.if_unit, ilen); + pppdumpm(m, ilen); + } + + hdr = *mtod(m, struct ppp_header *); + proto = ntohs(hdr.ph_protocol); + +#ifdef VJC + /* + * See if we have a VJ-compressed packet to uncompress. + */ + if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) { + char *pkttype = proto == PPP_VJC_COMP? "": "un"; + + if (sc->sc_flags & SC_REJ_COMP_TCP) { + if (ppp_debug) + printf("ppp%d: %scomp pkt w/o compression; flags 0x%x\n", + sc->sc_if.if_unit, pkttype, sc->sc_flags); + sc->sc_if.if_ierrors++; + return; + } + + m->m_data += PPP_HEADER_LEN; + m->m_len -= PPP_HEADER_LEN; + ilen -= PPP_HEADER_LEN; + xlen = sl_uncompress_tcp_part((u_char **)(&m->m_data), + m->m_len, ilen, + COMPTYPE(proto), &sc->sc_comp); + + if (xlen == 0) { + if (ppp_debug) + printf("ppp%d: sl_uncompress failed on type %scomp\n", + sc->sc_if.if_unit, pkttype); + sc->sc_if.if_ierrors++; + return; + } + + /* adjust the first mbuf by the decompressed amt */ + xlen += PPP_HEADER_LEN; + m->m_len += xlen - ilen; + ilen = xlen; + m->m_data -= PPP_HEADER_LEN; + proto = PPP_IP; + +#if NBPFILTER > 0 + /* put the ppp header back in place */ + hdr.ph_protocol = htons(PPP_IP); + *mtod(m, struct ppp_header *) = hdr; +#endif /* NBPFILTER */ + } +#endif /* VJC */ + + /* get this packet as an mbuf chain */ + if ((m = ppp_btom(sc)) == NULL) { + sc->sc_if.if_ierrors++; + return; + } + m->m_pkthdr.len = ilen; + m->m_pkthdr.rcvif = &sc->sc_if; + +#if NBPFILTER > 0 + /* See if bpf wants to look at the packet. */ + if (sc->sc_bpf) + bpf_mtap(sc->sc_bpf, m); +#endif + + switch (proto) { +#ifdef INET + case PPP_IP: + /* + * IP packet - take off the ppp header and pass it up to IP. + */ + if ((sc->sc_if.if_flags & IFF_UP) == 0) { + /* interface is down - drop the packet. */ + m_freem(m); + sc->sc_if.if_ierrors++; + return; + } + m->m_pkthdr.len -= PPP_HEADER_LEN; + m->m_data += PPP_HEADER_LEN; + m->m_len -= PPP_HEADER_LEN; + schednetisr(NETISR_IP); + inq = &ipintrq; + break; +#endif + + default: + /* + * Some other protocol - place on input queue for read(). + * Put a placeholder byte in canq for ttselect()/ttnread(). + */ + putc(0, &tp->t_canq); + ttwakeup(tp); + inq = &sc->sc_inq; + break; + } + + /* + * Put the packet on the appropriate input queue. + */ + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + if (ppp_debug) + printf("ppp%d: queue full\n", sc->sc_if.if_unit); + sc->sc_if.if_ierrors++; + sc->sc_if.if_iqdrops++; + m_freem(m); + } else + IF_ENQUEUE(inq, m); + + splx(s); + return; + } + + if (sc->sc_flags & SC_FLUSH) + return; + if (c == PPP_ESCAPE) { + sc->sc_flags |= SC_ESCAPED; + return; + } + if (c < 0x20 && (sc->sc_rasyncmap & (1 << c))) + return; + + if (sc->sc_flags & SC_ESCAPED) { + sc->sc_flags &= ~SC_ESCAPED; + c ^= PPP_TRANS; + } + + /* + * Initialize buffer on first octet received. + * First octet could be address or protocol (when compressing + * address/control). + * Second octet is control. + * Third octet is first or second (when compressing protocol) + * octet of protocol. + * Fourth octet is second octet of protocol. + */ + if (sc->sc_ilen == 0) { + /* reset the first input mbuf */ + m = sc->sc_m; + m->m_len = 0; + m->m_data = M_DATASTART(sc->sc_m) + HDROFF; + sc->sc_mc = m; + sc->sc_mp = mtod(m, char *); + sc->sc_fcs = PPP_INITFCS; + if (c != PPP_ALLSTATIONS) { + if (sc->sc_flags & SC_REJ_COMP_AC) { + if (ppp_debug) + printf("ppp%d: missing ALLSTATIONS, got 0x%x; flags %x\n", + sc->sc_if.if_unit, c, sc->sc_flags); + goto flush; + } + *sc->sc_mp++ = PPP_ALLSTATIONS; + *sc->sc_mp++ = PPP_UI; + sc->sc_ilen += 2; + m->m_len += 2; + } + } + if (sc->sc_ilen == 1 && c != PPP_UI) { + if (ppp_debug) + printf("ppp%d: missing UI, got 0x%x\n", sc->sc_if.if_unit, c); + goto flush; + } + if (sc->sc_ilen == 2 && (c & 1) == 1) { + /* RFC1331 says we have to accept a compressed protocol */ + *sc->sc_mp++ = 0; + sc->sc_ilen++; + sc->sc_mc->m_len++; + } + if (sc->sc_ilen == 3 && (c & 1) == 0) { + if (ppp_debug) + printf("ppp%d: bad protocol %x\n", sc->sc_if.if_unit, + (sc->sc_mp[-1] << 8) + c); + goto flush; + } + + /* packet beyond configured mru? */ + if (++sc->sc_ilen > sc->sc_mru + PPP_HEADER_LEN + PPP_FCS_LEN) { + if (ppp_debug) + printf("ppp%d: packet too big\n", sc->sc_if.if_unit); + goto flush; + } + + /* is this mbuf full? */ + m = sc->sc_mc; + if (M_TRAILINGSPACE(m) <= 0) { + sc->sc_mc = m = m->m_next; + if (m == NULL) { + printf("ppp%d: too few input mbufs!\n", sc->sc_if.if_unit); + goto flush; + } + m->m_len = 0; + m->m_data = M_DATASTART(m); + sc->sc_mp = mtod(m, char *); + } + + ++m->m_len; + *sc->sc_mp++ = c; + sc->sc_fcs = PPP_FCS(sc->sc_fcs, c); + return; + + flush: + sc->sc_if.if_ierrors++; + sc->sc_flags |= SC_FLUSH; +} + +/* + * Process an ioctl request to interface. + */ +pppioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + struct proc *p = curproc; /* XXX */ + register struct ppp_softc *sc = &ppp_softc[ifp->if_unit]; + register struct ifaddr *ifa = (struct ifaddr *)data; + register struct ifreq *ifr = (struct ifreq *)data; + int s = splimp(), error = 0; + + + switch (cmd) { + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_RUNNING) == 0) + ifp->if_flags &= ~IFF_UP; + break; + + case SIOCSIFADDR: + if (ifa->ifa_addr->sa_family != AF_INET) + error = EAFNOSUPPORT; + break; + + case SIOCSIFDSTADDR: + if (ifa->ifa_addr->sa_family != AF_INET) + error = EAFNOSUPPORT; + break; + + case SIOCSIFMTU: + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + sc->sc_if.if_mtu = ifr->ifr_mtu; + break; + + case SIOCGIFMTU: + ifr->ifr_mtu = sc->sc_if.if_mtu; + break; + + default: + error = EINVAL; + } + splx(s); + return (error); +} + +#define MAX_DUMP_BYTES 128 + +static void +pppdumpm(m0, pktlen) + struct mbuf *m0; + int pktlen; +{ + char buf[2*MAX_DUMP_BYTES+4]; + char *bp = buf; + struct mbuf *m; + static char digits[] = "0123456789abcdef"; + + for (m = m0; m && pktlen; m = m->m_next) { + int l = m->m_len; + u_char *rptr = (u_char *)m->m_data; + + if (pktlen > 0) { + l = min(l, pktlen); + pktlen -= l; + } + while (l--) { + if (bp > buf + sizeof(buf) - 4) + goto done; + *bp++ = digits[*rptr >> 4]; /* convert byte to ascii hex */ + *bp++ = digits[*rptr++ & 0xf]; + } + + if (m->m_next) { + if (bp > buf + sizeof(buf) - 3) + goto done; + *bp++ = '|'; + } + } +done: + if (m && pktlen) + *bp++ = '>'; + *bp = 0; + printf("%s\n", buf); +} + +static void +pppdumpb(b, l) + u_char *b; + int l; +{ + char buf[2*MAX_DUMP_BYTES+4]; + char *bp = buf; + static char digits[] = "0123456789abcdef"; + + while (l--) { + *bp++ = digits[*b >> 4]; /* convert byte to ascii hex */ + *bp++ = digits[*b++ & 0xf]; + if (bp >= buf + sizeof(buf) - 2) { + *bp++ = '>'; + break; + } + } + + *bp = 0; + printf("%s\n", buf); +} + + +#endif /* NPPP > 0 */ diff --git a/sys/net/if_ppp.h b/sys/net/if_ppp.h new file mode 100644 index 000000000000..adc3fd52d3fe --- /dev/null +++ b/sys/net/if_ppp.h @@ -0,0 +1,119 @@ +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Modified by Paul Mackerras (paulus@cs.anu.edu.au) + * Added PPP_MRU, sc_outm, sc_fastq, sc_bpf. + * + * $Id: if_ppp.h,v 1.2 1993/08/31 05:40:38 rgrimes Exp $ + * From: if_ppp.h,v 1.4 1993/08/29 11:22:37 paulus Exp $ + */ + +/* + * Standard PPP header. + */ +struct ppp_header { + u_char ph_address; /* Address Field */ + u_char ph_control; /* Control Field */ + u_short ph_protocol; /* Protocol Field */ +}; + +#define PPP_HEADER_LEN 4 /* octets, must == sizeof(struct ppp_header) */ +#define PPP_FCS_LEN 2 /* octets for FCS */ + +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol types. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_XNS 0x25 /* Xerox NS */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ + +/* + * Important FCS values. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#define PPP_MRU 1500 /* Default MRU (max receive unit) */ +#define PPP_MAXMRU 65000 /* Largest MRU we allow */ +#define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */ + +struct ppp_softc { + struct ifnet sc_if; /* network-visible interface */ + u_int sc_flags; /* see below */ + struct tty *sc_ttyp; /* pointer to tty structure */ + struct mbuf *sc_outm; /* mbuf chain being output currently */ + struct mbuf *sc_m; /* pointer to input mbuf chain */ + struct mbuf *sc_mc; /* pointer to current input mbuf */ + char *sc_mp; /* pointer to next char in input mbuf */ + short sc_ilen; /* length of input-packet-so-far */ + u_short sc_fcs; /* FCS so far (input) */ + u_short sc_outfcs; /* FCS so far for output packet */ + short sc_mru; /* max receive unit */ + u_long sc_asyncmap; /* async control character map */ + u_long sc_rasyncmap; /* receive async control char map */ + struct ifqueue sc_inq; /* TTY side input queue */ + struct ifqueue sc_fastq; /* IP interactive output packet queue */ +#ifdef VJC + struct slcompress sc_comp; /* vjc control buffer */ +#endif + u_int sc_bytessent; + u_int sc_bytesrcvd; + caddr_t sc_bpf; +}; + +/* flags */ +#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */ +#define SC_COMP_AC 0x00000002 /* header compression (output) */ +#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */ +#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */ +#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */ +#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */ +#define SC_MASK 0x0000ffff /* bits that user can change */ + +/* state bits */ +#define SC_ESCAPED 0x00010000 /* saw a PPP_ESCAPE */ +#define SC_FLUSH 0x00020000 /* flush input until next PPP_FLAG */ + +#define t_sc T_LINEP + +/* this stuff doesn't belong here... */ +#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ +#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ +#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */ +#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */ +#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ +#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */ +#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */ +#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */ +#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */ + +/* old copies of PPP may have defined this */ +#if !defined(ifr_mtu) +#define ifr_mtu ifr_metric +#endif + diff --git a/sys/net/if_sl.c b/sys/net/if_sl.c new file mode 100644 index 000000000000..3b02c15e768a --- /dev/null +++ b/sys/net/if_sl.c @@ -0,0 +1,828 @@ +/* + * Copyright (c) 1987, 1989 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. + * + * from: @(#)if_sl.c 7.22 (Berkeley) 4/20/91 + * $Id: if_sl.c,v 1.4 1993/10/16 17:43:20 rgrimes Exp $ + */ + +/* + * Serial Line interface + * + * 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. + */ + +/* $Header: /a/cvs/386BSD/src/sys/net/if_sl.c,v 1.4 1993/10/16 17:43:20 rgrimes Exp $ */ +/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ + +#include "sl.h" +#if NSL > 0 + +#include "param.h" +#include "systm.h" +#include "proc.h" +#include "mbuf.h" +#include "buf.h" +#include "dkstat.h" +#include "socket.h" +#include "ioctl.h" +#include "file.h" +#include "tty.h" +#include "kernel.h" +#include "conf.h" + +#include "if.h" +#include "if_types.h" +#include "netisr.h" +#include "route.h" +#if INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#else +Huh? Slip without inet? +#endif + +#include "machine/mtpr.h" + +#include "slcompress.h" +#include "if_slvar.h" +#include "slip.h" + +#include "bpfilter.h" +#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 296 +#define SLIP_HIWAT roundup(50,CBSIZE) + +/* + * SLIP ABORT ESCAPE MECHANISM: + * (inspired by HAYES modem escape arrangement) + * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape } + * signals a "soft" exit from slip mode by usermode process + */ + +#ifdef ABT_ESC +#undef ABT_ESC +#define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/ +#define ABT_WAIT 1 /* in seconds - idle before an escape & after */ +#define ABT_RECYCLE (5*2+2) /* in seconds - time window processing abort */ +#define ABT_SOFT 3 /* count of escapes */ +#endif /* ABT_ESC */ + +/* + * The following disgusting hack gets around the problem that IP TOS + * can't be set yet. We want to put "interactive" traffic on a high + * priority queue. To decide if traffic is interactive, we check that + * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. + */ +static u_short interactive_ports[8] = { + 0, 513, 0, 0, + 0, 21, 0, 23, +}; +#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) + +struct sl_softc sl_softc[NSL]; + +#define FRAME_END 0xc0 /* Frame End */ +#define FRAME_ESCAPE 0xdb /* Frame Esc */ +#define TRANS_FRAME_END 0xdc /* transposed frame end */ +#define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */ + +#define t_sc T_LINEP + +int sloutput(), slioctl(), ttrstrt(); +extern struct timeval time; + +/* + * Called from boot code to establish sl interfaces. + */ +slattach() +{ + register struct sl_softc *sc; + register int i = 0; + + for (sc = sl_softc; i < NSL; sc++) { + sc->sc_if.if_name = "sl"; + sc->sc_if.if_unit = i++; + sc->sc_if.if_mtu = SLMTU; + sc->sc_if.if_flags = IFF_POINTOPOINT; + sc->sc_if.if_type = IFT_SLIP; + sc->sc_if.if_ioctl = slioctl; + sc->sc_if.if_output = sloutput; + 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 +slinit(sc) + register struct sl_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 - sl_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); + return (1); +} + +/* + * Line specific open routine. + * Attach the given tty to the first available sl unit. + */ +/* ARGSUSED */ +slopen(dev, tp) + dev_t dev; + register struct tty *tp; +{ + struct proc *p = curproc; /* XXX */ + register struct sl_softc *sc; + register int nsl; + int error; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + + if (tp->t_line == SLIPDISC) + return (0); + + for (nsl = NSL, sc = sl_softc; --nsl >= 0; sc++) + if (sc->sc_ttyp == NULL) { + if (slinit(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); + return (0); + } + return (ENXIO); +} + +/* + * Line specific close routine. + * Detach the tty from the sl unit. + * Mimics part of ttyclose(). + */ +slclose(tp) + struct tty *tp; +{ + register struct sl_softc *sc; + int s; + + ttywflush(tp); + s = splimp(); /* actually, max(spltty, splnet) */ + tp->t_line = 0; + sc = (struct sl_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)); + sc->sc_ep = 0; + sc->sc_mp = 0; + sc->sc_buf = 0; + } + splx(s); +} + +/* + * Line specific (tty) ioctl routine. + * Provide a way to get the sl unit number. + */ +/* ARGSUSED */ +sltioctl(tp, cmd, data, flag) + struct tty *tp; + caddr_t data; +{ + struct sl_softc *sc = (struct sl_softc *)tp->t_sc; + int s; + + switch (cmd) { + case SLIOCGUNIT: + *(int *)data = sc->sc_if.if_unit; + break; + + case SLIOCGFLAGS: + *(int *)data = sc->sc_flags; + break; + + case SLIOCSFLAGS: +#define SC_MASK 0xffff + s = splimp(); + sc->sc_flags = + (sc->sc_flags &~ SC_MASK) | ((*(int *)data) & SC_MASK); + splx(s); + break; + + default: + return (-1); + } + return (0); +} + +/* + * Queue a packet. Start transmission if not active. + */ +sloutput(ifp, m, dst) + struct ifnet *ifp; + register struct mbuf *m; + struct sockaddr *dst; +{ + register struct sl_softc *sc = &sl_softc[ifp->if_unit]; + register struct ip *ip; + register struct ifqueue *ifq; + int s; + + /* + * `Cannot happen' (see slioctl). Someday we will extend + * the line protocol to support other address families. + */ + if (dst->sa_family != AF_INET) { + printf("sl%d: af%d not supported\n", sc->sc_if.if_unit, + dst->sa_family); + m_freem(m); + return (EAFNOSUPPORT); + } + + 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 (ENETDOWN); + } + ifq = &sc->sc_if.if_snd; + if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) { + u_short srcport = ntohs(((short *)ip)[ip->ip_hl << 1]); + u_short dstport = ntohs(((short *)ip)[(ip->ip_hl << 1) + 1]); + + if (INTERACTIVE(srcport) || INTERACTIVE(dstport)) { + ifq = &sc->sc_fastq; + } + + } else if (sc->sc_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) { + m_freem(m); + return (0); + } + s = splimp(); + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + m_freem(m); + splx(s); + sc->sc_if.if_oerrors++; + return (ENOBUFS); + } + IF_ENQUEUE(ifq, m); + sc->sc_if.if_lastchange = time; + if (RB_LEN(&sc->sc_ttyp->t_out) == 0) + slstart(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. + */ +slstart(tp) + register struct tty *tp; +{ + register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; + register struct mbuf *m; + register u_char *cp; + register struct ip *ip; + int s; + struct mbuf *m2; +#if NBPFILTER > 0 + u_char bpfbuf[SLMTU + SLIP_HDRLEN]; + register int len; +#endif + + for (;;) { + /* + * Call output process whether or not there is any output. + * We are being called in lieu of ttstart and must do what + * it would. + */ + (*tp->t_oproc)(tp); + if (RB_LEN(&tp->t_out) > SLIP_HIWAT) + return; + + /* + * This happens briefly when the line shuts down. + */ + if (sc == NULL) + return; + + /* + * Do not remove the packet from the IP queue if it + * doesn't look like the packet will fit into the + * current COM output queue, with a packet full of + * escapes this could be as bad as SLMTU*2. The value + * of RBSZ in tty.h also has to be upped to be at least + * SLMTU*2. + */ + if (min(RBSZ, 4 * SLMTU + 4) - RB_LEN(&tp->t_out) < 2 * SLMTU + 2) + return; + + /* + * Get a packet and send it to the interface. + */ + s = splimp(); + IF_DEQUEUE(&sc->sc_fastq, m); + if (m == NULL) + IF_DEQUEUE(&sc->sc_if.if_snd, m); + splx(s); + if (m == NULL) + return; + /* + * We do the header compression here rather than in sl_output + * because the packets will be out of order if we are using TOS + * queueing, and the connection id compression will get messed + * up 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); + } +#endif + if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) { + if (sc->sc_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; + + /* + * The extra FRAME_END will start up a new packet, and thus + * will flush any accumulated garbage. We do this whenever + * the line may have been idle for some time. + */ + if (RB_LEN(&tp->t_out) == 0) { + ++sc->sc_bytessent; + (void) putc(FRAME_END, &tp->t_out); + } + + while (m) { + register u_char *ep; + + cp = mtod(m, u_char *); ep = cp + m->m_len; + while (cp < ep) { + /* + * Find out how many bytes in the string we can + * handle without doing something special. + */ + register u_char *bp = cp; + + while (cp < ep) { + switch (*cp++) { + case FRAME_ESCAPE: + case FRAME_END: + --cp; + goto out; + } + } + out: + if (cp > bp) { + /* + * Put the non-special bytes + * into the tty output queue. + */ + sc->sc_bytessent += rb_write( + &tp->t_out, + (char *) bp, + cp - bp); + } + /* + * If there are characters left in the mbuf, + * the first one must be special.. + * Put it out in a different form. + */ + if (cp < ep) { + if (putc(FRAME_ESCAPE, &tp->t_out)) + break; + if (putc(*cp++ == FRAME_ESCAPE ? + TRANS_FRAME_ESCAPE : TRANS_FRAME_END, + &tp->t_out)) { + (void) unputc(&tp->t_out); + break; + } + sc->sc_bytessent += 2; + } + } + MFREE(m, m2); + m = m2; + } + + if (putc(FRAME_END, &tp->t_out)) { + /* + * 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_out); + (void) putc(FRAME_END, &tp->t_out); + sc->sc_if.if_collisions++; + } else { + ++sc->sc_bytessent; + sc->sc_if.if_opackets++; + } + sc->sc_if.if_obytes = sc->sc_bytessent; + } +} + +/* + * Copy data buffer to mbuf chain; add ifnet pointer. + */ +static struct mbuf * +sl_btom(sc, len) + register struct sl_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)((int)sc->sc_buf &~ MCLOFSET); + } 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. + */ +slinput(c, tp) + register int c; + register struct tty *tp; +{ + register struct sl_softc *sc; + register struct mbuf *m; + register int len; + int s; +#if NBPFILTER > 0 + u_char chdr[CHDR_LEN]; +#endif + tk_nin++; + sc = (struct sl_softc *)tp->t_sc; + if (sc == NULL) + return; + if ((c & TTY_ERRORMASK) || (((tp->t_state & TS_CARR_ON) == 0) + && ((tp->t_cflag & CLOCAL) == 0))) { + /* XXX */ + sc->sc_flags |= SC_ERROR; + return; + } + + ++sc->sc_bytesrcvd; + ++sc->sc_if.if_ibytes; + +#ifdef ABT_ESC + if (sc->sc_flags & SC_ABORT) { + /* if we see an abort after "idle" time, count it */ + if (c == ABT_ESC && time.tv_sec >= sc->sc_lasttime + ABT_WAIT) { + sc->sc_abortcount++; + /* record when the first abort escape arrived */ + if (sc->sc_abortcount == 1) + sc->sc_starttime = time.tv_sec; + } + /* + * if we have an abort, see that we have not run out of time, + * or that we have an "idle" time after the complete escape + * sequence + */ + if (sc->sc_abortcount) { + if (time.tv_sec >= sc->sc_starttime + ABT_RECYCLE) + sc->sc_abortcount = 0; + if (sc->sc_abortcount >= ABT_SOFT && + time.tv_sec >= sc->sc_lasttime + ABT_WAIT) { + slclose(tp); + return; + } + } + sc->sc_lasttime = time.tv_sec; + } +#endif + + switch (c) { + + case TRANS_FRAME_ESCAPE: + if (sc->sc_escape) + c = FRAME_ESCAPE; + break; + + case TRANS_FRAME_END: + if (sc->sc_escape) + c = FRAME_END; + break; + + case FRAME_ESCAPE: + sc->sc_escape = 1; + return; + + case FRAME_END: + if (sc->sc_flags & SC_ERROR) { + sc->sc_flags &= ~SC_ERROR; + goto newpack; + } + len = sc->sc_mp - sc->sc_buf; + if (len < 3) + /* less than min length packet - ignore */ + goto newpack; + +#if NBPFILTER > 0 + if (sc->sc_bpf) + /* + * Save the compressed header, so we can + * tack it on later. Note that we just + * 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_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_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_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 = sl_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); + 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; + } + sc->sc_flags |= SC_ERROR; +error: + sc->sc_if.if_ierrors++; +newpack: + sc->sc_mp = sc->sc_buf = sc->sc_ep - SLMAX; + sc->sc_escape = 0; +} + +/* + * Process an ioctl request. + */ +slioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + 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; + + default: + error = EINVAL; + } + splx(s); + return (error); +} +#endif diff --git a/sys/net/if_slvar.h b/sys/net/if_slvar.h new file mode 100644 index 000000000000..0121927411db --- /dev/null +++ b/sys/net/if_slvar.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1991 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. + * + * from: @(#)if_slvar.h 7.7 (Berkeley) 5/7/91 + * $Id: if_slvar.h,v 1.2 1993/10/16 17:43:22 rgrimes Exp $ + */ + +/* + * Definitions for SLIP interface data structures + * + * (This exists so programs like slstats can get at the definition + * of sl_softc.) + */ +struct sl_softc { + struct ifnet sc_if; /* network-visible interface */ + struct ifqueue sc_fastq; /* interactive output queue */ + struct tty *sc_ttyp; /* pointer to tty structure */ + u_char *sc_mp; /* pointer to next available buf char */ + u_char *sc_ep; /* pointer to last available buf char */ + u_char *sc_buf; /* input buffer */ + u_int sc_flags; /* see below */ + u_int sc_escape; /* =1 if last char input was FRAME_ESCAPE */ + u_int sc_bytessent; + u_int sc_bytesrcvd; + long sc_lasttime; /* last time a char arrived */ + long sc_starttime; /* last time a char arrived */ + long sc_abortcount; /* number of abort esacpe chars */ +#ifdef INET /* XXX */ + struct slcompress sc_comp; /* tcp compression data */ +#endif + caddr_t sc_bpf; +}; + +/* visible flags */ +#define SC_COMPRESS 0x0002 /* compress TCP traffic */ +#define SC_NOICMP 0x0004 /* supress ICMP traffic */ +#define SC_AUTOCOMP 0x0008 /* auto-enable TCP compression */ +/* internal flags (should be separate) */ +#define SC_ERROR 0x08000 /* had an input error - 30 Aug 92*/ +#define SC_ABORT 0x10000 /* have been sent an abort request */ + +/* this stuff doesn't belong here... */ +#define SLIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */ +#define SLIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */ +#define SLIOCGUNIT _IOR('t', 88, int) /* get slip unit number */ diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c new file mode 100644 index 000000000000..11eaa7f24cd5 --- /dev/null +++ b/sys/net/if_tun.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1988, Julian Onions. + * + * This source may be freely distributed, however I would be interested + * in any changes that are made. + * + * from: Revision 1.13 88/07/11 08:28:51 jpo + * from: 90/02/06 15:03 - Fixed a bug in where + * TIOCGPGRP and TIOCSPGRP were mixed up + * $Id: if_tun.c,v 1.2 1993/10/16 17:43:24 rgrimes Exp $ + */ + +/* if_tun.c - tunnel interface module & driver */ + +/* UNFINISHED CONVERSION TO 386BSD -wfj */ + +#include "tun.h" +#if NTUN > 0 + +/* + * Tunnel driver. + * + * This driver takes packets off the IP i/f and hands them up to a + * user process to have it's wicked way with. This driver has it's + * roots in a similar driver written by Phil Cockcroft (formerly) at + * UCL. This driver is based much more on read/write/select mode of + * operation though. + * + * Julian Onions <jpo@cs.nott.ac.uk> + * Nottingham University 1987. + */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "buf.h" +#include "protosw.h" +#include "socket.h" +#include "ioctl.h" +#include "errno.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/netisr.h" +#include "net/route.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#define TUNDEBUG if (tundebug) printf + +int tundebug = 0; +struct tunctl +{ + u_short tun_flags; /* misc flags */ + struct ifnet tun_if; /* the interface */ + int tun_pgrp; /* the process group - if any */ + struct proc *tun_rsel; /* read select */ + struct proc *tun_wsel; /* write select (not used) */ +} tunctl[NTUN]; + +extern int ifqmaxlen; + +int tunoutput (), tunioctl (), tuninit (); + +/* tunnel open - must be superuser & the device must be configured in */ +tunopen (dev, flag) +dev_t dev; +{ + register int unit; + struct tunctl *tp; + register struct ifnet *ifp; + + if (!suser ()) + return EACCES; + if ((unit = minor (dev)) >= NTUN) + return (ENXIO); + tp = &tunctl[unit]; + if (tp->tun_flags & TUN_OPEN) + return ENXIO; + if ((tp->tun_flags & TUN_INITED) == 0) { + tp->tun_flags = TUN_INITED; + tunattach (unit); + } + ifp = &tp->tun_if; + tp->tun_flags |= TUN_OPEN; + TUNDEBUG ("%s%d: open\n", ifp->if_name, ifp->if_unit); + return (0); +} + +/* tunclose - close the device - mark i/f down & delete routing info */ +tunclose (dev, flag) +dev_t dev; +{ + int s; + int rcoll; + register int unit = minor (dev); + register struct tunctl *tp = &tunctl[unit]; + register struct ifnet *ifp = &tp->tun_if; + register struct mbuf *m; + + rcoll = tp->tun_flags & TUN_RCOLL; + tp->tun_flags &= TUN_INITED; + + /* + * junk all pending output + */ + do { + s = splimp (); + IF_DEQUEUE (&ifp->if_snd, m); + splx (s); + if (m) /* actually - m_freem checks this - but what the hell */ + m_freem (m); + } while (m != 0); + if (ifp->if_flags & IFF_UP) { + s = splimp (); + if_down (ifp); + if (ifp->if_flags & IFF_RUNNING) + rtinit (ifp->if_addrlist, (int)SIOCDELRT, RTF_HOST); + splx (s); + } + tp -> tun_pgrp = 0; + if (tp -> tun_rsel) + selwakeup (tp->tun_rsel, rcoll); + + tp -> tun_rsel = tp -> tun_wsel = (struct proc *)0; + + TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit); + return (0); +} + +/* + * attach an interface N.B. argument is not same as other drivers + */ +int +tunattach (unit) +int unit; +{ + register struct ifnet *ifp = &tunctl[unit].tun_if; + register struct sockaddr_in *sin; + + ifp->if_unit = unit; + ifp->if_name = "tun"; + ifp->if_mtu = TUNMTU; + ifp->if_ioctl = tunioctl; + ifp->if_output = tunoutput; + ifp->if_init = tuninit; +#ifndef BSD4_3 + sin = (struct sockaddr_in *) & ifp->if_addr; + sin->sin_family = AF_INET; +#endif + ifp->if_flags = IFF_POINTOPOINT; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_collisions = ifp->if_ierrors = ifp->if_oerrors = 0; + ifp->if_ipackets = ifp->if_opackets = 0; + if_attach (ifp); + TUNDEBUG ("%s%d: tunattach\n", ifp->if_name, ifp->if_unit); + return 0; +} + +int +tuninit (unit) +int unit; +{ + register struct tunctl *tp = &tunctl[unit]; + register struct ifnet *ifp = &tp->tun_if; +#ifndef BSD4_3 + register struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) & ifp->if_addr; + if (sin->sin_addr.s_addr == 0) /* if address still unknown */ + return; + if_rtinit (ifp, RTF_UP); +#endif + ifp->if_flags |= IFF_UP | IFF_RUNNING; + tp->tun_flags |= TUN_IASET; + TUNDEBUG ("%s%d: tuninit\n", ifp->if_name, ifp->if_unit); + return 0; +} + +/* + * Process an ioctl request. + * The problem here is 4.2 pass a struct ifreq * to this routine, + * sun only passes a struct sockaddr * since 3.2 at least. This is + * rather upsetting! Also, sun doesn't pass the SIOCDSTADDR ioctl through + * so we can't detect this being set directly. This is the reason for + * tuncheckready. + * Under 4.3 and SunOs 4.0 we now get the SIOCSIFDSTADDR ioctl, and we get a + * struct in_ifaddr * for data. (tte) + */ + +#if !defined(BSD4_3) && defined(sun) + +/* + * workaround for not being able to detect DSTADDR being set. + */ + +tuncheckready (ifp) +struct ifnet *ifp; +{ + struct tunctl *tp = &tunctl[ifp->if_unit]; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) &ifp->if_dstaddr; + if (sin->sin_addr.s_addr == 0) + return 0; + tp -> tun_flags |= TUN_DSTADDR; + return 1; +} +#else +#define tuncheckready(dummy) 1 +#endif /* !defined(BSD4_3) && defined(sun) */ + +int +tunioctl (ifp, cmd, data) +register struct ifnet *ifp; +int cmd; +caddr_t data; +{ +#ifndef BSD4_3 +#ifdef sun + struct sockaddr_in *sin = (struct sockaddr_in *) data; +#else + struct sockaddr_in *sin; + struct ifreq *ifr = (struct ifreq *) data; +#endif +#endif /* BSD4_3 */ + + int s = splimp (), error = 0; + struct tunctl *tp = &tunctl[ifp->if_unit]; + + switch (cmd) + { + case SIOCSIFADDR: +#ifndef BSD4_3 + if (ifp->if_flags & IFF_RUNNING) + if_rtinit (ifp, -1); /* delete previous route */ +#ifndef sun + sin = (struct sockaddr_in *)&ifr -> ifr_addr; +#endif + ifp->if_addr = *((struct sockaddr *) sin); + ifp->if_net = in_netof (sin->sin_addr); + ifp->if_host[0] = in_lnaof (sin->sin_addr); +#endif + tuninit (ifp->if_unit); + break; + case SIOCSIFDSTADDR: + tp->tun_flags |= TUN_DSTADDR; +#ifndef BSD4_3 +#ifndef sun + sin = (struct sockaddr_in *)&ifr -> ifr_addr; +#endif + ifp->if_dstaddr = *((struct sockaddr *)sin); +#endif BSD4_3 + TUNDEBUG ("%s%d: destination addres set\n", ifp->if_name, + ifp -> if_unit); + break; + default: + error = EINVAL; + } + splx (s); + return (error); +} + +/* + * tunoutput - queue packets from higher level ready to put out. + */ +tunoutput (ifp, m0, dst) +struct ifnet *ifp; +struct mbuf *m0; +struct sockaddr *dst; +{ + struct tunctl *tp; + struct proc *p; + int s; + + TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit); + tp = &tunctl[ifp->if_unit]; + if ((tp->tun_flags & TUN_READY) != TUN_READY) { + if(tuncheckready(ifp) == 0) { + TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, + ifp->if_unit, tp->tun_flags); + m_freem (m0); + return EHOSTDOWN; + } + } + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + s = splimp (); + if (IF_QFULL (&ifp->if_snd)) + { + IF_DROP (&ifp->if_snd); + m_freem (m0); + splx (s); + ifp->if_collisions++; + return (ENOBUFS); + } + IF_ENQUEUE (&ifp->if_snd, m0); + splx (s); + ifp->if_opackets++; + break; +#endif + default: + m_freem (m0); + return EAFNOSUPPORT; + } + + if (tp->tun_flags & TUN_RWAIT) { + tp->tun_flags &= ~TUN_RWAIT; + wakeup ((caddr_t) tp); + } + if (tp->tun_flags & TUN_ASYNC && tp -> tun_pgrp != 0) { + if (tp->tun_pgrp > 0) + gsignal (tp->tun_pgrp, SIGIO); + else if ((p = pfind (-tp->tun_pgrp)) != 0) + psignal (p, SIGIO); + } + if (tp->tun_rsel) { + selwakeup (tp->tun_rsel, tp->tun_flags & TUN_RCOLL); + tp->tun_flags &= ~TUN_RCOLL; + tp->tun_rsel = (struct proc *) 0; + } + return 0; +} + +/* + * the cdevsw interface is now pretty minimal. + */ + +tuncioctl (dev, cmd, data, flag) +dev_t dev; +caddr_t data; +{ + int unit = minor(dev); + struct tunctl *tp = &tunctl[unit]; + int s; + + switch (cmd) { + case TUNSDEBUG: + tundebug = *(int *)data; + break; + + case TUNGDEBUG: + *(int *)data = tundebug; + break; + + case FIONBIO: + if (*(int *)data) + tp->tun_flags |= TUN_NBIO; + else + tp->tun_flags &= ~TUN_NBIO; + break; + + case FIOASYNC: + if (*(int *)data) + tp->tun_flags |= TUN_ASYNC; + else + tp->tun_flags &= ~TUN_ASYNC; + break; + + case FIONREAD: + s = splimp (); + if (tp->tun_if.if_snd.ifq_head) + *(int *)data = tp->tun_if.if_snd.ifq_head->m_len; + else *(int *)data = 0; + splx (s); + break; + + case TIOCSPGRP: + tp->tun_pgrp = *(int *)data; + break; + + case TIOCGPGRP: + *(int *)data = tp->tun_pgrp; + break; + + default: + return (ENOTTY); + } + return (0); +} + +/* + * The cdevsw read interface - reads a packet at a time, or at least as much + * of a packet as can be read. + */ +tunread (dev, uio) +dev_t dev; +struct uio *uio; +{ + register struct ifnet *ifp; + register struct mbuf *m, *m0; + int unit = minor (dev); + int len, s; + int error = 0; + struct tunctl *tp; + + tp = &tunctl[unit]; + ifp = &tp->tun_if; + TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit); + if ((tp->tun_flags & TUN_READY) != TUN_READY) { + if(tuncheckready(ifp) == 0) { + TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name, + ifp->if_unit, tp->tun_flags); + return EHOSTDOWN; + } + } + + tp->tun_flags &= ~TUN_RWAIT; + + s = splimp (); + do { + IF_DEQUEUE (&ifp->if_snd, m0); + if (m0 == 0) { + if (tp -> tun_flags & TUN_NBIO) { + splx (s); + return EWOULDBLOCK; + } + tp->tun_flags |= TUN_RWAIT; + sleep ((caddr_t) tp, PZERO + 1); + } + } while (m0 == 0); + splx (s); + + while (m0 && uio->uio_resid > 0 && error == 0) { + len = MIN (uio->uio_resid, m0->m_len); + if (len == 0) + break; + error = uiomove (mtod (m0, caddr_t), len, + UIO_READ, uio); + MFREE (m0, m); + m0 = m; + } + + if (m0 != 0) { + TUNDEBUG ("Dropping mbuf\n"); + m_freem (m0); + } + return error; +} + +/* + * the cdevsw write interface - an atomic write is a packet - or else! + */ + +tunwrite (dev, uio) +int dev; +struct uio *uio; +{ + int error = 0; + int unit = minor (dev); + struct mbuf *top, **mp, *m; + struct ifnet *ifp = &(tunctl[unit].tun_if); + int s; + + TUNDEBUG ("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit); + + if (uio->uio_resid < 0 || uio->uio_resid > TUNMTU) { + TUNDEBUG ("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit, + uio->uio_resid); + return EIO; + } + top = 0; + mp = ⊤ + while (error == 0 && uio->uio_resid > 0) { + MGET (m, M_DONTWAIT, MT_DATA); + if (m == 0) { + error = ENOBUFS; + break; + } + m->m_len = MIN (MLEN, uio->uio_resid); + error = uiomove (mtod (m, caddr_t), m->m_len, UIO_WRITE, uio); + *mp = m; + mp = &m->m_next; + } + if (error) { + if (top) + m_freem (top); + return error; + } + +#ifdef BSD4_3 + /* + * Place interface pointer before the data + * for the receiving protocol. + */ + if (top->m_off <= MMAXOFF && + top->m_off >= MMINOFF + sizeof(struct ifnet *)) { + top->m_off -= sizeof(struct ifnet *); + top->m_len += sizeof(struct ifnet *); + } else { + MGET(m, M_DONTWAIT, MT_HEADER); + if (m == (struct mbuf *)0) + return (ENOBUFS); + m->m_len = sizeof(struct ifnet *); + m->m_next = top; + top = m; + } + *(mtod(top, struct ifnet **)) = ifp; +#endif /* BSD4_3 */ + + s = splimp (); + if (IF_QFULL (&ipintrq)) { + IF_DROP (&ipintrq); + splx (s); + ifp->if_collisions++; + m_freem (top); + return ENOBUFS; + } + IF_ENQUEUE (&ipintrq, top); + splx (s); + ifp->if_ipackets++; + schednetisr (NETISR_IP); + return error; +} + +/* + * tunselect - the select interface, this is only useful on reads really. + * The write detect always returns true, write never blocks anyway, it either + * accepts the packet or drops it. + */ +tunselect (dev, rw) +dev_t dev; +int rw; +{ + int unit = minor (dev); + register struct tunctl *tp = &tunctl[unit]; + struct ifnet *ifp = &tp->tun_if; + int s = splimp (); + + TUNDEBUG ("%s%d: tunselect\n", ifp->if_name, ifp->if_unit); + switch (rw) { + case FREAD: + if (ifp->if_snd.ifq_len > 0) { + splx (s); + TUNDEBUG ("%s%d: tunselect q=%d\n", ifp->if_name, + ifp->if_unit, ifp->if_snd.ifq_len); + return 1; + } + if (tp->tun_rsel && tp->tun_rsel->p_wchan == + (caddr_t) & selwait) + tp->tun_flags |= TUN_RCOLL; + else + tp->tun_rsel = u.u_procp; + break; + case FWRITE: + splx (s); + return 1; + } + splx (s); + TUNDEBUG ("%s%d: tunselect waiting\n", ifp->if_name, ifp->if_unit); + return 0; +} +#endif NTUN diff --git a/sys/net/if_types.h b/sys/net/if_types.h new file mode 100644 index 000000000000..a6ee046052c0 --- /dev/null +++ b/sys/net/if_types.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1989 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. + * + * from: @(#)if_types.h 7.3 (Berkeley) 6/28/90 + * $Id: if_types.h,v 1.3 1993/10/16 17:43:25 rgrimes Exp $ + */ + + +/* interface types for benefit of parsing media address headers */ +#define IFT_OTHER 0x1 /* none of the following */ +#define IFT_1822 0x2 /* old-style arpanet imp */ +#define IFT_HDH1822 0x3 /* HDH arpanet imp */ +#define IFT_X25DDN 0x4 /* x25 to imp */ +#define IFT_X25 0x5 /* PDN X25 interface */ +#define IFT_ETHER 0x6 /* Ethernet I or II */ +#define IFT_ISO88023 0x7 /* CMSA CD */ +#define IFT_ISO88024 0x8 /* Token Bus */ +#define IFT_ISO88025 0x9 /* Token Ring */ +#define IFT_ISO88026 0xa /* MAN */ +#define IFT_STARLAN 0xb +#define IFT_P10 0xc /* Proteon 10MBit ring */ +#define IFT_P80 0xd /* Proteon 10MBit ring */ +#define IFT_HY 0xe /* Hyperchannel */ +#define IFT_FDDI 0xf +#define IFT_LAPB 0x10 +#define IFT_SDLC 0x11 +#define IFT_T1 0x12 +#define IFT_CEPT 0x13 +#define IFT_ISDNBASIC 0x14 +#define IFT_ISDNPRIMARY 0x15 +#define IFT_PTPSERIAL 0x16 +#define IFT_LOOP 0x18 /* loopback */ +#define IFT_EON 0x19 /* ISO over IP */ +#define IFT_XETHER 0x1a /* obsolete 3MB experimental ethernet */ +#define IFT_NSIP 0x1b /* XNS over IP */ +#define IFT_SLIP 0x1c /* IP over generic TTY */ +#define IFT_PPP 0x1d /* PPP over generic TTY */ diff --git a/sys/net/netisr.h b/sys/net/netisr.h new file mode 100644 index 000000000000..e2962cf77979 --- /dev/null +++ b/sys/net/netisr.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1980, 1986, 1989 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. + * + * from: @(#)netisr.h 7.8 (Berkeley) 5/7/91 + * $Id: netisr.h,v 1.2.2.1 1993/11/14 18:51:06 rgrimes Exp $ + */ + +/* + * The networking code runs off software interrupts. + * + * You can switch into the network by doing splnet() and return by splx(). + * The software interrupt level for the network is higher than the software + * level for the clock (so you can enter the network in routines called + * at timeout time). + */ +#if defined(vax) || defined(tahoe) +#define setsoftnet() mtpr(SIRR, 12) +#endif + +/* + * Each ``pup-level-1'' input queue has a bit in a ``netisr'' status + * word which is used to de-multiplex a single software + * interrupt used for scheduling the network code to calls + * on the lowest level routine of each protocol. + */ +#define NETISR_RAW 0 /* same as AF_UNSPEC */ +#define NETISR_IP 2 /* same as AF_INET */ +#define NETISR_IMP 3 /* same as AF_IMPLINK */ +#define NETISR_NS 6 /* same as AF_NS */ +#define NETISR_ISO 7 /* same as AF_ISO */ +#define NETISR_CCITT 10 /* same as AF_CCITT */ + +#define schednetisr(anisr) { netisr |= 1<<(anisr); setsoftnet(); } + +#ifdef i386 +/* XXX Temporary -- soon to vanish - wfj */ +#define NETISR_SCLK 11 /* softclock */ +#define NETISR_AST 12 /* ast -- resched */ + +#undef schednetisr +#define schednetisr(anisr) {\ + netisr |= 1<<(anisr); \ +} +#endif /* i386 */ + +#ifndef LOCORE +#ifdef KERNEL +extern int netisr; /* scheduling bits for network */ +#endif +#endif diff --git a/sys/net/ppp.h b/sys/net/ppp.h new file mode 100644 index 000000000000..c2d4a79c4baa --- /dev/null +++ b/sys/net/ppp.h @@ -0,0 +1,41 @@ +/* + * ppp.h - PPP global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * from: unknown + * $Id: ppp.h,v 1.2 1993/10/16 17:43:29 rgrimes Exp $ + */ + +#ifndef __PPP_H__ +#define __PPP_H__ + +#define NPPP 1 /* One PPP interface supported (per process) */ + +/* + * Data Link Layer header = Address, Control, Protocol. + */ +#define ALLSTATIONS 0xff /* All-Stations Address */ +#define UI 0x03 /* Unnumbered Information */ +#define LCP 0xc021 /* Link Control Protocol */ +#define IPCP 0x8021 /* IP Control Protocol */ +#define UPAP 0xc023 /* User/Password Authentication Protocol */ +#define CHAP 0xc223 /* Crytpographic Handshake Protocol */ +#define IP_VJ_COMP 0x002d /* VJ TCP compressed IP packet */ +#define DLLHEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) +#define MTU 1500 /* Default MTU */ + +#endif /* __PPP_H__ */ diff --git a/sys/net/radix.c b/sys/net/radix.c new file mode 100644 index 000000000000..40749968511d --- /dev/null +++ b/sys/net/radix.c @@ -0,0 +1,641 @@ +/* + * Copyright (c) 1988, 1989 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. + * + * from: @(#)radix.c 7.9 (Berkeley) 2/4/91 + * $Id: radix.c,v 1.3 1993/10/16 17:43:30 rgrimes Exp $ + */ + +/* + * Routines to build and maintain radix trees for routing lookups. + */ +#ifndef RNF_NORMAL +#include "param.h" +#include "systm.h" +#include "radix.h" +#include "malloc.h" +#define M_DONTWAIT M_NOWAIT +#endif +struct radix_node_head *mask_rnhead; +#define rn_maskhead mask_rnhead->rnh_treetop +struct radix_mask *rn_mkfreelist; +struct radix_node_head *radix_node_head; +#undef Bcmp +#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l)) +/* + * The data structure for the keys is a radix tree with one way + * branching removed. The index rn_b at an internal node n represents a bit + * position to be tested. The tree is arranged so that all descendants + * of a node n have keys whose bits all agree up to position rn_b - 1. + * (We say the index of n is rn_b.) + * + * There is at least one descendant which has a one bit at position rn_b, + * and at least one with a zero there. + * + * A route is determined by a pair of key and mask. We require that the + * bit-wise logical and of the key and mask to be the key. + * We define the index of a route to associated with the mask to be + * the first bit number in the mask where 0 occurs (with bit number 0 + * representing the highest order bit). + * + * We say a mask is normal if every bit is 0, past the index of the mask. + * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b, + * and m is a normal mask, then the route applies to every descendant of n. + * If the index(m) < rn_b, this implies the trailing last few bits of k + * before bit b are all 0, (and hence consequently true of every descendant + * of n), so the route applies to all descendants of the node as well. + * + * The present version of the code makes no use of normal routes, + * but similar logic shows that a non-normal mask m such that + * index(m) <= index(n) could potentially apply to many children of n. + * Thus, for each non-host route, we attach its mask to a list at an internal + * node as high in the tree as we can go. + */ + +struct radix_node * +rn_search(v, head) + struct radix_node *head; + register caddr_t v; +{ + register struct radix_node *x; + + for (x = head; x->rn_b >= 0;) { + if (x->rn_bmask & v[x->rn_off]) + x = x->rn_r; + else + x = x->rn_l; + } + return x; +}; + +struct radix_node * +rn_search_m(v, head, m) + struct radix_node *head; + register caddr_t v, m; +{ + register struct radix_node *x; + + for (x = head; x->rn_b >= 0;) { + if ((x->rn_bmask & m[x->rn_off]) && + (x->rn_bmask & v[x->rn_off])) + x = x->rn_r; + else + x = x->rn_l; + } + return x; +}; + + +static int gotOddMasks; +static char maskedKey[MAXKEYLEN]; + +struct radix_node * +rn_match(v, head) + struct radix_node *head; + caddr_t v; +{ + register struct radix_node *t = head, *x; + register caddr_t cp = v, cp2, cp3; + caddr_t cplim, mstart; + struct radix_node *saved_t; + int off = t->rn_off, vlen = *(u_char *)cp, matched_off; + + /* + * Open code rn_search(v, head) to avoid overhead of extra + * subroutine call. + */ + for (; t->rn_b >= 0; ) { + if (t->rn_bmask & cp[t->rn_off]) + t = t->rn_r; + else + t = t->rn_l; + } + /* + * See if we match exactly as a host destination + */ + cp += off; cp2 = t->rn_key + off; cplim = v + vlen; + for (; cp < cplim; cp++, cp2++) + if (*cp != *cp2) + goto on1; + /* + * This extra grot is in case we are explicitly asked + * to look up the default. Ugh! + */ + if ((t->rn_flags & RNF_ROOT) && t->rn_dupedkey) + t = t->rn_dupedkey; + return t; +on1: + matched_off = cp - v; + saved_t = t; + do { + if (t->rn_mask) { + /* + * Even if we don't match exactly as a hosts; + * we may match if the leaf we wound up at is + * a route to a net. + */ + cp3 = matched_off + t->rn_mask; + cp2 = matched_off + t->rn_key; + for (; cp < cplim; cp++) + if ((*cp2++ ^ *cp) & *cp3++) + break; + if (cp == cplim) + return t; + cp = matched_off + v; + } + } while (t = t->rn_dupedkey); + t = saved_t; + /* start searching up the tree */ + do { + register struct radix_mask *m; + t = t->rn_p; + if (m = t->rn_mklist) { + /* + * After doing measurements here, it may + * turn out to be faster to open code + * rn_search_m here instead of always + * copying and masking. + */ + off = min(t->rn_off, matched_off); + mstart = maskedKey + off; + do { + cp2 = mstart; + cp3 = m->rm_mask + off; + for (cp = v + off; cp < cplim;) + *cp2++ = *cp++ & *cp3++; + x = rn_search(maskedKey, t); + while (x && x->rn_mask != m->rm_mask) + x = x->rn_dupedkey; + if (x && + (Bcmp(mstart, x->rn_key + off, + vlen - off) == 0)) + return x; + } while (m = m->rm_mklist); + } + } while (t != head); + return 0; +}; + +#ifdef RN_DEBUG +int rn_nodenum; +struct radix_node *rn_clist; +int rn_saveinfo; +#endif + +struct radix_node * +rn_newpair(v, b, nodes) + caddr_t v; + struct radix_node nodes[2]; +{ + register struct radix_node *tt = nodes, *t = tt + 1; + t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7); + t->rn_l = tt; t->rn_off = b >> 3; + tt->rn_b = -1; tt->rn_key = v; tt->rn_p = t; + tt->rn_flags = t->rn_flags = RNF_ACTIVE; +#ifdef RN_DEBUG + tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; + tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; +#endif + return t; +} + +int rn_debug = 1; +struct radix_node * +rn_insert(v, head, dupentry, nodes) + caddr_t v; + struct radix_node *head; + int *dupentry; + struct radix_node nodes[2]; +{ + int head_off = head->rn_off, vlen = (int)*((u_char *)v); + register struct radix_node *t = rn_search(v, head); + register caddr_t cp = v + head_off; + register int b; + struct radix_node *tt; + /* + *find first bit at which v and t->rn_key differ + */ + { + register caddr_t cp2 = t->rn_key + head_off; + register int cmp_res; + caddr_t cplim = v + vlen; + + while (cp < cplim) + if (*cp2++ != *cp++) + goto on1; + *dupentry = 1; + return t; +on1: + *dupentry = 0; + cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; + for (b = (cp - v) << 3; cmp_res; b--) + cmp_res >>= 1; + } + { + register struct radix_node *p, *x = head; + cp = v; + do { + p = x; + if (cp[x->rn_off] & x->rn_bmask) + x = x->rn_r; + else x = x->rn_l; + } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */ +#ifdef RN_DEBUG + if (rn_debug) + printf("Going In:\n"), traverse(p); +#endif + t = rn_newpair(v, b, nodes); tt = t->rn_l; + if ((cp[p->rn_off] & p->rn_bmask) == 0) + p->rn_l = t; + else + p->rn_r = t; + x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */ + if ((cp[t->rn_off] & t->rn_bmask) == 0) { + t->rn_r = x; + } else { + t->rn_r = tt; t->rn_l = x; + } +#ifdef RN_DEBUG + if (rn_debug) + printf("Coming out:\n"), traverse(p); +#endif + } + return (tt); +} + +struct radix_node * +rn_addmask(netmask, search, skip) +caddr_t netmask; +{ + register struct radix_node *x; + register caddr_t cp, cplim; + register int b, mlen, j; + int maskduplicated; + + mlen = *(u_char *)netmask; + if (search) { + x = rn_search(netmask, rn_maskhead); + mlen = *(u_char *)netmask; + if (Bcmp(netmask, x->rn_key, mlen) == 0) + return (x); + } + R_Malloc(x, struct radix_node *, MAXKEYLEN + 2 * sizeof (*x)); + if (x == 0) + return (0); + Bzero(x, MAXKEYLEN + 2 * sizeof (*x)); + cp = (caddr_t)(x + 2); + Bcopy(netmask, cp, mlen); + netmask = cp; + x = rn_insert(netmask, rn_maskhead, &maskduplicated, x); + /* + * Calculate index of mask. + */ + cplim = netmask + mlen; + for (cp = netmask + skip; cp < cplim; cp++) + if (*(u_char *)cp != 0xff) + break; + b = (cp - netmask) << 3; + if (cp != cplim) { + if (*cp != 0) { + gotOddMasks = 1; + for (j = 0x80; j; b++, j >>= 1) + if ((j & *cp) == 0) + break; + } + } + x->rn_b = -1 - b; + return (x); +} + +struct radix_node * +rn_addroute(v, netmask, head, treenodes) +struct radix_node *head; + caddr_t netmask, v; + struct radix_node treenodes[2]; +{ + register int j; + register caddr_t cp; + register struct radix_node *t, *x, *tt; + short b = 0, b_leaf; + int vlen = *(u_char *)v, mlen, keyduplicated; + caddr_t cplim; unsigned char *maskp; + struct radix_mask *m, **mp; + struct radix_node *saved_tt; + + /* + * In dealing with non-contiguous masks, there may be + * many different routes which have the same mask. + * We will find it useful to have a unique pointer to + * the mask to speed avoiding duplicate references at + * nodes and possibly save time in calculating indices. + */ + if (netmask) { + x = rn_search(netmask, rn_maskhead); + mlen = *(u_char *)netmask; + if (Bcmp(netmask, x->rn_key, mlen) != 0) { + x = rn_addmask(netmask, 0, head->rn_off); + if (x == 0) + return (0); + } + netmask = x->rn_key; + b = -1 - x->rn_b; + } + /* + * Deal with duplicated keys: attach node to previous instance + */ + saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes); + if (keyduplicated) { + do { + if (tt->rn_mask == netmask) + return (0); + t = tt; + } while (tt = tt->rn_dupedkey); + /* + * If the mask is not duplicated, we wouldn't + * find it among possible duplicate key entries + * anyway, so the above test doesn't hurt. + * + * XXX: we really ought to sort the masks + * for a duplicated key the same way as in a masklist. + * It is an unfortunate pain having to relocate + * the head of the list. + */ + t->rn_dupedkey = tt = treenodes; +#ifdef RN_DEBUG + t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; + tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; +#endif + t = saved_tt; + tt->rn_key = (caddr_t) v; + tt->rn_b = -1; + tt->rn_flags = t->rn_flags & ~RNF_ROOT; + } + /* + * Put mask in tree. + */ + if (netmask) { + tt->rn_mask = netmask; + tt->rn_b = x->rn_b; + } + t = saved_tt->rn_p; + b_leaf = -1 - t->rn_b; + if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r; + /* Promote general routes from below */ + if (x->rn_b < 0) { + if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) { + MKGet(m); + if (m) { + Bzero(m, sizeof *m); + m->rm_b = x->rn_b; + m->rm_mask = x->rn_mask; + x->rn_mklist = t->rn_mklist = m; + } + } + } else if (x->rn_mklist) { + /* + * Skip over masks whose index is > that of new node + */ + for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist) + if (m->rm_b >= b_leaf) + break; + t->rn_mklist = m; *mp = 0; + } + /* Add new route to highest possible ancestor's list */ + if ((netmask == 0) || (b > t->rn_b )) + return tt; /* can't lift at all */ + b_leaf = tt->rn_b; + do { + x = t; + t = t->rn_p; + } while (b <= t->rn_b && x != head); + /* + * Search through routes associated with node to + * insert new route according to index. + * For nodes of equal index, place more specific + * masks first. + */ + cplim = netmask + mlen; + for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist) { + if (m->rm_b < b_leaf) + continue; + if (m->rm_b > b_leaf) + break; + if (m->rm_mask == netmask) { + m->rm_refs++; + tt->rn_mklist = m; + return tt; + } + maskp = (u_char *)m->rm_mask; + for (cp = netmask; cp < cplim; cp++) + if (*(u_char *)cp > *maskp++) + goto on2; + } +on2: + MKGet(m); + if (m == 0) { + printf("Mask for route not entered\n"); + return (tt); + } + Bzero(m, sizeof *m); + m->rm_b = b_leaf; + m->rm_mask = netmask; + m->rm_mklist = *mp; + *mp = m; + tt->rn_mklist = m; + return tt; +} + +struct radix_node * +rn_delete(v, netmask, head) + caddr_t v, netmask; + struct radix_node *head; +{ + register struct radix_node *t, *p, *x = head; + register struct radix_node *tt = rn_search(v, x); + int b, head_off = x->rn_off, vlen = * (u_char *) v; + struct radix_mask *m, *saved_m, **mp; + struct radix_node *dupedkey, *saved_tt = tt; + + if (tt == 0 || + Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off)) + return (0); + /* + * Delete our route from mask lists. + */ + if (dupedkey = tt->rn_dupedkey) { + if (netmask) + netmask = rn_search(netmask, rn_maskhead)->rn_key; + while (tt->rn_mask != netmask) + if ((tt = tt->rn_dupedkey) == 0) + return (0); + } + if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0) + goto on1; + if (m->rm_mask != tt->rn_mask) { + printf("rn_delete: inconsistent annotation\n"); + goto on1; + } + if (--m->rm_refs >= 0) + goto on1; + b = -1 - tt->rn_b; + t = saved_tt->rn_p; + if (b > t->rn_b) + goto on1; /* Wasn't lifted at all */ + do { + x = t; + t = t->rn_p; + } while (b <= t->rn_b && x != head); + for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist) + if (m == saved_m) { + *mp = m->rm_mklist; + MKFree(m); + break; + } + if (m == 0) + printf("rn_delete: couldn't find our annotation\n"); +on1: + /* + * Eliminate us from tree + */ + if (tt->rn_flags & RNF_ROOT) + return (0); +#ifdef RN_DEBUG + /* Get us out of the creation list */ + for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {} + if (t) t->rn_ybro = tt->rn_ybro; +#endif RN_DEBUG + t = tt->rn_p; + if (dupedkey) { + if (tt == saved_tt) { + x = dupedkey; x->rn_p = t; + if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x; +#ifndef RN_DEBUG + x++; t = tt + 1; *x = *t; p = t->rn_p; +#else + x++; b = x->rn_info; t = tt + 1; *x = *t; p = t->rn_p; + x->rn_info = b; +#endif + if (p->rn_l == t) p->rn_l = x; else p->rn_r = x; + x->rn_l->rn_p = x; x->rn_r->rn_p = x; + } else { + for (p = saved_tt; p && p->rn_dupedkey != tt;) + p = p->rn_dupedkey; + if (p) p->rn_dupedkey = tt->rn_dupedkey; + else printf("rn_delete: couldn't find us\n"); + } + goto out; + } + if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l; + p = t->rn_p; + if (p->rn_r == t) p->rn_r = x; else p->rn_l = x; + x->rn_p = p; + /* + * Demote routes attached to us. + */ + if (t->rn_mklist) { + if (x->rn_b >= 0) { + for (mp = &x->rn_mklist; m = *mp;) + mp = &m->rm_mklist; + *mp = t->rn_mklist; + } else { + for (m = t->rn_mklist; m;) { + struct radix_mask *mm = m->rm_mklist; + if (m == x->rn_mklist && (--(m->rm_refs) < 0)) { + x->rn_mklist = 0; + MKFree(m); + } else + printf("%s %x at %x\n", + "rn_delete: Orphaned Mask", m, x); + m = mm; + } + } + } + /* + * We may be holding an active internal node in the tree. + */ + x = tt + 1; + if (t != x) { +#ifndef RN_DEBUG + *t = *x; +#else + b = t->rn_info; *t = *x; t->rn_info = b; +#endif + t->rn_l->rn_p = t; t->rn_r->rn_p = t; + p = x->rn_p; + if (p->rn_l == x) p->rn_l = t; else p->rn_r = t; + } +out: + tt->rn_flags &= ~RNF_ACTIVE; + tt[1].rn_flags &= ~RNF_ACTIVE; + return (tt); +} +char rn_zeros[MAXKEYLEN], rn_ones[MAXKEYLEN]; + +rn_inithead(head, off, af) +struct radix_node_head **head; +int off; +{ + register struct radix_node_head *rnh; + register struct radix_node *t, *tt, *ttt; + if (*head) + return (1); + R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh)); + if (rnh == 0) + return (0); + Bzero(rnh, sizeof (*rnh)); + *head = rnh; + t = rn_newpair(rn_zeros, off, rnh->rnh_nodes); + ttt = rnh->rnh_nodes + 2; + t->rn_r = ttt; + t->rn_p = t; + tt = t->rn_l; + tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE; + tt->rn_b = -1 - off; + *ttt = *tt; + ttt->rn_key = rn_ones; + rnh->rnh_af = af; + rnh->rnh_treetop = t; + if (radix_node_head == 0) { + caddr_t cp = rn_ones, cplim = rn_ones + MAXKEYLEN; + while (cp < cplim) + *cp++ = -1; + if (rn_inithead(&radix_node_head, 0, 0) == 0) { + Free(rnh); + *head = 0; + return (0); + } + mask_rnhead = radix_node_head; + } + rnh->rnh_next = radix_node_head->rnh_next; + if (radix_node_head != rnh) + radix_node_head->rnh_next = rnh; + return (1); +} diff --git a/sys/net/radix.h b/sys/net/radix.h new file mode 100644 index 000000000000..b71d89fbc687 --- /dev/null +++ b/sys/net/radix.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1988, 1989 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. + * + * from: @(#)radix.h 7.4 (Berkeley) 6/28/90 + * $Id: radix.h,v 1.2 1993/10/16 17:43:32 rgrimes Exp $ + */ + +/* + * Radix search tree node layout. + */ + +struct radix_node { + struct radix_mask *rn_mklist; /* list of masks contained in subtree */ + struct radix_node *rn_p; /* parent */ + short rn_b; /* bit offset; -1-index(netmask) */ + char rn_bmask; /* node: mask for bit test*/ + u_char rn_flags; /* enumerated next */ +#define RNF_NORMAL 1 /* leaf contains normal route */ +#define RNF_ROOT 2 /* leaf is root leaf for tree */ +#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ + union { + struct { /* leaf only data: */ + caddr_t rn_Key; /* object of search */ + caddr_t rn_Mask; /* netmask, if present */ + struct radix_node *rn_Dupedkey; + } rn_leaf; + struct { /* node only data: */ + int rn_Off; /* where to start compare */ + struct radix_node *rn_L;/* progeny */ + struct radix_node *rn_R;/* progeny */ + }rn_node; + } rn_u; +#ifdef RN_DEBUG + int rn_info; + struct radix_node *rn_twin; + struct radix_node *rn_ybro; +#endif +}; + +#define MAXKEYLEN 32 + +#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey +#define rn_key rn_u.rn_leaf.rn_Key +#define rn_mask rn_u.rn_leaf.rn_Mask +#define rn_off rn_u.rn_node.rn_Off +#define rn_l rn_u.rn_node.rn_L +#define rn_r rn_u.rn_node.rn_R + +/* + * Annotations to tree concerning potential routes applying to subtrees. + */ + +extern struct radix_mask { + short rm_b; /* bit offset; -1-index(netmask) */ + char rm_unused; /* cf. rn_bmask */ + u_char rm_flags; /* cf. rn_flags */ + struct radix_mask *rm_mklist; /* more masks to try */ + caddr_t rm_mask; /* the mask */ + int rm_refs; /* # of references to this struct */ +} *rn_mkfreelist; + +#define MKGet(m) {\ + if (rn_mkfreelist) {\ + m = rn_mkfreelist; \ + rn_mkfreelist = (m)->rm_mklist; \ + } else \ + R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ + +#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} + +extern struct radix_node_head { + struct radix_node_head *rnh_next; + struct radix_node *rnh_treetop; + int rnh_af; + struct radix_node rnh_nodes[3]; +} *radix_node_head; + + +#ifndef KERNEL +#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n)) +#define Bzero(p, n) bzero((char *)(p), (int)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) +#define Free(p) free((char *)p); +#else +#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned long)(n), M_RTABLE, M_DONTWAIT)) +#define Free(p) free((caddr_t)p, M_RTABLE); +#endif /*KERNEL*/ diff --git a/sys/net/raw_cb.c b/sys/net/raw_cb.c new file mode 100644 index 000000000000..81371469390b --- /dev/null +++ b/sys/net/raw_cb.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1980, 1986 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. + * + * from: @(#)raw_cb.c 7.11 (Berkeley) 6/28/90 + * $Id: raw_cb.c,v 1.2 1993/10/16 17:43:34 rgrimes Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "socket.h" +#include "socketvar.h" +#include "domain.h" +#include "protosw.h" +#include "errno.h" + +#include "if.h" +#include "route.h" +#include "raw_cb.h" +#include "../netinet/in.h" + +#include "machine/mtpr.h" + +/* + * Routines to manage the raw protocol control blocks. + * + * TODO: + * hash lookups by protocol family/protocol + address family + * take care of unique address problems per AF? + * redo address binding to allow wildcards + */ + +u_long raw_sendspace = RAWSNDQ; +u_long raw_recvspace = RAWRCVQ; + +/* + * Allocate a control block and a nominal amount + * of buffer space for the socket. + */ +raw_attach(so, proto) + register struct socket *so; + int proto; +{ + register struct rawcb *rp = sotorawcb(so); + int error; + + /* + * It is assumed that raw_attach is called + * after space has been allocated for the + * rawcb. + */ + if (rp == 0) + return (ENOBUFS); + if (error = soreserve(so, raw_sendspace, raw_recvspace)) + return (error); + rp->rcb_socket = so; + rp->rcb_proto.sp_family = so->so_proto->pr_domain->dom_family; + rp->rcb_proto.sp_protocol = proto; + insque(rp, &rawcb); + return (0); +} + +/* + * Detach the raw connection block and discard + * socket resources. + */ +raw_detach(rp) + register struct rawcb *rp; +{ + struct socket *so = rp->rcb_socket; + + so->so_pcb = 0; + sofree(so); + remque(rp); +#ifdef notdef + if (rp->rcb_laddr) + m_freem(dtom(rp->rcb_laddr)); + rp->rcb_laddr = 0; +#endif + free((caddr_t)(rp), M_PCB); +} + +/* + * Disconnect and possibly release resources. + */ +raw_disconnect(rp) + struct rawcb *rp; +{ + +#ifdef notdef + if (rp->rcb_faddr) + m_freem(dtom(rp->rcb_faddr)); + rp->rcb_faddr = 0; +#endif + if (rp->rcb_socket->so_state & SS_NOFDREF) + raw_detach(rp); +} + +#ifdef notdef +raw_bind(so, nam) + register struct socket *so; + struct mbuf *nam; +{ + struct sockaddr *addr = mtod(nam, struct sockaddr *); + register struct rawcb *rp; + + if (ifnet == 0) + return (EADDRNOTAVAIL); + rp = sotorawcb(so); + nam = m_copym(nam, 0, M_COPYALL, M_WAITOK); + rp->rcb_laddr = mtod(nam, struct sockaddr *); + return (0); +} +#endif diff --git a/sys/net/raw_cb.h b/sys/net/raw_cb.h new file mode 100644 index 000000000000..cd562b43f044 --- /dev/null +++ b/sys/net/raw_cb.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1980, 1986 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. + * + * from: @(#)raw_cb.h 7.6 (Berkeley) 6/28/90 + * $Id: raw_cb.h,v 1.2 1993/10/16 17:43:36 rgrimes Exp $ + */ + +/* + * Raw protocol interface control block. Used + * to tie a socket to the generic raw interface. + */ +struct rawcb { + struct rawcb *rcb_next; /* doubly linked list */ + struct rawcb *rcb_prev; + struct socket *rcb_socket; /* back pointer to socket */ + struct sockaddr *rcb_faddr; /* destination address */ + struct sockaddr *rcb_laddr; /* socket's address */ + struct sockproto rcb_proto; /* protocol family, protocol */ +}; + +#define sotorawcb(so) ((struct rawcb *)(so)->so_pcb) + +/* + * Nominal space allocated to a raw socket. + */ +#define RAWSNDQ 8192 +#define RAWRCVQ 8192 + +#ifdef KERNEL +struct rawcb rawcb; /* head of list */ +#endif diff --git a/sys/net/raw_usrreq.c b/sys/net/raw_usrreq.c new file mode 100644 index 000000000000..5b4061afa71e --- /dev/null +++ b/sys/net/raw_usrreq.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1980, 1986 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. + * + * from: @(#)raw_usrreq.c 7.9 (Berkeley) 6/28/90 + * $Id: raw_usrreq.c,v 1.3 1993/10/16 17:43:38 rgrimes Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "domain.h" +#include "protosw.h" +#include "socket.h" +#include "socketvar.h" +#include "errno.h" + +#include "if.h" +#include "route.h" +#include "netisr.h" +#include "raw_cb.h" + +#include "machine/mtpr.h" + +/* + * Initialize raw connection block q. + */ +raw_init() +{ + + rawcb.rcb_next = rawcb.rcb_prev = &rawcb; + rawintrq.ifq_maxlen = IFQ_MAXLEN; +} + + +/* + * Raw protocol input routine. Find the socket + * associated with the packet(s) and move them over. If + * nothing exists for this packet, drop it. + */ +/* + * Raw protocol interface. + */ +raw_input(m0, proto, src, dst) + struct mbuf *m0; + register struct sockproto *proto; + struct sockaddr *src, *dst; +{ + register struct rawcb *rp; + register struct mbuf *m = m0; + register int sockets = 0; + struct socket *last; + + last = 0; + for (rp = rawcb.rcb_next; rp != &rawcb; rp = rp->rcb_next) { + if (rp->rcb_proto.sp_family != proto->sp_family) + continue; + if (rp->rcb_proto.sp_protocol && + rp->rcb_proto.sp_protocol != proto->sp_protocol) + continue; + /* + * We assume the lower level routines have + * placed the address in a canonical format + * suitable for a structure comparison. + * + * Note that if the lengths are not the same + * the comparison will fail at the first byte. + */ +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), a1->sa_len) == 0) + if (rp->rcb_laddr && !equal(rp->rcb_laddr, dst)) + continue; + if (rp->rcb_faddr && !equal(rp->rcb_faddr, src)) + continue; + if (last) { + struct mbuf *n; + if (n = m_copy(m, 0, (int)M_COPYALL)) { + if (sbappendaddr(&last->so_rcv, src, + n, (struct mbuf *)0) == 0) + /* should notify about lost packet */ + m_freem(n); + else { + sorwakeup(last); + sockets++; + } + } + } + last = rp->rcb_socket; + } + if (last) { + if (sbappendaddr(&last->so_rcv, src, + m, (struct mbuf *)0) == 0) + m_freem(m); + else { + sorwakeup(last); + sockets++; + } + } else + m_freem(m); + return (sockets); +} + +/*ARGSUSED*/ +raw_ctlinput(cmd, arg) + int cmd; + struct sockaddr *arg; +{ + + if (cmd < 0 || cmd > PRC_NCMDS) + return; + /* INCOMPLETE */ +} + +/*ARGSUSED*/ +raw_usrreq(so, req, m, nam, control) + struct socket *so; + int req; + struct mbuf *m, *nam, *control; +{ + register struct rawcb *rp = sotorawcb(so); + register int error = 0; + int len; + + if (req == PRU_CONTROL) + return (EOPNOTSUPP); + if (control && control->m_len) { + error = EOPNOTSUPP; + goto release; + } + if (rp == 0) { + error = EINVAL; + goto release; + } + switch (req) { + + /* + * Allocate a raw control block and fill in the + * necessary info to allow packets to be routed to + * the appropriate raw interface routine. + */ + case PRU_ATTACH: + if ((so->so_state & SS_PRIV) == 0) { + error = EACCES; + break; + } + error = raw_attach(so, (int)nam); + break; + + /* + * Destroy state just before socket deallocation. + * Flush data or not depending on the options. + */ + case PRU_DETACH: + if (rp == 0) { + error = ENOTCONN; + break; + } + raw_detach(rp); + break; + +#ifdef notdef + /* + * If a socket isn't bound to a single address, + * the raw input routine will hand it anything + * within that protocol family (assuming there's + * nothing else around it should go to). + */ + case PRU_CONNECT: + if (rp->rcb_faddr) { + error = EISCONN; + break; + } + nam = m_copym(nam, 0, M_COPYALL, M_WAIT); + rp->rcb_faddr = mtod(nam, struct sockaddr *); + soisconnected(so); + break; + + case PRU_BIND: + if (rp->rcb_laddr) { + error = EINVAL; /* XXX */ + break; + } + error = raw_bind(so, nam); + break; +#endif + + case PRU_CONNECT2: + error = EOPNOTSUPP; + goto release; + + case PRU_DISCONNECT: + if (rp->rcb_faddr == 0) { + error = ENOTCONN; + break; + } + raw_disconnect(rp); + soisdisconnected(so); + break; + + /* + * Mark the connection as being incapable of further input. + */ + case PRU_SHUTDOWN: + socantsendmore(so); + break; + + /* + * Ship a packet out. The appropriate raw output + * routine handles any massaging necessary. + */ + case PRU_SEND: + if (nam) { + if (rp->rcb_faddr) { + error = EISCONN; + break; + } + rp->rcb_faddr = mtod(nam, struct sockaddr *); + } else if (rp->rcb_faddr == 0) { + error = ENOTCONN; + break; + } + error = (*so->so_proto->pr_output)(m, so); + m = NULL; + if (nam) + rp->rcb_faddr = 0; + break; + + case PRU_ABORT: + raw_disconnect(rp); + sofree(so); + soisdisconnected(so); + break; + + case PRU_SENSE: + /* + * stat: don't bother with a blocksize. + */ + return (0); + + /* + * Not supported. + */ + case PRU_RCVOOB: + case PRU_RCVD: + return(EOPNOTSUPP); + + case PRU_LISTEN: + case PRU_ACCEPT: + case PRU_SENDOOB: + error = EOPNOTSUPP; + break; + + case PRU_SOCKADDR: + if (rp->rcb_laddr == 0) { + error = EINVAL; + break; + } + len = rp->rcb_laddr->sa_len; + bcopy((caddr_t)rp->rcb_laddr, mtod(nam, caddr_t), (unsigned)len); + nam->m_len = len; + break; + + case PRU_PEERADDR: + if (rp->rcb_faddr == 0) { + error = ENOTCONN; + break; + } + len = rp->rcb_faddr->sa_len; + bcopy((caddr_t)rp->rcb_faddr, mtod(nam, caddr_t), (unsigned)len); + nam->m_len = len; + break; + + default: + panic("raw_usrreq"); + } +release: + if (m != NULL) + m_freem(m); + return (error); +} + +rawintr() {} /* XXX - referenced by locore. will soon go away */ diff --git a/sys/net/route.c b/sys/net/route.c new file mode 100644 index 000000000000..6f9afaf8bdf7 --- /dev/null +++ b/sys/net/route.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) 1980, 1986, 1991 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. + * + * from: @(#)route.c 7.22 (Berkeley) 6/27/91 + * $Id: route.c,v 1.2 1993/10/16 17:43:39 rgrimes Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "proc.h" +#include "mbuf.h" +#include "socket.h" +#include "socketvar.h" +#include "domain.h" +#include "protosw.h" +#include "ioctl.h" + +#include "if.h" +#include "af.h" +#include "route.h" +#include "raw_cb.h" + +#include "../netinet/in.h" +#include "../netinet/in_var.h" + +#ifdef NS +#include "../netns/ns.h" +#endif +#include "machine/mtpr.h" +#include "netisr.h" + +#define SA(p) ((struct sockaddr *)(p)) + +int rttrash; /* routes not in table but not freed */ +struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ +int rthashsize = RTHASHSIZ; /* for netstat, etc. */ + +static int rtinits_done = 0; +struct radix_node_head *ns_rnhead, *in_rnhead; +struct radix_node *rn_match(), *rn_delete(), *rn_addroute(); + +rtinitheads() +{ + if (rtinits_done == 0 && +#ifdef NS + rn_inithead(&ns_rnhead, 16, AF_NS) && +#endif + rn_inithead(&in_rnhead, 32, AF_INET)) + rtinits_done = 1; +} + +/* + * Packet routing routines. + */ +rtalloc(ro) + register struct route *ro; +{ + if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) + return; /* XXX */ + ro->ro_rt = rtalloc1(&ro->ro_dst, 1); +} + +struct rtentry * +rtalloc1(dst, report) + register struct sockaddr *dst; + int report; +{ + register struct radix_node_head *rnh; + register struct rtentry *rt; + register struct radix_node *rn; + struct rtentry *newrt = 0; + int s = splnet(), err = 0, msgtype = RTM_MISS; + + for (rnh = radix_node_head; rnh && (dst->sa_family != rnh->rnh_af); ) + rnh = rnh->rnh_next; + if (rnh && rnh->rnh_treetop && + (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) && + ((rn->rn_flags & RNF_ROOT) == 0)) { + newrt = rt = (struct rtentry *)rn; + if (report && (rt->rt_flags & RTF_CLONING)) { + if ((err = rtrequest(RTM_RESOLVE, dst, SA(0), + SA(0), 0, &newrt)) || + ((rt->rt_flags & RTF_XRESOLVE) + && (msgtype = RTM_RESOLVE))) /* intended! */ + goto miss; + } else + rt->rt_refcnt++; + } else { + rtstat.rts_unreach++; + miss: if (report) + rt_missmsg(msgtype, dst, SA(0), SA(0), SA(0), 0, err); + } + splx(s); + return (newrt); +} + +rtfree(rt) + register struct rtentry *rt; +{ + register struct ifaddr *ifa; + if (rt == 0) + panic("rtfree"); + rt->rt_refcnt--; + if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { + rttrash--; + if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) + panic ("rtfree 2"); + free((caddr_t)rt, M_RTABLE); + } +} + +/* + * Force a routing table entry to the specified + * destination to go through the given gateway. + * Normally called as a result of a routing redirect + * message from the network layer. + * + * N.B.: must be called at splnet + * + */ +rtredirect(dst, gateway, netmask, flags, src, rtp) + struct sockaddr *dst, *gateway, *netmask, *src; + int flags; + struct rtentry **rtp; +{ + register struct rtentry *rt = 0; + int error = 0; + short *stat = 0; + + /* verify the gateway is directly reachable */ + if (ifa_ifwithnet(gateway) == 0) { + error = ENETUNREACH; + goto done; + } + rt = rtalloc1(dst, 0); + /* + * If the redirect isn't from our current router for this dst, + * it's either old or wrong. If it redirects us to ourselves, + * we have a routing loop, perhaps as a result of an interface + * going down recently. + */ +#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) + if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway)) + error = EINVAL; + else if (ifa_ifwithaddr(gateway)) + error = EHOSTUNREACH; + if (error) + goto done; + /* + * Create a new entry if we just got back a wildcard entry + * or the the lookup failed. This is necessary for hosts + * which use routing redirects generated by smart gateways + * to dynamically build the routing tables. + */ + if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) + goto create; + /* + * Don't listen to the redirect if it's + * for a route to an interface. + */ + if (rt->rt_flags & RTF_GATEWAY) { + if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { + /* + * Changing from route to net => route to host. + * Create new route, rather than smashing route to net. + */ + create: + flags |= RTF_GATEWAY | RTF_DYNAMIC; + error = rtrequest((int)RTM_ADD, dst, gateway, + SA(0), flags, + (struct rtentry **)0); + stat = &rtstat.rts_dynamic; + } else { + /* + * Smash the current notion of the gateway to + * this destination. Should check about netmask!!! + */ + if (gateway->sa_len <= rt->rt_gateway->sa_len) { + Bcopy(gateway, rt->rt_gateway, gateway->sa_len); + rt->rt_flags |= RTF_MODIFIED; + flags |= RTF_MODIFIED; + stat = &rtstat.rts_newgateway; + } else + error = ENOSPC; + } + } else + error = EHOSTUNREACH; +done: + if (rt) { + if (rtp && !error) + *rtp = rt; + else + rtfree(rt); + } + if (error) + rtstat.rts_badredirect++; + else + (stat && (*stat)++); + rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error); +} + +/* +* Routing table ioctl interface. +*/ +rtioctl(req, data, p) + int req; + caddr_t data; + struct proc *p; +{ +#ifndef COMPAT_43 + return (EOPNOTSUPP); +#else + register struct ortentry *entry = (struct ortentry *)data; + int error; + struct sockaddr *netmask = 0; + + if (req == SIOCADDRT) + req = RTM_ADD; + else if (req == SIOCDELRT) + req = RTM_DELETE; + else + return (EINVAL); + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); +#if BYTE_ORDER != BIG_ENDIAN + if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { + entry->rt_dst.sa_family = entry->rt_dst.sa_len; + entry->rt_dst.sa_len = 16; + } + if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { + entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; + entry->rt_gateway.sa_len = 16; + } +#else + if (entry->rt_dst.sa_len == 0) + entry->rt_dst.sa_len = 16; + if (entry->rt_gateway.sa_len == 0) + entry->rt_gateway.sa_len = 16; +#endif + if ((entry->rt_flags & RTF_HOST) == 0) + switch (entry->rt_dst.sa_family) { +#ifdef INET + case AF_INET: + { + extern struct sockaddr_in icmpmask; + struct sockaddr_in *dst_in = + (struct sockaddr_in *)&entry->rt_dst; + + in_sockmaskof(dst_in->sin_addr, &icmpmask); + netmask = (struct sockaddr *)&icmpmask; + } + break; +#endif +#ifdef NS + case AF_NS: + { + extern struct sockaddr_ns ns_netmask; + netmask = (struct sockaddr *)&ns_netmask; + } +#endif + } + error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask, + entry->rt_flags, (struct rtentry **)0); + rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL), + &(entry->rt_dst), &(entry->rt_gateway), + netmask, SA(0), entry->rt_flags, error); + return (error); +#endif +} + +struct ifaddr * +ifa_ifwithroute(flags, dst, gateway) +int flags; +struct sockaddr *dst, *gateway; +{ + register struct ifaddr *ifa; + if ((flags & RTF_GATEWAY) == 0) { + /* + * If we are adding a route to an interface, + * and the interface is a pt to pt link + * we should search for the destination + * as our clue to the interface. Otherwise + * we can use the local address. + */ + ifa = 0; + if (flags & RTF_HOST) + ifa = ifa_ifwithdstaddr(dst); + if (ifa == 0) + ifa = ifa_ifwithaddr(gateway); + } else { + /* + * If we are adding a route to a remote net + * or host, the gateway may still be on the + * other end of a pt to pt link. + */ + ifa = ifa_ifwithdstaddr(gateway); + } + if (ifa == 0) + ifa = ifa_ifwithnet(gateway); + if (ifa == 0) { + struct rtentry *rt = rtalloc1(dst, 0); + if (rt == 0) + return (0); + rt->rt_refcnt--; + if ((ifa = rt->rt_ifa) == 0) + return (0); + } + if (ifa->ifa_addr->sa_family != dst->sa_family) { + struct ifaddr *oifa = ifa, *ifaof_ifpforaddr(); + ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); + if (ifa == 0) + ifa = oifa; + } + return (ifa); +} + +#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +rtrequest(req, dst, gateway, netmask, flags, ret_nrt) + int req, flags; + struct sockaddr *dst, *gateway, *netmask; + struct rtentry **ret_nrt; +{ + int s = splnet(), len, error = 0; + register struct rtentry *rt; + register struct radix_node *rn; + register struct radix_node_head *rnh; + struct ifaddr *ifa, *ifa_ifwithdstaddr(); + struct sockaddr *ndst; + u_char af = dst->sa_family; +#define senderr(x) { error = x ; goto bad; } + + if (rtinits_done == 0) + rtinitheads(); + for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) + rnh = rnh->rnh_next; + if (rnh == 0) + senderr(ESRCH); + if (flags & RTF_HOST) + netmask = 0; + switch (req) { + case RTM_DELETE: + if (ret_nrt && (rt = *ret_nrt)) { + RTFREE(rt); + *ret_nrt = 0; + } + if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask, + rnh->rnh_treetop)) == 0) + senderr(ESRCH); + if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) + panic ("rtrequest delete"); + rt = (struct rtentry *)rn; + rt->rt_flags &= ~RTF_UP; + if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) + ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); + rttrash++; + if (rt->rt_refcnt <= 0) + rtfree(rt); + break; + + case RTM_RESOLVE: + if (ret_nrt == 0 || (rt = *ret_nrt) == 0) + senderr(EINVAL); + ifa = rt->rt_ifa; + flags = rt->rt_flags & ~RTF_CLONING; + gateway = rt->rt_gateway; + if ((netmask = rt->rt_genmask) == 0) + flags |= RTF_HOST; + goto makeroute; + + case RTM_ADD: + if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) + senderr(ENETUNREACH); + makeroute: + len = sizeof (*rt) + ROUNDUP(gateway->sa_len) + + ROUNDUP(dst->sa_len); + R_Malloc(rt, struct rtentry *, len); + if (rt == 0) + senderr(ENOBUFS); + Bzero(rt, len); + ndst = (struct sockaddr *)(rt + 1); + if (netmask) { + rt_maskedcopy(dst, ndst, netmask); + } else + Bcopy(dst, ndst, dst->sa_len); + rn = rn_addroute((caddr_t)ndst, (caddr_t)netmask, + rnh->rnh_treetop, rt->rt_nodes); + if (rn == 0) { + free((caddr_t)rt, M_RTABLE); + senderr(EEXIST); + } + rt->rt_ifa = ifa; + rt->rt_ifp = ifa->ifa_ifp; + rt->rt_flags = RTF_UP | flags; + rt->rt_gateway = (struct sockaddr *) + (rn->rn_key + ROUNDUP(dst->sa_len)); + Bcopy(gateway, rt->rt_gateway, gateway->sa_len); + if (req == RTM_RESOLVE) + rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ + if (ifa->ifa_rtrequest) + ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); + if (ret_nrt) { + *ret_nrt = rt; + rt->rt_refcnt++; + } + break; + } +bad: + splx(s); + return (error); +} + +rt_maskedcopy(src, dst, netmask) +struct sockaddr *src, *dst, *netmask; +{ + register u_char *cp1 = (u_char *)src; + register u_char *cp2 = (u_char *)dst; + register u_char *cp3 = (u_char *)netmask; + u_char *cplim = cp2 + *cp3; + u_char *cplim2 = cp2 + *cp1; + + *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ + cp3 += 2; + if (cplim > cplim2) + cplim = cplim2; + while (cp2 < cplim) + *cp2++ = *cp1++ & *cp3++; + if (cp2 < cplim2) + bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); +} +/* + * Set up a routing table entry, normally + * for an interface. + */ +rtinit(ifa, cmd, flags) + register struct ifaddr *ifa; + int cmd, flags; +{ + register struct rtentry *rt; + register struct sockaddr *dst; + register struct sockaddr *deldst; + struct mbuf *m = 0; + int error; + + dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; + if (ifa->ifa_flags & IFA_ROUTE) { + if ((rt = ifa->ifa_rt) && (rt->rt_flags & RTF_UP) == 0) { + RTFREE(rt); + ifa->ifa_rt = 0; + } + } + if (cmd == RTM_DELETE) { + if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { + m = m_get(M_WAIT, MT_SONAME); + deldst = mtod(m, struct sockaddr *); + rt_maskedcopy(dst, deldst, ifa->ifa_netmask); + dst = deldst; + } + if (rt = rtalloc1(dst, 0)) { + rt->rt_refcnt--; + if (rt->rt_ifa != ifa) { + if (m) + (void) m_free(m); + return (flags & RTF_HOST ? EHOSTUNREACH + : ENETUNREACH); + } + } + } + error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, + flags | ifa->ifa_flags, &ifa->ifa_rt); + if (m) + (void) m_free(m); + if (cmd == RTM_ADD && error == 0 && (rt = ifa->ifa_rt) + && rt->rt_ifa != ifa) { + rt->rt_ifa = ifa; + rt->rt_ifp = ifa->ifa_ifp; + } + return (error); +} diff --git a/sys/net/route.h b/sys/net/route.h new file mode 100644 index 000000000000..2d27b3586021 --- /dev/null +++ b/sys/net/route.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 1980, 1986 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. + * + * from: @(#)route.h 7.13 (Berkeley) 4/25/91 + * $Id: route.h,v 1.2 1993/10/16 17:43:41 rgrimes Exp $ + */ + +/* + * Kernel resident routing tables. + * + * The routing tables are initialized when interface addresses + * are set by making entries for all directly connected interfaces. + */ + +/* + * A route consists of a destination address and a reference + * to a routing entry. These are often held by protocols + * in their control blocks, e.g. inpcb. + */ +struct route { + struct rtentry *ro_rt; + struct sockaddr ro_dst; +}; + +/* + * These numbers are used by reliable protocols for determining + * retransmission behavior and are included in the routing structure. + */ +struct rt_metrics { + u_long rmx_locks; /* Kernel must leave these values alone */ + u_long rmx_mtu; /* MTU for this path */ + u_long rmx_hopcount; /* max hops expected */ + u_long rmx_expire; /* lifetime for route, e.g. redirect */ + u_long rmx_recvpipe; /* inbound delay-bandwith product */ + u_long rmx_sendpipe; /* outbound delay-bandwith product */ + u_long rmx_ssthresh; /* outbound gateway buffer limit */ + u_long rmx_rtt; /* estimated round trip time */ + u_long rmx_rttvar; /* estimated rtt variance */ +}; + +/* + * rmx_rtt and rmx_rttvar are stored as microseconds; + * RTTTOPRHZ(rtt) converts to a value suitable for use + * by a protocol slowtimo counter. + */ +#define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ +#define RTTTOPRHZ(r) ((r) / (RTM_RTTUNIT / PR_SLOWHZ)) + +/* + * We distinguish between routes to hosts and routes to networks, + * preferring the former if available. For each route we infer + * the interface to use from the gateway address supplied when + * the route was entered. Routes that forward packets through + * gateways are marked so that the output routines know to address the + * gateway rather than the ultimate destination. + */ +#ifndef RNF_NORMAL +#include "radix.h" +#endif +struct rtentry { + struct radix_node rt_nodes[2]; /* tree glue, and other values */ +#define rt_key(r) ((struct sockaddr *)((r)->rt_nodes->rn_key)) +#define rt_mask(r) ((struct sockaddr *)((r)->rt_nodes->rn_mask)) + struct sockaddr *rt_gateway; /* value */ + short rt_flags; /* up/down?, host/net */ + short rt_refcnt; /* # held references */ + u_long rt_use; /* raw # packets forwarded */ + struct ifnet *rt_ifp; /* the answer: interface to use */ + struct ifaddr *rt_ifa; /* the answer: interface to use */ + struct sockaddr *rt_genmask; /* for generation of cloned routes */ + caddr_t rt_llinfo; /* pointer to link level info cache */ + struct rt_metrics rt_rmx; /* metrics used by rx'ing protocols */ + short rt_idle; /* easy to tell llayer still live */ +}; + +/* + * Following structure necessary for 4.3 compatibility; + * We should eventually move it to a compat file. + */ +struct ortentry { + u_long rt_hash; /* to speed lookups */ + struct sockaddr rt_dst; /* key */ + struct sockaddr rt_gateway; /* value */ + short rt_flags; /* up/down?, host/net */ + short rt_refcnt; /* # held references */ + u_long rt_use; /* raw # packets forwarded */ + struct ifnet *rt_ifp; /* the answer: interface to use */ +}; + +#define RTF_UP 0x1 /* route useable */ +#define RTF_GATEWAY 0x2 /* destination is a gateway */ +#define RTF_HOST 0x4 /* host entry (net otherwise) */ +#define RTF_REJECT 0x8 /* host or net unreachable */ +#define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ +#define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ +#define RTF_DONE 0x40 /* message confirmed */ +#define RTF_MASK 0x80 /* subnet mask present */ +#define RTF_CLONING 0x100 /* generate new routes on use */ +#define RTF_XRESOLVE 0x200 /* external daemon resolves name */ +#define RTF_LLINFO 0x400 /* generated by ARP or ESIS */ +#define RTF_PROTO2 0x4000 /* protocol specific routing flag */ +#define RTF_PROTO1 0x8000 /* protocol specific routing flag */ + + +/* + * Routing statistics. + */ +struct rtstat { + short rts_badredirect; /* bogus redirect calls */ + short rts_dynamic; /* routes created by redirects */ + short rts_newgateway; /* routes modified by redirects */ + short rts_unreach; /* lookups which failed */ + short rts_wildcard; /* lookups satisfied by a wildcard */ +}; +/* + * Structures for routing messages. + */ +struct rt_msghdr { + u_short rtm_msglen; /* to skip over non-understood messages */ + u_char rtm_version; /* future binary compatability */ + u_char rtm_type; /* message type */ + u_short rtm_index; /* index for associated ifp */ + pid_t rtm_pid; /* identify sender */ + int rtm_addrs; /* bitmask identifying sockaddrs in msg */ + int rtm_seq; /* for sender to identify action */ + int rtm_errno; /* why failed */ + int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ + int rtm_use; /* from rtentry */ + u_long rtm_inits; /* which metrics we are initializing */ + struct rt_metrics rtm_rmx; /* metrics themselves */ +}; + +struct route_cb { + int ip_count; + int ns_count; + int iso_count; + int any_count; +}; +#define RTM_VERSION 2 /* Up the ante and ignore older versions */ + +#define RTM_ADD 0x1 /* Add Route */ +#define RTM_DELETE 0x2 /* Delete Route */ +#define RTM_CHANGE 0x3 /* Change Metrics or flags */ +#define RTM_GET 0x4 /* Report Metrics */ +#define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */ +#define RTM_REDIRECT 0x6 /* Told to use different route */ +#define RTM_MISS 0x7 /* Lookup failed on this address */ +#define RTM_LOCK 0x8 /* fix specified metrics */ +#define RTM_OLDADD 0x9 /* caused by SIOCADDRT */ +#define RTM_OLDDEL 0xa /* caused by SIOCDELRT */ +#define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ + +#define RTV_MTU 0x1 /* init or lock _mtu */ +#define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ +#define RTV_EXPIRE 0x4 /* init or lock _hopcount */ +#define RTV_RPIPE 0x8 /* init or lock _recvpipe */ +#define RTV_SPIPE 0x10 /* init or lock _sendpipe */ +#define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ +#define RTV_RTT 0x40 /* init or lock _rtt */ +#define RTV_RTTVAR 0x80 /* init or lock _rttvar */ + +#define RTA_DST 0x1 /* destination sockaddr present */ +#define RTA_GATEWAY 0x2 /* gateway sockaddr present */ +#define RTA_NETMASK 0x4 /* netmask sockaddr present */ +#define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ +#define RTA_IFP 0x10 /* interface name sockaddr present */ +#define RTA_IFA 0x20 /* interface addr sockaddr present */ +#define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ + +#ifdef KERNEL +struct route_cb route_cb; +#endif + +#ifdef KERNEL +#define RTFREE(rt) \ + if ((rt)->rt_refcnt <= 1) \ + rtfree(rt); \ + else \ + (rt)->rt_refcnt--; + +#ifdef GATEWAY +#define RTHASHSIZ 64 +#else +#define RTHASHSIZ 8 +#endif +#if (RTHASHSIZ & (RTHASHSIZ - 1)) == 0 +#define RTHASHMOD(h) ((h) & (RTHASHSIZ - 1)) +#else +#define RTHASHMOD(h) ((h) % RTHASHSIZ) +#endif +struct mbuf *rthost[RTHASHSIZ]; +struct mbuf *rtnet[RTHASHSIZ]; +struct rtstat rtstat; +struct rtentry *rtalloc1(); +#endif diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c new file mode 100644 index 000000000000..47550ee05b31 --- /dev/null +++ b/sys/net/rtsock.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 1988, 1991 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. + * + * from: @(#)rtsock.c 7.18 (Berkeley) 6/27/91 + * $Id: rtsock.c,v 1.3 1993/10/16 17:43:43 rgrimes Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "proc.h" +#include "socket.h" +#include "socketvar.h" +#include "domain.h" +#include "protosw.h" + +#include "af.h" +#include "if.h" +#include "route.h" +#include "raw_cb.h" + +#include "machine/mtpr.h" + +struct sockaddr route_dst = { 2, PF_ROUTE, }; +struct sockaddr route_src = { 2, PF_ROUTE, }; +struct sockproto route_proto = { PF_ROUTE, }; + +/*ARGSUSED*/ +route_usrreq(so, req, m, nam, control) + register struct socket *so; + int req; + struct mbuf *m, *nam, *control; +{ + register int error = 0; + register struct rawcb *rp = sotorawcb(so); + int s; + if (req == PRU_ATTACH) { + MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK); + if (so->so_pcb = (caddr_t)rp) + bzero(so->so_pcb, sizeof(*rp)); + + } + if (req == PRU_DETACH && rp) { + int af = rp->rcb_proto.sp_protocol; + if (af == AF_INET) + route_cb.ip_count--; + else if (af == AF_NS) + route_cb.ns_count--; + else if (af == AF_ISO) + route_cb.iso_count--; + route_cb.any_count--; + } + s = splnet(); + error = raw_usrreq(so, req, m, nam, control); + rp = sotorawcb(so); + if (req == PRU_ATTACH && rp) { + int af = rp->rcb_proto.sp_protocol; + if (error) { + free((caddr_t)rp, M_PCB); + splx(s); + return (error); + } + if (af == AF_INET) + route_cb.ip_count++; + else if (af == AF_NS) + route_cb.ns_count++; + else if (af == AF_ISO) + route_cb.iso_count++; + rp->rcb_faddr = &route_src; + route_cb.any_count++; + soisconnected(so); + so->so_options |= SO_USELOOPBACK; + } + splx(s); + return (error); +} +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +/*ARGSUSED*/ +route_output(m, so) + register struct mbuf *m; + struct socket *so; +{ + register struct rt_msghdr *rtm = 0; + register struct rtentry *rt = 0; + struct rtentry *saved_nrt = 0; + struct sockaddr *dst = 0, *gate = 0, *netmask = 0, *genmask = 0; + struct sockaddr *ifpaddr = 0, *ifaaddr = 0; + caddr_t cp, lim; + int len, error = 0; + struct ifnet *ifp = 0; + struct ifaddr *ifa = 0; + struct ifaddr *ifaof_ifpforaddr(), *ifa_ifwithroute(); + +#define senderr(e) { error = e; goto flush;} + if (m == 0 || m->m_len < sizeof(long)) + return (ENOBUFS); + if ((m = m_pullup(m, sizeof(long))) == 0) + return (ENOBUFS); + if ((m->m_flags & M_PKTHDR) == 0) + panic("route_output"); + len = m->m_pkthdr.len; + if (len < sizeof(*rtm) || + len != mtod(m, struct rt_msghdr *)->rtm_msglen) + senderr(EINVAL); + R_Malloc(rtm, struct rt_msghdr *, len); + if (rtm == 0) + senderr(ENOBUFS); + m_copydata(m, 0, len, (caddr_t)rtm); + if (rtm->rtm_version != RTM_VERSION) + senderr(EPROTONOSUPPORT); + rtm->rtm_pid = curproc->p_pid; + lim = len + (caddr_t) rtm; + cp = (caddr_t) (rtm + 1); + if (rtm->rtm_addrs & RTA_DST) { + dst = (struct sockaddr *)cp; + ADVANCE(cp, dst); + } else + senderr(EINVAL); + if ((rtm->rtm_addrs & RTA_GATEWAY) && cp < lim) { + gate = (struct sockaddr *)cp; + ADVANCE(cp, gate); + } + if ((rtm->rtm_addrs & RTA_NETMASK) && cp < lim) { + netmask = (struct sockaddr *)cp; + ADVANCE(cp, netmask); + } + if ((rtm->rtm_addrs & RTA_GENMASK) && cp < lim) { + struct radix_node *t, *rn_addmask(); + genmask = (struct sockaddr *)cp; + ADVANCE(cp, genmask); + t = rn_addmask(genmask, 1, 2); + if (t && Bcmp(genmask, t->rn_key, *(u_char *)genmask) == 0) + genmask = (struct sockaddr *)(t->rn_key); + else + senderr(ENOBUFS); + } + if ((rtm->rtm_addrs & RTA_IFP) && cp < lim) { + ifpaddr = (struct sockaddr *)cp; + ADVANCE(cp, ifpaddr); + } + if ((rtm->rtm_addrs & RTA_IFA) && cp < lim) { + ifaaddr = (struct sockaddr *)cp; + } + switch (rtm->rtm_type) { + case RTM_ADD: + if (gate == 0) + senderr(EINVAL); + error = rtrequest(RTM_ADD, dst, gate, netmask, + rtm->rtm_flags, &saved_nrt); + if (error == 0 && saved_nrt) { + rt_setmetrics(rtm->rtm_inits, + &rtm->rtm_rmx, &saved_nrt->rt_rmx); + saved_nrt->rt_refcnt--; + saved_nrt->rt_genmask = genmask; + } + break; + + case RTM_DELETE: + error = rtrequest(RTM_DELETE, dst, gate, netmask, + rtm->rtm_flags, (struct rtentry **)0); + break; + + case RTM_GET: + case RTM_CHANGE: + case RTM_LOCK: + rt = rtalloc1(dst, 0); + if (rt == 0) + senderr(ESRCH); + if (rtm->rtm_type != RTM_GET) { + if (Bcmp(dst, rt_key(rt), dst->sa_len) != 0) + senderr(ESRCH); + if (rt->rt_nodes->rn_dupedkey && + (netmask == 0 || + Bcmp(netmask, rt_mask(rt), netmask->sa_len))) + senderr(ETOOMANYREFS); + } + switch(rtm->rtm_type) { + + case RTM_GET: + dst = rt_key(rt); len = sizeof(*rtm); + ADVANCE(len, dst); + rtm->rtm_addrs |= RTA_DST; + if (gate = rt->rt_gateway) { + ADVANCE(len, gate); + rtm->rtm_addrs |= RTA_GATEWAY; + } else + rtm->rtm_addrs &= ~RTA_GATEWAY; + if (netmask = rt_mask(rt)) { + ADVANCE(len, netmask); + rtm->rtm_addrs |= RTA_NETMASK; + } else + rtm->rtm_addrs &= ~RTA_NETMASK; + if (genmask = rt->rt_genmask) { + ADVANCE(len, genmask); + rtm->rtm_addrs |= RTA_GENMASK; + } else + rtm->rtm_addrs &= ~RTA_GENMASK; + if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) { + if (rt->rt_ifp == 0) + goto badif; + for (ifa = rt->rt_ifp->if_addrlist; + ifa && ifa->ifa_addr->sa_family != AF_LINK; + ifa = ifa->ifa_next){} + if (ifa && rt->rt_ifa) { + ifpaddr = ifa->ifa_addr; + ADVANCE(len, ifpaddr); + ifaaddr = rt->rt_ifa->ifa_addr; + ADVANCE(len, ifaaddr); + rtm->rtm_addrs |= RTA_IFP | RTA_IFA; + } else { + badif: ifpaddr = 0; + rtm->rtm_addrs &= ~(RTA_IFP | RTA_IFA); + } + } + if (len > rtm->rtm_msglen) { + struct rt_msghdr *new_rtm; + R_Malloc(new_rtm, struct rt_msghdr *, len); + if (new_rtm == 0) + senderr(ENOBUFS); + Bcopy(rtm, new_rtm, rtm->rtm_msglen); + Free(rtm); rtm = new_rtm; + } + rtm->rtm_msglen = len; + rtm->rtm_flags = rt->rt_flags; + rtm->rtm_rmx = rt->rt_rmx; + cp = (caddr_t) (1 + rtm); + len = ROUNDUP(dst->sa_len); + Bcopy(dst, cp, len); cp += len; + if (gate) { + len = ROUNDUP(gate->sa_len); + Bcopy(gate, cp, len); cp += len; + } + if (netmask) { + len = ROUNDUP(netmask->sa_len); + Bcopy(netmask, cp, len); cp += len; + } + if (genmask) { + len = ROUNDUP(genmask->sa_len); + Bcopy(genmask, cp, len); cp += len; + } + if (ifpaddr) { + len = ROUNDUP(ifpaddr->sa_len); + Bcopy(ifpaddr, cp, len); cp += len; + len = ROUNDUP(ifaaddr->sa_len); + Bcopy(ifaaddr, cp, len); cp += len; + } + break; + + case RTM_CHANGE: + if (gate && + (gate->sa_len > (len = rt->rt_gateway->sa_len))) + senderr(EDQUOT); + /* new gateway could require new ifaddr, ifp; + flags may also be different; ifp may be specified + by ll sockaddr when protocol address is ambiguous */ + if (ifpaddr && (ifa = ifa_ifwithnet(ifpaddr)) && + (ifp = ifa->ifa_ifp)) + ifa = ifaof_ifpforaddr(ifaaddr ? ifaaddr : gate, + ifp); + else if ((ifaaddr && (ifa = ifa_ifwithaddr(ifaaddr))) || + (ifa = ifa_ifwithroute(rt->rt_flags, + rt_key(rt), gate))) + ifp = ifa->ifa_ifp; + if (ifa) { + register struct ifaddr *oifa = rt->rt_ifa; + if (oifa != ifa) { + if (oifa && oifa->ifa_rtrequest) + oifa->ifa_rtrequest(RTM_DELETE, + rt, gate); + rt->rt_ifa = ifa; + rt->rt_ifp = ifp; + } + } + if (gate) + Bcopy(gate, rt->rt_gateway, len); + rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, + &rt->rt_rmx); + if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest) + rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, gate); + if (genmask) + rt->rt_genmask = genmask; + /* + * Fall into + */ + case RTM_LOCK: + rt->rt_rmx.rmx_locks |= + (rtm->rtm_inits & rtm->rtm_rmx.rmx_locks); + rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits); + break; + } + goto cleanup; + + default: + senderr(EOPNOTSUPP); + } + +flush: + if (rtm) { + if (error) + rtm->rtm_errno = error; + else + rtm->rtm_flags |= RTF_DONE; + } +cleanup: + if (rt) + rtfree(rt); + { + register struct rawcb *rp = 0; + /* + * Check to see if we don't want our own messages. + */ + if ((so->so_options & SO_USELOOPBACK) == 0) { + if (route_cb.any_count <= 1) { + if (rtm) + Free(rtm); + m_freem(m); + return (error); + } + /* There is another listener, so construct message */ + rp = sotorawcb(so); + } + if (rtm) { + m_copyback(m, 0, rtm->rtm_msglen, (caddr_t)rtm); + Free(rtm); + } + if (rp) + rp->rcb_proto.sp_family = 0; /* Avoid us */ + if (dst) + route_proto.sp_protocol = dst->sa_family; + raw_input(m, &route_proto, &route_src, &route_dst); + if (rp) + rp->rcb_proto.sp_family = PF_ROUTE; + } + return (error); +} + +rt_setmetrics(which, in, out) + u_long which; + register struct rt_metrics *in, *out; +{ +#define metric(f, e) if (which & (f)) out->e = in->e; + metric(RTV_RPIPE, rmx_recvpipe); + metric(RTV_SPIPE, rmx_sendpipe); + metric(RTV_SSTHRESH, rmx_ssthresh); + metric(RTV_RTT, rmx_rtt); + metric(RTV_RTTVAR, rmx_rttvar); + metric(RTV_HOPCOUNT, rmx_hopcount); + metric(RTV_MTU, rmx_mtu); + metric(RTV_EXPIRE, rmx_expire); +#undef metric +} + +/* + * Copy data from a buffer back into the indicated mbuf chain, + * starting "off" bytes from the beginning, extending the mbuf + * chain if necessary. + */ +m_copyback(m0, off, len, cp) + struct mbuf *m0; + register int off; + register int len; + caddr_t cp; + +{ + register int mlen; + register struct mbuf *m = m0, *n; + int totlen = 0; + + if (m0 == 0) + return; + while (off > (mlen = m->m_len)) { + off -= mlen; + totlen += mlen; + if (m->m_next == 0) { + n = m_getclr(M_DONTWAIT, m->m_type); + if (n == 0) + goto out; + n->m_len = min(MLEN, len + off); + m->m_next = n; + } + m = m->m_next; + } + while (len > 0) { + mlen = min (m->m_len - off, len); + bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen); + cp += mlen; + len -= mlen; + mlen += off; + off = 0; + totlen += mlen; + if (len == 0) + break; + if (m->m_next == 0) { + n = m_get(M_DONTWAIT, m->m_type); + if (n == 0) + break; + n->m_len = min(MLEN, len); + m->m_next = n; + } + m = m->m_next; + } +out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen)) + m->m_pkthdr.len = totlen; +} + +/* + * The miss message and losing message are very similar. + */ + +rt_missmsg(type, dst, gate, mask, src, flags, error) +register struct sockaddr *dst; +struct sockaddr *gate, *mask, *src; +{ + register struct rt_msghdr *rtm; + register struct mbuf *m; + int dlen = ROUNDUP(dst->sa_len); + int len = dlen + sizeof(*rtm); + + if (route_cb.any_count == 0) + return; + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == 0) + return; + m->m_pkthdr.len = m->m_len = min(len, MHLEN); + m->m_pkthdr.rcvif = 0; + rtm = mtod(m, struct rt_msghdr *); + bzero((caddr_t)rtm, sizeof(*rtm)); /*XXX assumes sizeof(*rtm) < MHLEN*/ + rtm->rtm_flags = RTF_DONE | flags; + rtm->rtm_msglen = len; + rtm->rtm_version = RTM_VERSION; + rtm->rtm_type = type; + rtm->rtm_addrs = RTA_DST; + if (type == RTM_OLDADD || type == RTM_OLDDEL) { + rtm->rtm_pid = curproc->p_pid; + } + m_copyback(m, sizeof (*rtm), dlen, (caddr_t)dst); + if (gate) { + dlen = ROUNDUP(gate->sa_len); + m_copyback(m, len , dlen, (caddr_t)gate); + len += dlen; + rtm->rtm_addrs |= RTA_GATEWAY; + } + if (mask) { + dlen = ROUNDUP(mask->sa_len); + m_copyback(m, len , dlen, (caddr_t)mask); + len += dlen; + rtm->rtm_addrs |= RTA_NETMASK; + } + if (src) { + dlen = ROUNDUP(src->sa_len); + m_copyback(m, len , dlen, (caddr_t)src); + len += dlen; + rtm->rtm_addrs |= RTA_AUTHOR; + } + if (m->m_pkthdr.len != len) { + m_freem(m); + return; + } + rtm->rtm_errno = error; + rtm->rtm_msglen = len; + route_proto.sp_protocol = dst->sa_family; + raw_input(m, &route_proto, &route_src, &route_dst); +} + +#include "kinfo.h" +struct walkarg { + int w_op, w_arg; + int w_given, w_needed; + caddr_t w_where; + struct { + struct rt_msghdr m_rtm; + char m_sabuf[128]; + } w_m; +#define w_rtm w_m.m_rtm +}; +/* + * This is used in dumping the kernel table via getkinfo(). + */ +rt_dumpentry(rn, w) + struct radix_node *rn; + register struct walkarg *w; +{ + register struct sockaddr *sa; + int n, error; + + for (; rn; rn = rn->rn_dupedkey) { + int count = 0, size = sizeof(w->w_rtm); + register struct rtentry *rt = (struct rtentry *)rn; + + if (rn->rn_flags & RNF_ROOT) + continue; + if (w->w_op == KINFO_RT_FLAGS && !(rt->rt_flags & w->w_arg)) + continue; +#define next(a, l) {size += (l); w->w_rtm.rtm_addrs |= (a); } + w->w_rtm.rtm_addrs = 0; + if (sa = rt_key(rt)) + next(RTA_DST, ROUNDUP(sa->sa_len)); + if (sa = rt->rt_gateway) + next(RTA_GATEWAY, ROUNDUP(sa->sa_len)); + if (sa = rt_mask(rt)) + next(RTA_NETMASK, ROUNDUP(sa->sa_len)); + if (sa = rt->rt_genmask) + next(RTA_GENMASK, ROUNDUP(sa->sa_len)); + w->w_needed += size; + if (w->w_where == NULL || w->w_needed > 0) + continue; + w->w_rtm.rtm_msglen = size; + w->w_rtm.rtm_flags = rt->rt_flags; + w->w_rtm.rtm_use = rt->rt_use; + w->w_rtm.rtm_rmx = rt->rt_rmx; + w->w_rtm.rtm_index = rt->rt_ifp->if_index; +#undef next +#define next(l) {n = (l); Bcopy(sa, cp, n); cp += n;} + if (size <= sizeof(w->w_m)) { + register caddr_t cp = (caddr_t)(w->w_m.m_sabuf); + if (sa = rt_key(rt)) + next(ROUNDUP(sa->sa_len)); + if (sa = rt->rt_gateway) + next(ROUNDUP(sa->sa_len)); + if (sa = rt_mask(rt)) + next(ROUNDUP(sa->sa_len)); + if (sa = rt->rt_genmask) + next(ROUNDUP(sa->sa_len)); +#undef next +#define next(s, l) {n = (l); \ + if (error = copyout((caddr_t)(s), w->w_where, n)) return (error); \ + w->w_where += n;} + + next(&w->w_m, size); /* Copy rtmsg and sockaddrs back */ + continue; + } + next(&w->w_rtm, sizeof(w->w_rtm)); + if (sa = rt_key(rt)) + next(sa, ROUNDUP(sa->sa_len)); + if (sa = rt->rt_gateway) + next(sa, ROUNDUP(sa->sa_len)); + if (sa = rt_mask(rt)) + next(sa, ROUNDUP(sa->sa_len)); + if (sa = rt->rt_genmask) + next(sa, ROUNDUP(sa->sa_len)); + } + return (0); +#undef next +} + +kinfo_rtable(op, where, given, arg, needed) + int op, arg; + caddr_t where; + int *given, *needed; +{ + register struct radix_node_head *rnh; + int s, error = 0; + u_char af = ki_af(op); + struct walkarg w; + + op &= 0xffff; + if (op != KINFO_RT_DUMP && op != KINFO_RT_FLAGS) + return (EINVAL); + + Bzero(&w, sizeof(w)); + if ((w.w_where = where) && given) + w.w_given = *given; + w.w_needed = 0 - w.w_given; + w.w_arg = arg; + w.w_op = op; + w.w_rtm.rtm_version = RTM_VERSION; + w.w_rtm.rtm_type = RTM_GET; + + s = splnet(); + for (rnh = radix_node_head; rnh; rnh = rnh->rnh_next) { + if (rnh->rnh_af == 0) + continue; + if (af && af != rnh->rnh_af) + continue; + error = rt_walk(rnh->rnh_treetop, rt_dumpentry, &w); + if (error) + break; + } + w.w_needed += w.w_given; + if (where && given) + *given = w.w_where - where; + else + w.w_needed = (11 * w.w_needed) / 10; + *needed = w.w_needed; + splx(s); + return (error); +} + +rt_walk(rn, f, w) + register struct radix_node *rn; + register int (*f)(); + struct walkarg *w; +{ + int error; + for (;;) { + while (rn->rn_b >= 0) + rn = rn->rn_l; /* First time through node, go left */ + if (error = (*f)(rn, w)) + return (error); /* Process Leaf */ + while (rn->rn_p->rn_r == rn) { /* if coming back from right */ + rn = rn->rn_p; /* go back up */ + if (rn->rn_flags & RNF_ROOT) + return 0; + } + rn = rn->rn_p->rn_r; /* otherwise, go right*/ + } +} + +/* + * Definitions of protocols supported in the ROUTE domain. + */ + +int raw_init(),raw_usrreq(),raw_input(),raw_ctlinput(); +extern struct domain routedomain; /* or at least forward */ + +struct protosw routesw[] = { +{ SOCK_RAW, &routedomain, 0, PR_ATOMIC|PR_ADDR, + raw_input, route_output, raw_ctlinput, 0, + route_usrreq, + raw_init, 0, 0, 0, +} +}; + +int unp_externalize(), unp_dispose(); + +struct domain routedomain = + { PF_ROUTE, "route", 0, 0, 0, + routesw, &routesw[sizeof(routesw)/sizeof(routesw[0])] }; diff --git a/sys/net/slcompress.c b/sys/net/slcompress.c new file mode 100644 index 000000000000..48495a1228ea --- /dev/null +++ b/sys/net/slcompress.c @@ -0,0 +1,551 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989, 1991 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + * + * Modified March 14, 1993 by David Greenman, upgraded to slcompress.c + * from tcpdump 2.2.1. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * $Id: slcompress.c,v 1.3 1993/08/27 02:10:31 rgrimes Exp $ + * From: slcompress.c,v 1.22 92/05/24 11:48:20 van Exp $ (LBL) + */ +#ifndef lint +static char rcsid[] = + "$Id: slcompress.c,v 1.3 1993/08/27 02:10:31 rgrimes Exp $"; +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include "slcompress.h" + +#ifndef SL_NO_STATS +#define INCR(counter) ++comp->counter; +#else +#define INCR(counter) +#endif + +#define BCMP(p1, p2, n) bcmp((char *)(p1), (char *)(p2), (int)(n)) +#define BCOPY(p1, p2, n) bcopy((char *)(p1), (char *)(p2), (int)(n)) +#ifndef KERNEL +#define ovbcopy bcopy +#endif + + +void +sl_compress_init(comp) + struct slcompress *comp; +{ + register u_int i; + register struct cstate *tstate = comp->tstate; + + bzero((char *)comp, sizeof(*comp)); + for (i = MAX_STATES - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_STATES - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = SLF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \ + cp += 3; \ + } else { \ + (f) = htonl(ntohl(f) + (u_long)*cp++); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \ + cp += 3; \ + } else { \ + (f) = htons(ntohs(f) + (u_long)*cp++); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons((cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_long)*cp++); \ + } \ +} + + +u_char +sl_compress_tcp(m, ip, comp, compress_cid) + struct mbuf *m; + register struct ip *ip; + struct slcompress *comp; + int compress_cid; +{ + register struct cstate *cs = comp->last_cs->cs_next; + register u_int hlen = ip->ip_hl; + register struct tcphdr *oth; + register struct tcphdr *th; + register u_int deltaS, deltaA; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). (We assume that the caller has already made sure the + * packet is IP proto TCP). + */ + if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) + return (TYPE_IP); + + th = (struct tcphdr *)&((int *)ip)[hlen]; + if ((th->th_flags & (TH_SYN|TH_FIN|TH_RST|TH_ACK)) != TH_ACK) + return (TYPE_IP); + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(sls_packets) + if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr || + ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr || + *(int *)th != ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(sls_searches) + if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr + && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr + && *(int *)th == ((int *)&cs->cs_ip)[cs->cs_ip.ip_hl]) + goto found; + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(sls_misses) + comp->last_cs = lcs; + hlen += th->th_off; + hlen <<= 2; + if (hlen > m->m_len) + return (TYPE_IP); + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) + comp->last_cs = lcs; + else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + oth = (struct tcphdr *)&((int *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += th->th_off; + hlen <<= 2; + if (hlen > m->m_len) + return (TYPE_IP); + + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] || + ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] || + ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] || + th->th_off != oth->th_off || + (deltaS > 5 && + BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) || + (th->th_off > 5 && + BCMP(th + 1, oth + 1, (th->th_off - 5) << 2))) + goto uncompressed; + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (th->th_flags & TH_URG) { + deltaS = ntohs(th->th_urp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->th_urp != oth->th_urp) + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + + if (deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if (deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack)) { + if (deltaA > 0xffff) + goto uncompressed; + ENCODE(deltaA); + changes |= NEW_A; + } + + if (deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq)) { + if (deltaS > 0xffff) + goto uncompressed; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (ip->ip_len != cs->cs_ip.ip_len && + ntohs(cs->cs_ip.ip_len) == hlen) + break; + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && + deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (th->th_flags & TH_PUSH) + changes |= TCP_PUSH_BIT; + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->th_sum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = cp - new_seq; + cp = (u_char *)ip; + if (compress_cid == 0 || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + cp += hlen; + *cp++ = changes | NEW_C; + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + cp += hlen; + *cp++ = changes; + } + m->m_len -= hlen; + m->m_data += hlen; + *cp++ = deltaA >> 8; + *cp++ = deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(sls_compressed) + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet ('uncompressed' + * means a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + BCOPY(ip, &cs->cs_ip, hlen); + ip->ip_p = cs->cs_id; + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + + +int +sl_uncompress_tcp(bufp, len, type, comp) + u_char **bufp; + int len; + u_int type; + struct slcompress *comp; +{ + return sl_uncompress_tcp_part(bufp, len, len, type, comp); +} + + +/* + * Uncompress a packet of total length total_len. The first buflen + * bytes are at *bufp; this must include the entire (compressed or + * uncompressed) TCP/IP header. In addition, there must be enough + * clear space before *bufp to build a full-length TCP/IP header. + */ +int +sl_uncompress_tcp_part(bufp, buflen, total_len, type, comp) + u_char **bufp; + int buflen, total_len; + u_int type; + struct slcompress *comp; +{ + register u_char *cp; + register u_int hlen, changes; + register struct tcphdr *th; + register struct cstate *cs; + register struct ip *ip; + + switch (type) { + + case TYPE_UNCOMPRESSED_TCP: + ip = (struct ip *) *bufp; + if (ip->ip_p >= MAX_STATES) + goto bad; + cs = &comp->rstate[comp->last_recv = ip->ip_p]; + comp->flags &=~ SLF_TOSS; + ip->ip_p = IPPROTO_TCP; + hlen = ip->ip_hl; + hlen += ((struct tcphdr *)&((int *)ip)[hlen])->th_off; + hlen <<= 2; + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_ip.ip_sum = 0; + cs->cs_hlen = hlen; + INCR(sls_uncompressedin) + return (total_len); + + default: + goto bad; + + case TYPE_COMPRESSED_TCP: + break; + } + /* We've got a compressed packet. */ + INCR(sls_compressedin) + cp = *bufp; + changes = *cp++; + if (changes & NEW_C) { + /* Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. */ + if (*cp >= MAX_STATES) + goto bad; + + comp->flags &=~ SLF_TOSS; + comp->last_recv = *cp++; + } else { + /* this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. */ + if (comp->flags & SLF_TOSS) { + INCR(sls_tossed) + return (0); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = cs->cs_ip.ip_hl << 2; + th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->th_sum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) + th->th_flags |= TH_PUSH; + else + th->th_flags &=~ TH_PUSH; + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + th->th_ack = htonl(ntohl(th->th_ack) + i); + th->th_seq = htonl(ntohl(th->th_seq) + i); + } + break; + + case SPECIAL_D: + th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) + - cs->cs_hlen); + break; + + default: + if (changes & NEW_U) { + th->th_flags |= TH_URG; + DECODEU(th->th_urp) + } else + th->th_flags &=~ TH_URG; + if (changes & NEW_W) + DECODES(th->th_win) + if (changes & NEW_A) + DECODEL(th->th_ack) + if (changes & NEW_S) + DECODEL(th->th_seq) + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip.ip_id) + } else + cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1); + + /* + * At this point, cp points to the first byte of data in the + * packet. If we're not aligned on a 4-byte boundary, copy the + * data down so the ip & tcp headers will be aligned. Then back up + * cp by the tcp/ip header length to make room for the reconstructed + * header (we assume the packet we were handed has enough space to + * prepend 128 bytes of header). Adjust the length to account for + * the new header & fill in the IP total length. + */ + buflen -= (cp - *bufp); + total_len -= (cp - *bufp); + if (buflen < 0) + /* we must have dropped some characters (crc should detect + * this but the old slip framing won't) */ + goto bad; + + if ((int)cp & 3) { + if (buflen > 0) + (void) ovbcopy(cp, (caddr_t)((int)cp &~ 3), buflen); + cp = (u_char *)((int)cp &~ 3); + } + cp -= cs->cs_hlen; + total_len += cs->cs_hlen; + cs->cs_ip.ip_len = htons(total_len); + BCOPY(&cs->cs_ip, cp, cs->cs_hlen); + *bufp = cp; + + /* recompute the ip header checksum */ + { + register u_short *bp = (u_short *)cp; + for (changes = 0; hlen > 0; hlen -= 2) + changes += *bp++; + changes = (changes & 0xffff) + (changes >> 16); + changes = (changes & 0xffff) + (changes >> 16); + ((struct ip *)cp)->ip_sum = ~ changes; + } + return (total_len); +bad: + comp->flags |= SLF_TOSS; + INCR(sls_errorin) + return (0); +} diff --git a/sys/net/slcompress.h b/sys/net/slcompress.h new file mode 100644 index 000000000000..f713e47f32d1 --- /dev/null +++ b/sys/net/slcompress.h @@ -0,0 +1,166 @@ +/* slcompress.h 7.4 90/06/28 */ +/* + * Definitions for tcp compression routines. + * + * $Id: slcompress.h,v 1.2 1993/08/27 02:10:32 rgrimes Exp $ + * From: slcompress.h,v 1.13 1993/08/09 02:37:32 paulus Exp + * From: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp + * + * Copyright (c) 1989 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. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + * + * Paul Mackerras (paulus@cs.anu.edu.au), June 1993: + * - added sl_uncompress_tcp_part. + */ + +#define MAX_STATES 16 /* must be > 2 and < 256 */ +#define MAX_HDR MLEN /* XXX 4bsd-ism: should really be 128 */ + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used cstate (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip csu_ip; /* ip/tcp hdr from most recent packet */ + } slcs_u; +}; +#define cs_ip slcs_u.csu_ip +#define cs_hdr slcs_u.csu_hdr + +/* + * all the state data for one serial line (we need one of these + * per line). + */ +struct slcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; +#ifndef SL_NO_STATS + int sls_packets; /* outbound packets */ + int sls_compressed; /* outbound compressed packets */ + int sls_searches; /* searches for connection state */ + int sls_misses; /* times couldn't find conn. state */ + int sls_uncompressedin; /* inbound uncompressed packets */ + int sls_compressedin; /* inbound compressed packets */ + int sls_errorin; /* inbound unknown type packets */ + int sls_tossed; /* inbound packets tossed because of error */ +#endif + struct cstate tstate[MAX_STATES]; /* xmit connection states */ + struct cstate rstate[MAX_STATES]; /* receive connection states */ +}; +/* flag values */ +#define SLF_TOSS 1 /* tossing rcvd frames because of input err */ + +extern void sl_compress_init __P((struct slcompress *)); +extern u_char sl_compress_tcp __P((struct mbuf *m, struct ip *ip, + struct slcompress *, int comp_cid_flag)); +extern int sl_uncompress_tcp __P((u_char **bufp, int len, u_int type, + struct slcompress *)); +extern int sl_uncompress_tcp_part __P((u_char **bufp, int buflen, + int total_len, u_int type, + struct slcompress *)); diff --git a/sys/net/slip.h b/sys/net/slip.h new file mode 100644 index 000000000000..e70a6a4377d0 --- /dev/null +++ b/sys/net/slip.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * from: unknown + * $Id: slip.h,v 1.2 1993/10/16 17:43:44 rgrimes Exp $ + */ + +/* + * Definitions that user level programs might need to know to interact + * with serial line IP (slip) lines. + */ + +/* + * ioctl to get slip interface unit number (e.g., sl0, sl1, etc.) + * assigned to some terminal line with a slip module pushed on it. + */ +#ifdef __STDC__ +#define SLIOGUNIT _IOR('B', 1, int) +#else +#define SLIOGUNIT _IOR(B, 1, int) +#endif + +/* + * definitions of the pseudo- link-level header attached to slip + * packets grabbed by the packet filter (bpf) traffic monitor. + */ +#define SLIP_HDRLEN 16 + +#define SLX_DIR 0 +#define SLX_CHDR 1 +#define CHDR_LEN 15 + +#define SLIPDIR_IN 0 +#define SLIPDIR_OUT 1 + |
