summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett Wollman <wollman@FreeBSD.org>1995-09-21 17:19:28 +0000
committerGarrett Wollman <wollman@FreeBSD.org>1995-09-21 17:19:28 +0000
commitefe4b0eb615ac4074dbc728c95f7837e0b2e6ae3 (patch)
tree98f61fca9d20fc9cab107b4368411a7eb293a58c
Notes
-rw-r--r--sys/netinet/if_ether.c555
-rw-r--r--sys/netinet/if_ether.h222
-rw-r--r--sys/netinet/igmp.c317
-rw-r--r--sys/netinet/in.c622
-rw-r--r--sys/netinet/in_pcb.c497
-rw-r--r--sys/netinet/in_proto.c183
-rw-r--r--sys/netinet/in_var.h200
-rw-r--r--sys/netinet/ip.h168
-rw-r--r--sys/netinet/ip_mroute.h173
-rw-r--r--sys/netinet/ip_var.h188
-rw-r--r--sys/netinet/raw_ip.c411
-rw-r--r--sys/netinet/tcp_input.c1660
-rw-r--r--sys/netinet/tcp_output.c603
-rw-r--r--sys/netinet/tcp_seq.h76
-rw-r--r--sys/netinet/tcp_subr.c445
-rw-r--r--sys/netinet/tcp_timer.c336
-rw-r--r--sys/netinet/tcp_usrreq.c529
-rw-r--r--sys/netinet/tcp_var.h280
-rw-r--r--sys/netinet/udp_usrreq.c640
19 files changed, 8105 insertions, 0 deletions
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
new file mode 100644
index 000000000000..15749567caca
--- /dev/null
+++ b/sys/netinet/if_ether.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_ether.c 8.2 (Berkeley) 9/26/94
+ */
+
+/*
+ * Ethernet address resolution protocol.
+ * TODO:
+ * add "inuse/lock" bit (or ref. count) along with valid bit
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#define SIN(s) ((struct sockaddr_in *)s)
+#define SDL(s) ((struct sockaddr_dl *)s)
+#define SRP(s) ((struct sockaddr_inarp *)s)
+
+/*
+ * ARP trailer negotiation. Trailer protocol is not IP specific,
+ * but ARP request/response use IP addresses.
+ */
+#define ETHERTYPE_IPTRAILERS ETHERTYPE_TRAIL
+
+
+/* timer values */
+int arpt_prune = (5*60*1); /* walk list every 5 minutes */
+int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */
+int arpt_down = 20; /* once declared down, don't send for 20 secs */
+#define rt_expire rt_rmx.rmx_expire
+
+static void arprequest __P((struct arpcom *, u_long *, u_long *, u_char *));
+static void arptfree __P((struct llinfo_arp *));
+static void arptimer __P((void *));
+static struct llinfo_arp *arplookup __P((u_long, int, int));
+static void in_arpinput __P((struct mbuf *));
+
+extern struct ifnet loif;
+extern struct timeval time;
+struct llinfo_arp llinfo_arp = {&llinfo_arp, &llinfo_arp};
+struct ifqueue arpintrq = {0, 0, 0, 50};
+int arp_inuse, arp_allocated, arp_intimer;
+int arp_maxtries = 5;
+int useloopback = 1; /* use loopback interface for local traffic */
+int arpinit_done = 0;
+
+/*
+ * Timeout routine. Age arp_tab entries periodically.
+ */
+/* ARGSUSED */
+static void
+arptimer(ignored_arg)
+ void *ignored_arg;
+{
+ int s = splnet();
+ register struct llinfo_arp *la = llinfo_arp.la_next;
+
+ timeout(arptimer, (caddr_t)0, arpt_prune * hz);
+ while (la != &llinfo_arp) {
+ register struct rtentry *rt = la->la_rt;
+ la = la->la_next;
+ if (rt->rt_expire && rt->rt_expire <= time.tv_sec)
+ arptfree(la->la_prev); /* timer has expired, clear */
+ }
+ splx(s);
+}
+
+/*
+ * Parallel to llc_rtrequest.
+ */
+void
+arp_rtrequest(req, rt, sa)
+ int req;
+ register struct rtentry *rt;
+ struct sockaddr *sa;
+{
+ register struct sockaddr *gate = rt->rt_gateway;
+ register struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
+
+ if (!arpinit_done) {
+ arpinit_done = 1;
+ timeout(arptimer, (caddr_t)0, hz);
+ }
+ if (rt->rt_flags & RTF_GATEWAY)
+ return;
+ switch (req) {
+
+ case RTM_ADD:
+ /*
+ * XXX: If this is a manually added route to interface
+ * such as older version of routed or gated might provide,
+ * restore cloning bit.
+ */
+ if ((rt->rt_flags & RTF_HOST) == 0 &&
+ SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
+ rt->rt_flags |= RTF_CLONING;
+ if (rt->rt_flags & RTF_CLONING) {
+ /*
+ * Case 1: This route should come from a route to iface.
+ */
+ rt_setgate(rt, rt_key(rt),
+ (struct sockaddr *)&null_sdl);
+ gate = rt->rt_gateway;
+ SDL(gate)->sdl_type = rt->rt_ifp->if_type;
+ SDL(gate)->sdl_index = rt->rt_ifp->if_index;
+ rt->rt_expire = time.tv_sec;
+ break;
+ }
+ /* Announce a new entry if requested. */
+ if (rt->rt_flags & RTF_ANNOUNCE)
+ arprequest((struct arpcom *)rt->rt_ifp,
+ &SIN(rt_key(rt))->sin_addr.s_addr,
+ &SIN(rt_key(rt))->sin_addr.s_addr,
+ (u_char *)LLADDR(SDL(gate)));
+ /*FALLTHROUGH*/
+ case RTM_RESOLVE:
+ if (gate->sa_family != AF_LINK ||
+ gate->sa_len < sizeof(null_sdl)) {
+ log(LOG_DEBUG, "arp_rtrequest: bad gateway value");
+ break;
+ }
+ SDL(gate)->sdl_type = rt->rt_ifp->if_type;
+ SDL(gate)->sdl_index = rt->rt_ifp->if_index;
+ if (la != 0)
+ break; /* This happens on a route change */
+ /*
+ * Case 2: This route may come from cloning, or a manual route
+ * add with a LL address.
+ */
+ R_Malloc(la, struct llinfo_arp *, sizeof(*la));
+ rt->rt_llinfo = (caddr_t)la;
+ if (la == 0) {
+ log(LOG_DEBUG, "arp_rtrequest: malloc failed\n");
+ break;
+ }
+ arp_inuse++, arp_allocated++;
+ Bzero(la, sizeof(*la));
+ la->la_rt = rt;
+ rt->rt_flags |= RTF_LLINFO;
+ insque(la, &llinfo_arp);
+ if (SIN(rt_key(rt))->sin_addr.s_addr ==
+ (IA_SIN(rt->rt_ifa))->sin_addr.s_addr) {
+ /*
+ * This test used to be
+ * if (loif.if_flags & IFF_UP)
+ * It allowed local traffic to be forced
+ * through the hardware by configuring the loopback down.
+ * However, it causes problems during network configuration
+ * for boards that can't receive packets they send.
+ * It is now necessary to clear "useloopback" and remove
+ * the route to force traffic out to the hardware.
+ */
+ rt->rt_expire = 0;
+ Bcopy(((struct arpcom *)rt->rt_ifp)->ac_enaddr,
+ LLADDR(SDL(gate)), SDL(gate)->sdl_alen = 6);
+ if (useloopback)
+ rt->rt_ifp = &loif;
+
+ }
+ break;
+
+ case RTM_DELETE:
+ if (la == 0)
+ break;
+ arp_inuse--;
+ remque(la);
+ rt->rt_llinfo = 0;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (la->la_hold)
+ m_freem(la->la_hold);
+ Free((caddr_t)la);
+ }
+}
+
+/*
+ * Broadcast an ARP packet, asking who has addr on interface ac.
+ */
+void
+arpwhohas(ac, addr)
+ register struct arpcom *ac;
+ register struct in_addr *addr;
+{
+ arprequest(ac, &ac->ac_ipaddr.s_addr, &addr->s_addr, ac->ac_enaddr);
+}
+
+/*
+ * Broadcast an ARP request. Caller specifies:
+ * - arp header source ip address
+ * - arp header target ip address
+ * - arp header source ethernet address
+ */
+static void
+arprequest(ac, sip, tip, enaddr)
+ register struct arpcom *ac;
+ register u_long *sip, *tip;
+ register u_char *enaddr;
+{
+ register struct mbuf *m;
+ register struct ether_header *eh;
+ register struct ether_arp *ea;
+ struct sockaddr sa;
+
+ if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL)
+ return;
+ m->m_len = sizeof(*ea);
+ m->m_pkthdr.len = sizeof(*ea);
+ MH_ALIGN(m, sizeof(*ea));
+ ea = mtod(m, struct ether_arp *);
+ eh = (struct ether_header *)sa.sa_data;
+ bzero((caddr_t)ea, sizeof (*ea));
+ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
+ sizeof(eh->ether_dhost));
+ eh->ether_type = ETHERTYPE_ARP; /* if_output will swap */
+ ea->arp_hrd = htons(ARPHRD_ETHER);
+ ea->arp_pro = htons(ETHERTYPE_IP);
+ ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */
+ ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */
+ ea->arp_op = htons(ARPOP_REQUEST);
+ bcopy((caddr_t)enaddr, (caddr_t)ea->arp_sha, sizeof(ea->arp_sha));
+ bcopy((caddr_t)sip, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa));
+ bcopy((caddr_t)tip, (caddr_t)ea->arp_tpa, sizeof(ea->arp_tpa));
+ sa.sa_family = AF_UNSPEC;
+ sa.sa_len = sizeof(sa);
+ (*ac->ac_if.if_output)(&ac->ac_if, m, &sa, (struct rtentry *)0);
+}
+
+/*
+ * Resolve an IP address into an ethernet address. If success,
+ * desten is filled in. If there is no entry in arptab,
+ * set one up and broadcast a request for the IP address.
+ * Hold onto this mbuf and resend it once the address
+ * is finally resolved. A return value of 1 indicates
+ * that desten has been filled in and the packet should be sent
+ * normally; a 0 return indicates that the packet has been
+ * taken over here, either now or for later transmission.
+ */
+int
+arpresolve(ac, rt, m, dst, desten)
+ register struct arpcom *ac;
+ register struct rtentry *rt;
+ struct mbuf *m;
+ register struct sockaddr *dst;
+ register u_char *desten;
+{
+ register struct llinfo_arp *la;
+ struct sockaddr_dl *sdl;
+
+ if (m->m_flags & M_BCAST) { /* broadcast */
+ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
+ sizeof(etherbroadcastaddr));
+ return (1);
+ }
+ if (m->m_flags & M_MCAST) { /* multicast */
+ ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten);
+ return(1);
+ }
+ if (rt)
+ la = (struct llinfo_arp *)rt->rt_llinfo;
+ else {
+ if (la = arplookup(SIN(dst)->sin_addr.s_addr, 1, 0))
+ rt = la->la_rt;
+ }
+ if (la == 0 || rt == 0) {
+ log(LOG_DEBUG, "arpresolve: can't allocate llinfo");
+ m_freem(m);
+ return (0);
+ }
+ sdl = SDL(rt->rt_gateway);
+ /*
+ * Check the address family and length is valid, the address
+ * is resolved; otherwise, try to resolve.
+ */
+ if ((rt->rt_expire == 0 || rt->rt_expire > time.tv_sec) &&
+ sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {
+ bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
+ return 1;
+ }
+ /*
+ * There is an arptab entry, but no ethernet address
+ * response yet. Replace the held mbuf with this
+ * latest one.
+ */
+ if (la->la_hold)
+ m_freem(la->la_hold);
+ la->la_hold = m;
+ if (rt->rt_expire) {
+ rt->rt_flags &= ~RTF_REJECT;
+ if (la->la_asked == 0 || rt->rt_expire != time.tv_sec) {
+ rt->rt_expire = time.tv_sec;
+ if (la->la_asked++ < arp_maxtries)
+ arpwhohas(ac, &(SIN(dst)->sin_addr));
+ else {
+ rt->rt_flags |= RTF_REJECT;
+ rt->rt_expire += arpt_down;
+ la->la_asked = 0;
+ }
+
+ }
+ }
+ return (0);
+}
+
+/*
+ * Common length and type checks are done here,
+ * then the protocol-specific routine is called.
+ */
+void
+arpintr()
+{
+ register struct mbuf *m;
+ register struct arphdr *ar;
+ int s;
+
+ while (arpintrq.ifq_head) {
+ s = splimp();
+ IF_DEQUEUE(&arpintrq, m);
+ splx(s);
+ if (m == 0 || (m->m_flags & M_PKTHDR) == 0)
+ panic("arpintr");
+ if (m->m_len >= sizeof(struct arphdr) &&
+ (ar = mtod(m, struct arphdr *)) &&
+ ntohs(ar->ar_hrd) == ARPHRD_ETHER &&
+ m->m_len >=
+ sizeof(struct arphdr) + 2 * ar->ar_hln + 2 * ar->ar_pln)
+
+ switch (ntohs(ar->ar_pro)) {
+
+ case ETHERTYPE_IP:
+ case ETHERTYPE_IPTRAILERS:
+ in_arpinput(m);
+ continue;
+ }
+ m_freem(m);
+ }
+}
+
+/*
+ * ARP for Internet protocols on 10 Mb/s Ethernet.
+ * Algorithm is that given in RFC 826.
+ * In addition, a sanity check is performed on the sender
+ * protocol address, to catch impersonators.
+ * We no longer handle negotiations for use of trailer protocol:
+ * Formerly, ARP replied for protocol type ETHERTYPE_TRAIL sent
+ * along with IP replies if we wanted trailers sent to us,
+ * and also sent them in response to IP replies.
+ * This allowed either end to announce the desire to receive
+ * trailer packets.
+ * We no longer reply to requests for ETHERTYPE_TRAIL protocol either,
+ * but formerly didn't normally send requests.
+ */
+static void
+in_arpinput(m)
+ struct mbuf *m;
+{
+ register struct ether_arp *ea;
+ register struct arpcom *ac = (struct arpcom *)m->m_pkthdr.rcvif;
+ struct ether_header *eh;
+ register struct llinfo_arp *la = 0;
+ register struct rtentry *rt;
+ struct in_ifaddr *ia, *maybe_ia = 0;
+ struct sockaddr_dl *sdl;
+ struct sockaddr sa;
+ struct in_addr isaddr, itaddr, myaddr;
+ int op;
+
+ ea = mtod(m, struct ether_arp *);
+ op = ntohs(ea->arp_op);
+ bcopy((caddr_t)ea->arp_spa, (caddr_t)&isaddr, sizeof (isaddr));
+ bcopy((caddr_t)ea->arp_tpa, (caddr_t)&itaddr, sizeof (itaddr));
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == &ac->ac_if) {
+ maybe_ia = ia;
+ if ((itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) ||
+ (isaddr.s_addr == ia->ia_addr.sin_addr.s_addr))
+ break;
+ }
+ if (maybe_ia == 0)
+ goto out;
+ myaddr = ia ? ia->ia_addr.sin_addr : maybe_ia->ia_addr.sin_addr;
+ if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
+ sizeof (ea->arp_sha)))
+ goto out; /* it's from me, ignore it. */
+ if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)etherbroadcastaddr,
+ sizeof (ea->arp_sha))) {
+ log(LOG_ERR,
+ "arp: ether address is broadcast for IP address %x!\n",
+ ntohl(isaddr.s_addr));
+ goto out;
+ }
+ if (isaddr.s_addr == myaddr.s_addr) {
+ log(LOG_ERR,
+ "duplicate IP address %x!! sent from ethernet address: %s\n",
+ ntohl(isaddr.s_addr), ether_sprintf(ea->arp_sha));
+ itaddr = myaddr;
+ goto reply;
+ }
+ la = arplookup(isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, 0);
+ if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) {
+ if (sdl->sdl_alen &&
+ bcmp((caddr_t)ea->arp_sha, LLADDR(sdl), sdl->sdl_alen))
+ log(LOG_INFO, "arp info overwritten for %x by %s\n",
+ ntohl(isaddr.s_addr), ether_sprintf(ea->arp_sha));
+ bcopy((caddr_t)ea->arp_sha, LLADDR(sdl),
+ sdl->sdl_alen = sizeof(ea->arp_sha));
+ if (rt->rt_expire)
+ rt->rt_expire = time.tv_sec + arpt_keep;
+ rt->rt_flags &= ~RTF_REJECT;
+ la->la_asked = 0;
+ if (la->la_hold) {
+ (*ac->ac_if.if_output)(&ac->ac_if, la->la_hold,
+ rt_key(rt), rt);
+ la->la_hold = 0;
+ }
+ }
+reply:
+ if (op != ARPOP_REQUEST) {
+ out:
+ m_freem(m);
+ return;
+ }
+ if (itaddr.s_addr == myaddr.s_addr) {
+ /* I am the target */
+ bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
+ sizeof(ea->arp_sha));
+ bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
+ sizeof(ea->arp_sha));
+ } else {
+ la = arplookup(itaddr.s_addr, 0, SIN_PROXY);
+ if (la == NULL)
+ goto out;
+ rt = la->la_rt;
+ bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
+ sizeof(ea->arp_sha));
+ sdl = SDL(rt->rt_gateway);
+ bcopy(LLADDR(sdl), (caddr_t)ea->arp_sha, sizeof(ea->arp_sha));
+ }
+
+ bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa, sizeof(ea->arp_spa));
+ bcopy((caddr_t)&itaddr, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa));
+ ea->arp_op = htons(ARPOP_REPLY);
+ ea->arp_pro = htons(ETHERTYPE_IP); /* let's be sure! */
+ eh = (struct ether_header *)sa.sa_data;
+ bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost,
+ sizeof(eh->ether_dhost));
+ eh->ether_type = ETHERTYPE_ARP;
+ sa.sa_family = AF_UNSPEC;
+ sa.sa_len = sizeof(sa);
+ (*ac->ac_if.if_output)(&ac->ac_if, m, &sa, (struct rtentry *)0);
+ return;
+}
+
+/*
+ * Free an arp entry.
+ */
+static void
+arptfree(la)
+ register struct llinfo_arp *la;
+{
+ register struct rtentry *rt = la->la_rt;
+ register struct sockaddr_dl *sdl;
+ if (rt == 0)
+ panic("arptfree");
+ if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
+ sdl->sdl_family == AF_LINK) {
+ sdl->sdl_alen = 0;
+ la->la_asked = 0;
+ rt->rt_flags &= ~RTF_REJECT;
+ return;
+ }
+ rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),
+ 0, (struct rtentry **)0);
+}
+/*
+ * Lookup or enter a new address in arptab.
+ */
+static struct llinfo_arp *
+arplookup(addr, create, proxy)
+ u_long addr;
+ int create, proxy;
+{
+ register struct rtentry *rt;
+ static struct sockaddr_inarp sin = {sizeof(sin), AF_INET };
+
+ sin.sin_addr.s_addr = addr;
+ sin.sin_other = proxy ? SIN_PROXY : 0;
+ rt = rtalloc1((struct sockaddr *)&sin, create);
+ if (rt == 0)
+ return (0);
+ rt->rt_refcnt--;
+ if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
+ rt->rt_gateway->sa_family != AF_LINK) {
+ if (create)
+ log(LOG_DEBUG,
+ "arplookup couldn't create %x\n", ntohl(addr));
+ return (0);
+ }
+ return ((struct llinfo_arp *)rt->rt_llinfo);
+}
+
+int
+arpioctl(cmd, data)
+ int cmd;
+ caddr_t data;
+{
+ return (EOPNOTSUPP);
+}
diff --git a/sys/netinet/if_ether.h b/sys/netinet/if_ether.h
new file mode 100644
index 000000000000..8d44bf5f3950
--- /dev/null
+++ b/sys/netinet/if_ether.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_ether.h 8.3 (Berkeley) 5/2/95
+ */
+
+/*
+ * Structure of a 10Mb/s Ethernet header.
+ */
+struct ether_header {
+ u_char ether_dhost[6];
+ u_char ether_shost[6];
+ u_short ether_type;
+};
+
+#define ETHERTYPE_PUP 0x0200 /* PUP protocol */
+#define ETHERTYPE_IP 0x0800 /* IP protocol */
+#define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */
+#define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */
+
+/*
+ * The ETHERTYPE_NTRAILER packet types starting at ETHERTYPE_TRAIL have
+ * (type-ETHERTYPE_TRAIL)*512 bytes of data followed
+ * by an ETHER type (as given above) and then the (variable-length) header.
+ */
+#define ETHERTYPE_TRAIL 0x1000 /* Trailer packet */
+#define ETHERTYPE_NTRAILER 16
+
+#define ETHERMTU 1500
+#define ETHERMIN (60-14)
+
+#ifdef KERNEL
+/*
+ * Macro to map an IP multicast address to an Ethernet multicast address.
+ * The high-order 25 bits of the Ethernet address are statically assigned,
+ * and the low-order 23 bits are taken from the low end of the IP address.
+ */
+#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \
+ /* struct in_addr *ipaddr; */ \
+ /* u_char enaddr[6]; */ \
+{ \
+ (enaddr)[0] = 0x01; \
+ (enaddr)[1] = 0x00; \
+ (enaddr)[2] = 0x5e; \
+ (enaddr)[3] = ((u_char *)ipaddr)[1] & 0x7f; \
+ (enaddr)[4] = ((u_char *)ipaddr)[2]; \
+ (enaddr)[5] = ((u_char *)ipaddr)[3]; \
+}
+#endif
+
+/*
+ * Ethernet Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. Structure below is adapted
+ * to resolving internet addresses. Field names used correspond to
+ * RFC 826.
+ */
+struct ether_arp {
+ struct arphdr ea_hdr; /* fixed-size header */
+ u_char arp_sha[6]; /* sender hardware address */
+ u_char arp_spa[4]; /* sender protocol address */
+ u_char arp_tha[6]; /* target hardware address */
+ u_char arp_tpa[4]; /* target protocol address */
+};
+#define arp_hrd ea_hdr.ar_hrd
+#define arp_pro ea_hdr.ar_pro
+#define arp_hln ea_hdr.ar_hln
+#define arp_pln ea_hdr.ar_pln
+#define arp_op ea_hdr.ar_op
+
+
+/*
+ * Structure shared between the ethernet driver modules and
+ * the address resolution code. For example, each ec_softc or il_softc
+ * begins with this structure.
+ */
+struct arpcom {
+ struct ifnet ac_if; /* network-visible interface */
+ u_char ac_enaddr[6]; /* ethernet hardware address */
+ struct in_addr ac_ipaddr; /* copy of ip address- XXX */
+ struct ether_multi *ac_multiaddrs; /* list of ether multicast addrs */
+ int ac_multicnt; /* length of ac_multiaddrs list */
+};
+
+struct llinfo_arp {
+ struct llinfo_arp *la_next;
+ struct llinfo_arp *la_prev;
+ struct rtentry *la_rt;
+ struct mbuf *la_hold; /* last packet until resolved/timeout */
+ long la_asked; /* last time we QUERIED for this addr */
+#define la_timer la_rt->rt_rmx.rmx_expire /* deletion time in seconds */
+};
+
+struct sockaddr_inarp {
+ u_char sin_len;
+ u_char sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ struct in_addr sin_srcaddr;
+ u_short sin_tos;
+ u_short sin_other;
+#define SIN_PROXY 1
+};
+/*
+ * IP and ethernet specific routing flags
+ */
+#define RTF_USETRAILERS RTF_PROTO1 /* use trailers */
+#define RTF_ANNOUNCE RTF_PROTO2 /* announce new arp entry */
+
+#ifdef KERNEL
+u_char etherbroadcastaddr[6];
+u_char ether_ipmulticast_min[6];
+u_char ether_ipmulticast_max[6];
+struct ifqueue arpintrq;
+
+struct llinfo_arp llinfo_arp; /* head of the llinfo queue */
+
+void arp_rtrequest __P((int, struct rtentry *, struct sockaddr *));
+void arpintr __P((void));
+int arpresolve __P((struct arpcom *,
+ struct rtentry *, struct mbuf *, struct sockaddr *, u_char *));
+void arpwhohas __P((struct arpcom *, struct in_addr *));
+
+int ether_addmulti __P((struct ifreq *, struct arpcom *));
+int ether_delmulti __P((struct ifreq *, struct arpcom *));
+
+/*
+ * Ethernet multicast address structure. There is one of these for each
+ * multicast address or range of multicast addresses that we are supposed
+ * to listen to on a particular interface. They are kept in a linked list,
+ * rooted in the interface's arpcom structure. (This really has nothing to
+ * do with ARP, or with the Internet address family, but this appears to be
+ * the minimally-disrupting place to put it.)
+ */
+struct ether_multi {
+ u_char enm_addrlo[6]; /* low or only address of range */
+ u_char enm_addrhi[6]; /* high or only address of range */
+ struct arpcom *enm_ac; /* back pointer to arpcom */
+ u_int enm_refcount; /* no. claims to this addr/range */
+ struct ether_multi *enm_next; /* ptr to next ether_multi */
+};
+
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the ether_multi records.
+ */
+struct ether_multistep {
+ struct ether_multi *e_enm;
+};
+
+/*
+ * Macro for looking up the ether_multi record for a given range of Ethernet
+ * multicast addresses connected to a given arpcom structure. If no matching
+ * record is found, "enm" returns NULL.
+ */
+#define ETHER_LOOKUP_MULTI(addrlo, addrhi, ac, enm) \
+ /* u_char addrlo[6]; */ \
+ /* u_char addrhi[6]; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ for ((enm) = (ac)->ac_multiaddrs; \
+ (enm) != NULL && \
+ (bcmp((enm)->enm_addrlo, (addrlo), 6) != 0 || \
+ bcmp((enm)->enm_addrhi, (addrhi), 6) != 0); \
+ (enm) = (enm)->enm_next); \
+}
+
+/*
+ * Macro to step through all of the ether_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. ETHER_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "enm" when there
+ * are no remaining records.
+ */
+#define ETHER_NEXT_MULTI(step, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ if (((enm) = (step).e_enm) != NULL) \
+ (step).e_enm = (enm)->enm_next; \
+}
+
+#define ETHER_FIRST_MULTI(step, ac, enm) \
+ /* struct ether_multistep step; */ \
+ /* struct arpcom *ac; */ \
+ /* struct ether_multi *enm; */ \
+{ \
+ (step).e_enm = (ac)->ac_multiaddrs; \
+ ETHER_NEXT_MULTI((step), (enm)); \
+}
+
+#endif
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
new file mode 100644
index 000000000000..f3720852a2ba
--- /dev/null
+++ b/sys/netinet/igmp.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 1988 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)igmp.c 8.2 (Berkeley) 5/3/95
+ */
+
+/* Internet Group Management Protocol (IGMP) routines. */
+
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/igmp.h>
+#include <netinet/igmp_var.h>
+
+extern struct ifnet loif;
+
+static int igmp_timers_are_running = 0;
+static u_long igmp_all_hosts_group;
+
+static void igmp_sendreport __P((struct in_multi *));
+
+void
+igmp_init()
+{
+ /*
+ * To avoid byte-swapping the same value over and over again.
+ */
+ igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+}
+
+void
+igmp_input(m, iphlen)
+ register struct mbuf *m;
+ register int iphlen;
+{
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register int igmplen;
+ register struct ifnet *ifp = m->m_pkthdr.rcvif;
+ register int minlen;
+ register struct in_multi *inm;
+ register struct in_ifaddr *ia;
+ struct in_multistep step;
+
+ ++igmpstat.igps_rcv_total;
+
+ ip = mtod(m, struct ip *);
+ igmplen = ip->ip_len;
+
+ /*
+ * Validate lengths
+ */
+ if (igmplen < IGMP_MINLEN) {
+ ++igmpstat.igps_rcv_tooshort;
+ m_freem(m);
+ return;
+ }
+ minlen = iphlen + IGMP_MINLEN;
+ if ((m->m_flags & M_EXT || m->m_len < minlen) &&
+ (m = m_pullup(m, minlen)) == 0) {
+ ++igmpstat.igps_rcv_tooshort;
+ return;
+ }
+
+ /*
+ * Validate checksum
+ */
+ m->m_data += iphlen;
+ m->m_len -= iphlen;
+ igmp = mtod(m, struct igmp *);
+ if (in_cksum(m, igmplen)) {
+ ++igmpstat.igps_rcv_badsum;
+ m_freem(m);
+ return;
+ }
+ m->m_data -= iphlen;
+ m->m_len += iphlen;
+ ip = mtod(m, struct ip *);
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ ++igmpstat.igps_rcv_queries;
+
+ if (ifp == &loif)
+ break;
+
+ if (ip->ip_dst.s_addr != igmp_all_hosts_group) {
+ ++igmpstat.igps_rcv_badqueries;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * Start the timers in all of our membership records for
+ * the interface on which the query arrived, except those
+ * that are already running and those that belong to the
+ * "all-hosts" group.
+ */
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_ifp == ifp && inm->inm_timer == 0 &&
+ inm->inm_addr.s_addr != igmp_all_hosts_group) {
+ inm->inm_timer =
+ IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+
+ break;
+
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ ++igmpstat.igps_rcv_reports;
+
+ if (ifp == &loif)
+ break;
+
+ if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr)) ||
+ igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
+ ++igmpstat.igps_rcv_badreports;
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * KLUDGE: if the IP source address of the report has an
+ * unspecified (i.e., zero) subnet number, as is allowed for
+ * a booting host, replace it with the correct subnet number
+ * so that a process-level multicast routing demon can
+ * determine which subnet it arrived from. This is necessary
+ * to compensate for the lack of any way for a process to
+ * determine the arrival interface of an incoming packet.
+ */
+ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) {
+ IFP_TO_IA(ifp, ia);
+ if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
+ }
+
+ /*
+ * If we belong to the group being reported, stop
+ * our timer for that group.
+ */
+ IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
+ if (inm != NULL) {
+ inm->inm_timer = 0;
+ ++igmpstat.igps_rcv_ourreports;
+ }
+
+ break;
+ }
+
+ /*
+ * Pass all valid IGMP packets up to any process(es) listening
+ * on a raw IGMP socket.
+ */
+ rip_input(m);
+}
+
+void
+igmp_joingroup(inm)
+ struct in_multi *inm;
+{
+ register int s = splnet();
+
+ if (inm->inm_addr.s_addr == igmp_all_hosts_group ||
+ inm->inm_ifp == &loif)
+ inm->inm_timer = 0;
+ else {
+ igmp_sendreport(inm);
+ inm->inm_timer = IGMP_RANDOM_DELAY(inm->inm_addr);
+ igmp_timers_are_running = 1;
+ }
+ splx(s);
+}
+
+void
+igmp_leavegroup(inm)
+ struct in_multi *inm;
+{
+ /*
+ * No action required on leaving a group.
+ */
+}
+
+void
+igmp_fasttimo()
+{
+ register struct in_multi *inm;
+ register int s;
+ struct in_multistep step;
+
+ /*
+ * Quick check to see if any work needs to be done, in order
+ * to minimize the overhead of fasttimo processing.
+ */
+ if (!igmp_timers_are_running)
+ return;
+
+ s = splnet();
+ igmp_timers_are_running = 0;
+ IN_FIRST_MULTI(step, inm);
+ while (inm != NULL) {
+ if (inm->inm_timer == 0) {
+ /* do nothing */
+ } else if (--inm->inm_timer == 0) {
+ igmp_sendreport(inm);
+ } else {
+ igmp_timers_are_running = 1;
+ }
+ IN_NEXT_MULTI(step, inm);
+ }
+ splx(s);
+}
+
+static void
+igmp_sendreport(inm)
+ register struct in_multi *inm;
+{
+ register struct mbuf *m;
+ register struct igmp *igmp;
+ register struct ip *ip;
+ register struct ip_moptions *imo;
+ struct ip_moptions simo;
+
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+ /*
+ * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
+ * is smaller than mbuf size returned by MGETHDR.
+ */
+ m->m_data += max_linkhdr;
+ m->m_len = sizeof(struct ip) + IGMP_MINLEN;
+ m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
+ ip->ip_off = 0;
+ ip->ip_p = IPPROTO_IGMP;
+ ip->ip_src.s_addr = INADDR_ANY;
+ ip->ip_dst = inm->inm_addr;
+
+ m->m_data += sizeof(struct ip);
+ m->m_len -= sizeof(struct ip);
+ igmp = mtod(m, struct igmp *);
+ igmp->igmp_type = IGMP_HOST_MEMBERSHIP_REPORT;
+ igmp->igmp_code = 0;
+ igmp->igmp_group = inm->inm_addr;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
+ m->m_data -= sizeof(struct ip);
+ m->m_len += sizeof(struct ip);
+
+ imo = &simo;
+ bzero((caddr_t)imo, sizeof(*imo));
+ imo->imo_multicast_ifp = inm->inm_ifp;
+ imo->imo_multicast_ttl = 1;
+ /*
+ * Request loopback of the report if we are acting as a multicast
+ * router, so that the process-level routing demon can hear it.
+ */
+#ifdef MROUTING
+ {
+ extern struct socket *ip_mrouter;
+ imo->imo_multicast_loop = (ip_mrouter != NULL);
+ }
+#endif
+ ip_output(m, NULL, NULL, 0, imo);
+
+ ++igmpstat.igps_snd_reports;
+}
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
new file mode 100644
index 000000000000..374a6229cf77
--- /dev/null
+++ b/sys/netinet/in.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in.c 8.4 (Berkeley) 1/9/95
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#ifdef INET
+/*
+ * Return the network number from an internet address.
+ */
+u_long
+in_netof(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+ register u_long net;
+ register struct in_ifaddr *ia;
+
+ if (IN_CLASSA(i))
+ net = i & IN_CLASSA_NET;
+ else if (IN_CLASSB(i))
+ net = i & IN_CLASSB_NET;
+ else if (IN_CLASSC(i))
+ net = i & IN_CLASSC_NET;
+ else if (IN_CLASSD(i))
+ net = i & IN_CLASSD_NET;
+ else
+ return (0);
+
+ /*
+ * Check whether network is a subnet;
+ * if so, return subnet number.
+ */
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (net == ia->ia_net)
+ return (i & ia->ia_subnetmask);
+ return (net);
+}
+
+#ifndef SUBNETSARELOCAL
+#define SUBNETSARELOCAL 1
+#endif
+int subnetsarelocal = SUBNETSARELOCAL;
+/*
+ * Return 1 if an internet address is for a ``local'' host
+ * (one to which we have a connection). If subnetsarelocal
+ * is true, this includes other subnets of the local net.
+ * Otherwise, it includes only the directly-connected (sub)nets.
+ */
+in_localaddr(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+ register struct in_ifaddr *ia;
+
+ if (subnetsarelocal) {
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if ((i & ia->ia_netmask) == ia->ia_net)
+ return (1);
+ } else {
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if ((i & ia->ia_subnetmask) == ia->ia_subnet)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ */
+in_canforward(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+ register u_long net;
+
+ if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i))
+ return (0);
+ if (IN_CLASSA(i)) {
+ net = i & IN_CLASSA_NET;
+ if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Trim a mask in a sockaddr
+ */
+void
+in_socktrim(ap)
+struct sockaddr_in *ap;
+{
+ register char *cplim = (char *) &ap->sin_addr;
+ register char *cp = (char *) (&ap->sin_addr + 1);
+
+ ap->sin_len = 0;
+ while (--cp >= cplim)
+ if (*cp) {
+ (ap)->sin_len = cp - (char *) (ap) + 1;
+ break;
+ }
+}
+
+int in_interfaces; /* number of external internet interfaces */
+extern struct ifnet loif;
+
+/*
+ * Generic internet control operations (ioctl's).
+ * Ifp is 0 if not an interface-specific ioctl.
+ */
+/* ARGSUSED */
+in_control(so, cmd, data, ifp)
+ struct socket *so;
+ u_long cmd;
+ caddr_t data;
+ register struct ifnet *ifp;
+{
+ register struct ifreq *ifr = (struct ifreq *)data;
+ register struct in_ifaddr *ia = 0;
+ register struct ifaddr *ifa;
+ struct in_ifaddr *oia;
+ struct in_aliasreq *ifra = (struct in_aliasreq *)data;
+ struct sockaddr_in oldaddr;
+ int error, hostIsNew, maskIsNew;
+ u_long i;
+
+ /*
+ * Find address for this interface, if it exists.
+ */
+ if (ifp)
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+
+ switch (cmd) {
+
+ case SIOCAIFADDR:
+ case SIOCDIFADDR:
+ if (ifra->ifra_addr.sin_family == AF_INET)
+ for (oia = ia; ia; ia = ia->ia_next) {
+ if (ia->ia_ifp == ifp &&
+ ia->ia_addr.sin_addr.s_addr ==
+ ifra->ifra_addr.sin_addr.s_addr)
+ break;
+ }
+ if (cmd == SIOCDIFADDR && ia == 0)
+ return (EADDRNOTAVAIL);
+ /* FALLTHROUGH */
+ case SIOCSIFADDR:
+ case SIOCSIFNETMASK:
+ case SIOCSIFDSTADDR:
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+
+ if (ifp == 0)
+ panic("in_control");
+ if (ia == (struct in_ifaddr *)0) {
+ oia = (struct in_ifaddr *)
+ malloc(sizeof *oia, M_IFADDR, M_WAITOK);
+ if (oia == (struct in_ifaddr *)NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)oia, sizeof *oia);
+ if (ia = in_ifaddr) {
+ for ( ; ia->ia_next; ia = ia->ia_next)
+ continue;
+ ia->ia_next = oia;
+ } else
+ in_ifaddr = oia;
+ ia = oia;
+ if (ifa = ifp->if_addrlist) {
+ for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
+ continue;
+ ifa->ifa_next = (struct ifaddr *) ia;
+ } else
+ ifp->if_addrlist = (struct ifaddr *) ia;
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ ia->ia_ifa.ifa_dstaddr
+ = (struct sockaddr *)&ia->ia_dstaddr;
+ ia->ia_ifa.ifa_netmask
+ = (struct sockaddr *)&ia->ia_sockmask;
+ ia->ia_sockmask.sin_len = 8;
+ if (ifp->if_flags & IFF_BROADCAST) {
+ ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
+ ia->ia_broadaddr.sin_family = AF_INET;
+ }
+ ia->ia_ifp = ifp;
+ if (ifp != &loif)
+ in_interfaces++;
+ }
+ break;
+
+ case SIOCSIFBRDADDR:
+ if ((so->so_state & SS_PRIV) == 0)
+ return (EPERM);
+ /* FALLTHROUGH */
+
+ case SIOCGIFADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ if (ia == (struct in_ifaddr *)0)
+ return (EADDRNOTAVAIL);
+ break;
+ }
+ switch (cmd) {
+
+ case SIOCGIFADDR:
+ *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr;
+ break;
+
+ case SIOCGIFBRDADDR:
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return (EINVAL);
+ *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr;
+ break;
+
+ case SIOCGIFDSTADDR:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr;
+ break;
+
+ case SIOCGIFNETMASK:
+ *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask;
+ break;
+
+ case SIOCSIFDSTADDR:
+ if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
+ return (EINVAL);
+ oldaddr = ia->ia_dstaddr;
+ ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr;
+ if (ifp->if_ioctl && (error = (*ifp->if_ioctl)
+ (ifp, SIOCSIFDSTADDR, (caddr_t)ia))) {
+ ia->ia_dstaddr = oldaddr;
+ return (error);
+ }
+ if (ia->ia_flags & IFA_ROUTE) {
+ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ ia->ia_ifa.ifa_dstaddr =
+ (struct sockaddr *)&ia->ia_dstaddr;
+ rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP);
+ }
+ break;
+
+ case SIOCSIFBRDADDR:
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return (EINVAL);
+ ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr;
+ break;
+
+ case SIOCSIFADDR:
+ return (in_ifinit(ifp, ia,
+ (struct sockaddr_in *) &ifr->ifr_addr, 1));
+
+ case SIOCSIFNETMASK:
+ i = ifra->ifra_addr.sin_addr.s_addr;
+ ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i);
+ break;
+
+ case SIOCAIFADDR:
+ maskIsNew = 0;
+ hostIsNew = 1;
+ error = 0;
+ if (ia->ia_addr.sin_family == AF_INET) {
+ if (ifra->ifra_addr.sin_len == 0) {
+ ifra->ifra_addr = ia->ia_addr;
+ hostIsNew = 0;
+ } else if (ifra->ifra_addr.sin_addr.s_addr ==
+ ia->ia_addr.sin_addr.s_addr)
+ hostIsNew = 0;
+ }
+ if (ifra->ifra_mask.sin_len) {
+ in_ifscrub(ifp, ia);
+ ia->ia_sockmask = ifra->ifra_mask;
+ ia->ia_subnetmask =
+ ntohl(ia->ia_sockmask.sin_addr.s_addr);
+ maskIsNew = 1;
+ }
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ (ifra->ifra_dstaddr.sin_family == AF_INET)) {
+ in_ifscrub(ifp, ia);
+ ia->ia_dstaddr = ifra->ifra_dstaddr;
+ maskIsNew = 1; /* We lie; but the effect's the same */
+ }
+ if (ifra->ifra_addr.sin_family == AF_INET &&
+ (hostIsNew || maskIsNew))
+ error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
+ if ((ifp->if_flags & IFF_BROADCAST) &&
+ (ifra->ifra_broadaddr.sin_family == AF_INET))
+ ia->ia_broadaddr = ifra->ifra_broadaddr;
+ return (error);
+
+ case SIOCDIFADDR:
+ in_ifscrub(ifp, ia);
+ if ((ifa = ifp->if_addrlist) == (struct ifaddr *)ia)
+ ifp->if_addrlist = ifa->ifa_next;
+ else {
+ while (ifa->ifa_next &&
+ (ifa->ifa_next != (struct ifaddr *)ia))
+ ifa = ifa->ifa_next;
+ if (ifa->ifa_next)
+ ifa->ifa_next = ((struct ifaddr *)ia)->ifa_next;
+ else
+ printf("Couldn't unlink inifaddr from ifp\n");
+ }
+ oia = ia;
+ if (oia == (ia = in_ifaddr))
+ in_ifaddr = ia->ia_next;
+ else {
+ while (ia->ia_next && (ia->ia_next != oia))
+ ia = ia->ia_next;
+ if (ia->ia_next)
+ ia->ia_next = oia->ia_next;
+ else
+ printf("Didn't unlink inifadr from list\n");
+ }
+ IFAFREE((&oia->ia_ifa));
+ break;
+
+ default:
+ if (ifp == 0 || ifp->if_ioctl == 0)
+ return (EOPNOTSUPP);
+ return ((*ifp->if_ioctl)(ifp, cmd, data));
+ }
+ return (0);
+}
+
+/*
+ * Delete any existing route for an interface.
+ */
+void
+in_ifscrub(ifp, ia)
+ register struct ifnet *ifp;
+ register struct in_ifaddr *ia;
+{
+
+ if ((ia->ia_flags & IFA_ROUTE) == 0)
+ return;
+ if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST);
+ else
+ rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0);
+ ia->ia_flags &= ~IFA_ROUTE;
+}
+
+/*
+ * Initialize an interface's internet address
+ * and routing table entry.
+ */
+in_ifinit(ifp, ia, sin, scrub)
+ register struct ifnet *ifp;
+ register struct in_ifaddr *ia;
+ struct sockaddr_in *sin;
+ int scrub;
+{
+ register u_long i = ntohl(sin->sin_addr.s_addr);
+ struct sockaddr_in oldaddr;
+ int s = splimp(), flags = RTF_UP, error, ether_output();
+
+ oldaddr = ia->ia_addr;
+ ia->ia_addr = *sin;
+ /*
+ * Give the interface a chance to initialize
+ * if this is its first address,
+ * and to validate the address if necessary.
+ */
+ if (ifp->if_ioctl &&
+ (error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia))) {
+ splx(s);
+ ia->ia_addr = oldaddr;
+ return (error);
+ }
+ if (ifp->if_output == ether_output) { /* XXX: Another Kludge */
+ ia->ia_ifa.ifa_rtrequest = arp_rtrequest;
+ ia->ia_ifa.ifa_flags |= RTF_CLONING;
+ }
+ splx(s);
+ if (scrub) {
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr;
+ in_ifscrub(ifp, ia);
+ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
+ }
+ if (IN_CLASSA(i))
+ ia->ia_netmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(i))
+ ia->ia_netmask = IN_CLASSB_NET;
+ else
+ ia->ia_netmask = IN_CLASSC_NET;
+ /*
+ * The subnet mask usually includes at least the standard network part,
+ * but may may be smaller in the case of supernetting.
+ * If it is set, we believe it.
+ */
+ if (ia->ia_subnetmask == 0) {
+ ia->ia_subnetmask = ia->ia_netmask;
+ ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask);
+ } else
+ ia->ia_netmask &= ia->ia_subnetmask;
+ ia->ia_net = i & ia->ia_netmask;
+ ia->ia_subnet = i & ia->ia_subnetmask;
+ in_socktrim(&ia->ia_sockmask);
+ /*
+ * Add route for the network.
+ */
+ ia->ia_ifa.ifa_metric = ifp->if_metric;
+ if (ifp->if_flags & IFF_BROADCAST) {
+ ia->ia_broadaddr.sin_addr.s_addr =
+ htonl(ia->ia_subnet | ~ia->ia_subnetmask);
+ ia->ia_netbroadcast.s_addr =
+ htonl(ia->ia_net | ~ ia->ia_netmask);
+ } else if (ifp->if_flags & IFF_LOOPBACK) {
+ ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
+ flags |= RTF_HOST;
+ } else if (ifp->if_flags & IFF_POINTOPOINT) {
+ if (ia->ia_dstaddr.sin_family != AF_INET)
+ return (0);
+ flags |= RTF_HOST;
+ }
+ if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
+ ia->ia_flags |= IFA_ROUTE;
+ /*
+ * If the interface supports multicast, join the "all hosts"
+ * multicast group on that interface.
+ */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ struct in_addr addr;
+
+ addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
+ in_addmulti(&addr, ifp);
+ }
+ return (error);
+}
+
+
+/*
+ * Return 1 if the address might be a local broadcast address.
+ */
+in_broadcast(in, ifp)
+ struct in_addr in;
+ struct ifnet *ifp;
+{
+ register struct ifaddr *ifa;
+ u_long t;
+
+ if (in.s_addr == INADDR_BROADCAST ||
+ in.s_addr == INADDR_ANY)
+ return 1;
+ if ((ifp->if_flags & IFF_BROADCAST) == 0)
+ return 0;
+ t = ntohl(in.s_addr);
+ /*
+ * Look through the list of addresses for a match
+ * with a broadcast address.
+ */
+#define ia ((struct in_ifaddr *)ifa)
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == AF_INET &&
+ (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr ||
+ in.s_addr == ia->ia_netbroadcast.s_addr ||
+ /*
+ * Check for old-style (host 0) broadcast.
+ */
+ t == ia->ia_subnet || t == ia->ia_net))
+ return 1;
+ return (0);
+#undef ia
+}
+/*
+ * Add an address to the list of IP multicast addresses for a given interface.
+ */
+struct in_multi *
+in_addmulti(ap, ifp)
+ register struct in_addr *ap;
+ register struct ifnet *ifp;
+{
+ register struct in_multi *inm;
+ struct ifreq ifr;
+ struct in_ifaddr *ia;
+ int s = splnet();
+
+ /*
+ * See if address already in list.
+ */
+ IN_LOOKUP_MULTI(*ap, ifp, inm);
+ if (inm != NULL) {
+ /*
+ * Found it; just increment the reference count.
+ */
+ ++inm->inm_refcount;
+ }
+ else {
+ /*
+ * New address; allocate a new multicast record
+ * and link it into the interface's multicast list.
+ */
+ inm = (struct in_multi *)malloc(sizeof(*inm),
+ M_IPMADDR, M_NOWAIT);
+ if (inm == NULL) {
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_addr = *ap;
+ inm->inm_ifp = ifp;
+ inm->inm_refcount = 1;
+ IFP_TO_IA(ifp, ia);
+ if (ia == NULL) {
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ inm->inm_ia = ia;
+ inm->inm_next = ia->ia_multiaddrs;
+ ia->ia_multiaddrs = inm;
+ /*
+ * Ask the network driver to update its multicast reception
+ * filter appropriately for the new address.
+ */
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = *ap;
+ if ((ifp->if_ioctl == NULL) ||
+ (*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) {
+ ia->ia_multiaddrs = inm->inm_next;
+ free(inm, M_IPMADDR);
+ splx(s);
+ return (NULL);
+ }
+ /*
+ * Let IGMP know that we have joined a new IP multicast group.
+ */
+ igmp_joingroup(inm);
+ }
+ splx(s);
+ return (inm);
+}
+
+/*
+ * Delete a multicast address record.
+ */
+int
+in_delmulti(inm)
+ register struct in_multi *inm;
+{
+ register struct in_multi **p;
+ struct ifreq ifr;
+ int s = splnet();
+
+ if (--inm->inm_refcount == 0) {
+ /*
+ * No remaining claims to this record; let IGMP know that
+ * we are leaving the multicast group.
+ */
+ igmp_leavegroup(inm);
+ /*
+ * Unlink from list.
+ */
+ for (p = &inm->inm_ia->ia_multiaddrs;
+ *p != inm;
+ p = &(*p)->inm_next)
+ continue;
+ *p = (*p)->inm_next;
+ /*
+ * Notify the network driver to update its multicast reception
+ * filter.
+ */
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_family = AF_INET;
+ ((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr =
+ inm->inm_addr;
+ (*inm->inm_ifp->if_ioctl)(inm->inm_ifp, SIOCDELMULTI,
+ (caddr_t)&ifr);
+ free(inm, M_IPMADDR);
+ }
+ splx(s);
+}
+#endif
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
new file mode 100644
index 000000000000..bfc293c4c3c1
--- /dev/null
+++ b/sys/netinet/in_pcb.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 1982, 1986, 1991, 1993, 1995
+ * 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.
+ *
+ * @(#)in_pcb.c 8.4 (Berkeley) 5/24/95
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+struct in_addr zeroin_addr;
+
+int
+in_pcballoc(so, head)
+ struct socket *so;
+ struct inpcb *head;
+{
+ register struct inpcb *inp;
+
+ MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
+ if (inp == NULL)
+ return (ENOBUFS);
+ bzero((caddr_t)inp, sizeof(*inp));
+ inp->inp_head = head;
+ inp->inp_socket = so;
+ insque(inp, head);
+ so->so_pcb = (caddr_t)inp;
+ return (0);
+}
+
+int
+in_pcbbind(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct socket *so = inp->inp_socket;
+ register struct inpcb *head = inp->inp_head;
+ register struct sockaddr_in *sin;
+ struct proc *p = curproc; /* XXX */
+ u_short lport = 0;
+ int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
+ int error;
+
+ if (in_ifaddr == 0)
+ return (EADDRNOTAVAIL);
+ if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
+ return (EINVAL);
+ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
+ ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+ (so->so_options & SO_ACCEPTCONN) == 0))
+ wild = INPLOOKUP_WILDCARD;
+ if (nam) {
+ sin = mtod(nam, struct sockaddr_in *);
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+#ifdef notdef
+ /*
+ * We should check the family, but old programs
+ * incorrectly fail to initialize it.
+ */
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+#endif
+ lport = sin->sin_port;
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
+ /*
+ * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
+ * allow complete duplication of binding if
+ * SO_REUSEPORT is set, or if SO_REUSEADDR is set
+ * and a multicast address is bound on both
+ * new and duplicated sockets.
+ */
+ if (so->so_options & SO_REUSEADDR)
+ reuseport = SO_REUSEADDR|SO_REUSEPORT;
+ } else if (sin->sin_addr.s_addr != INADDR_ANY) {
+ sin->sin_port = 0; /* yech... */
+ if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ return (EADDRNOTAVAIL);
+ }
+ if (lport) {
+ struct inpcb *t;
+
+ /* GROSS */
+ if (ntohs(lport) < IPPORT_RESERVED &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return (EACCES);
+ t = in_pcblookup(head, zeroin_addr, 0,
+ sin->sin_addr, lport, wild);
+ if (t && (reuseport & t->inp_socket->so_options) == 0)
+ return (EADDRINUSE);
+ }
+ inp->inp_laddr = sin->sin_addr;
+ }
+ if (lport == 0)
+ do {
+ if (head->inp_lport++ < IPPORT_RESERVED ||
+ head->inp_lport > IPPORT_USERRESERVED)
+ head->inp_lport = IPPORT_RESERVED;
+ lport = htons(head->inp_lport);
+ } while (in_pcblookup(head,
+ zeroin_addr, 0, inp->inp_laddr, lport, wild));
+ inp->inp_lport = lport;
+ return (0);
+}
+
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
+int
+in_pcbconnect(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ struct in_ifaddr *ia;
+ struct sockaddr_in *ifaddr;
+ register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof (*sin))
+ return (EINVAL);
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (sin->sin_port == 0)
+ return (EADDRNOTAVAIL);
+ if (in_ifaddr) {
+ /*
+ * If the destination address is INADDR_ANY,
+ * use the primary local address.
+ * If the supplied address is INADDR_BROADCAST,
+ * and the primary interface supports broadcast,
+ * choose the broadcast address for that interface.
+ */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+#define sintosa(sin) ((struct sockaddr *)(sin))
+#define ifatoia(ifa) ((struct in_ifaddr *)(ifa))
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+ else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+ (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+ sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+ }
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ register struct route *ro;
+
+ ia = (struct in_ifaddr *)0;
+ /*
+ * If route is known or can be allocated now,
+ * our src addr is taken from the i/f, else punt.
+ */
+ ro = &inp->inp_route;
+ if (ro->ro_rt &&
+ (satosin(&ro->ro_dst)->sin_addr.s_addr !=
+ sin->sin_addr.s_addr ||
+ inp->inp_socket->so_options & SO_DONTROUTE)) {
+ RTFREE(ro->ro_rt);
+ ro->ro_rt = (struct rtentry *)0;
+ }
+ if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
+ (ro->ro_rt == (struct rtentry *)0 ||
+ ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
+ /* No route yet, so try to acquire one */
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ sin->sin_addr;
+ rtalloc(ro);
+ }
+ /*
+ * If we found a route, use the address
+ * corresponding to the outgoing interface
+ * unless it is the loopback (in case a route
+ * to our address on another net goes to loopback).
+ */
+ if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
+ ia = ifatoia(ro->ro_rt->rt_ifa);
+ if (ia == 0) {
+ u_short fport = sin->sin_port;
+
+ sin->sin_port = 0;
+ ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
+ if (ia == 0)
+ ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
+ sin->sin_port = fport;
+ if (ia == 0)
+ ia = in_ifaddr;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ /*
+ * If the destination address is multicast and an outgoing
+ * interface has been set as a multicast option, use the
+ * address of that interface as our source address.
+ */
+ if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
+ inp->inp_moptions != NULL) {
+ struct ip_moptions *imo;
+ struct ifnet *ifp;
+
+ imo = inp->inp_moptions;
+ if (imo->imo_multicast_ifp != NULL) {
+ ifp = imo->imo_multicast_ifp;
+ for (ia = in_ifaddr; ia; ia = ia->ia_next)
+ if (ia->ia_ifp == ifp)
+ break;
+ if (ia == 0)
+ return (EADDRNOTAVAIL);
+ }
+ }
+ ifaddr = (struct sockaddr_in *)&ia->ia_addr;
+ }
+ if (in_pcblookup(inp->inp_head,
+ sin->sin_addr,
+ sin->sin_port,
+ inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+ inp->inp_lport,
+ 0))
+ return (EADDRINUSE);
+ if (inp->inp_laddr.s_addr == INADDR_ANY) {
+ if (inp->inp_lport == 0)
+ (void)in_pcbbind(inp, (struct mbuf *)0);
+ inp->inp_laddr = ifaddr->sin_addr;
+ }
+ inp->inp_faddr = sin->sin_addr;
+ inp->inp_fport = sin->sin_port;
+ return (0);
+}
+
+int
+in_pcbdisconnect(inp)
+ struct inpcb *inp;
+{
+
+ inp->inp_faddr.s_addr = INADDR_ANY;
+ inp->inp_fport = 0;
+ if (inp->inp_socket->so_state & SS_NOFDREF)
+ in_pcbdetach(inp);
+}
+
+int
+in_pcbdetach(inp)
+ struct inpcb *inp;
+{
+ struct socket *so = inp->inp_socket;
+
+ so->so_pcb = 0;
+ sofree(so);
+ if (inp->inp_options)
+ (void)m_free(inp->inp_options);
+ if (inp->inp_route.ro_rt)
+ rtfree(inp->inp_route.ro_rt);
+ ip_freemoptions(inp->inp_moptions);
+ remque(inp);
+ FREE(inp, M_PCB);
+}
+
+int
+in_setsockaddr(inp, nam)
+ register struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_lport;
+ sin->sin_addr = inp->inp_laddr;
+}
+
+int
+in_setpeeraddr(inp, nam)
+ struct inpcb *inp;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *sin;
+
+ nam->m_len = sizeof (*sin);
+ sin = mtod(nam, struct sockaddr_in *);
+ bzero((caddr_t)sin, sizeof (*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_port = inp->inp_fport;
+ sin->sin_addr = inp->inp_faddr;
+}
+
+/*
+ * Pass some notification to all connections of a protocol
+ * associated with address dst. The local address and/or port numbers
+ * may be specified to limit the search. The "usual action" will be
+ * taken, depending on the ctlinput cmd. The caller must filter any
+ * cmds that are uninteresting (e.g., no error in the map).
+ * Call the protocol specific routine (if any) to report
+ * any errors for each matching socket.
+ *
+ * Must be called at splnet.
+ */
+int
+in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
+ struct inpcb *head;
+ struct sockaddr *dst;
+ u_int fport_arg, lport_arg;
+ struct in_addr laddr;
+ int cmd;
+ void (*notify) __P((struct inpcb *, int));
+{
+ extern u_char inetctlerrmap[];
+ register struct inpcb *inp, *oinp;
+ struct in_addr faddr;
+ u_short fport = fport_arg, lport = lport_arg;
+ int errno;
+
+ if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
+ return;
+ faddr = ((struct sockaddr_in *)dst)->sin_addr;
+ if (faddr.s_addr == INADDR_ANY)
+ return;
+
+ /*
+ * Redirects go to all references to the destination,
+ * and use in_rtchange to invalidate the route cache.
+ * Dead host indications: notify all references to the destination.
+ * Otherwise, if we have knowledge of the local port and address,
+ * deliver only to that socket.
+ */
+ if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
+ fport = 0;
+ lport = 0;
+ laddr.s_addr = 0;
+ if (cmd != PRC_HOSTDEAD)
+ notify = in_rtchange;
+ }
+ errno = inetctlerrmap[cmd];
+ for (inp = head->inp_next; inp != head;) {
+ if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_socket == 0 ||
+ (lport && inp->inp_lport != lport) ||
+ (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
+ (fport && inp->inp_fport != fport)) {
+ inp = inp->inp_next;
+ continue;
+ }
+ oinp = inp;
+ inp = inp->inp_next;
+ if (notify)
+ (*notify)(oinp, errno);
+ }
+}
+
+/*
+ * Check for alternatives when higher level complains
+ * about service problems. For now, invalidate cached
+ * routing information. If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+int
+in_losing(inp)
+ struct inpcb *inp;
+{
+ register struct rtentry *rt;
+ struct rt_addrinfo info;
+
+ if ((rt = inp->inp_route.ro_rt)) {
+ inp->inp_route.ro_rt = 0;
+ bzero((caddr_t)&info, sizeof(info));
+ info.rti_info[RTAX_DST] =
+ (struct sockaddr *)&inp->inp_route.ro_dst;
+ info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+ info.rti_info[RTAX_NETMASK] = rt_mask(rt);
+ rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
+ if (rt->rt_flags & RTF_DYNAMIC)
+ (void) rtrequest(RTM_DELETE, rt_key(rt),
+ rt->rt_gateway, rt_mask(rt), rt->rt_flags,
+ (struct rtentry **)0);
+ else
+ /*
+ * A new route can be allocated
+ * the next time output is attempted.
+ */
+ rtfree(rt);
+ }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+void
+in_rtchange(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ if (inp->inp_route.ro_rt) {
+ rtfree(inp->inp_route.ro_rt);
+ inp->inp_route.ro_rt = 0;
+ /*
+ * A new route can be allocated the next time
+ * output is attempted.
+ */
+ }
+}
+
+struct inpcb *
+in_pcblookup(head, faddr, fport_arg, laddr, lport_arg, flags)
+ struct inpcb *head;
+ struct in_addr faddr, laddr;
+ u_int fport_arg, lport_arg;
+ int flags;
+{
+ register struct inpcb *inp, *match = 0;
+ int matchwild = 3, wildcard;
+ u_short fport = fport_arg, lport = lport_arg;
+
+ for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
+ if (inp->inp_lport != lport)
+ continue;
+ wildcard = 0;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (laddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_laddr.s_addr != laddr.s_addr)
+ continue;
+ } else {
+ if (laddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (faddr.s_addr == INADDR_ANY)
+ wildcard++;
+ else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+ inp->inp_fport != fport)
+ continue;
+ } else {
+ if (faddr.s_addr != INADDR_ANY)
+ wildcard++;
+ }
+ if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
+ continue;
+ if (wildcard < matchwild) {
+ match = inp;
+ matchwild = wildcard;
+ if (matchwild == 0)
+ break;
+ }
+ }
+ return (match);
+}
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
new file mode 100644
index 000000000000..c24036de0f9b
--- /dev/null
+++ b/sys/netinet/in_proto.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_proto.c 8.2 (Berkeley) 2/9/95
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/radix.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/in_pcb.h>
+#include <netinet/igmp_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+/*
+ * TCP/IP protocol family: IP, ICMP, UDP, TCP.
+ */
+
+#ifdef NSIP
+void idpip_input(), nsip_ctlinput();
+#endif
+
+#ifdef TPIP
+void tpip_input(), tpip_ctlinput(), tp_init(), tp_slowtimo(), tp_drain();
+int tp_ctloutput(), tp_usrreq();
+#endif
+
+#ifdef EON
+void eoninput(), eonctlinput(), eonprotoinit();
+#endif /* EON */
+
+extern struct domain inetdomain;
+
+struct protosw inetsw[] = {
+{ 0, &inetdomain, 0, 0,
+ 0, ip_output, 0, 0,
+ 0,
+ ip_init, 0, ip_slowtimo, ip_drain, ip_sysctl
+},
+{ SOCK_DGRAM, &inetdomain, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,
+ udp_input, 0, udp_ctlinput, ip_ctloutput,
+ udp_usrreq,
+ udp_init, 0, 0, 0, udp_sysctl
+},
+{ SOCK_STREAM, &inetdomain, IPPROTO_TCP, PR_CONNREQUIRED|PR_WANTRCVD,
+ tcp_input, 0, tcp_ctlinput, tcp_ctloutput,
+ tcp_usrreq,
+ tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
+},
+{ SOCK_RAW, &inetdomain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
+ rip_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ 0, 0, 0, 0,
+},
+{ SOCK_RAW, &inetdomain, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR,
+ icmp_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ 0, 0, 0, 0, icmp_sysctl
+},
+{ SOCK_RAW, &inetdomain, IPPROTO_IGMP, PR_ATOMIC|PR_ADDR,
+ igmp_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ igmp_init, igmp_fasttimo, 0, 0,
+},
+#ifdef TPIP
+{ SOCK_SEQPACKET,&inetdomain, IPPROTO_TP, PR_CONNREQUIRED|PR_WANTRCVD,
+ tpip_input, 0, tpip_ctlinput, tp_ctloutput,
+ tp_usrreq,
+ tp_init, 0, tp_slowtimo, tp_drain,
+},
+#endif
+/* EON (ISO CLNL over IP) */
+#ifdef EON
+{ SOCK_RAW, &inetdomain, IPPROTO_EON, 0,
+ eoninput, 0, eonctlinput, 0,
+ 0,
+ eonprotoinit, 0, 0, 0,
+},
+#endif
+#ifdef NSIP
+{ SOCK_RAW, &inetdomain, IPPROTO_IDP, PR_ATOMIC|PR_ADDR,
+ idpip_input, rip_output, nsip_ctlinput, 0,
+ rip_usrreq,
+ 0, 0, 0, 0,
+},
+#endif
+ /* raw wildcard */
+{ SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR,
+ rip_input, rip_output, 0, rip_ctloutput,
+ rip_usrreq,
+ rip_init, 0, 0, 0,
+},
+};
+
+struct domain inetdomain =
+ { AF_INET, "internet", 0, 0, 0,
+ inetsw, &inetsw[sizeof(inetsw)/sizeof(inetsw[0])], 0,
+ rn_inithead, 32, sizeof(struct sockaddr_in) };
+
+#include "imp.h"
+#if NIMP > 0
+extern struct domain impdomain;
+int rimp_output(), hostslowtimo();
+
+struct protosw impsw[] = {
+{ SOCK_RAW, &impdomain, 0, PR_ATOMIC|PR_ADDR,
+ 0, rimp_output, 0, 0,
+ rip_usrreq,
+ 0, 0, hostslowtimo, 0,
+},
+};
+
+struct domain impdomain =
+ { AF_IMPLINK, "imp", 0, 0, 0,
+ impsw, &impsw[sizeof (impsw)/sizeof(impsw[0])] };
+#endif
+
+#include "hy.h"
+#if NHY > 0
+/*
+ * HYPERchannel protocol family: raw interface.
+ */
+int rhy_output();
+extern struct domain hydomain;
+
+struct protosw hysw[] = {
+{ SOCK_RAW, &hydomain, 0, PR_ATOMIC|PR_ADDR,
+ 0, rhy_output, 0, 0,
+ rip_usrreq,
+ 0, 0, 0, 0,
+},
+};
+
+struct domain hydomain =
+ { AF_HYLINK, "hy", 0, 0, 0, hysw, &hysw[sizeof (hysw)/sizeof(hysw[0])] };
+#endif
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h
new file mode 100644
index 000000000000..f816a71e28ef
--- /dev/null
+++ b/sys/netinet/in_var.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1985, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_var.h 8.2 (Berkeley) 1/9/95
+ */
+
+/*
+ * Interface address, Internet version. One of these structures
+ * is allocated for each interface with an Internet address.
+ * The ifaddr structure contains the protocol-independent part
+ * of the structure and is assumed to be first.
+ */
+struct in_ifaddr {
+ struct ifaddr ia_ifa; /* protocol-independent info */
+#define ia_ifp ia_ifa.ifa_ifp
+#define ia_flags ia_ifa.ifa_flags
+ /* ia_{,sub}net{,mask} in host order */
+ u_long ia_net; /* network number of interface */
+ u_long ia_netmask; /* mask of net part */
+ u_long ia_subnet; /* subnet number, including net */
+ u_long ia_subnetmask; /* mask of subnet part */
+ struct in_addr ia_netbroadcast; /* to recognize net broadcasts */
+ struct in_ifaddr *ia_next; /* next in list of internet addresses */
+ struct sockaddr_in ia_addr; /* reserve space for interface name */
+ struct sockaddr_in ia_dstaddr; /* reserve space for broadcast addr */
+#define ia_broadaddr ia_dstaddr
+ struct sockaddr_in ia_sockmask; /* reserve space for general netmask */
+ struct in_multi *ia_multiaddrs; /* list of multicast addresses */
+};
+
+struct in_aliasreq {
+ char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ struct sockaddr_in ifra_addr;
+ struct sockaddr_in ifra_broadaddr;
+#define ifra_dstaddr ifra_broadaddr
+ struct sockaddr_in ifra_mask;
+};
+/*
+ * Given a pointer to an in_ifaddr (ifaddr),
+ * return a pointer to the addr as a sockaddr_in.
+ */
+#define IA_SIN(ia) (&(((struct in_ifaddr *)(ia))->ia_addr))
+
+#define IN_LNAOF(in, ifa) \
+ ((ntohl((in).s_addr) & ~((struct in_ifaddr *)(ifa)->ia_subnetmask))
+
+
+#ifdef KERNEL
+extern struct in_ifaddr *in_ifaddr;
+extern struct ifqueue ipintrq; /* ip packet input queue */
+void in_socktrim __P((struct sockaddr_in *));
+
+
+/*
+ * Macro for finding the interface (ifnet structure) corresponding to one
+ * of our IP addresses.
+ */
+#define INADDR_TO_IFP(addr, ifp) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ for (ia = in_ifaddr; \
+ ia != NULL && IA_SIN(ia)->sin_addr.s_addr != (addr).s_addr; \
+ ia = ia->ia_next) \
+ continue; \
+ (ifp) = (ia == NULL) ? NULL : ia->ia_ifp; \
+}
+
+/*
+ * Macro for finding the internet address structure (in_ifaddr) corresponding
+ * to a given interface (ifnet structure).
+ */
+#define IFP_TO_IA(ifp, ia) \
+ /* struct ifnet *ifp; */ \
+ /* struct in_ifaddr *ia; */ \
+{ \
+ for ((ia) = in_ifaddr; \
+ (ia) != NULL && (ia)->ia_ifp != (ifp); \
+ (ia) = (ia)->ia_next) \
+ continue; \
+}
+#endif
+
+/*
+ * Internet multicast address structure. There is one of these for each IP
+ * multicast group to which this host belongs on a given network interface.
+ * They are kept in a linked list, rooted in the interface's in_ifaddr
+ * structure.
+ */
+struct in_multi {
+ struct in_addr inm_addr; /* IP multicast address */
+ struct ifnet *inm_ifp; /* back pointer to ifnet */
+ struct in_ifaddr *inm_ia; /* back pointer to in_ifaddr */
+ u_int inm_refcount; /* no. membership claims by sockets */
+ u_int inm_timer; /* IGMP membership report timer */
+ struct in_multi *inm_next; /* ptr to next multicast address */
+};
+
+#ifdef KERNEL
+/*
+ * Structure used by macros below to remember position when stepping through
+ * all of the in_multi records.
+ */
+struct in_multistep {
+ struct in_ifaddr *i_ia;
+ struct in_multi *i_inm;
+};
+
+/*
+ * Macro for looking up the in_multi record for a given IP multicast address
+ * on a given interface. If no matching record is found, "inm" returns NULL.
+ */
+#define IN_LOOKUP_MULTI(addr, ifp, inm) \
+ /* struct in_addr addr; */ \
+ /* struct ifnet *ifp; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ register struct in_ifaddr *ia; \
+\
+ IFP_TO_IA((ifp), ia); \
+ if (ia == NULL) \
+ (inm) = NULL; \
+ else \
+ for ((inm) = ia->ia_multiaddrs; \
+ (inm) != NULL && (inm)->inm_addr.s_addr != (addr).s_addr; \
+ (inm) = inm->inm_next) \
+ continue; \
+}
+
+/*
+ * Macro to step through all of the in_multi records, one at a time.
+ * The current position is remembered in "step", which the caller must
+ * provide. IN_FIRST_MULTI(), below, must be called to initialize "step"
+ * and get the first record. Both macros return a NULL "inm" when there
+ * are no remaining records.
+ */
+#define IN_NEXT_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ if (((inm) = (step).i_inm) != NULL) \
+ (step).i_inm = (inm)->inm_next; \
+ else \
+ while ((step).i_ia != NULL) { \
+ (inm) = (step).i_ia->ia_multiaddrs; \
+ (step).i_ia = (step).i_ia->ia_next; \
+ if ((inm) != NULL) { \
+ (step).i_inm = (inm)->inm_next; \
+ break; \
+ } \
+ } \
+}
+
+#define IN_FIRST_MULTI(step, inm) \
+ /* struct in_multistep step; */ \
+ /* struct in_multi *inm; */ \
+{ \
+ (step).i_ia = in_ifaddr; \
+ (step).i_inm = NULL; \
+ IN_NEXT_MULTI((step), (inm)); \
+}
+
+int in_ifinit __P((struct ifnet *,
+ struct in_ifaddr *, struct sockaddr_in *, int));
+struct in_multi *in_addmulti __P((struct in_addr *, struct ifnet *));
+int in_delmulti __P((struct in_multi *));
+void in_ifscrub __P((struct ifnet *, struct in_ifaddr *));
+int in_control __P((struct socket *, u_long, caddr_t, struct ifnet *));
+#endif
diff --git a/sys/netinet/ip.h b/sys/netinet/ip.h
new file mode 100644
index 000000000000..347cb222a287
--- /dev/null
+++ b/sys/netinet/ip.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.2 (Berkeley) 6/1/94
+ */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ *
+ * We declare ip_len and ip_off to be short, rather than u_short
+ * pragmatically since otherwise unsigned comparisons can result
+ * against negative integers quite easily, and fail in subtle ways.
+ */
+struct ip {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char ip_hl:4, /* header length */
+ ip_v:4; /* version */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char ip_v:4, /* version */
+ ip_hl:4; /* header length */
+#endif
+ u_char ip_tos; /* type of service */
+ short ip_len; /* total length */
+ u_short ip_id; /* identification */
+ short ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* dont fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ u_char ip_ttl; /* time to live */
+ u_char ip_p; /* protocol */
+ u_short ip_sum; /* checksum */
+ struct in_addr ip_src,ip_dst; /* source and dest address */
+};
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+/*
+ * Definitions for IP precedence (also in ip_tos) (hopefully unused)
+ */
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ u_char ipt_code; /* IPOPT_TS */
+ u_char ipt_len; /* size of structure (variable) */
+ u_char ipt_ptr; /* index of current entry */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char ipt_flg:4, /* flags, see below */
+ ipt_oflw:4; /* overflow counter */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char ipt_oflw:4, /* overflow counter */
+ ipt_flg:4; /* flags, see below */
+#endif
+ union ipt_timestamp {
+ n_long ipt_time[1];
+ struct ipt_ta {
+ struct in_addr ipt_addr;
+ n_long ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+};
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h
new file mode 100644
index 000000000000..44db850863b4
--- /dev/null
+++ b/sys/netinet/ip_mroute.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1989 Stephen Deering.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * 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.
+ *
+ * @(#)ip_mroute.h 8.2 (Berkeley) 4/28/95
+ */
+
+/*
+ * Definitions for the kernel part of DVMRP,
+ * a Distance-Vector Multicast Routing Protocol.
+ * (See RFC-1075.)
+ *
+ * Written by David Waitzman, BBN Labs, August 1988.
+ * Modified by Steve Deering, Stanford, February 1989.
+ *
+ * MROUTING 1.0
+ */
+
+
+/*
+ * DVMRP-specific setsockopt commands.
+ */
+#define DVMRP_INIT 100
+#define DVMRP_DONE 101
+#define DVMRP_ADD_VIF 102
+#define DVMRP_DEL_VIF 103
+#define DVMRP_ADD_LGRP 104
+#define DVMRP_DEL_LGRP 105
+#define DVMRP_ADD_MRT 106
+#define DVMRP_DEL_MRT 107
+
+
+/*
+ * Types and macros for handling bitmaps with one bit per virtual interface.
+ */
+#define MAXVIFS 32
+typedef u_long vifbitmap_t;
+typedef u_short vifi_t; /* type of a vif index */
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+
+
+/*
+ * Agument structure for DVMRP_ADD_VIF.
+ * (DVMRP_DEL_VIF takes a single vifi_t argument.)
+ */
+struct vifctl {
+ vifi_t vifc_vifi; /* the index of the vif to be added */
+ u_char vifc_flags; /* VIFF_ flags defined below */
+ u_char vifc_threshold; /* min ttl required to forward on vif */
+ struct in_addr vifc_lcl_addr; /* local interface address */
+ struct in_addr vifc_rmt_addr; /* remote address (tunnels only) */
+};
+
+#define VIFF_TUNNEL 0x1 /* vif represents a tunnel end-point */
+
+
+/*
+ * Argument structure for DVMRP_ADD_LGRP and DVMRP_DEL_LGRP.
+ */
+struct lgrplctl {
+ vifi_t lgc_vifi;
+ struct in_addr lgc_gaddr;
+};
+
+
+/*
+ * Argument structure for DVMRP_ADD_MRT.
+ * (DVMRP_DEL_MRT takes a single struct in_addr argument, containing origin.)
+ */
+struct mrtctl {
+ struct in_addr mrtc_origin; /* subnet origin of multicasts */
+ struct in_addr mrtc_originmask; /* subnet mask for origin */
+ vifi_t mrtc_parent; /* incoming vif */
+ vifbitmap_t mrtc_children; /* outgoing children vifs */
+ vifbitmap_t mrtc_leaves; /* subset of outgoing children vifs */
+};
+
+
+#ifdef KERNEL
+
+/*
+ * The kernel's virtual-interface structure.
+ */
+struct vif {
+ u_char v_flags; /* VIFF_ flags defined above */
+ u_char v_threshold; /* min ttl required to forward on vif */
+ struct in_addr v_lcl_addr; /* local interface address */
+ struct in_addr v_rmt_addr; /* remote address (tunnels only) */
+ struct ifnet *v_ifp; /* pointer to interface */
+ struct in_addr *v_lcl_grps; /* list of local grps (phyints only) */
+ int v_lcl_grps_max; /* malloc'ed number of v_lcl_grps */
+ int v_lcl_grps_n; /* used number of v_lcl_grps */
+ u_long v_cached_group; /* last grp looked-up (phyints only) */
+ int v_cached_result; /* last look-up result (phyints only) */
+};
+
+/*
+ * The kernel's multicast route structure.
+ */
+struct mrt {
+ struct in_addr mrt_origin; /* subnet origin of multicasts */
+ struct in_addr mrt_originmask; /* subnet mask for origin */
+ vifi_t mrt_parent; /* incoming vif */
+ vifbitmap_t mrt_children; /* outgoing children vifs */
+ vifbitmap_t mrt_leaves; /* subset of outgoing children vifs */
+ struct mrt *mrt_next; /* forward link */
+};
+
+
+#define MRTHASHSIZ 64
+#if (MRTHASHSIZ & (MRTHASHSIZ - 1)) == 0 /* from sys:route.h */
+#define MRTHASHMOD(h) ((h) & (MRTHASHSIZ - 1))
+#else
+#define MRTHASHMOD(h) ((h) % MRTHASHSIZ)
+#endif
+
+/*
+ * The kernel's multicast routing statistics.
+ */
+struct mrtstat {
+ u_long mrts_mrt_lookups; /* # multicast route lookups */
+ u_long mrts_mrt_misses; /* # multicast route cache misses */
+ u_long mrts_grp_lookups; /* # group address lookups */
+ u_long mrts_grp_misses; /* # group address cache misses */
+ u_long mrts_no_route; /* no route for packet's origin */
+ u_long mrts_bad_tunnel; /* malformed tunnel options */
+ u_long mrts_cant_tunnel; /* no room for tunnel options */
+ u_long mrts_wrong_if; /* arrived on the wrong interface */
+};
+
+
+int ip_mrouter_cmd __P((int, struct socket *, struct mbuf *));
+int ip_mrouter_done __P((void));
+
+#endif /* KERNEL */
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
new file mode 100644
index 000000000000..5be4fdae984a
--- /dev/null
+++ b/sys/netinet/ip_var.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_var.h 8.2 (Berkeley) 1/9/95
+ */
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ caddr_t ih_next, ih_prev; /* for protocol sequence q's */
+ u_char ih_x1; /* (unused) */
+ u_char ih_pr; /* protocol */
+ short ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+};
+
+/*
+ * Ip reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ * They are timed out after ipq_ttl drops to 0, and may also
+ * be reclaimed if memory becomes tight.
+ */
+struct ipq {
+ struct ipq *next,*prev; /* to other reass headers */
+ u_char ipq_ttl; /* time for reass q to live */
+ u_char ipq_p; /* protocol of this fragment */
+ u_short ipq_id; /* sequence id for reassembly */
+ struct ipasfrag *ipq_next,*ipq_prev;
+ /* to ip headers of fragments */
+ struct in_addr ipq_src,ipq_dst;
+};
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_next must be at same offset as ipq_next above
+ */
+struct ipasfrag {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_char ip_hl:4,
+ ip_v:4;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u_char ip_v:4,
+ ip_hl:4;
+#endif
+ u_char ipf_mff; /* XXX overlays ip_tos: use low bit
+ * to avoid destroying tos;
+ * copied from (ip_off&IP_MF) */
+ short ip_len;
+ u_short ip_id;
+ short ip_off;
+ u_char ip_ttl;
+ u_char ip_p;
+ u_short ip_sum;
+ struct ipasfrag *ipf_next; /* next fragment */
+ struct ipasfrag *ipf_prev; /* previous fragment */
+};
+
+/*
+ * Structure stored in mbuf in inpcb.ip_options
+ * and passed to ip_output when ip options are in use.
+ * The actual length of the options (including ipopt_dst)
+ * is in m_len.
+ */
+#define MAX_IPOPTLEN 40
+
+struct ipoption {
+ struct in_addr ipopt_dst; /* first-hop dst if source routed */
+ char ipopt_list[MAX_IPOPTLEN]; /* options proper */
+};
+
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+struct ip_moptions {
+ struct ifnet *imo_multicast_ifp; /* ifp for outgoing multicasts */
+ u_char imo_multicast_ttl; /* TTL for outgoing multicasts */
+ u_char imo_multicast_loop; /* 1 => hear sends if a member */
+ u_short imo_num_memberships; /* no. memberships this socket */
+ struct in_multi *imo_membership[IP_MAX_MEMBERSHIPS];
+};
+
+struct ipstat {
+ u_long ips_total; /* total packets received */
+ u_long ips_badsum; /* checksum bad */
+ u_long ips_tooshort; /* packet too short */
+ u_long ips_toosmall; /* not enough data */
+ u_long ips_badhlen; /* ip header length < data size */
+ u_long ips_badlen; /* ip length < ip header length */
+ u_long ips_fragments; /* fragments received */
+ u_long ips_fragdropped; /* frags dropped (dups, out of space) */
+ u_long ips_fragtimeout; /* fragments timed out */
+ u_long ips_forward; /* packets forwarded */
+ u_long ips_cantforward; /* packets rcvd for unreachable dest */
+ u_long ips_redirectsent; /* packets forwarded on same net */
+ u_long ips_noproto; /* unknown or unsupported protocol */
+ u_long ips_delivered; /* datagrams delivered to upper level*/
+ u_long ips_localout; /* total ip packets generated here */
+ u_long ips_odropped; /* lost packets due to nobufs, etc. */
+ u_long ips_reassembled; /* total packets reassembled ok */
+ u_long ips_fragmented; /* datagrams sucessfully fragmented */
+ u_long ips_ofragments; /* output fragments created */
+ u_long ips_cantfrag; /* don't fragment flag was set, etc. */
+ u_long ips_badoptions; /* error in option processing */
+ u_long ips_noroute; /* packets discarded due to no route */
+ u_long ips_badvers; /* ip version != 4 */
+ u_long ips_rawout; /* total raw ip packets generated */
+};
+
+#ifdef KERNEL
+/* flags passed to ip_output as last parameter */
+#define IP_FORWARDING 0x1 /* most of ip header exists */
+#define IP_RAWOUTPUT 0x2 /* raw ip header exists */
+#define IP_ROUTETOIF SO_DONTROUTE /* bypass routing tables */
+#define IP_ALLOWBROADCAST SO_BROADCAST /* can send broadcast packets */
+
+struct ipstat ipstat;
+struct ipq ipq; /* ip reass. queue */
+u_short ip_id; /* ip packet ctr, for ids */
+int ip_defttl; /* default IP ttl */
+
+int in_control __P((struct socket *, u_long, caddr_t, struct ifnet *));
+int ip_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+void ip_deq __P((struct ipasfrag *));
+int ip_dooptions __P((struct mbuf *));
+void ip_drain __P((void));
+void ip_enq __P((struct ipasfrag *, struct ipasfrag *));
+void ip_forward __P((struct mbuf *, int));
+void ip_freef __P((struct ipq *));
+void ip_freemoptions __P((struct ip_moptions *));
+int ip_getmoptions __P((int, struct ip_moptions *, struct mbuf **));
+void ip_init __P((void));
+int ip_mforward __P((struct mbuf *, struct ifnet *));
+int ip_optcopy __P((struct ip *, struct ip *));
+int ip_output __P((struct mbuf *,
+ struct mbuf *, struct route *, int, struct ip_moptions *));
+int ip_pcbopts __P((struct mbuf **, struct mbuf *));
+struct ip *
+ ip_reass __P((struct ipasfrag *, struct ipq *));
+struct in_ifaddr *
+ ip_rtaddr __P((struct in_addr));
+int ip_setmoptions __P((int, struct ip_moptions **, struct mbuf *));
+void ip_slowtimo __P((void));
+struct mbuf *
+ ip_srcroute __P((void));
+void ip_stripoptions __P((struct mbuf *, struct mbuf *));
+int ip_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+void ipintr __P((void));
+int rip_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+void rip_init __P((void));
+void rip_input __P((struct mbuf *));
+int rip_output __P((struct mbuf *, struct socket *, u_long));
+int rip_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+#endif
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
new file mode 100644
index 000000000000..ad621f053878
--- /dev/null
+++ b/sys/netinet/raw_ip.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)raw_ip.c 8.7 (Berkeley) 5/15/95
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/in_pcb.h>
+
+struct inpcb rawinpcb;
+
+/*
+ * Nominal space allocated to a raw ip socket.
+ */
+#define RIPSNDQ 8192
+#define RIPRCVQ 8192
+
+/*
+ * Raw interface to IP protocol.
+ */
+
+/*
+ * Initialize raw connection block q.
+ */
+void
+rip_init()
+{
+
+ rawinpcb.inp_next = rawinpcb.inp_prev = &rawinpcb;
+}
+
+struct sockaddr_in ripsrc = { sizeof(ripsrc), AF_INET };
+/*
+ * Setup generic address and protocol structures
+ * for raw_input routine, then pass them along with
+ * mbuf chain.
+ */
+void
+rip_input(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct inpcb *inp;
+ struct socket *last = 0;
+
+ ripsrc.sin_addr = ip->ip_src;
+ for (inp = rawinpcb.inp_next; inp != &rawinpcb; inp = inp->inp_next) {
+ if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
+ continue;
+ if (inp->inp_laddr.s_addr &&
+ inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
+ continue;
+ if (inp->inp_faddr.s_addr &&
+ inp->inp_faddr.s_addr != ip->ip_src.s_addr)
+ continue;
+ if (last) {
+ struct mbuf *n;
+ if (n = m_copy(m, 0, (int)M_COPYALL)) {
+ if (sbappendaddr(&last->so_rcv,
+ (struct sockaddr *)&ripsrc, n,
+ (struct mbuf *)0) == 0)
+ /* should notify about lost packet */
+ m_freem(n);
+ else
+ sorwakeup(last);
+ }
+ }
+ last = inp->inp_socket;
+ }
+ if (last) {
+ if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&ripsrc,
+ m, (struct mbuf *)0) == 0)
+ m_freem(m);
+ else
+ sorwakeup(last);
+ } else {
+ m_freem(m);
+ ipstat.ips_noproto++;
+ ipstat.ips_delivered--;
+ }
+}
+
+/*
+ * Generate IP header and pass packet to ip_output.
+ * Tack on options user may have setup with control call.
+ */
+int
+rip_output(m, so, dst)
+ register struct mbuf *m;
+ struct socket *so;
+ u_long dst;
+{
+ register struct ip *ip;
+ register struct inpcb *inp = sotoinpcb(so);
+ struct mbuf *opts;
+ int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;
+
+ /*
+ * If the user handed us a complete IP packet, use it.
+ * Otherwise, allocate an mbuf for a header and fill it in.
+ */
+ if ((inp->inp_flags & INP_HDRINCL) == 0) {
+ M_PREPEND(m, sizeof(struct ip), M_WAIT);
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_off = 0;
+ ip->ip_p = inp->inp_ip.ip_p;
+ ip->ip_len = m->m_pkthdr.len;
+ ip->ip_src = inp->inp_laddr;
+ ip->ip_dst.s_addr = dst;
+ ip->ip_ttl = MAXTTL;
+ opts = inp->inp_options;
+ } else {
+ ip = mtod(m, struct ip *);
+ if (ip->ip_id == 0)
+ ip->ip_id = htons(ip_id++);
+ opts = NULL;
+ /* XXX prevent ip_output from overwriting header fields */
+ flags |= IP_RAWOUTPUT;
+ ipstat.ips_rawout++;
+ }
+ return (ip_output(m, opts, &inp->inp_route, flags, inp->inp_moptions));
+}
+
+/*
+ * Raw IP socket option processing.
+ */
+int
+rip_ctloutput(op, so, level, optname, m)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **m;
+{
+ register struct inpcb *inp = sotoinpcb(so);
+ register int error;
+
+ if (level != IPPROTO_IP) {
+ if (op == PRCO_SETOPT && *m)
+ (void) m_free(*m);
+ return (EINVAL);
+ }
+
+ switch (optname) {
+
+ case IP_HDRINCL:
+ error = 0;
+ if (op == PRCO_SETOPT) {
+ if (*m == 0 || (*m)->m_len < sizeof (int))
+ error = EINVAL;
+ else if (*mtod(*m, int *))
+ inp->inp_flags |= INP_HDRINCL;
+ else
+ inp->inp_flags &= ~INP_HDRINCL;
+ if (*m)
+ (void)m_free(*m);
+ } else {
+ *m = m_get(M_WAIT, MT_SOOPTS);
+ (*m)->m_len = sizeof (int);
+ *mtod(*m, int *) = inp->inp_flags & INP_HDRINCL;
+ }
+ return (error);
+
+ case DVMRP_INIT:
+ case DVMRP_DONE:
+ case DVMRP_ADD_VIF:
+ case DVMRP_DEL_VIF:
+ case DVMRP_ADD_LGRP:
+ case DVMRP_DEL_LGRP:
+ case DVMRP_ADD_MRT:
+ case DVMRP_DEL_MRT:
+#ifdef MROUTING
+ if (op == PRCO_SETOPT) {
+ error = ip_mrouter_cmd(optname, so, *m);
+ if (*m)
+ (void)m_free(*m);
+ } else
+ error = EINVAL;
+ return (error);
+#else
+ if (op == PRCO_SETOPT && *m)
+ (void)m_free(*m);
+ return (EOPNOTSUPP);
+#endif
+
+ default:
+ if (optname >= DVMRP_INIT) {
+#ifdef MROUTING
+ if (op == PRCO_SETOPT) {
+ error = ip_mrouter_cmd(optname, so, *m);
+ if (*m)
+ (void)m_free(*m);
+ } else
+ error = EINVAL;
+ return (error);
+#else
+ if (op == PRCO_SETOPT && *m)
+ (void)m_free(*m);
+ return (EOPNOTSUPP);
+#endif
+ }
+
+ }
+ return (ip_ctloutput(op, so, level, optname, m));
+}
+
+u_long rip_sendspace = RIPSNDQ;
+u_long rip_recvspace = RIPRCVQ;
+
+/*ARGSUSED*/
+int
+rip_usrreq(so, req, m, nam, control)
+ register struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register int error = 0;
+ register struct inpcb *inp = sotoinpcb(so);
+#ifdef MROUTING
+ extern struct socket *ip_mrouter;
+#endif
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (inp)
+ panic("rip_attach");
+ if ((so->so_state & SS_PRIV) == 0) {
+ error = EACCES;
+ break;
+ }
+ if ((error = soreserve(so, rip_sendspace, rip_recvspace)) ||
+ (error = in_pcballoc(so, &rawinpcb)))
+ break;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_ip.ip_p = (int)nam;
+ break;
+
+ case PRU_DISCONNECT:
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PRU_ABORT:
+ soisdisconnected(so);
+ /* FALLTHROUGH */
+ case PRU_DETACH:
+ if (inp == 0)
+ panic("rip_detach");
+#ifdef MROUTING
+ if (so == ip_mrouter)
+ ip_mrouter_done();
+#endif
+ in_pcbdetach(inp);
+ break;
+
+ case PRU_BIND:
+ {
+ struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof(*addr)) {
+ error = EINVAL;
+ break;
+ }
+ if ((ifnet == 0) ||
+ ((addr->sin_family != AF_INET) &&
+ (addr->sin_family != AF_IMPLINK)) ||
+ (addr->sin_addr.s_addr &&
+ ifa_ifwithaddr((struct sockaddr *)addr) == 0)) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ inp->inp_laddr = addr->sin_addr;
+ break;
+ }
+ case PRU_CONNECT:
+ {
+ struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
+
+ if (nam->m_len != sizeof(*addr)) {
+ error = EINVAL;
+ break;
+ }
+ if (ifnet == 0) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ if ((addr->sin_family != AF_INET) &&
+ (addr->sin_family != AF_IMPLINK)) {
+ error = EAFNOSUPPORT;
+ break;
+ }
+ inp->inp_faddr = addr->sin_addr;
+ soisconnected(so);
+ break;
+ }
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ 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:
+ {
+ register u_long dst;
+
+ if (so->so_state & SS_ISCONNECTED) {
+ if (nam) {
+ error = EISCONN;
+ break;
+ }
+ dst = inp->inp_faddr.s_addr;
+ } else {
+ if (nam == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ dst = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ }
+ error = rip_output(m, so, dst);
+ m = NULL;
+ break;
+ }
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ /*
+ * Not supported.
+ */
+ case PRU_RCVOOB:
+ case PRU_RCVD:
+ case PRU_LISTEN:
+ case PRU_ACCEPT:
+ case PRU_SENDOOB:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, nam);
+ break;
+
+ default:
+ panic("rip_usrreq");
+ }
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+}
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
new file mode 100644
index 000000000000..9bd82ee92a26
--- /dev/null
+++ b/sys/netinet/tcp_input.c
@@ -0,0 +1,1660 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995
+ * 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.
+ *
+ * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95
+ */
+
+#ifndef TUBA_INCLUDE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <machine/cpu.h> /* before tcp_seq.h, for tcp_random18() */
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+int tcprexmtthresh = 3;
+struct tcpiphdr tcp_saveti;
+struct inpcb *tcp_last_inpcb = &tcb;
+
+extern u_long sb_max;
+
+#endif /* TUBA_INCLUDE */
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp. Return TH_FIN if reassembly now includes
+ * a segment with FIN. The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ (tp)->seg_next == (struct tcpiphdr *)(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ tcpstat.tcps_rcvpack++;\
+ tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+ sbappend(&(so)->so_rcv, (m)); \
+ sorwakeup(so); \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#ifndef TUBA_INCLUDE
+
+int
+tcp_reass(tp, ti, m)
+ register struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ struct mbuf *m;
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_inpcb->inp_socket;
+ int flags;
+
+ /*
+ * Call with ti==0 after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == 0)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = tp->seg_next; q != (struct tcpiphdr *)tp;
+ q = (struct tcpiphdr *)q->ti_next)
+ if (SEQ_GT(q->ti_seq, ti->ti_seq))
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) {
+ register int i;
+ q = (struct tcpiphdr *)q->ti_prev;
+ /* conversion to int (in i) handles seq wraparound */
+ i = q->ti_seq + q->ti_len - ti->ti_seq;
+ if (i > 0) {
+ if (i >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ m_freem(m);
+ return (0);
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = (struct tcpiphdr *)(q->ti_next);
+ }
+ tcpstat.tcps_rcvoopack++;
+ tcpstat.tcps_rcvoobyte += ti->ti_len;
+ REASS_MBUF(ti) = m; /* XXX */
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct tcpiphdr *)tp) {
+ register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+ if (i <= 0)
+ break;
+ if (i < q->ti_len) {
+ q->ti_seq += i;
+ q->ti_len -= i;
+ m_adj(REASS_MBUF(q), i);
+ break;
+ }
+ q = (struct tcpiphdr *)q->ti_next;
+ m = REASS_MBUF((struct tcpiphdr *)q->ti_prev);
+ remque(q->ti_prev);
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque(ti, q->ti_prev);
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (TCPS_HAVERCVDSYN(tp->t_state) == 0)
+ return (0);
+ ti = tp->seg_next;
+ if (ti == (struct tcpiphdr *)tp || ti->ti_seq != tp->rcv_nxt)
+ return (0);
+ if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+ return (0);
+ do {
+ tp->rcv_nxt += ti->ti_len;
+ flags = ti->ti_flags & TH_FIN;
+ remque(ti);
+ m = REASS_MBUF(ti);
+ ti = (struct tcpiphdr *)ti->ti_next;
+ if (so->so_state & SS_CANTRCVMORE)
+ m_freem(m);
+ else
+ sbappend(&so->so_rcv, m);
+ } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
+ sorwakeup(so);
+ return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct tcpiphdr *ti;
+ register struct inpcb *inp;
+ u_char *optp = NULL;
+ int optlen;
+ int len, tlen, off;
+ register struct tcpcb *tp = 0;
+ register int tiflags;
+ struct socket *so;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+ short ostate;
+ struct in_addr laddr;
+ int dropsocket = 0;
+ int iss = 0;
+ u_long tiwin, ts_val, ts_ecr;
+ int ts_present = 0;
+
+ tcpstat.tcps_rcvtotal++;
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof (struct ip))
+ ip_stripoptions(m, (struct mbuf *)0);
+ if (m->m_len < sizeof (struct tcpiphdr)) {
+ if ((m = m_pullup(m, sizeof (struct tcpiphdr))) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ ti = mtod(m, struct tcpiphdr *);
+ }
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ len = sizeof (struct ip) + tlen;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_len = (u_short)tlen;
+ HTONS(ti->ti_len);
+ if (ti->ti_sum = in_cksum(m, len)) {
+ tcpstat.tcps_rcvbadsum++;
+ goto drop;
+ }
+#endif /* TUBA_INCLUDE */
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if (off < sizeof (struct tcphdr) || off > tlen) {
+ tcpstat.tcps_rcvbadoff++;
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ if (m->m_len < sizeof(struct ip) + off) {
+ if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) {
+ tcpstat.tcps_rcvshort++;
+ return;
+ }
+ ti = mtod(m, struct tcpiphdr *);
+ }
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, u_char *) + sizeof (struct tcpiphdr);
+ /*
+ * Do quick retrieval of timestamp options ("options
+ * prediction?"). If timestamp is the only option and it's
+ * formatted as recommended in RFC 1323 appendix A, we
+ * quickly get the values now and not bother calling
+ * tcp_dooptions(), etc.
+ */
+ if ((optlen == TCPOLEN_TSTAMP_APPA ||
+ (optlen > TCPOLEN_TSTAMP_APPA &&
+ optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
+ *(u_long *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
+ (ti->ti_flags & TH_SYN) == 0) {
+ ts_present = 1;
+ ts_val = ntohl(*(u_long *)(optp + 4));
+ ts_ecr = ntohl(*(u_long *)(optp + 8));
+ optp = NULL; /* we've parsed the options */
+ }
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Locate pcb for segment.
+ */
+findpcb:
+ inp = tcp_last_inpcb;
+ if (inp->inp_lport != ti->ti_dport ||
+ inp->inp_fport != ti->ti_sport ||
+ inp->inp_faddr.s_addr != ti->ti_src.s_addr ||
+ inp->inp_laddr.s_addr != ti->ti_dst.s_addr) {
+ inp = in_pcblookup(&tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport, INPLOOKUP_WILDCARD);
+ if (inp)
+ tcp_last_inpcb = inp;
+ ++tcpstat.tcps_pcbcachemiss;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ */
+ if (inp == 0)
+ goto dropwithreset;
+ tp = intotcpcb(inp);
+ if (tp == 0)
+ goto dropwithreset;
+ if (tp->t_state == TCPS_CLOSED)
+ goto drop;
+
+ /* Unscale the window into a 32-bit value. */
+ if ((tiflags & TH_SYN) == 0)
+ tiwin = ti->ti_win << tp->snd_scale;
+ else
+ tiwin = ti->ti_win;
+
+ so = inp->inp_socket;
+ if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {
+ if (so->so_options & SO_DEBUG) {
+ ostate = tp->t_state;
+ tcp_saveti = *ti;
+ }
+ if (so->so_options & SO_ACCEPTCONN) {
+ if ((tiflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) {
+ /*
+ * Note: dropwithreset makes sure we don't
+ * send a reset in response to a RST.
+ */
+ if (tiflags & TH_ACK) {
+ tcpstat.tcps_badsyn++;
+ goto dropwithreset;
+ }
+ goto drop;
+ }
+ so = sonewconn(so, 0);
+ if (so == 0)
+ goto drop;
+ /*
+ * This is ugly, but ....
+ *
+ * Mark socket as temporary until we're
+ * committed to keeping it. The code at
+ * ``drop'' and ``dropwithreset'' check the
+ * flag dropsocket to see if the temporary
+ * socket created here should be discarded.
+ * We mark the socket as discardable until
+ * we're committed to it below in TCPS_LISTEN.
+ */
+ dropsocket++;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_laddr = ti->ti_dst;
+ inp->inp_lport = ti->ti_dport;
+#if BSD>=43
+ inp->inp_options = ip_srcroute();
+#endif
+ tp = intotcpcb(inp);
+ tp->t_state = TCPS_LISTEN;
+
+ /* Compute proper scaling value from buffer space
+ */
+ while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ TCP_MAXWIN << tp->request_r_scale < so->so_rcv.sb_hiwat)
+ tp->request_r_scale++;
+ }
+ }
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(tp, optp, optlen, ti,
+ &ts_present, &ts_val, &ts_ecr);
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+ (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) &&
+ ti->ti_seq == tp->rcv_nxt &&
+ tiwin && tiwin == tp->snd_wnd &&
+ tp->snd_nxt == tp->snd_max) {
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record the timestamp.
+ */
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+
+ if (ti->ti_len == 0) {
+ if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+ SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+ tp->snd_cwnd >= tp->snd_wnd) {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ ++tcpstat.tcps_predack;
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else if (tp->t_rtt &&
+ SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tp->seg_next == (struct tcpiphdr *)tp &&
+ ti->ti_len <= sbspace(&so->so_rcv)) {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ ++tcpstat.tcps_preddat;
+ tp->rcv_nxt += ti->ti_len;
+ tcpstat.tcps_rcvpack++;
+ tcpstat.tcps_rcvbyte += ti->ti_len;
+ /*
+ * Drop TCP, IP headers and TCP options then add data
+ * to socket buffer.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ sbappend(&so->so_rcv, m);
+ sorwakeup(so);
+ tp->t_flags |= TF_DELACK;
+ return;
+ }
+ }
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ { int win;
+
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state) {
+
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN: {
+ struct mbuf *am;
+ register struct sockaddr_in *sin;
+
+#ifdef already_done
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+#endif
+ /*
+ * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
+ * in_broadcast() should never return true on a received
+ * packet with M_BCAST not set.
+ */
+ if (m->m_flags & (M_BCAST|M_MCAST) ||
+ IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
+ goto drop;
+ am = m_get(M_DONTWAIT, MT_SONAME); /* XXX */
+ if (am == NULL)
+ goto drop;
+ am->m_len = sizeof (struct sockaddr_in);
+ sin = mtod(am, struct sockaddr_in *);
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_addr = ti->ti_src;
+ sin->sin_port = ti->ti_sport;
+ bzero((caddr_t)sin->sin_zero, sizeof(sin->sin_zero));
+ laddr = inp->inp_laddr;
+ if (inp->inp_laddr.s_addr == INADDR_ANY)
+ inp->inp_laddr = ti->ti_dst;
+ if (in_pcbconnect(inp, am)) {
+ inp->inp_laddr = laddr;
+ (void) m_free(am);
+ goto drop;
+ }
+ (void) m_free(am);
+ tp->t_template = tcp_template(tp);
+ if (tp->t_template == 0) {
+ tp = tcp_drop(tp, ENOBUFS);
+ dropsocket = 0; /* socket is already gone */
+ goto drop;
+ }
+ if (optp)
+ tcp_dooptions(tp, optp, optlen, ti,
+ &ts_present, &ts_val, &ts_ecr);
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/4;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ dropsocket = 0; /* committed to socket */
+ tcpstat.tcps_accepts++;
+ goto trimthenstep6;
+ }
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max)))
+ goto dropwithreset;
+ if (tiflags & TH_RST) {
+ if (tiflags & TH_ACK)
+ tp = tcp_drop(tp, ECONNREFUSED);
+ goto drop;
+ }
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ if (tiflags & TH_ACK) {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+ tcpstat.tcps_connects++;
+ soisconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+ /* Do window scaling on this connection? */
+ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+ (void) tcp_reass(tp, (struct tcpiphdr *)0,
+ (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(tp, tp->t_rtt);
+ } else
+ tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd) {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ tcpstat.tcps_rcvpackafterwin++;
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ }
+
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * First check timestamp, if present.
+ * Then check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ *
+ * RFC 1323 PAWS: If we have a timestamp reply on this segment
+ * and it's less than ts_recent, drop it.
+ */
+ if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
+ TSTMP_LT(ts_val, tp->ts_recent)) {
+
+ /* Check to see if ts_recent is over 24 days old. */
+ if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ /*
+ * Invalidate ts_recent. If this segment updates
+ * ts_recent, the age will be reset later and ts_recent
+ * will get a valid value. If it does not, setting
+ * ts_recent to zero will at least satisfy the
+ * requirement that zero be placed in the timestamp
+ * echo reply when ts_recent isn't valid. The
+ * age isn't reset until we get a valid ts_recent
+ * because we don't want out-of-order segments to be
+ * dropped when ts_recent is old.
+ */
+ tp->ts_recent = 0;
+ } else {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ tcpstat.tcps_pawsdrop++;
+ goto dropafterack;
+ }
+ }
+
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0) {
+ if (tiflags & TH_SYN) {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ /*
+ * If segment is just one to the left of the window,
+ * check two special cases:
+ * 1. Don't toss RST in response to 4.2-style keepalive.
+ * 2. If the only thing to drop is a FIN, we can drop
+ * it, but check the ACK or we will get into FIN
+ * wars if our FINs crossed (both CLOSING).
+ * In either case, send ACK to resynchronize,
+ * but keep on processing for RST or ACK.
+ */
+ if ((tiflags & TH_FIN && todrop == ti->ti_len + 1)
+#ifdef TCP_COMPAT_42
+ || (tiflags & TH_RST && ti->ti_seq == tp->rcv_nxt - 1)
+#endif
+ ) {
+ todrop = ti->ti_len;
+ tiflags &= ~TH_FIN;
+ } else {
+ /*
+ * Handle the case when a bound socket connects
+ * to itself. Allow packets with a SYN and
+ * an ACK to continue with the processing.
+ */
+ if (todrop != 0 || (tiflags & TH_ACK) == 0)
+ goto dropafterack;
+ }
+ tp->t_flags |= TF_ACKNOW;
+ } else {
+ tcpstat.tcps_rcvpartduppack++;
+ tcpstat.tcps_rcvpartdupbyte += todrop;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ((so->so_state & SS_NOFDREF) &&
+ tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+ tp = tcp_close(tp);
+ tcpstat.tcps_rcvafterclose++;
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0) {
+ tcpstat.tcps_rcvpackafterwin++;
+ if (todrop >= ti->ti_len) {
+ tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if (tiflags & TH_SYN &&
+ tp->t_state == TCPS_TIME_WAIT &&
+ SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+ iss = tp->snd_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findpcb;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_rcvwinprobe++;
+ } else
+ goto dropafterack;
+ } else
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record its timestamp.
+ */
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len +
+ ((tiflags & (TH_SYN|TH_FIN)) != 0))) {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST) switch (tp->t_state) {
+
+ case TCPS_SYN_RECEIVED:
+ so->so_error = ECONNREFUSED;
+ goto close;
+
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ so->so_error = ECONNRESET;
+ close:
+ tp->t_state = TCPS_CLOSED;
+ tcpstat.tcps_drops++;
+ tp = tcp_close(tp);
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tp = tcp_close(tp);
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN) {
+ tp = tcp_drop(tp, ECONNRESET);
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0)
+ goto drop;
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST.
+ */
+ case TCPS_SYN_RECEIVED:
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tcpstat.tcps_connects++;
+ soisconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+ /* Do window scaling? */
+ if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ (TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+ (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+ tcpstat.tcps_rcvdupack++;
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshhold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 ||
+ ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == tcprexmtthresh) {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win =
+ min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+ tp->t_maxseg;
+
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ goto drop;
+ } else if (tp->t_dupacks > tcprexmtthresh) {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(tp);
+ goto drop;
+ }
+ } else
+ tp->t_dupacks = 0;
+ break;
+ }
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tp->t_dupacks > tcprexmtthresh &&
+ tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+ tcpstat.tcps_rcvacktoomuch++;
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+
+ /*
+ * If we have a timestamp reply, update smoothed
+ * round trip time. If no timestamp is present but
+ * transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp,tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ } else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg * (maxseg / cwnd) per packet).
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > so->so_snd.sb_cc) {
+ tp->snd_wnd -= so->so_snd.sb_cc;
+ sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ } else {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state) {
+
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked) {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_CANTRCVMORE) {
+ soisdisconnected(so);
+ tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ }
+ tp->t_state = TCPS_FIN_WAIT_2;
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked) {
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisdisconnected(so);
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked) {
+ tp = tcp_close(tp);
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ goto dropafterack;
+ }
+ }
+
+step6:
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq &&
+ (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))) {
+ /* keep track of pure window updates */
+ if (ti->ti_len == 0 &&
+ tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd)
+ tcpstat.tcps_rcvwinupd++;
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > sb_max) {
+ ti->ti_urp = 0; /* XXX */
+ tiflags &= ~TH_URG; /* XXX */
+ goto dodata; /* XXX */
+ }
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_oobmark = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt) - 1;
+ if (so->so_oobmark == 0)
+ so->so_state |= SS_RCVATMARK;
+ sohasoutofband(so);
+ tp->t_oobflags &= ~(TCPOOB_HAVEDATA | TCPOOB_HADDATA);
+ }
+ /*
+ * Remove out of band data so doesn't get presented to user.
+ * This can happen independent of advancing the URG pointer,
+ * but if two URG's are pending at once, some out-of-band
+ * data may creep in... ick.
+ */
+ if (ti->ti_urp <= ti->ti_len
+#ifdef SO_OOBINLINE
+ && (so->so_options & SO_OOBINLINE) == 0
+#endif
+ )
+ tcp_pulloutofband(so, ti, m);
+ } else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata: /* XXX */
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ((ti->ti_len || (tiflags&TH_FIN)) &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ TCP_REASS(tp, ti, m, so, tiflags);
+ /*
+ * Note the amount of data that peer has sent into
+ * our window, in order to estimate the sender's
+ * buffer size.
+ */
+ len = so->so_rcv.sb_hiwat - (tp->rcv_adv - tp->rcv_nxt);
+ } else {
+ m_freem(m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN) {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ socantrcvmore(so);
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_CLOSE_WAIT;
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ tp->t_state = TCPS_CLOSING;
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisdisconnected(so);
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+ if (so->so_options & SO_DEBUG)
+ tcp_trace(TA_INPUT, ostate, tp, &tcp_saveti, 0);
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW))
+ (void) tcp_output(tp);
+ return;
+
+dropafterack:
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ goto drop;
+ m_freem(m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ return;
+
+dropwithreset:
+ /*
+ * Generate a RST, dropping incoming segment.
+ * Make ACK acceptable to originator of segment.
+ * Don't bother to respond if destination was broadcast/multicast.
+ */
+ if ((tiflags & TH_RST) || m->m_flags & (M_BCAST|M_MCAST) ||
+ IN_MULTICAST(ntohl(ti->ti_dst.s_addr)))
+ goto drop;
+ if (tiflags & TH_ACK)
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else {
+ if (tiflags & TH_SYN)
+ ti->ti_len++;
+ tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+ /* destroy temporarily created socket */
+ if (dropsocket)
+ (void) soabort(so);
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
+ tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
+ m_freem(m);
+ /* destroy temporarily created socket */
+ if (dropsocket)
+ (void) soabort(so);
+ return;
+#ifndef TUBA_INCLUDE
+}
+
+void
+tcp_dooptions(tp, cp, cnt, ti, ts_present, ts_val, ts_ecr)
+ struct tcpcb *tp;
+ u_char *cp;
+ int cnt;
+ struct tcpiphdr *ti;
+ int *ts_present;
+ u_long *ts_val, *ts_ecr;
+{
+ u_short mss;
+ int opt, optlen;
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt) {
+
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ bcopy((char *) cp + 2, (char *) &mss, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(tp, mss); /* sets t_maxseg */
+ break;
+
+ case TCPOPT_WINDOW:
+ if (optlen != TCPOLEN_WINDOW)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ tp->t_flags |= TF_RCVD_SCALE;
+ tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
+ break;
+
+ case TCPOPT_TIMESTAMP:
+ if (optlen != TCPOLEN_TIMESTAMP)
+ continue;
+ *ts_present = 1;
+ bcopy((char *)cp + 2, (char *) ts_val, sizeof(*ts_val));
+ NTOHL(*ts_val);
+ bcopy((char *)cp + 6, (char *) ts_ecr, sizeof(*ts_ecr));
+ NTOHL(*ts_ecr);
+
+ /*
+ * A timestamp received in a SYN makes
+ * it ok to send timestamp requests and replies.
+ */
+ if (ti->ti_flags & TH_SYN) {
+ tp->t_flags |= TF_RCVD_TSTMP;
+ tp->ts_recent = *ts_val;
+ tp->ts_recent_age = tcp_now;
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+void
+tcp_pulloutofband(so, ti, m)
+ struct socket *so;
+ struct tcpiphdr *ti;
+ register struct mbuf *m;
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0) {
+ if (m->m_len > cnt) {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next;
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+void
+tcp_xmit_timer(tp, rtt)
+ register struct tcpcb *tp;
+ short rtt;
+{
+ register short delta;
+
+ tcpstat.tcps_rttupdated++;
+ if (tp->t_srtt != 0) {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ tp->t_rttmin, TCPTV_REXMTMAX);
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+int
+tcp_mss(tp, offer)
+ register struct tcpcb *tp;
+ u_int offer;
+{
+ struct route *ro;
+ register struct rtentry *rt;
+ struct ifnet *ifp;
+ register int rtt, mss;
+ u_long bufsize;
+ struct inpcb *inp;
+ struct socket *so;
+ extern int tcp_mssdflt;
+
+ inp = tp->t_inpcb;
+ ro = &inp->inp_route;
+
+ if ((rt = ro->ro_rt) == (struct rtentry *)0) {
+ /* No route yet, so try to acquire one */
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ ro->ro_dst.sa_family = AF_INET;
+ ro->ro_dst.sa_len = sizeof(ro->ro_dst);
+ ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+ inp->inp_faddr;
+ rtalloc(ro);
+ }
+ if ((rt = ro->ro_rt) == (struct rtentry *)0)
+ return (tcp_mssdflt);
+ }
+ ifp = rt->rt_ifp;
+ so = inp->inp_socket;
+
+#ifdef RTV_MTU /* if route characteristics exist ... */
+ /*
+ * While we're here, check if there's an initial rtt
+ * or rttvar. Convert from the route-table units
+ * to scaled multiples of the slow timeout timer.
+ */
+ if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) {
+ /*
+ * XXX the lock bit for MTU indicates that the value
+ * is also a minimum value; this is subject to time.
+ */
+ if (rt->rt_rmx.rmx_locks & RTV_RTT)
+ tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
+ tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ if (rt->rt_rmx.rmx_rttvar)
+ tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ else
+ /* default variation is +- 1 rtt */
+ tp->t_rttvar =
+ tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
+ tp->t_rttmin, TCPTV_REXMTMAX);
+ }
+ /*
+ * if there's an mtu associated with the route, use it
+ */
+ if (rt->rt_rmx.rmx_mtu)
+ mss = rt->rt_rmx.rmx_mtu - sizeof(struct tcpiphdr);
+ else
+#endif /* RTV_MTU */
+ {
+ mss = ifp->if_mtu - sizeof(struct tcpiphdr);
+#if (MCLBYTES & (MCLBYTES - 1)) == 0
+ if (mss > MCLBYTES)
+ mss &= ~(MCLBYTES-1);
+#else
+ if (mss > MCLBYTES)
+ mss = mss / MCLBYTES * MCLBYTES;
+#endif
+ if (!in_localaddr(inp->inp_faddr))
+ mss = min(mss, tcp_mssdflt);
+ }
+ /*
+ * The current mss, t_maxseg, is initialized to the default value.
+ * If we compute a smaller value, reduce the current mss.
+ * If we compute a larger value, return it for use in sending
+ * a max seg size option, but don't store it for use
+ * unless we received an offer at least that large from peer.
+ * However, do not accept offers under 32 bytes.
+ */
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32); /* sanity */
+ if (mss < tp->t_maxseg || offer != 0) {
+ /*
+ * If there's a pipesize, change the socket buffer
+ * to that size. Make the socket buffers an integral
+ * number of mss units; if the mss is larger than
+ * the socket buffer, decrease the mss.
+ */
+#ifdef RTV_SPIPE
+ if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0)
+#endif
+ bufsize = so->so_snd.sb_hiwat;
+ if (bufsize < mss)
+ mss = bufsize;
+ else {
+ bufsize = roundup(bufsize, mss);
+ if (bufsize > sb_max)
+ bufsize = sb_max;
+ (void)sbreserve(&so->so_snd, bufsize);
+ }
+ tp->t_maxseg = mss;
+
+#ifdef RTV_RPIPE
+ if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0)
+#endif
+ bufsize = so->so_rcv.sb_hiwat;
+ if (bufsize > mss) {
+ bufsize = roundup(bufsize, mss);
+ if (bufsize > sb_max)
+ bufsize = sb_max;
+ (void)sbreserve(&so->so_rcv, bufsize);
+ }
+ }
+ tp->snd_cwnd = mss;
+
+#ifdef RTV_SSTHRESH
+ if (rt->rt_rmx.rmx_ssthresh) {
+ /*
+ * There's some sort of gateway or interface
+ * buffer limit on the path. Use this to set
+ * the slow start threshhold, but set the
+ * threshold to no less than 2*mss.
+ */
+ tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh);
+ }
+#endif /* RTV_MTU */
+ return (mss);
+}
+#endif /* TUBA_INCLUDE */
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
new file mode 100644
index 000000000000..84f71d97f6d8
--- /dev/null
+++ b/sys/netinet/tcp_output.c
@@ -0,0 +1,603 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
+ * 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.
+ *
+ * @(#)tcp_output.c 8.4 (Berkeley) 5/24/95
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#define TCPOUTFLAGS
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+#ifdef notyet
+extern struct mbuf *m_copypack();
+#endif
+
+
+#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(tp)
+ register struct tcpcb *tp;
+{
+ register struct socket *so = tp->t_inpcb->inp_socket;
+ register long len, win;
+ int off, flags, error;
+ register struct mbuf *m;
+ register struct tcpiphdr *ti;
+ u_char opt[MAX_TCPOPTLEN];
+ unsigned optlen, hdrlen;
+ int idle, sendalot;
+
+ /*
+ * Determine length of data that should be transmitted,
+ * and flags that will be used.
+ * If there is some data or critical controls (SYN, RST)
+ * to send, then transmit; otherwise, investigate further.
+ */
+ idle = (tp->snd_max == tp->snd_una);
+ if (idle && tp->t_idle >= tp->t_rxtcur)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tp->snd_cwnd = tp->t_maxseg;
+again:
+ sendalot = 0;
+ off = tp->snd_nxt - tp->snd_una;
+ win = min(tp->snd_wnd, tp->snd_cwnd);
+
+ flags = tcp_outflags[tp->t_state];
+ /*
+ * If in persist timeout with window of 0, send 1 byte.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, we will send what we can
+ * and go to transmit state.
+ */
+ if (tp->t_force) {
+ if (win == 0) {
+ /*
+ * If we still have some data to send, then
+ * clear the FIN bit. Usually this would
+ * happen below when it realizes that we
+ * aren't sending all the data. However,
+ * if we have exactly 1 byte of unset data,
+ * then it won't clear the FIN bit below,
+ * and if we are in persist state, we wind
+ * up sending the packet without recording
+ * that we sent the FIN bit.
+ *
+ * We can't just blindly clear the FIN bit,
+ * because if we don't have any more data
+ * to send then the probe will be the FIN
+ * itself.
+ */
+ if (off < so->so_snd.sb_cc)
+ flags &= ~TH_FIN;
+ win = 1;
+ } else {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+
+ len = min(so->so_snd.sb_cc, win) - off;
+
+ if (len < 0) {
+ /*
+ * If FIN has been sent but not acked,
+ * but we haven't been called to retransmit,
+ * len will be -1. Otherwise, window shrank
+ * after we sent into it. If window shrank to 0,
+ * cancel pending retransmit and pull snd_nxt
+ * back to (closed) window. We will enter persist
+ * state below. If the window didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->snd_nxt = tp->snd_una;
+ }
+ }
+ if (len > tp->t_maxseg) {
+ len = tp->t_maxseg;
+ sendalot = 1;
+ }
+ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+ flags &= ~TH_FIN;
+
+ win = sbspace(&so->so_rcv);
+
+ /*
+ * Sender silly window avoidance. If connection is idle
+ * and can send all data, a maximum segment,
+ * at least a maximum default-size segment do it,
+ * or are forced, do it; otherwise don't bother.
+ * If peer's buffer is tiny, then send
+ * when window is at least half open.
+ * If retransmitting (possibly after persist timer forced us
+ * to send into a small window), then must resend.
+ */
+ if (len) {
+ if (len == tp->t_maxseg)
+ goto send;
+ if ((idle || tp->t_flags & TF_NODELAY) &&
+ len + off >= so->so_snd.sb_cc)
+ goto send;
+ if (tp->t_force)
+ goto send;
+ if (len >= tp->max_sndwnd / 2)
+ goto send;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ goto send;
+ }
+
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input). If the difference is at least two
+ * max size segments, or at least 50% of the maximum possible
+ * window, then want to send a window update to peer.
+ */
+ if (win > 0) {
+ /*
+ * "adv" is the amount we can increase the window,
+ * taking into account that we are limited by
+ * TCP_MAXWIN << tp->rcv_scale.
+ */
+ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+ (tp->rcv_adv - tp->rcv_nxt);
+
+ if (adv >= (long) (2 * tp->t_maxseg))
+ goto send;
+ if (2 * adv >= (long) so->so_rcv.sb_hiwat)
+ goto send;
+ }
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (tp->t_flags & TF_ACKNOW)
+ goto send;
+ if (flags & (TH_SYN|TH_RST))
+ goto send;
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ goto send;
+ /*
+ * If our state indicates that FIN should be sent
+ * and we have not yet done so, or we're retransmitting the FIN,
+ * then we need to send.
+ */
+ if (flags & TH_FIN &&
+ ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+ goto send;
+
+ /*
+ * TCP window updates are not reliable, rather a polling protocol
+ * using ``persist'' packets is used to insure receipt of window
+ * updates. The three ``states'' for the output side are:
+ * idle not doing retransmits or persists
+ * persisting to move a small or zero window
+ * (re)transmitting and thereby not persisting
+ *
+ * tp->t_timer[TCPT_PERSIST]
+ * is set when we are in persist state.
+ * tp->t_force
+ * is set when we are called to send a persist packet.
+ * tp->t_timer[TCPT_REXMT]
+ * is set when we are retransmitting
+ * The output side is idle when both timers are zero.
+ *
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise force out a byte.
+ */
+ if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->t_timer[TCPT_PERSIST] == 0) {
+ tp->t_rxtshift = 0;
+ tcp_setpersist(tp);
+ }
+
+ /*
+ * No reason to send a segment, just return.
+ */
+ return (0);
+
+send:
+ /*
+ * Before ESTABLISHED, force sending of initial options
+ * unless TCP set not to do any options.
+ * NOTE: we assume that the IP/TCP header plus TCP options
+ * always fit in a single mbuf, leaving room for a maximum
+ * link header, i.e.
+ * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+ */
+ optlen = 0;
+ hdrlen = sizeof (struct tcpiphdr);
+ if (flags & TH_SYN) {
+ tp->snd_nxt = tp->iss;
+ if ((tp->t_flags & TF_NOOPT) == 0) {
+ u_short mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = htons((u_short) tcp_mss(tp, 0));
+ bcopy((caddr_t)&mss, (caddr_t)(opt + 2), sizeof(mss));
+ optlen = 4;
+
+ if ((tp->t_flags & TF_REQ_SCALE) &&
+ ((flags & TH_ACK) == 0 ||
+ (tp->t_flags & TF_RCVD_SCALE))) {
+ *((u_long *) (opt + optlen)) = htonl(
+ TCPOPT_NOP << 24 |
+ TCPOPT_WINDOW << 16 |
+ TCPOLEN_WINDOW << 8 |
+ tp->request_r_scale);
+ optlen += 4;
+ }
+ }
+ }
+
+ /*
+ * Send a timestamp and echo-reply if this is a SYN and our side
+ * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
+ * and our peer have sent timestamps in our SYN's.
+ */
+ if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
+ (flags & TH_RST) == 0 &&
+ ((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
+ (tp->t_flags & TF_RCVD_TSTMP))) {
+ u_long *lp = (u_long *)(opt + optlen);
+
+ /* Form timestamp option as shown in appendix A of RFC 1323. */
+ *lp++ = htonl(TCPOPT_TSTAMP_HDR);
+ *lp++ = htonl(tcp_now);
+ *lp = htonl(tp->ts_recent);
+ optlen += TCPOLEN_TSTAMP_APPA;
+ }
+
+ hdrlen += optlen;
+
+ /*
+ * Adjust data length if insertion of options will
+ * bump the packet length beyond the t_maxseg length.
+ */
+ if (len > tp->t_maxseg - optlen) {
+ len = tp->t_maxseg - optlen;
+ sendalot = 1;
+ flags &= ~TH_FIN;
+ }
+
+
+#ifdef DIAGNOSTIC
+ if (max_linkhdr + hdrlen > MHLEN)
+ panic("tcphdr too big");
+#endif
+
+ /*
+ * Grab a header mbuf, attaching a copy of data to
+ * be transmitted, and initialize the header from
+ * the template for sends on this connection.
+ */
+ if (len) {
+ if (tp->t_force && len == 1)
+ tcpstat.tcps_sndprobe++;
+ else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+ tcpstat.tcps_sndrexmitpack++;
+ tcpstat.tcps_sndrexmitbyte += len;
+ } else {
+ tcpstat.tcps_sndpack++;
+ tcpstat.tcps_sndbyte += len;
+ }
+#ifdef notyet
+ if ((m = m_copypack(so->so_snd.sb_mb, off,
+ (int)len, max_linkhdr + hdrlen)) == 0) {
+ error = ENOBUFS;
+ goto out;
+ }
+ /*
+ * m_copypack left space for our hdr; use it.
+ */
+ m->m_len += hdrlen;
+ m->m_data -= hdrlen;
+#else
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ m->m_data += max_linkhdr;
+ m->m_len = hdrlen;
+ if (len <= MHLEN - hdrlen - max_linkhdr) {
+ m_copydata(so->so_snd.sb_mb, off, (int) len,
+ mtod(m, caddr_t) + hdrlen);
+ m->m_len += len;
+ } else {
+ m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
+ if (m->m_next == 0) {
+ (void) m_free(m);
+ error = ENOBUFS;
+ goto out;
+ }
+ }
+#endif
+ /*
+ * If we're sending everything we've got, set PUSH.
+ * (This will keep happy those implementations which only
+ * give data to the user when a buffer fills or
+ * a PUSH comes in.)
+ */
+ if (off + len == so->so_snd.sb_cc)
+ flags |= TH_PUSH;
+ } else {
+ if (tp->t_flags & TF_ACKNOW)
+ tcpstat.tcps_sndacks++;
+ else if (flags & (TH_SYN|TH_FIN|TH_RST))
+ tcpstat.tcps_sndctrl++;
+ else if (SEQ_GT(tp->snd_up, tp->snd_una))
+ tcpstat.tcps_sndurg++;
+ else
+ tcpstat.tcps_sndwinup++;
+
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto out;
+ }
+ m->m_data += max_linkhdr;
+ m->m_len = hdrlen;
+ }
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ ti = mtod(m, struct tcpiphdr *);
+ if (tp->t_template == 0)
+ panic("tcp_output");
+ bcopy((caddr_t)tp->t_template, (caddr_t)ti, sizeof (struct tcpiphdr));
+
+ /*
+ * Fill in fields, remembering maximum advertised
+ * window for use in delaying messages about window sizes.
+ * If resending a FIN, be sure not to use a new sequence number.
+ */
+ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
+ tp->snd_nxt == tp->snd_max)
+ tp->snd_nxt--;
+ /*
+ * If we are doing retransmissions, then snd_nxt will
+ * not reflect the first unsent octet. For ACK only
+ * packets, we do not want the sequence number of the
+ * retransmitted packet, we want the sequence number
+ * of the next unsent octet. So, if there is no data
+ * (and no SYN or FIN), use snd_max instead of snd_nxt
+ * when filling in ti_seq. But if we are in persist
+ * state, snd_max might reflect one byte beyond the
+ * right edge of the window, so use snd_nxt in that
+ * case, since we know we aren't doing a retransmission.
+ * (retransmit and persist are mutually exclusive...)
+ */
+ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ ti->ti_seq = htonl(tp->snd_nxt);
+ else
+ ti->ti_seq = htonl(tp->snd_max);
+ ti->ti_ack = htonl(tp->rcv_nxt);
+ if (optlen) {
+ bcopy((caddr_t)opt, (caddr_t)(ti + 1), optlen);
+ ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
+ }
+ ti->ti_flags = flags;
+ /*
+ * Calculate receive window. Don't shrink window,
+ * but avoid silly window syndrome.
+ */
+ if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
+ win = 0;
+ if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+ win = (long)TCP_MAXWIN << tp->rcv_scale;
+ if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+ win = (long)(tp->rcv_adv - tp->rcv_nxt);
+ ti->ti_win = htons((u_short) (win>>tp->rcv_scale));
+ if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
+ ti->ti_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));
+ ti->ti_flags |= TH_URG;
+ } else
+ /*
+ * If no urgent pointer to send, then we pull
+ * the urgent pointer to the left edge of the send window
+ * so that it doesn't drift into the send window on sequence
+ * number wraparound.
+ */
+ tp->snd_up = tp->snd_una; /* drag it along */
+
+ /*
+ * Put TCP length in extended header, and then
+ * checksum extended header and data.
+ */
+ if (len + optlen)
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) +
+ optlen + len));
+ ti->ti_sum = in_cksum(m, (int)(hdrlen + len));
+
+ /*
+ * In transmit state, time the transmission and arrange for
+ * the retransmit. In persist state, just set snd_max.
+ */
+ if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+ tcp_seq startseq = tp->snd_nxt;
+
+ /*
+ * Advance snd_nxt over sequence space of this segment.
+ */
+ if (flags & (TH_SYN|TH_FIN)) {
+ if (flags & TH_SYN)
+ tp->snd_nxt++;
+ if (flags & TH_FIN) {
+ tp->snd_nxt++;
+ tp->t_flags |= TF_SENTFIN;
+ }
+ }
+ tp->snd_nxt += len;
+ if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
+ tp->snd_max = tp->snd_nxt;
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tp->t_rtt == 0) {
+ tp->t_rtt = 1;
+ tp->t_rtseq = startseq;
+ tcpstat.tcps_segstimed++;
+ }
+ }
+
+ /*
+ * Set retransmit timer if not currently set,
+ * and not doing an ack or a keep-alive probe.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->snd_nxt != tp->snd_una) {
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ if (tp->t_timer[TCPT_PERSIST]) {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+ } else
+ if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+ tp->snd_max = tp->snd_nxt + len;
+
+ /*
+ * Trace.
+ */
+ if (so->so_options & SO_DEBUG)
+ tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0);
+
+ /*
+ * Fill in IP length and desired time to live and
+ * send to IP level. There should be a better way
+ * to handle ttl and tos; we could keep them in
+ * the template, but need a way to checksum without them.
+ */
+ m->m_pkthdr.len = hdrlen + len;
+#ifdef TUBA
+ if (tp->t_tuba_pcb)
+ error = tuba_output(m, tp);
+ else
+#endif
+ {
+ ((struct ip *)ti)->ip_len = m->m_pkthdr.len;
+ ((struct ip *)ti)->ip_ttl = tp->t_inpcb->inp_ip.ip_ttl; /* XXX */
+ ((struct ip *)ti)->ip_tos = tp->t_inpcb->inp_ip.ip_tos; /* XXX */
+#if BSD >= 43
+ error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+ so->so_options & SO_DONTROUTE, 0);
+#else
+ error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route,
+ so->so_options & SO_DONTROUTE);
+#endif
+ }
+ if (error) {
+out:
+ if (error == ENOBUFS) {
+ tcp_quench(tp->t_inpcb, 0);
+ return (0);
+ }
+ if ((error == EHOSTUNREACH || error == ENETDOWN)
+ && TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_softerror = error;
+ return (0);
+ }
+ return (error);
+ }
+ tcpstat.tcps_sndtotal++;
+
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertised window.
+ * Any pending ACK has now been sent.
+ */
+ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+ tp->rcv_adv = tp->rcv_nxt + win;
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ if (sendalot)
+ goto again;
+ return (0);
+}
+
+void
+tcp_setpersist(tp)
+ register struct tcpcb *tp;
+{
+ register t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+ if (tp->t_timer[TCPT_REXMT])
+ panic("tcp_output REXMT");
+ /*
+ * Start/restart persistance timer.
+ */
+ TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+ t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+ tp->t_rxtshift++;
+}
diff --git a/sys/netinet/tcp_seq.h b/sys/netinet/tcp_seq.h
new file mode 100644
index 000000000000..08a6331d319d
--- /dev/null
+++ b/sys/netinet/tcp_seq.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1995
+ * 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.
+ *
+ * @(#)tcp_seq.h 8.3 (Berkeley) 6/21/95
+ */
+
+/*
+ * TCP sequence numbers are 32 bit integers operated
+ * on with modular arithmetic. These macros can be
+ * used to compare such integers.
+ */
+#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+ (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
+ (tp)->iss
+
+#ifdef KERNEL
+/*
+ * Increment for tcp_iss each second.
+ * This is designed to increment at the standard 250 KB/s,
+ * but with a random component averaging 128 KB.
+ * We also increment tcp_iss by a quarter of this amount
+ * each time we use the value for a new connection.
+ * If defined, the tcp_random18() macro should produce a
+ * number in the range [0-0x3ffff] that is hard to predict.
+ */
+#ifndef tcp_random18
+#define tcp_random18() ((random() >> 14) & 0x3ffff)
+#endif
+#define TCP_ISSINCR (122*1024 + tcp_random18())
+
+tcp_seq tcp_iss; /* tcp initial send seq # */
+#else
+#define TCP_ISSINCR (250*1024) /* increment for tcp_iss each second */
+#endif
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
new file mode 100644
index 000000000000..9803ec9429cd
--- /dev/null
+++ b/sys/netinet/tcp_subr.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
+ * 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.
+ *
+ * @(#)tcp_subr.c 8.2 (Berkeley) 5/24/95
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+
+/* patchable/settable parameters for tcp */
+int tcp_mssdflt = TCP_MSS;
+int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
+int tcp_do_rfc1323 = 1;
+
+extern struct inpcb *tcp_last_inpcb;
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init()
+{
+
+ tcp_iss = random(); /* wrong, but better than a constant */
+ tcb.inp_next = tcb.inp_prev = &tcb;
+ if (max_protohdr < sizeof(struct tcpiphdr))
+ max_protohdr = sizeof(struct tcpiphdr);
+ if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
+ panic("tcp_init");
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, allocates an mbuf and fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+struct tcpiphdr *
+tcp_template(tp)
+ struct tcpcb *tp;
+{
+ register struct inpcb *inp = tp->t_inpcb;
+ register struct mbuf *m;
+ register struct tcpiphdr *n;
+
+ if ((n = tp->t_template) == 0) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return (0);
+ m->m_len = sizeof (struct tcpiphdr);
+ n = mtod(m, struct tcpiphdr *);
+ }
+ n->ti_next = n->ti_prev = 0;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = inp->inp_laddr;
+ n->ti_dst = inp->inp_faddr;
+ n->ti_sport = inp->inp_lport;
+ n->ti_dport = inp->inp_fport;
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+ return (n);
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(tp, ti, m, ack, seq, flags)
+ struct tcpcb *tp;
+ register struct tcpiphdr *ti;
+ register struct mbuf *m;
+ tcp_seq ack, seq;
+ int flags;
+{
+ register int tlen;
+ int win = 0;
+ struct route *ro = 0;
+
+ if (tp) {
+ win = sbspace(&tp->t_inpcb->inp_socket->so_rcv);
+ ro = &tp->t_inpcb->inp_route;
+ }
+ if (m == 0) {
+ m = m_gethdr(M_DONTWAIT, MT_HEADER);
+ if (m == NULL)
+ return;
+#ifdef TCP_COMPAT_42
+ tlen = 1;
+#else
+ tlen = 0;
+#endif
+ m->m_data += max_linkhdr;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ m_freem(m->m_next);
+ m->m_next = 0;
+ m->m_data = (caddr_t)ti;
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long);
+ xchg(ti->ti_dport, ti->ti_sport, u_short);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+ m->m_pkthdr.len = tlen;
+ m->m_pkthdr.rcvif = (struct ifnet *) 0;
+ ti->ti_next = ti->ti_prev = 0;
+ ti->ti_x1 = 0;
+ ti->ti_seq = htonl(seq);
+ ti->ti_ack = htonl(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ ti->ti_win = htons((u_short) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((u_short)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = in_cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+ (void) ip_output(m, NULL, ro, 0, NULL);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(inp)
+ struct inpcb *inp;
+{
+ register struct tcpcb *tp;
+
+ tp = malloc(sizeof(*tp), M_PCB, M_NOWAIT);
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+ bzero((char *) tp, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp;
+ tp->t_maxseg = tcp_mssdflt;
+
+ tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_inpcb = inp;
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
+ tp->t_rttmin = TCPTV_MIN;
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ inp->inp_ip.ip_ttl = ip_defttl;
+ inp->inp_ppcb = (caddr_t)tp;
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *
+tcp_drop(tp, errno)
+ register struct tcpcb *tp;
+ int errno;
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ tcpstat.tcps_drops++;
+ } else
+ tcpstat.tcps_conndrops++;
+ if (errno == ETIMEDOUT && tp->t_softerror)
+ errno = tp->t_softerror;
+ so->so_error = errno;
+ return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(tp)
+ register struct tcpcb *tp;
+{
+ register struct tcpiphdr *t;
+ struct inpcb *inp = tp->t_inpcb;
+ struct socket *so = inp->inp_socket;
+ register struct mbuf *m;
+#ifdef RTV_RTT
+ register struct rtentry *rt;
+
+ /*
+ * If we sent enough data to get some meaningful characteristics,
+ * save them in the routing entry. 'Enough' is arbitrarily
+ * defined as the sendpipesize (default 4K) * 16. This would
+ * give us 16 rtt samples assuming we only get one sample per
+ * window (the usual case on a long haul net). 16 samples is
+ * enough for the srtt filter to converge to within 5% of the correct
+ * value; fewer samples and we could save a very bogus rtt.
+ *
+ * Don't update the default route's characteristics and don't
+ * update anything that the user "locked".
+ */
+ if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) &&
+ (rt = inp->inp_route.ro_rt) &&
+ ((struct sockaddr_in *)rt_key(rt))->sin_addr.s_addr != INADDR_ANY) {
+ register u_long i;
+
+ if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
+ i = tp->t_srtt *
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
+ if (rt->rt_rmx.rmx_rtt && i)
+ /*
+ * filter this update to half the old & half
+ * the new values, converting scale.
+ * See route.h and tcp_var.h for a
+ * description of the scaling constants.
+ */
+ rt->rt_rmx.rmx_rtt =
+ (rt->rt_rmx.rmx_rtt + i) / 2;
+ else
+ rt->rt_rmx.rmx_rtt = i;
+ }
+ if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
+ i = tp->t_rttvar *
+ (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
+ if (rt->rt_rmx.rmx_rttvar && i)
+ rt->rt_rmx.rmx_rttvar =
+ (rt->rt_rmx.rmx_rttvar + i) / 2;
+ else
+ rt->rt_rmx.rmx_rttvar = i;
+ }
+ /*
+ * update the pipelimit (ssthresh) if it has been updated
+ * already or if a pipesize was specified & the threshhold
+ * got below half the pipesize. I.e., wait for bad news
+ * before we start updating, then update on both good
+ * and bad news.
+ */
+ if ((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&
+ (i = tp->snd_ssthresh) && rt->rt_rmx.rmx_ssthresh ||
+ i < (rt->rt_rmx.rmx_sendpipe / 2)) {
+ /*
+ * convert the limit from user data bytes to
+ * packets then to packet data bytes.
+ */
+ i = (i + tp->t_maxseg / 2) / tp->t_maxseg;
+ if (i < 2)
+ i = 2;
+ i *= (u_long)(tp->t_maxseg + sizeof (struct tcpiphdr));
+ if (rt->rt_rmx.rmx_ssthresh)
+ rt->rt_rmx.rmx_ssthresh =
+ (rt->rt_rmx.rmx_ssthresh + i) / 2;
+ else
+ rt->rt_rmx.rmx_ssthresh = i;
+ }
+ }
+#endif /* RTV_RTT */
+ /* free the reassembly queue, if any */
+ t = tp->seg_next;
+ while (t != (struct tcpiphdr *)tp) {
+ t = (struct tcpiphdr *)t->ti_next;
+ m = REASS_MBUF((struct tcpiphdr *)t->ti_prev);
+ remque(t->ti_prev);
+ m_freem(m);
+ }
+ if (tp->t_template)
+ (void) m_free(dtom(tp->t_template));
+ free(tp, M_PCB);
+ inp->inp_ppcb = 0;
+ soisdisconnected(so);
+ /* clobber input pcb cache if we're closing the cached connection */
+ if (inp == tcp_last_inpcb)
+ tcp_last_inpcb = &tcb;
+ in_pcbdetach(inp);
+ tcpstat.tcps_closed++;
+ return ((struct tcpcb *)0);
+}
+
+void
+tcp_drain()
+{
+
+}
+
+/*
+ * Notify a tcp user of an asynchronous error;
+ * store error as soft error, but wake up user
+ * (for now, won't do anything until can select for soft error).
+ */
+void
+tcp_notify(inp, error)
+ struct inpcb *inp;
+ int error;
+{
+ register struct tcpcb *tp = (struct tcpcb *)inp->inp_ppcb;
+ register struct socket *so = inp->inp_socket;
+
+ /*
+ * Ignore some errors if we are hooked up.
+ * If connection hasn't completed, has retransmitted several times,
+ * and receives a second error, give up now. This is better
+ * than waiting a long time to establish a connection that
+ * can never complete.
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (error == EHOSTUNREACH || error == ENETUNREACH ||
+ error == EHOSTDOWN)) {
+ return;
+ } else if (tp->t_state < TCPS_ESTABLISHED && tp->t_rxtshift > 3 &&
+ tp->t_softerror)
+ so->so_error = error;
+ else
+ tp->t_softerror = error;
+ wakeup((caddr_t) &so->so_timeo);
+ sorwakeup(so);
+ sowwakeup(so);
+}
+
+void
+tcp_ctlinput(cmd, sa, ip)
+ int cmd;
+ struct sockaddr *sa;
+ register struct ip *ip;
+{
+ register struct tcphdr *th;
+ extern struct in_addr zeroin_addr;
+ extern u_char inetctlerrmap[];
+ void (*notify) __P((struct inpcb *, int)) = tcp_notify;
+
+ if (cmd == PRC_QUENCH)
+ notify = tcp_quench;
+ else if (!PRC_IS_REDIRECT(cmd) &&
+ ((unsigned)cmd > PRC_NCMDS || inetctlerrmap[cmd] == 0))
+ return;
+ if (ip) {
+ th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ in_pcbnotify(&tcb, sa, th->th_dport, ip->ip_src, th->th_sport,
+ cmd, notify);
+ } else
+ in_pcbnotify(&tcb, sa, 0, zeroin_addr, 0, cmd, notify);
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment. We will gradually open it again as we proceed.
+ */
+void
+tcp_quench(inp, errno)
+ struct inpcb *inp;
+ int errno;
+{
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if (tp)
+ tp->snd_cwnd = tp->t_maxseg;
+}
diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c
new file mode 100644
index 000000000000..5883d740b6cd
--- /dev/null
+++ b/sys/netinet/tcp_timer.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
+ * 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.
+ *
+ * @(#)tcp_timer.c 8.2 (Berkeley) 5/24/95
+ */
+
+#ifndef TUBA_INCLUDE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+
+#include <machine/cpu.h> /* before tcp_seq.h, for tcp_random18() */
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+
+int tcp_keepidle = TCPTV_KEEP_IDLE;
+int tcp_keepintvl = TCPTV_KEEPINTVL;
+int tcp_keepcnt = TCPTV_KEEPCNT; /* max idle probes */
+int tcp_maxpersistidle = TCPTV_KEEP_IDLE; /* max idle time in persist */
+int tcp_maxidle;
+#else /* TUBA_INCLUDE */
+
+extern int tcp_maxpersistidle;
+#endif /* TUBA_INCLUDE */
+
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo()
+{
+ register struct inpcb *inp;
+ register struct tcpcb *tp;
+ int s = splnet();
+
+ inp = tcb.inp_next;
+ if (inp)
+ for (; inp != &tcb; inp = inp->inp_next)
+ if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
+ (tp->t_flags & TF_DELACK)) {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_delack++;
+ (void) tcp_output(tp);
+ }
+ splx(s);
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo()
+{
+ register struct inpcb *ip, *ipnxt;
+ register struct tcpcb *tp;
+ int s = splnet();
+ register int i;
+
+ tcp_maxidle = tcp_keepcnt * tcp_keepintvl;
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = tcb.inp_next;
+ if (ip == 0) {
+ splx(s);
+ return;
+ }
+ for (; ip != &tcb; ip = ipnxt) {
+ ipnxt = ip->inp_next;
+ tp = intotcpcb(ip);
+ if (tp == 0 || tp->t_state == TCPS_LISTEN)
+ continue;
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+ (void) tcp_usrreq(tp->t_inpcb->inp_socket,
+ PRU_SLOWTIMO, (struct mbuf *)0,
+ (struct mbuf *)i, (struct mbuf *)0);
+ if (ipnxt->inp_prev != ip)
+ goto tpgone;
+ }
+ }
+ tp->t_idle++;
+ if (tp->t_rtt)
+ tp->t_rtt++;
+tpgone:
+ ;
+ }
+ tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
+#ifdef TCP_COMPAT_42
+ if ((int)tcp_iss < 0)
+ tcp_iss = TCP_ISSINCR; /* XXX */
+#endif
+ tcp_now++; /* for timestamps */
+ splx(s);
+}
+#ifndef TUBA_INCLUDE
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(tp)
+ struct tcpcb *tp;
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+int tcp_totbackoff = 511; /* sum of tcp_backoff[] */
+
+/*
+ * TCP timer processing.
+ */
+struct tcpcb *
+tcp_timers(tp, timer)
+ register struct tcpcb *tp;
+ int timer;
+{
+ register int rexmt;
+
+ switch (timer) {
+
+ /*
+ * 2 MSL timeout in shutdown went off. If we're closed but
+ * still waiting for peer to close and connection has been idle
+ * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+ * control block. Otherwise, check again in a bit.
+ */
+ case TCPT_2MSL:
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ tp->t_idle <= tcp_maxidle)
+ tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ case TCPT_REXMT:
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tcpstat.tcps_timeoutdrop++;
+ tp = tcp_drop(tp, tp->t_softerror ?
+ tp->t_softerror : ETIMEDOUT);
+ break;
+ }
+ tcpstat.tcps_rexmttimeo++;
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ tp->t_rttmin, TCPTV_REXMTMAX);
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+ in_losing(tp->t_inpcb);
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
+ }
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtt = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshhold size.
+ * For a threshhold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshhold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Persistance timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ tcpstat.tcps_persisttimeo++;
+ /*
+ * Hack: if the peer is dead/unreachable, we do not
+ * time out if the window is closed. After a full
+ * backoff, drop the connection if the idle time
+ * (no responses to probes) reaches the maximum
+ * backoff that we would use if retransmitting.
+ */
+ if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
+ (tp->t_idle >= tcp_maxpersistidle ||
+ tp->t_idle >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
+ tcpstat.tcps_persistdrop++;
+ tp = tcp_drop(tp, ETIMEDOUT);
+ break;
+ }
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case TCPT_KEEP:
+ tcpstat.tcps_keeptimeo++;
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+ if (tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE &&
+ tp->t_state <= TCPS_CLOSE_WAIT) {
+ if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
+ goto dropit;
+ /*
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
+ */
+ tcpstat.tcps_keepprobe++;
+#ifdef TCP_COMPAT_42
+ /*
+ * The keepalive packet must have nonzero length
+ * to get a 4.2 host to respond.
+ */
+ tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt - 1, tp->snd_una - 1, 0);
+#else
+ tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+#endif
+ tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+ } else
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ break;
+ dropit:
+ tcpstat.tcps_keepdrops++;
+ tp = tcp_drop(tp, ETIMEDOUT);
+ break;
+ }
+ return (tp);
+}
+#endif /* TUBA_INCLUDE */
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
new file mode 100644
index 000000000000..cf9ffff3fb7d
--- /dev/null
+++ b/sys/netinet/tcp_usrreq.c
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993, 1995
+ * 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.
+ *
+ * @(#)tcp_usrreq.c 8.5 (Berkeley) 6/21/95
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_debug.h>
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+extern char *tcpstates[];
+
+/*
+ * Process a TCP user request for TCP tb. If this is a send request
+ * then m is the mbuf chain of send data. If this is a timer expiration
+ * (called from the software clock routine), then timertype tells which timer.
+ */
+/*ARGSUSED*/
+int
+tcp_usrreq(so, req, m, nam, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register struct inpcb *inp;
+ register struct tcpcb *tp;
+ int s;
+ int error = 0;
+ int ostate;
+
+ if (req == PRU_CONTROL)
+ return (in_control(so, (u_long)m, (caddr_t)nam,
+ (struct ifnet *)control));
+ if (control && control->m_len) {
+ m_freem(control);
+ if (m)
+ m_freem(m);
+ return (EINVAL);
+ }
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ /*
+ * When a TCP is attached to a socket, then there will be
+ * a (struct inpcb) pointed at by the socket, and this
+ * structure will point at a subsidary (struct tcpcb).
+ */
+ if (inp == 0 && req != PRU_ATTACH) {
+ splx(s);
+#if 0
+ /*
+ * The following corrects an mbuf leak under rare
+ * circumstances, but has not been fully tested.
+ */
+ if (m && req != PRU_SENSE)
+ m_freem(m);
+#else
+ /* safer version of fix for mbuf leak */
+ if (m && (req == PRU_SEND || req == PRU_SENDOOB))
+ m_freem(m);
+#endif
+ return (EINVAL); /* XXX */
+ }
+ if (inp) {
+ tp = intotcpcb(inp);
+ /* WHAT IF TP IS 0? */
+#ifdef KPROF
+ tcp_acounts[tp->t_state][req]++;
+#endif
+ ostate = tp->t_state;
+ } else
+ ostate = 0;
+ switch (req) {
+
+ /*
+ * TCP attaches to socket via PRU_ATTACH, reserving space,
+ * and an internet control block.
+ */
+ case PRU_ATTACH:
+ if (inp) {
+ error = EISCONN;
+ break;
+ }
+ error = tcp_attach(so);
+ if (error)
+ break;
+ if ((so->so_options & SO_LINGER) && so->so_linger == 0)
+ so->so_linger = TCP_LINGERTIME;
+ tp = sototcpcb(so);
+ break;
+
+ /*
+ * PRU_DETACH detaches the TCP protocol from the socket.
+ * If the protocol state is non-embryonic, then can't
+ * do this directly: have to initiate a PRU_DISCONNECT,
+ * which may finish later; embryonic TCB's can just
+ * be discarded here.
+ */
+ case PRU_DETACH:
+ if (tp->t_state > TCPS_LISTEN)
+ tp = tcp_disconnect(tp);
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Give the socket an address.
+ */
+ case PRU_BIND:
+ error = in_pcbbind(inp, nam);
+ if (error)
+ break;
+ break;
+
+ /*
+ * Prepare to accept connections.
+ */
+ case PRU_LISTEN:
+ if (inp->inp_lport == 0)
+ error = in_pcbbind(inp, (struct mbuf *)0);
+ if (error == 0)
+ tp->t_state = TCPS_LISTEN;
+ break;
+
+ /*
+ * Initiate connection to peer.
+ * Create a template for use in transmissions on this connection.
+ * Enter SYN_SENT state, and mark socket as connecting.
+ * Start keep-alive timer, and seed output sequence space.
+ * Send initial segment on connection.
+ */
+ case PRU_CONNECT:
+ if (inp->inp_lport == 0) {
+ error = in_pcbbind(inp, (struct mbuf *)0);
+ if (error)
+ break;
+ }
+ error = in_pcbconnect(inp, nam);
+ if (error)
+ break;
+ tp->t_template = tcp_template(tp);
+ if (tp->t_template == 0) {
+ in_pcbdisconnect(inp);
+ error = ENOBUFS;
+ break;
+ }
+ /* Compute window scaling to request. */
+ while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
+ tp->request_r_scale++;
+ soisconnecting(so);
+ tcpstat.tcps_connattempt++;
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/4;
+ tcp_sendseqinit(tp);
+ error = tcp_output(tp);
+ break;
+
+ /*
+ * Create a TCP connection between two sockets.
+ */
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ /*
+ * Initiate disconnect from peer.
+ * If connection never passed embryonic stage, just drop;
+ * else if don't need to let data drain, then can just drop anyways,
+ * else have to begin TCP shutdown process: mark socket disconnecting,
+ * drain unread data, state switch to reflect user close, and
+ * send segment (e.g. FIN) to peer. Socket will be really disconnected
+ * when peer sends FIN and acks ours.
+ *
+ * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
+ */
+ case PRU_DISCONNECT:
+ tp = tcp_disconnect(tp);
+ break;
+
+ /*
+ * Accept a connection. Essentially all the work is
+ * done at higher levels; just return the address
+ * of the peer, storing through addr.
+ */
+ case PRU_ACCEPT:
+ in_setpeeraddr(inp, nam);
+ break;
+
+ /*
+ * Mark the connection as being incapable of further output.
+ */
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ tp = tcp_usrclosed(tp);
+ if (tp)
+ error = tcp_output(tp);
+ break;
+
+ /*
+ * After a receive, possibly send window update to peer.
+ */
+ case PRU_RCVD:
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Do a send by putting data in output queue and updating urgent
+ * marker if URG set. Possibly send more data.
+ */
+ case PRU_SEND:
+ sbappend(&so->so_snd, m);
+ error = tcp_output(tp);
+ break;
+
+ /*
+ * Abort the TCP.
+ */
+ case PRU_ABORT:
+ tp = tcp_drop(tp, ECONNABORTED);
+ break;
+
+ case PRU_SENSE:
+ ((struct stat *) m)->st_blksize = so->so_snd.sb_hiwat;
+ (void) splx(s);
+ return (0);
+
+ case PRU_RCVOOB:
+ if ((so->so_oobmark == 0 &&
+ (so->so_state & SS_RCVATMARK) == 0) ||
+ so->so_options & SO_OOBINLINE ||
+ tp->t_oobflags & TCPOOB_HADDATA) {
+ error = EINVAL;
+ break;
+ }
+ if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
+ error = EWOULDBLOCK;
+ break;
+ }
+ m->m_len = 1;
+ *mtod(m, caddr_t) = tp->t_iobc;
+ if (((int)nam & MSG_PEEK) == 0)
+ tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
+ break;
+
+ case PRU_SENDOOB:
+ if (sbspace(&so->so_snd) < -512) {
+ m_freem(m);
+ error = ENOBUFS;
+ break;
+ }
+ /*
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section.
+ * Otherwise, snd_up should be one lower.
+ */
+ sbappend(&so->so_snd, m);
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ error = tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, nam);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, nam);
+ break;
+
+ /*
+ * TCP slow timer went off; going through this
+ * routine for tracing's sake.
+ */
+ case PRU_SLOWTIMO:
+ tp = tcp_timers(tp, (int)nam);
+ req |= (int)nam << 8; /* for debug's sake */
+ break;
+
+ default:
+ panic("tcp_usrreq");
+ }
+ if (tp && (so->so_options & SO_DEBUG))
+ tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0, req);
+ splx(s);
+ return (error);
+}
+
+int
+tcp_ctloutput(op, so, level, optname, mp)
+ int op;
+ struct socket *so;
+ int level, optname;
+ struct mbuf **mp;
+{
+ int error = 0, s;
+ struct inpcb *inp;
+ register struct tcpcb *tp;
+ register struct mbuf *m;
+ register int i;
+
+ s = splnet();
+ inp = sotoinpcb(so);
+ if (inp == NULL) {
+ splx(s);
+ if (op == PRCO_SETOPT && *mp)
+ (void) m_free(*mp);
+ return (ECONNRESET);
+ }
+ if (level != IPPROTO_TCP) {
+ error = ip_ctloutput(op, so, level, optname, mp);
+ splx(s);
+ return (error);
+ }
+ tp = intotcpcb(inp);
+
+ switch (op) {
+
+ case PRCO_SETOPT:
+ m = *mp;
+ switch (optname) {
+
+ case TCP_NODELAY:
+ if (m == NULL || m->m_len < sizeof (int))
+ error = EINVAL;
+ else if (*mtod(m, int *))
+ tp->t_flags |= TF_NODELAY;
+ else
+ tp->t_flags &= ~TF_NODELAY;
+ break;
+
+ case TCP_MAXSEG:
+ if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
+ tp->t_maxseg = i;
+ else
+ error = EINVAL;
+ break;
+
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ if (m)
+ (void) m_free(m);
+ break;
+
+ case PRCO_GETOPT:
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ m->m_len = sizeof(int);
+
+ switch (optname) {
+ case TCP_NODELAY:
+ *mtod(m, int *) = tp->t_flags & TF_NODELAY;
+ break;
+ case TCP_MAXSEG:
+ *mtod(m, int *) = tp->t_maxseg;
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+ break;
+ }
+ splx(s);
+ return (error);
+}
+
+u_long tcp_sendspace = 1024*8;
+u_long tcp_recvspace = 1024*8;
+
+/*
+ * Attach TCP protocol to socket, allocating
+ * internet protocol control block, tcp control block,
+ * bufer space, and entering LISTEN state if to accept connections.
+ */
+int
+tcp_attach(so)
+ struct socket *so;
+{
+ register struct tcpcb *tp;
+ struct inpcb *inp;
+ int error;
+
+ if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+ error = soreserve(so, tcp_sendspace, tcp_recvspace);
+ if (error)
+ return (error);
+ }
+ error = in_pcballoc(so, &tcb);
+ if (error)
+ return (error);
+ inp = sotoinpcb(so);
+ tp = tcp_newtcpcb(inp);
+ if (tp == 0) {
+ int nofd = so->so_state & SS_NOFDREF; /* XXX */
+
+ so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */
+ in_pcbdetach(inp);
+ so->so_state |= nofd;
+ return (ENOBUFS);
+ }
+ tp->t_state = TCPS_CLOSED;
+ return (0);
+}
+
+/*
+ * Initiate (or continue) disconnect.
+ * If embryonic state, just send reset (once).
+ * If in ``let data drain'' option and linger null, just drop.
+ * Otherwise (hard), mark socket disconnecting and drop
+ * current input data; switch states based on user close, and
+ * send segment to peer (with FIN).
+ */
+struct tcpcb *
+tcp_disconnect(tp)
+ register struct tcpcb *tp;
+{
+ struct socket *so = tp->t_inpcb->inp_socket;
+
+ if (tp->t_state < TCPS_ESTABLISHED)
+ tp = tcp_close(tp);
+ else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
+ tp = tcp_drop(tp, 0);
+ else {
+ soisdisconnecting(so);
+ sbflush(&so->so_rcv);
+ tp = tcp_usrclosed(tp);
+ if (tp)
+ (void) tcp_output(tp);
+ }
+ return (tp);
+}
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+struct tcpcb *
+tcp_usrclosed(tp)
+ register struct tcpcb *tp;
+{
+
+ switch (tp->t_state) {
+
+ case TCPS_CLOSED:
+ case TCPS_LISTEN:
+ case TCPS_SYN_SENT:
+ tp->t_state = TCPS_CLOSED;
+ tp = tcp_close(tp);
+ break;
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_FIN_WAIT_1;
+ break;
+
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_LAST_ACK;
+ break;
+ }
+ if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
+ soisdisconnected(tp->t_inpcb->inp_socket);
+ return (tp);
+}
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
new file mode 100644
index 000000000000..0ed951756c0c
--- /dev/null
+++ b/sys/netinet/tcp_var.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994, 1995
+ * 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.
+ *
+ * @(#)tcp_var.h 8.4 (Berkeley) 5/24/95
+ */
+
+/*
+ * Kernel variables for tcp.
+ */
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb {
+ struct tcpiphdr *seg_next; /* sequencing queue */
+ struct tcpiphdr *seg_prev;
+ short t_state; /* state of this connection */
+ short t_timer[TCPT_NTIMERS]; /* tcp timers */
+ short t_rxtshift; /* log(2) of rexmt exp. backoff */
+ short t_rxtcur; /* current retransmit value */
+ short t_dupacks; /* consecutive dup acks recd */
+ u_short t_maxseg; /* maximum segment size */
+ char t_force; /* 1 if forcing out a byte */
+ u_short t_flags;
+#define TF_ACKNOW 0x0001 /* ack peer immediately */
+#define TF_DELACK 0x0002 /* ack, but try to delay it */
+#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
+#define TF_NOOPT 0x0008 /* don't use tcp options */
+#define TF_SENTFIN 0x0010 /* have sent FIN */
+#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
+#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
+#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
+#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
+#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
+
+ struct tcpiphdr *t_template; /* skeletal packet for transmit */
+ struct inpcb *t_inpcb; /* back pointer to internet pcb */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+/* send sequence variables */
+ tcp_seq snd_una; /* send unacknowledged */
+ tcp_seq snd_nxt; /* send next */
+ tcp_seq snd_up; /* send urgent pointer */
+ tcp_seq snd_wl1; /* window update seg seq number */
+ tcp_seq snd_wl2; /* window update seg ack number */
+ tcp_seq iss; /* initial send sequence number */
+ u_long snd_wnd; /* send window */
+/* receive sequence variables */
+ u_long rcv_wnd; /* receive window */
+ tcp_seq rcv_nxt; /* receive next */
+ tcp_seq rcv_up; /* receive urgent pointer */
+ tcp_seq irs; /* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+/* receive variables */
+ tcp_seq rcv_adv; /* advertised window */
+/* retransmit variables */
+ tcp_seq snd_max; /* highest sequence number sent;
+ * used to recognize retransmits
+ */
+/* congestion control (for slow start, source quench, retransmit after loss) */
+ u_long snd_cwnd; /* congestion-controlled window */
+ u_long snd_ssthresh; /* snd_cwnd size threshhold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ u_short t_idle; /* inactivity time */
+ short t_rtt; /* round trip time */
+ tcp_seq t_rtseq; /* sequence number being timed */
+ short t_srtt; /* smoothed round-trip time */
+ short t_rttvar; /* variance in round-trip time */
+ u_short t_rttmin; /* minimum rtt allowed */
+ u_long max_sndwnd; /* largest window peer has offered */
+
+/* out-of-band data */
+ char t_oobflags; /* have some */
+ char t_iobc; /* input character */
+#define TCPOOB_HAVEDATA 0x01
+#define TCPOOB_HADDATA 0x02
+ short t_softerror; /* possible error not yet reported */
+
+/* RFC 1323 variables */
+ u_char snd_scale; /* window scaling for send window */
+ u_char rcv_scale; /* window scaling for recv window */
+ u_char request_r_scale; /* pending window scaling */
+ u_char requested_s_scale;
+ u_long ts_recent; /* timestamp echo data */
+ u_long ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+
+/* TUBA stuff */
+ caddr_t t_tuba_pcb; /* next level down pcb for TCP over z */
+};
+
+#define intotcpcb(ip) ((struct tcpcb *)(ip)->inp_ppcb)
+#define sototcpcb(so) (intotcpcb(sotoinpcb(so)))
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
+#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
+#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
+#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define TCP_REXMTVAL(tp) \
+ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+/* XXX
+ * We want to avoid doing m_pullup on incoming packets but that
+ * means avoiding dtom on the tcp reassembly code. That in turn means
+ * keeping an mbuf pointer in the reassembly queue (since we might
+ * have a cluster). As a quick hack, the source & destination
+ * port numbers (which are no longer needed once we've located the
+ * tcpcb) are overlayed with an mbuf pointer.
+ */
+#define REASS_MBUF(ti) (*(struct mbuf **)&((ti)->ti_t))
+
+/*
+ * TCP statistics.
+ * Many of these should be kept per connection,
+ * but that's inconvenient at the moment.
+ */
+struct tcpstat {
+ u_long tcps_connattempt; /* connections initiated */
+ u_long tcps_accepts; /* connections accepted */
+ u_long tcps_connects; /* connections established */
+ u_long tcps_drops; /* connections dropped */
+ u_long tcps_conndrops; /* embryonic connections dropped */
+ u_long tcps_closed; /* conn. closed (includes drops) */
+ u_long tcps_segstimed; /* segs where we tried to get rtt */
+ u_long tcps_rttupdated; /* times we succeeded */
+ u_long tcps_delack; /* delayed acks sent */
+ u_long tcps_timeoutdrop; /* conn. dropped in rxmt timeout */
+ u_long tcps_rexmttimeo; /* retransmit timeouts */
+ u_long tcps_persisttimeo; /* persist timeouts */
+ u_long tcps_keeptimeo; /* keepalive timeouts */
+ u_long tcps_keepprobe; /* keepalive probes sent */
+ u_long tcps_keepdrops; /* connections dropped in keepalive */
+
+ u_long tcps_sndtotal; /* total packets sent */
+ u_long tcps_sndpack; /* data packets sent */
+ u_long tcps_sndbyte; /* data bytes sent */
+ u_long tcps_sndrexmitpack; /* data packets retransmitted */
+ u_long tcps_sndrexmitbyte; /* data bytes retransmitted */
+ u_long tcps_sndacks; /* ack-only packets sent */
+ u_long tcps_sndprobe; /* window probes sent */
+ u_long tcps_sndurg; /* packets sent with URG only */
+ u_long tcps_sndwinup; /* window update-only packets sent */
+ u_long tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */
+
+ u_long tcps_rcvtotal; /* total packets received */
+ u_long tcps_rcvpack; /* packets received in sequence */
+ u_long tcps_rcvbyte; /* bytes received in sequence */
+ u_long tcps_rcvbadsum; /* packets received with ccksum errs */
+ u_long tcps_rcvbadoff; /* packets received with bad offset */
+ u_long tcps_rcvshort; /* packets received too short */
+ u_long tcps_rcvduppack; /* duplicate-only packets received */
+ u_long tcps_rcvdupbyte; /* duplicate-only bytes received */
+ u_long tcps_rcvpartduppack; /* packets with some duplicate data */
+ u_long tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */
+ u_long tcps_rcvoopack; /* out-of-order packets received */
+ u_long tcps_rcvoobyte; /* out-of-order bytes received */
+ u_long tcps_rcvpackafterwin; /* packets with data after window */
+ u_long tcps_rcvbyteafterwin; /* bytes rcvd after window */
+ u_long tcps_rcvafterclose; /* packets rcvd after "close" */
+ u_long tcps_rcvwinprobe; /* rcvd window probe packets */
+ u_long tcps_rcvdupack; /* rcvd duplicate acks */
+ u_long tcps_rcvacktoomuch; /* rcvd acks for unsent data */
+ u_long tcps_rcvackpack; /* rcvd ack packets */
+ u_long tcps_rcvackbyte; /* bytes acked by rcvd acks */
+ u_long tcps_rcvwinupd; /* rcvd window update packets */
+ u_long tcps_pawsdrop; /* segments dropped due to PAWS */
+ u_long tcps_predack; /* times hdr predict ok for acks */
+ u_long tcps_preddat; /* times hdr predict ok for data pkts */
+ u_long tcps_pcbcachemiss;
+ u_long tcps_persistdrop; /* timeout in persist state */
+ u_long tcps_badsyn; /* bogus SYN, e.g. premature ACK */
+};
+
+#ifdef KERNEL
+struct inpcb tcb; /* head of queue of active tcpcb's */
+struct tcpstat tcpstat; /* tcp statistics */
+u_long tcp_now; /* for RFC 1323 timestamps */
+
+int tcp_attach __P((struct socket *));
+void tcp_canceltimers __P((struct tcpcb *));
+struct tcpcb *
+ tcp_close __P((struct tcpcb *));
+void tcp_ctlinput __P((int, struct sockaddr *, struct ip *));
+int tcp_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+struct tcpcb *
+ tcp_disconnect __P((struct tcpcb *));
+struct tcpcb *
+ tcp_drop __P((struct tcpcb *, int));
+void tcp_dooptions __P((struct tcpcb *,
+ u_char *, int, struct tcpiphdr *, int *, u_long *, u_long *));
+void tcp_drain __P((void));
+void tcp_fasttimo __P((void));
+void tcp_init __P((void));
+void tcp_input __P((struct mbuf *, int));
+int tcp_mss __P((struct tcpcb *, u_int));
+struct tcpcb *
+ tcp_newtcpcb __P((struct inpcb *));
+void tcp_notify __P((struct inpcb *, int));
+int tcp_output __P((struct tcpcb *));
+void tcp_pulloutofband __P((struct socket *,
+ struct tcpiphdr *, struct mbuf *));
+void tcp_quench __P((struct inpcb *, int));
+int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *));
+void tcp_respond __P((struct tcpcb *,
+ struct tcpiphdr *, struct mbuf *, u_long, u_long, int));
+void tcp_setpersist __P((struct tcpcb *));
+void tcp_slowtimo __P((void));
+struct tcpiphdr *
+ tcp_template __P((struct tcpcb *));
+struct tcpcb *
+ tcp_timers __P((struct tcpcb *, int));
+void tcp_trace __P((int, int, struct tcpcb *, struct tcpiphdr *, int));
+struct tcpcb *
+ tcp_usrclosed __P((struct tcpcb *));
+int tcp_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+void tcp_xmit_timer __P((struct tcpcb *, int));
+#endif
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
new file mode 100644
index 000000000000..07c26fc69c32
--- /dev/null
+++ b/sys/netinet/udp_usrreq.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
+ * 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.
+ *
+ * @(#)udp_usrreq.c 8.6 (Berkeley) 5/23/95
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
+#ifndef COMPAT_42
+int udpcksum = 1;
+#else
+int udpcksum = 0; /* XXX */
+#endif
+
+struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
+struct inpcb *udp_last_inpcb = &udb;
+
+static void udp_detach __P((struct inpcb *));
+static void udp_notify __P((struct inpcb *, int));
+static struct mbuf *udp_saveopt __P((caddr_t, int, int));
+
+void
+udp_init()
+{
+ udb.inp_next = udb.inp_prev = &udb;
+}
+
+void
+udp_input(m, iphlen)
+ register struct mbuf *m;
+ int iphlen;
+{
+ register struct ip *ip;
+ register struct udphdr *uh;
+ register struct inpcb *inp;
+ struct mbuf *opts = 0;
+ int len;
+ struct ip save_ip;
+
+ udpstat.udps_ipackets++;
+
+ /*
+ * Strip IP options, if any; should skip this,
+ * make available to user, and use on returned packets,
+ * but we don't yet have a way to check the checksum
+ * with options still present.
+ */
+ if (iphlen > sizeof (struct ip)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ */
+ ip = mtod(m, struct ip *);
+ if (m->m_len < iphlen + sizeof(struct udphdr)) {
+ if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) {
+ udpstat.udps_hdrops++;
+ return;
+ }
+ ip = mtod(m, struct ip *);
+ }
+ uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = ntohs((u_short)uh->uh_ulen);
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ udpstat.udps_badlen++;
+ goto bad;
+ }
+ m_adj(m, len - ip->ip_len);
+ /* ip->ip_len = len; */
+ }
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (uh->uh_sum) {
+ ((struct ipovly *)ip)->ih_next = 0;
+ ((struct ipovly *)ip)->ih_prev = 0;
+ ((struct ipovly *)ip)->ih_x1 = 0;
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+ if (uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) {
+ udpstat.udps_badsum++;
+ m_freem(m);
+ return;
+ }
+ }
+
+ if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
+ in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
+ struct socket *last;
+ /*
+ * Deliver a multicast or broadcast datagram to *all* sockets
+ * for which the local and remote addresses and ports match
+ * those of the incoming datagram. This allows more than
+ * one process to receive multi/broadcasts on the same port.
+ * (This really ought to be done for unicast datagrams as
+ * well, but that would cause problems with existing
+ * applications that open both address-specific sockets and
+ * a wildcard socket listening to the same port -- they would
+ * end up receiving duplicates of every unicast datagram.
+ * Those applications open the multiple sockets to overcome an
+ * inadequacy of the UDP socket interface, but for backwards
+ * compatibility we avoid the problem here rather than
+ * fixing the interface. Maybe 4.5BSD will remedy this?)
+ */
+
+ /*
+ * Construct sockaddr format source address.
+ */
+ udp_in.sin_port = uh->uh_sport;
+ udp_in.sin_addr = ip->ip_src;
+ m->m_len -= sizeof (struct udpiphdr);
+ m->m_data += sizeof (struct udpiphdr);
+ /*
+ * Locate pcb(s) for datagram.
+ * (Algorithm copied from raw_intr().)
+ */
+ last = NULL;
+ for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
+ if (inp->inp_lport != uh->uh_dport)
+ continue;
+ if (inp->inp_laddr.s_addr != INADDR_ANY) {
+ if (inp->inp_laddr.s_addr !=
+ ip->ip_dst.s_addr)
+ continue;
+ }
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ if (inp->inp_faddr.s_addr !=
+ ip->ip_src.s_addr ||
+ inp->inp_fport != uh->uh_sport)
+ continue;
+ }
+
+ if (last != NULL) {
+ struct mbuf *n;
+
+ if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
+ if (sbappendaddr(&last->so_rcv,
+ (struct sockaddr *)&udp_in,
+ n, (struct mbuf *)0) == 0) {
+ m_freem(n);
+ udpstat.udps_fullsock++;
+ } else
+ sorwakeup(last);
+ }
+ }
+ last = inp->inp_socket;
+ /*
+ * Don't look for additional matches if this one does
+ * not have either the SO_REUSEPORT or SO_REUSEADDR
+ * socket options set. This heuristic avoids searching
+ * through all pcbs in the common case of a non-shared
+ * port. It * assumes that an application will never
+ * clear these options after setting them.
+ */
+ if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0))
+ break;
+ }
+
+ if (last == NULL) {
+ /*
+ * No matching pcb found; discard datagram.
+ * (No need to send an ICMP Port Unreachable
+ * for a broadcast or multicast datgram.)
+ */
+ udpstat.udps_noportbcast++;
+ goto bad;
+ }
+ if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
+ m, (struct mbuf *)0) == 0) {
+ udpstat.udps_fullsock++;
+ goto bad;
+ }
+ sorwakeup(last);
+ return;
+ }
+ /*
+ * Locate pcb for datagram.
+ */
+ inp = udp_last_inpcb;
+ if (inp->inp_lport != uh->uh_dport ||
+ inp->inp_fport != uh->uh_sport ||
+ inp->inp_faddr.s_addr != ip->ip_src.s_addr ||
+ inp->inp_laddr.s_addr != ip->ip_dst.s_addr) {
+ inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport,
+ ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD);
+ if (inp)
+ udp_last_inpcb = inp;
+ udpstat.udpps_pcbcachemiss++;
+ }
+ if (inp == 0) {
+ udpstat.udps_noport++;
+ if (m->m_flags & (M_BCAST | M_MCAST)) {
+ udpstat.udps_noportbcast++;
+ goto bad;
+ }
+ *ip = save_ip;
+ ip->ip_len += iphlen;
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
+ return;
+ }
+
+ /*
+ * Construct sockaddr format source address.
+ * Stuff source address and datagram in user buffer.
+ */
+ udp_in.sin_port = uh->uh_sport;
+ udp_in.sin_addr = ip->ip_src;
+ if (inp->inp_flags & INP_CONTROLOPTS) {
+ struct mbuf **mp = &opts;
+
+ if (inp->inp_flags & INP_RECVDSTADDR) {
+ *mp = udp_saveopt((caddr_t) &ip->ip_dst,
+ sizeof(struct in_addr), IP_RECVDSTADDR);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+#ifdef notyet
+ /* options were tossed above */
+ if (inp->inp_flags & INP_RECVOPTS) {
+ *mp = udp_saveopt((caddr_t) opts_deleted_above,
+ sizeof(struct in_addr), IP_RECVOPTS);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+ /* ip_srcroute doesn't do what we want here, need to fix */
+ if (inp->inp_flags & INP_RECVRETOPTS) {
+ *mp = udp_saveopt((caddr_t) ip_srcroute(),
+ sizeof(struct in_addr), IP_RECVRETOPTS);
+ if (*mp)
+ mp = &(*mp)->m_next;
+ }
+#endif
+ }
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_pkthdr.len -= iphlen;
+ m->m_data += iphlen;
+ if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
+ m, opts) == 0) {
+ udpstat.udps_fullsock++;
+ goto bad;
+ }
+ sorwakeup(inp->inp_socket);
+ return;
+bad:
+ m_freem(m);
+ if (opts)
+ m_freem(opts);
+}
+
+/*
+ * Create a "control" mbuf containing the specified data
+ * with the specified type for presentation with a datagram.
+ */
+struct mbuf *
+udp_saveopt(p, size, type)
+ caddr_t p;
+ register int size;
+ int type;
+{
+ register struct cmsghdr *cp;
+ struct mbuf *m;
+
+ if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
+ return ((struct mbuf *) NULL);
+ cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
+ bcopy(p, CMSG_DATA(cp), size);
+ size += sizeof(*cp);
+ m->m_len = size;
+ cp->cmsg_len = size;
+ cp->cmsg_level = IPPROTO_IP;
+ cp->cmsg_type = type;
+ return (m);
+}
+
+/*
+ * Notify a udp user of an asynchronous error;
+ * just wake up so that he can collect error status.
+ */
+static void
+udp_notify(inp, errno)
+ register struct inpcb *inp;
+ int errno;
+{
+ inp->inp_socket->so_error = errno;
+ sorwakeup(inp->inp_socket);
+ sowwakeup(inp->inp_socket);
+}
+
+void
+udp_ctlinput(cmd, sa, ip)
+ int cmd;
+ struct sockaddr *sa;
+ register struct ip *ip;
+{
+ register struct udphdr *uh;
+ extern struct in_addr zeroin_addr;
+ extern u_char inetctlerrmap[];
+
+ if (!PRC_IS_REDIRECT(cmd) &&
+ ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0))
+ return;
+ if (ip) {
+ uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+ in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport,
+ cmd, udp_notify);
+ } else
+ in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
+}
+
+int
+udp_output(inp, m, addr, control)
+ register struct inpcb *inp;
+ register struct mbuf *m;
+ struct mbuf *addr, *control;
+{
+ register struct udpiphdr *ui;
+ register int len = m->m_pkthdr.len;
+ struct in_addr laddr;
+ int s, error = 0;
+
+ if (control)
+ m_freem(control); /* XXX */
+
+ if (addr) {
+ laddr = inp->inp_laddr;
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ error = EISCONN;
+ goto release;
+ }
+ /*
+ * Must block input while temporarily connected.
+ */
+ s = splnet();
+ error = in_pcbconnect(inp, addr);
+ if (error) {
+ splx(s);
+ goto release;
+ }
+ } else {
+ if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ error = ENOTCONN;
+ goto release;
+ }
+ }
+ /*
+ * Calculate data length and get a mbuf
+ * for UDP and IP headers.
+ */
+ M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto release;
+ }
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ ui->ui_next = ui->ui_prev = 0;
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons((u_short)len + sizeof (struct udphdr));
+ ui->ui_src = inp->inp_laddr;
+ ui->ui_dst = inp->inp_faddr;
+ ui->ui_sport = inp->inp_lport;
+ ui->ui_dport = inp->inp_fport;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if (udpcksum) {
+ if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)
+ ui->ui_sum = 0xffff;
+ }
+ ((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;
+ ((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl; /* XXX */
+ ((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos; /* XXX */
+ udpstat.udps_opackets++;
+ error = ip_output(m, inp->inp_options, &inp->inp_route,
+ inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
+ inp->inp_moptions);
+
+ if (addr) {
+ in_pcbdisconnect(inp);
+ inp->inp_laddr = laddr;
+ splx(s);
+ }
+ return (error);
+
+release:
+ m_freem(m);
+ return (error);
+}
+
+u_long udp_sendspace = 9216; /* really max datagram size */
+u_long udp_recvspace = 40 * (1024 + sizeof(struct sockaddr_in));
+ /* 40 1K datagrams */
+
+/*ARGSUSED*/
+int
+udp_usrreq(so, req, m, addr, control)
+ struct socket *so;
+ int req;
+ struct mbuf *m, *addr, *control;
+{
+ struct inpcb *inp = sotoinpcb(so);
+ int error = 0;
+ int s;
+
+ if (req == PRU_CONTROL)
+ return (in_control(so, (u_long)m, (caddr_t)addr,
+ (struct ifnet *)control));
+ if (inp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+ /*
+ * Note: need to block udp_input while changing
+ * the udp pcb queue and/or pcb addresses.
+ */
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (inp != NULL) {
+ error = EINVAL;
+ break;
+ }
+ s = splnet();
+ error = in_pcballoc(so, &udb);
+ splx(s);
+ if (error)
+ break;
+ error = soreserve(so, udp_sendspace, udp_recvspace);
+ if (error)
+ break;
+ ((struct inpcb *) so->so_pcb)->inp_ip.ip_ttl = ip_defttl;
+ break;
+
+ case PRU_DETACH:
+ udp_detach(inp);
+ break;
+
+ case PRU_BIND:
+ s = splnet();
+ error = in_pcbbind(inp, addr);
+ splx(s);
+ break;
+
+ case PRU_LISTEN:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_CONNECT:
+ if (inp->inp_faddr.s_addr != INADDR_ANY) {
+ error = EISCONN;
+ break;
+ }
+ s = splnet();
+ error = in_pcbconnect(inp, addr);
+ splx(s);
+ if (error == 0)
+ soisconnected(so);
+ break;
+
+ case PRU_CONNECT2:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_ACCEPT:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_DISCONNECT:
+ if (inp->inp_faddr.s_addr == INADDR_ANY) {
+ error = ENOTCONN;
+ break;
+ }
+ s = splnet();
+ in_pcbdisconnect(inp);
+ inp->inp_laddr.s_addr = INADDR_ANY;
+ splx(s);
+ so->so_state &= ~SS_ISCONNECTED; /* XXX */
+ break;
+
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND:
+ return (udp_output(inp, m, addr, control));
+
+ case PRU_ABORT:
+ soisdisconnected(so);
+ udp_detach(inp);
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, addr);
+ break;
+
+ case PRU_PEERADDR:
+ in_setpeeraddr(inp, addr);
+ break;
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ case PRU_SENDOOB:
+ case PRU_FASTTIMO:
+ case PRU_SLOWTIMO:
+ case PRU_PROTORCV:
+ case PRU_PROTOSEND:
+ error = EOPNOTSUPP;
+ break;
+
+ case PRU_RCVD:
+ case PRU_RCVOOB:
+ return (EOPNOTSUPP); /* do not free mbuf's */
+
+ default:
+ panic("udp_usrreq");
+ }
+
+release:
+ if (control) {
+ printf("udp control data unexpectedly retained\n");
+ m_freem(control);
+ }
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+static void
+udp_detach(inp)
+ struct inpcb *inp;
+{
+ int s = splnet();
+
+ if (inp == udp_last_inpcb)
+ udp_last_inpcb = &udb;
+ in_pcbdetach(inp);
+ splx(s);
+}
+
+/*
+ * Sysctl for udp variables.
+ */
+udp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+ int *name;
+ u_int namelen;
+ void *oldp;
+ size_t *oldlenp;
+ void *newp;
+ size_t newlen;
+{
+ /* All sysctl names at this level are terminal. */
+ if (namelen != 1)
+ return (ENOTDIR);
+
+ switch (name[0]) {
+ case UDPCTL_CHECKSUM:
+ return (sysctl_int(oldp, oldlenp, newp, newlen, &udpcksum));
+ default:
+ return (ENOPROTOOPT);
+ }
+ /* NOTREACHED */
+}