aboutsummaryrefslogtreecommitdiffstats
path: root/slirp
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2009-09-14 14:32:27 -0700
committerDavid 'Digit' Turner <digit@google.com>2009-09-14 14:32:27 -0700
commit5d8f37ad78fc66901af50c762029a501561f3b23 (patch)
tree206790f8f21000850a98c4f9590a79e779106278 /slirp
parentcd059b15f2c7df69f4a087bd66900eb172e41d1c (diff)
downloadexternal_qemu-5d8f37ad78fc66901af50c762029a501561f3b23.zip
external_qemu-5d8f37ad78fc66901af50c762029a501561f3b23.tar.gz
external_qemu-5d8f37ad78fc66901af50c762029a501561f3b23.tar.bz2
Merge upstream QEMU 10.0.50 into the Android source tree.
This change integrates many changes from the upstream QEMU sources. Its main purpose is to enable correct ARMv6 and ARMv7 support to the Android emulator. Due to the nature of the upstream code base, this unfortunately also required changes to many other parts of the source. Note that to ensure easier integrations in the future, some source files and directories that have heavy Android-specific customization have been renamed with an -android suffix. The original files are still there for easier integration tracking, but *never* compiled. For example: net.c net-android.c qemu-char.c qemu-char-android.c slirp/ slirp-android/ etc... Tested on linux-x86, darwin-x86 and windows host machines.
Diffstat (limited to 'slirp')
-rw-r--r--slirp/COPYRIGHT61
-rw-r--r--slirp/bootp.c326
-rw-r--r--slirp/bootp.h115
-rw-r--r--slirp/cksum.c137
-rw-r--r--slirp/ctl.h7
-rw-r--r--slirp/debug.c393
-rw-r--r--slirp/debug.h39
-rw-r--r--slirp/icmp_var.h67
-rw-r--r--slirp/if.c296
-rw-r--r--slirp/if.h63
-rw-r--r--slirp/ip.h293
-rw-r--r--slirp/ip_icmp.c366
-rw-r--r--slirp/ip_icmp.h161
-rw-r--r--slirp/ip_input.c718
-rw-r--r--slirp/ip_output.c203
-rw-r--r--slirp/libslirp.h44
-rw-r--r--slirp/main.h56
-rw-r--r--slirp/mbuf.c227
-rw-r--r--slirp/mbuf.h142
-rw-r--r--slirp/misc.c907
-rw-r--r--slirp/misc.h89
-rw-r--r--slirp/sbuf.c189
-rw-r--r--slirp/sbuf.h30
-rw-r--r--slirp/slirp.c1086
-rw-r--r--slirp/slirp.h333
-rw-r--r--slirp/slirp_config.h206
-rw-r--r--slirp/socket.c768
-rw-r--r--slirp/socket.h94
-rw-r--r--slirp/tcp.h171
-rw-r--r--slirp/tcp_input.c1725
-rw-r--r--slirp/tcp_output.c599
-rw-r--r--slirp/tcp_subr.c1311
-rw-r--r--slirp/tcp_timer.c318
-rw-r--r--slirp/tcp_timer.h133
-rw-r--r--slirp/tcp_var.h230
-rw-r--r--slirp/tcpip.h77
-rw-r--r--slirp/tftp.c425
-rw-r--r--slirp/tftp.h33
-rw-r--r--slirp/udp.c670
-rw-r--r--slirp/udp.h108
40 files changed, 13216 insertions, 0 deletions
diff --git a/slirp/COPYRIGHT b/slirp/COPYRIGHT
new file mode 100644
index 0000000..1bc83d4
--- /dev/null
+++ b/slirp/COPYRIGHT
@@ -0,0 +1,61 @@
+Slirp was written by Danny Gasparovski.
+Copyright (c), 1995,1996 All Rights Reserved.
+
+Slirp is maintained by Kelly Price <tygris+slirp@erols.com>
+
+Slirp is free software; "free" as in you don't have to pay for it, and you
+are free to do whatever you want with it. I do not accept any donations,
+monetary or otherwise, for Slirp. Instead, I would ask you to pass this
+potential donation to your favorite charity. In fact, I encourage
+*everyone* who finds Slirp useful to make a small donation to their
+favorite charity (for example, GreenPeace). This is not a requirement, but
+a suggestion from someone who highly values the service they provide.
+
+The copyright terms and conditions:
+
+---BEGIN---
+
+ Copyright (c) 1995,1996 Danny Gasparovski. 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.
+
+ THIS SOFTWARE IS PROVIDED ``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
+ DANNY GASPAROVSKI 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.
+
+---END---
+
+This basically means you can do anything you want with the software, except
+1) call it your own, and 2) claim warranty on it. There is no warranty for
+this software. None. Nada. If you lose a million dollars while using
+Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***.
+
+If these conditions cannot be met due to legal restrictions (E.g. where it
+is against the law to give out Software without warranty), you must cease
+using the software and delete all copies you have.
+
+Slirp uses code that is copyrighted by the following people/organizations:
+
+Juha Pirkola.
+Gregory M. Christy.
+The Regents of the University of California.
+Carnegie Mellon University.
+The Australian National University.
+RSA Data Security, Inc.
+
+Please read the top of each source file for the details on the various
+copyrights.
diff --git a/slirp/bootp.c b/slirp/bootp.c
new file mode 100644
index 0000000..4e0082d
--- /dev/null
+++ b/slirp/bootp.c
@@ -0,0 +1,326 @@
+/*
+ * QEMU BOOTP/DHCP server
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <slirp.h>
+
+/* XXX: only DHCP is supported */
+
+#define NB_ADDR 16
+
+#define START_ADDR 15
+
+#define LEASE_TIME (24 * 3600)
+
+typedef struct {
+ uint8_t allocated;
+ uint8_t macaddr[6];
+} BOOTPClient;
+
+static BOOTPClient bootp_clients[NB_ADDR];
+
+const char *bootp_filename;
+
+static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
+
+#ifdef DEBUG
+#define dprintf(fmt, ...) \
+if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); }
+#else
+#define dprintf(fmt, ...)
+#endif
+
+static BOOTPClient *get_new_addr(struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_ADDR; i++) {
+ bc = &bootp_clients[i];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
+ return bc;
+}
+
+static BOOTPClient *request_addr(const struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ uint32_t req_addr = ntohl(paddr->s_addr);
+ uint32_t spec_addr = ntohl(special_addr.s_addr);
+ BOOTPClient *bc;
+
+ if (req_addr >= (spec_addr | START_ADDR) &&
+ req_addr < (spec_addr | (NB_ADDR + START_ADDR))) {
+ bc = &bootp_clients[(req_addr & 0xff) - START_ADDR];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
+ bc->allocated = 1;
+ return bc;
+ }
+ }
+ return NULL;
+}
+
+static BOOTPClient *find_addr(struct in_addr *paddr, const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_ADDR; i++) {
+ if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = htonl(ntohl(special_addr.s_addr) | (i + START_ADDR));
+ return bc;
+}
+
+static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
+ const struct in_addr **preq_addr)
+{
+ const uint8_t *p, *p_end;
+ int len, tag;
+
+ *pmsg_type = 0;
+ *preq_addr = NULL;
+
+ p = bp->bp_vend;
+ p_end = p + DHCP_OPT_LEN;
+ if (memcmp(p, rfc1533_cookie, 4) != 0)
+ return;
+ p += 4;
+ while (p < p_end) {
+ tag = p[0];
+ if (tag == RFC1533_PAD) {
+ p++;
+ } else if (tag == RFC1533_END) {
+ break;
+ } else {
+ p++;
+ if (p >= p_end)
+ break;
+ len = *p++;
+ dprintf("dhcp: tag=%d len=%d\n", tag, len);
+
+ switch(tag) {
+ case RFC2132_MSG_TYPE:
+ if (len >= 1)
+ *pmsg_type = p[0];
+ break;
+ case RFC2132_REQ_ADDR:
+ if (len >= 4)
+ *preq_addr = (struct in_addr *)p;
+ break;
+ default:
+ break;
+ }
+ p += len;
+ }
+ }
+ if (*pmsg_type == DHCPREQUEST && !*preq_addr && bp->bp_ciaddr.s_addr) {
+ *preq_addr = &bp->bp_ciaddr;
+ }
+}
+
+static void bootp_reply(const struct bootp_t *bp)
+{
+ BOOTPClient *bc = NULL;
+ struct mbuf *m;
+ struct bootp_t *rbp;
+ struct sockaddr_in saddr, daddr;
+ struct in_addr dns_addr;
+ const struct in_addr *preq_addr;
+ int dhcp_msg_type, val;
+ uint8_t *q;
+
+ /* extract exact DHCP msg type */
+ dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
+ dprintf("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
+ if (preq_addr)
+ dprintf(" req_addr=%08x\n", ntohl(preq_addr->s_addr));
+ else
+ dprintf("\n");
+
+ if (dhcp_msg_type == 0)
+ dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
+
+ if (dhcp_msg_type != DHCPDISCOVER &&
+ dhcp_msg_type != DHCPREQUEST)
+ return;
+ /* XXX: this is a hack to get the client mac address */
+ memcpy(client_ethaddr, bp->bp_hwaddr, 6);
+
+ if ((m = m_get()) == NULL)
+ return;
+ m->m_data += IF_MAXLINKHDR;
+ rbp = (struct bootp_t *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+ memset(rbp, 0, sizeof(struct bootp_t));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ if (preq_addr) {
+ bc = request_addr(preq_addr, client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = *preq_addr;
+ }
+ }
+ if (!bc) {
+ new_addr:
+ bc = get_new_addr(&daddr.sin_addr, client_ethaddr);
+ if (!bc) {
+ dprintf("no address left\n");
+ return;
+ }
+ }
+ memcpy(bc->macaddr, client_ethaddr, 6);
+ } else if (preq_addr) {
+ bc = request_addr(preq_addr, client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = *preq_addr;
+ memcpy(bc->macaddr, client_ethaddr, 6);
+ } else {
+ daddr.sin_addr.s_addr = 0;
+ }
+ } else {
+ bc = find_addr(&daddr.sin_addr, bp->bp_hwaddr);
+ if (!bc) {
+ /* if never assigned, behaves as if it was already
+ assigned (windows fix because it remembers its address) */
+ goto new_addr;
+ }
+ }
+
+ saddr.sin_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
+ saddr.sin_port = htons(BOOTP_SERVER);
+
+ daddr.sin_port = htons(BOOTP_CLIENT);
+
+ rbp->bp_op = BOOTP_REPLY;
+ rbp->bp_xid = bp->bp_xid;
+ rbp->bp_htype = 1;
+ rbp->bp_hlen = 6;
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+
+ rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
+ rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
+
+ q = rbp->bp_vend;
+ memcpy(q, rfc1533_cookie, 4);
+ q += 4;
+
+ if (bc) {
+ dprintf("%s addr=%08x\n",
+ (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
+ ntohl(daddr.sin_addr.s_addr));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPOFFER;
+ } else /* DHCPREQUEST */ {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPACK;
+ }
+
+ if (bootp_filename)
+ snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
+ bootp_filename);
+
+ *q++ = RFC2132_SRV_ID;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_NETMASK;
+ *q++ = 4;
+ *q++ = 0xff;
+ *q++ = 0xff;
+ *q++ = 0xff;
+ *q++ = 0x00;
+
+ if (!slirp_restrict) {
+ *q++ = RFC1533_GATEWAY;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_DNS;
+ *q++ = 4;
+ dns_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
+ memcpy(q, &dns_addr, 4);
+ q += 4;
+ }
+
+ *q++ = RFC2132_LEASE_TIME;
+ *q++ = 4;
+ val = htonl(LEASE_TIME);
+ memcpy(q, &val, 4);
+ q += 4;
+
+ if (*slirp_hostname) {
+ val = strlen(slirp_hostname);
+ *q++ = RFC1533_HOSTNAME;
+ *q++ = val;
+ memcpy(q, slirp_hostname, val);
+ q += val;
+ }
+ } else {
+ static const char nak_msg[] = "requested address not available";
+
+ dprintf("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr));
+
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPNAK;
+
+ *q++ = RFC2132_MESSAGE;
+ *q++ = sizeof(nak_msg) - 1;
+ memcpy(q, nak_msg, sizeof(nak_msg) - 1);
+ q += sizeof(nak_msg) - 1;
+ }
+ *q++ = RFC1533_END;
+
+ daddr.sin_addr.s_addr = 0xffffffffu;
+
+ m->m_len = sizeof(struct bootp_t) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+}
+
+void bootp_input(struct mbuf *m)
+{
+ struct bootp_t *bp = mtod(m, struct bootp_t *);
+
+ if (bp->bp_op == BOOTP_REQUEST) {
+ bootp_reply(bp);
+ }
+}
diff --git a/slirp/bootp.h b/slirp/bootp.h
new file mode 100644
index 0000000..3d80515
--- /dev/null
+++ b/slirp/bootp.h
@@ -0,0 +1,115 @@
+/* bootp/dhcp defines */
+
+#define BOOTP_SERVER 67
+#define BOOTP_CLIENT 68
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define RFC1533_COOKIE 99, 130, 83, 99
+#define RFC1533_PAD 0
+#define RFC1533_NETMASK 1
+#define RFC1533_TIMEOFFSET 2
+#define RFC1533_GATEWAY 3
+#define RFC1533_TIMESERVER 4
+#define RFC1533_IEN116NS 5
+#define RFC1533_DNS 6
+#define RFC1533_LOGSERVER 7
+#define RFC1533_COOKIESERVER 8
+#define RFC1533_LPRSERVER 9
+#define RFC1533_IMPRESSSERVER 10
+#define RFC1533_RESOURCESERVER 11
+#define RFC1533_HOSTNAME 12
+#define RFC1533_BOOTFILESIZE 13
+#define RFC1533_MERITDUMPFILE 14
+#define RFC1533_DOMAINNAME 15
+#define RFC1533_SWAPSERVER 16
+#define RFC1533_ROOTPATH 17
+#define RFC1533_EXTENSIONPATH 18
+#define RFC1533_IPFORWARDING 19
+#define RFC1533_IPSOURCEROUTING 20
+#define RFC1533_IPPOLICYFILTER 21
+#define RFC1533_IPMAXREASSEMBLY 22
+#define RFC1533_IPTTL 23
+#define RFC1533_IPMTU 24
+#define RFC1533_IPMTUPLATEAU 25
+#define RFC1533_INTMTU 26
+#define RFC1533_INTLOCALSUBNETS 27
+#define RFC1533_INTBROADCAST 28
+#define RFC1533_INTICMPDISCOVER 29
+#define RFC1533_INTICMPRESPOND 30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT 32
+#define RFC1533_INTSTATICROUTES 33
+#define RFC1533_LLTRAILERENCAP 34
+#define RFC1533_LLARPCACHETMO 35
+#define RFC1533_LLETHERNETENCAP 36
+#define RFC1533_TCPTTL 37
+#define RFC1533_TCPKEEPALIVETMO 38
+#define RFC1533_TCPKEEPALIVEGB 39
+#define RFC1533_NISDOMAIN 40
+#define RFC1533_NISSERVER 41
+#define RFC1533_NTPSERVER 42
+#define RFC1533_VENDOR 43
+#define RFC1533_NBNS 44
+#define RFC1533_NBDD 45
+#define RFC1533_NBNT 46
+#define RFC1533_NBSCOPE 47
+#define RFC1533_XFS 48
+#define RFC1533_XDM 49
+
+#define RFC2132_REQ_ADDR 50
+#define RFC2132_LEASE_TIME 51
+#define RFC2132_MSG_TYPE 53
+#define RFC2132_SRV_ID 54
+#define RFC2132_PARAM_LIST 55
+#define RFC2132_MESSAGE 56
+#define RFC2132_MAX_SIZE 57
+#define RFC2132_RENEWAL_TIME 58
+#define RFC2132_REBIND_TIME 59
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPACK 5
+#define DHCPNAK 6
+
+#define RFC1533_VENDOR_MAJOR 0
+#define RFC1533_VENDOR_MINOR 0
+
+#define RFC1533_VENDOR_MAGIC 128
+#define RFC1533_VENDOR_ADDPARM 129
+#define RFC1533_VENDOR_ETHDEV 130
+#define RFC1533_VENDOR_HOWTO 132
+#define RFC1533_VENDOR_MNUOPTS 160
+#define RFC1533_VENDOR_SELECTION 176
+#define RFC1533_VENDOR_MOTD 184
+#define RFC1533_VENDOR_NUMOFMOTD 8
+#define RFC1533_VENDOR_IMG 192
+#define RFC1533_VENDOR_NUMOFIMG 16
+
+#define RFC1533_END 255
+#define BOOTP_VENDOR_LEN 64
+#define DHCP_OPT_LEN 312
+
+struct bootp_t {
+ struct ip ip;
+ struct udphdr udp;
+ uint8_t bp_op;
+ uint8_t bp_htype;
+ uint8_t bp_hlen;
+ uint8_t bp_hops;
+ uint32_t bp_xid;
+ uint16_t bp_secs;
+ uint16_t unused;
+ struct in_addr bp_ciaddr;
+ struct in_addr bp_yiaddr;
+ struct in_addr bp_siaddr;
+ struct in_addr bp_giaddr;
+ uint8_t bp_hwaddr[16];
+ uint8_t bp_sname[64];
+ uint8_t bp_file[128];
+ uint8_t bp_vend[DHCP_OPT_LEN];
+};
+
+void bootp_input(struct mbuf *m);
diff --git a/slirp/cksum.c b/slirp/cksum.c
new file mode 100644
index 0000000..34977ff
--- /dev/null
+++ b/slirp/cksum.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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_cksum.c 8.1 (Berkeley) 6/10/93
+ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
+ */
+
+#include <slirp.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * XXX Since we will never span more than 1 mbuf, we can optimise this
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+int cksum(struct mbuf *m, int len)
+{
+ register u_int16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+
+ union {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
+
+ if (m->m_len == 0)
+ goto cont;
+ w = mtod(m, u_int16_t *);
+
+ mlen = m->m_len;
+
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_int8_t *)w;
+ w = (u_int16_t *)((int8_t *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto cont;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(u_int8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(u_int8_t *)w;
+
+cont:
+#ifdef DEBUG
+ if (len) {
+ DEBUG_ERROR((dfd, "cksum: out of data\n"));
+ DEBUG_ERROR((dfd, " len = %d\n", len));
+ }
+#endif
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/slirp/ctl.h b/slirp/ctl.h
new file mode 100644
index 0000000..4a8576d
--- /dev/null
+++ b/slirp/ctl.h
@@ -0,0 +1,7 @@
+#define CTL_CMD 0
+#define CTL_EXEC 1
+#define CTL_ALIAS 2
+#define CTL_DNS 3
+
+#define CTL_SPECIAL "10.0.2.0"
+#define CTL_LOCAL "10.0.2.15"
diff --git a/slirp/debug.c b/slirp/debug.c
new file mode 100644
index 0000000..bfef580
--- /dev/null
+++ b/slirp/debug.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * Portions copyright (c) 2000 Kelly Price.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+FILE *dfd = NULL;
+#ifdef DEBUG
+int dostats = 1;
+#else
+int dostats = 0;
+#endif
+int slirp_debug = 0;
+
+/* Carry over one item from main.c so that the tty's restored.
+ * Only done when the tty being used is /dev/tty --RedWolf */
+#ifndef CONFIG_QEMU
+extern struct termios slirp_tty_settings;
+extern int slirp_tty_restore;
+
+
+void
+debug_init(file, dbg)
+ char *file;
+ int dbg;
+{
+ /* Close the old debugging file */
+ if (dfd)
+ fclose(dfd);
+
+ dfd = fopen(file,"w");
+ if (dfd != NULL) {
+#if 0
+ fprintf(dfd,"Slirp %s - Debugging Started.\n", SLIRP_VERSION);
+#endif
+ fprintf(dfd,"Debugging Started level %i.\r\n",dbg);
+ fflush(dfd);
+ slirp_debug = dbg;
+ } else {
+ lprint("Error: Debugging file \"%s\" could not be opened: %s\r\n",
+ file, strerror(errno));
+ }
+}
+
+/*
+ * Dump a packet in the same format as tcpdump -x
+ */
+#ifdef DEBUG
+void
+dump_packet(dat, n)
+ void *dat;
+ int n;
+{
+ u_char *pptr = (u_char *)dat;
+ int j,k;
+
+ n /= 16;
+ n++;
+ DEBUG_MISC((dfd, "PACKET DUMPED: \n"));
+ for(j = 0; j < n; j++) {
+ for(k = 0; k < 6; k++)
+ DEBUG_MISC((dfd, "%02x ", *pptr++));
+ DEBUG_MISC((dfd, "\n"));
+ fflush(dfd);
+ }
+}
+#endif
+#endif
+
+#ifdef LOG_ENABLED
+#if 0
+/*
+ * Statistic routines
+ *
+ * These will print statistics to the screen, the debug file (dfd), or
+ * a buffer, depending on "type", so that the stats can be sent over
+ * the link as well.
+ */
+
+static void
+ttystats(ttyp)
+ struct ttys *ttyp;
+{
+ struct slirp_ifstats *is = &ttyp->ifstats;
+ char buff[512];
+
+ lprint(" \r\n");
+
+ if (IF_COMP & IF_COMPRESS)
+ strcpy(buff, "on");
+ else if (IF_COMP & IF_NOCOMPRESS)
+ strcpy(buff, "off");
+ else
+ strcpy(buff, "off (for now)");
+ lprint("Unit %d:\r\n", ttyp->unit);
+ lprint(" using %s encapsulation (VJ compression is %s)\r\n", (
+#ifdef USE_PPP
+ ttyp->proto==PROTO_PPP?"PPP":
+#endif
+ "SLIP"), buff);
+ lprint(" %d baudrate\r\n", ttyp->baud);
+ lprint(" interface is %s\r\n", ttyp->up?"up":"down");
+ lprint(" using fd %d, guardian pid is %d\r\n", ttyp->fd, ttyp->pid);
+#ifndef FULL_BOLT
+ lprint(" towrite is %d bytes\r\n", ttyp->towrite);
+#endif
+ if (ttyp->zeros)
+ lprint(" %d zeros have been typed\r\n", ttyp->zeros);
+ else if (ttyp->ones)
+ lprint(" %d ones have been typed\r\n", ttyp->ones);
+ lprint("Interface stats:\r\n");
+ lprint(" %6d output packets sent (%d bytes)\r\n", is->out_pkts, is->out_bytes);
+ lprint(" %6d output packets dropped (%d bytes)\r\n", is->out_errpkts, is->out_errbytes);
+ lprint(" %6d input packets received (%d bytes)\r\n", is->in_pkts, is->in_bytes);
+ lprint(" %6d input packets dropped (%d bytes)\r\n", is->in_errpkts, is->in_errbytes);
+ lprint(" %6d bad input packets\r\n", is->in_mbad);
+}
+
+static void
+allttystats(void)
+{
+ struct ttys *ttyp;
+
+ for (ttyp = ttys; ttyp; ttyp = ttyp->next)
+ ttystats(ttyp);
+}
+#endif
+
+static void
+ipstats(void)
+{
+ lprint(" \r\n");
+
+ lprint("IP stats:\r\n");
+ lprint(" %6d total packets received (%d were unaligned)\r\n",
+ ipstat.ips_total, ipstat.ips_unaligned);
+ lprint(" %6d with incorrect version\r\n", ipstat.ips_badvers);
+ lprint(" %6d with bad header checksum\r\n", ipstat.ips_badsum);
+ lprint(" %6d with length too short (len < sizeof(iphdr))\r\n", ipstat.ips_tooshort);
+ lprint(" %6d with length too small (len < ip->len)\r\n", ipstat.ips_toosmall);
+ lprint(" %6d with bad header length\r\n", ipstat.ips_badhlen);
+ lprint(" %6d with bad packet length\r\n", ipstat.ips_badlen);
+ lprint(" %6d fragments received\r\n", ipstat.ips_fragments);
+ lprint(" %6d fragments dropped\r\n", ipstat.ips_fragdropped);
+ lprint(" %6d fragments timed out\r\n", ipstat.ips_fragtimeout);
+ lprint(" %6d packets reassembled ok\r\n", ipstat.ips_reassembled);
+ lprint(" %6d outgoing packets fragmented\r\n", ipstat.ips_fragmented);
+ lprint(" %6d total outgoing fragments\r\n", ipstat.ips_ofragments);
+ lprint(" %6d with bad protocol field\r\n", ipstat.ips_noproto);
+ lprint(" %6d total packets delivered\r\n", ipstat.ips_delivered);
+}
+
+#ifndef CONFIG_QEMU
+static void
+vjstats(void)
+{
+ lprint(" \r\n");
+
+ lprint("VJ compression stats:\r\n");
+
+ lprint(" %6d outbound packets (%d compressed)\r\n",
+ comp_s.sls_packets, comp_s.sls_compressed);
+ lprint(" %6d searches for connection stats (%d misses)\r\n",
+ comp_s.sls_searches, comp_s.sls_misses);
+ lprint(" %6d inbound uncompressed packets\r\n", comp_s.sls_uncompressedin);
+ lprint(" %6d inbound compressed packets\r\n", comp_s.sls_compressedin);
+ lprint(" %6d inbound unknown type packets\r\n", comp_s.sls_errorin);
+ lprint(" %6d inbound packets tossed due to error\r\n", comp_s.sls_tossed);
+}
+#endif
+
+static void
+tcpstats(void)
+{
+ lprint(" \r\n");
+
+ lprint("TCP stats:\r\n");
+
+ lprint(" %6d packets sent\r\n", tcpstat.tcps_sndtotal);
+ lprint(" %6d data packets (%d bytes)\r\n",
+ tcpstat.tcps_sndpack, tcpstat.tcps_sndbyte);
+ lprint(" %6d data packets retransmitted (%d bytes)\r\n",
+ tcpstat.tcps_sndrexmitpack, tcpstat.tcps_sndrexmitbyte);
+ lprint(" %6d ack-only packets (%d delayed)\r\n",
+ tcpstat.tcps_sndacks, tcpstat.tcps_delack);
+ lprint(" %6d URG only packets\r\n", tcpstat.tcps_sndurg);
+ lprint(" %6d window probe packets\r\n", tcpstat.tcps_sndprobe);
+ lprint(" %6d window update packets\r\n", tcpstat.tcps_sndwinup);
+ lprint(" %6d control (SYN/FIN/RST) packets\r\n", tcpstat.tcps_sndctrl);
+ lprint(" %6d times tcp_output did nothing\r\n", tcpstat.tcps_didnuttin);
+
+ lprint(" %6d packets received\r\n", tcpstat.tcps_rcvtotal);
+ lprint(" %6d acks (for %d bytes)\r\n",
+ tcpstat.tcps_rcvackpack, tcpstat.tcps_rcvackbyte);
+ lprint(" %6d duplicate acks\r\n", tcpstat.tcps_rcvdupack);
+ lprint(" %6d acks for unsent data\r\n", tcpstat.tcps_rcvacktoomuch);
+ lprint(" %6d packets received in sequence (%d bytes)\r\n",
+ tcpstat.tcps_rcvpack, tcpstat.tcps_rcvbyte);
+ lprint(" %6d completely duplicate packets (%d bytes)\r\n",
+ tcpstat.tcps_rcvduppack, tcpstat.tcps_rcvdupbyte);
+
+ lprint(" %6d packets with some duplicate data (%d bytes duped)\r\n",
+ tcpstat.tcps_rcvpartduppack, tcpstat.tcps_rcvpartdupbyte);
+ lprint(" %6d out-of-order packets (%d bytes)\r\n",
+ tcpstat.tcps_rcvoopack, tcpstat.tcps_rcvoobyte);
+ lprint(" %6d packets of data after window (%d bytes)\r\n",
+ tcpstat.tcps_rcvpackafterwin, tcpstat.tcps_rcvbyteafterwin);
+ lprint(" %6d window probes\r\n", tcpstat.tcps_rcvwinprobe);
+ lprint(" %6d window update packets\r\n", tcpstat.tcps_rcvwinupd);
+ lprint(" %6d packets received after close\r\n", tcpstat.tcps_rcvafterclose);
+ lprint(" %6d discarded for bad checksums\r\n", tcpstat.tcps_rcvbadsum);
+ lprint(" %6d discarded for bad header offset fields\r\n",
+ tcpstat.tcps_rcvbadoff);
+
+ lprint(" %6d connection requests\r\n", tcpstat.tcps_connattempt);
+ lprint(" %6d connection accepts\r\n", tcpstat.tcps_accepts);
+ lprint(" %6d connections established (including accepts)\r\n", tcpstat.tcps_connects);
+ lprint(" %6d connections closed (including %d drop)\r\n",
+ tcpstat.tcps_closed, tcpstat.tcps_drops);
+ lprint(" %6d embryonic connections dropped\r\n", tcpstat.tcps_conndrops);
+ lprint(" %6d segments we tried to get rtt (%d succeeded)\r\n",
+ tcpstat.tcps_segstimed, tcpstat.tcps_rttupdated);
+ lprint(" %6d retransmit timeouts\r\n", tcpstat.tcps_rexmttimeo);
+ lprint(" %6d connections dropped by rxmt timeout\r\n",
+ tcpstat.tcps_timeoutdrop);
+ lprint(" %6d persist timeouts\r\n", tcpstat.tcps_persisttimeo);
+ lprint(" %6d keepalive timeouts\r\n", tcpstat.tcps_keeptimeo);
+ lprint(" %6d keepalive probes sent\r\n", tcpstat.tcps_keepprobe);
+ lprint(" %6d connections dropped by keepalive\r\n", tcpstat.tcps_keepdrops);
+ lprint(" %6d correct ACK header predictions\r\n", tcpstat.tcps_predack);
+ lprint(" %6d correct data packet header predictions\n", tcpstat.tcps_preddat);
+ lprint(" %6d TCP cache misses\r\n", tcpstat.tcps_socachemiss);
+
+
+/* lprint(" Packets received too short: %d\r\n", tcpstat.tcps_rcvshort); */
+/* lprint(" Segments dropped due to PAWS: %d\r\n", tcpstat.tcps_pawsdrop); */
+
+}
+
+static void
+udpstats(void)
+{
+ lprint(" \r\n");
+
+ lprint("UDP stats:\r\n");
+ lprint(" %6d datagrams received\r\n", udpstat.udps_ipackets);
+ lprint(" %6d with packets shorter than header\r\n", udpstat.udps_hdrops);
+ lprint(" %6d with bad checksums\r\n", udpstat.udps_badsum);
+ lprint(" %6d with data length larger than packet\r\n", udpstat.udps_badlen);
+ lprint(" %6d UDP socket cache misses\r\n", udpstat.udpps_pcbcachemiss);
+ lprint(" %6d datagrams sent\r\n", udpstat.udps_opackets);
+}
+
+static void
+icmpstats(void)
+{
+ lprint(" \r\n");
+ lprint("ICMP stats:\r\n");
+ lprint(" %6d ICMP packets received\r\n", icmpstat.icps_received);
+ lprint(" %6d were too short\r\n", icmpstat.icps_tooshort);
+ lprint(" %6d with bad checksums\r\n", icmpstat.icps_checksum);
+ lprint(" %6d with type not supported\r\n", icmpstat.icps_notsupp);
+ lprint(" %6d with bad type feilds\r\n", icmpstat.icps_badtype);
+ lprint(" %6d ICMP packets sent in reply\r\n", icmpstat.icps_reflect);
+}
+
+static void
+mbufstats(void)
+{
+ struct mbuf *m;
+ int i;
+
+ lprint(" \r\n");
+
+ lprint("Mbuf stats:\r\n");
+
+ lprint(" %6d mbufs allocated (%d max)\r\n", mbuf_alloced, mbuf_max);
+
+ i = 0;
+ for (m = m_freelist.m_next; m != &m_freelist; m = m->m_next)
+ i++;
+ lprint(" %6d mbufs on free list\r\n", i);
+
+ i = 0;
+ for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next)
+ i++;
+ lprint(" %6d mbufs on used list\r\n", i);
+ lprint(" %6d mbufs queued as packets\r\n\r\n", if_queued);
+}
+
+static void
+sockstats(void)
+{
+ char buff[256];
+ int n;
+ struct socket *so;
+
+ lprint(" \r\n");
+
+ lprint(
+ "Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\r\n");
+
+ for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+
+ n = sprintf(buff, "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE");
+ while (n < 17)
+ buff[n++] = ' ';
+ buff[17] = 0;
+ lprint("%s %3d %15s %5d ",
+ buff, so->s,
+ inet_ntoa(so->so_laddr), ntohs(so->so_lport));
+ lprint("%15s %5d %5d %5d\r\n",
+ inet_ntoa(so->so_faddr), ntohs(so->so_fport),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+
+ for (so = udb.so_next; so != &udb; so = so->so_next) {
+
+ n = sprintf(buff, "udp[%d sec]", (so->so_expire - curtime) / 1000);
+ while (n < 17)
+ buff[n++] = ' ';
+ buff[17] = 0;
+ lprint("%s %3d %15s %5d ",
+ buff, so->s,
+ inet_ntoa(so->so_laddr), ntohs(so->so_lport));
+ lprint("%15s %5d %5d %5d\r\n",
+ inet_ntoa(so->so_faddr), ntohs(so->so_fport),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+}
+#endif
+
+#ifndef CONFIG_QEMU
+void
+slirp_exit(exit_status)
+ int exit_status;
+{
+ struct ttys *ttyp;
+
+ DEBUG_CALL("slirp_exit");
+ DEBUG_ARG("exit_status = %d", exit_status);
+
+ if (dostats) {
+ lprint_print = (int (*) _P((void *, const char *, va_list)))vfprintf;
+ if (!dfd)
+ debug_init("slirp_stats", 0xf);
+ lprint_arg = (char **)&dfd;
+
+ ipstats();
+ tcpstats();
+ udpstats();
+ icmpstats();
+ mbufstats();
+ sockstats();
+ allttystats();
+ vjstats();
+ }
+
+ for (ttyp = ttys; ttyp; ttyp = ttyp->next)
+ tty_detached(ttyp, 1);
+
+ if (slirp_forked) {
+ /* Menendez time */
+ if (kill(getppid(), SIGQUIT) < 0)
+ lprint("Couldn't kill parent process %ld!\n",
+ (long) getppid());
+ }
+
+ /* Restore the terminal if we gotta */
+ if(slirp_tty_restore)
+ tcsetattr(0,TCSANOW, &slirp_tty_settings); /* NOW DAMMIT! */
+ exit(exit_status);
+}
+#endif
+
+void
+slirp_stats(void)
+{
+#ifdef LOG_ENABLED
+ ipstats();
+ tcpstats();
+ udpstats();
+ icmpstats();
+ mbufstats();
+ sockstats();
+#else
+ lprint("SLIRP statistics code not compiled.\n");
+#endif
+}
diff --git a/slirp/debug.h b/slirp/debug.h
new file mode 100644
index 0000000..c43eff7
--- /dev/null
+++ b/slirp/debug.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define PRN_STDERR 1
+#define PRN_SPRINTF 2
+
+extern FILE *dfd;
+extern FILE *lfd;
+extern int dostats;
+extern int slirp_debug;
+
+#define DBG_CALL 0x1
+#define DBG_MISC 0x2
+#define DBG_ERROR 0x4
+#define DEBUG_DEFAULT DBG_CALL|DBG_MISC|DBG_ERROR
+
+#ifdef DEBUG
+#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); }
+#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); }
+#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); }
+#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); }
+#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); }
+
+
+#else
+
+#define DEBUG_CALL(x)
+#define DEBUG_ARG(x, y)
+#define DEBUG_ARGS(x)
+#define DEBUG_MISC(x)
+#define DEBUG_ERROR(x)
+
+#endif
+
+void debug_init _P((char *, int));
diff --git a/slirp/icmp_var.h b/slirp/icmp_var.h
new file mode 100644
index 0000000..99d4c9e
--- /dev/null
+++ b/slirp/icmp_var.h
@@ -0,0 +1,67 @@
+/*
+ * 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. 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.
+ *
+ * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93
+ * icmp_var.h,v 1.4 1995/02/16 00:27:40 wollman Exp
+ */
+
+#ifndef _NETINET_ICMP_VAR_H_
+#define _NETINET_ICMP_VAR_H_
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol.
+ */
+struct icmpstat {
+/* statistics related to input messages processed */
+ u_long icps_received; /* #ICMP packets received */
+ u_long icps_tooshort; /* packet < ICMP_MINLEN */
+ u_long icps_checksum; /* bad checksum */
+ u_long icps_notsupp; /* #ICMP packets not supported */
+ u_long icps_badtype; /* #with bad type feild */
+ u_long icps_reflect; /* number of responses */
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */
+#define ICMPCTL_STATS 2 /* statistics (read-only) */
+#define ICMPCTL_MAXID 3
+
+#define ICMPCTL_NAMES { \
+ { 0, 0 }, \
+ { "maskrepl", CTLTYPE_INT }, \
+ { "stats", CTLTYPE_STRUCT }, \
+}
+
+#ifdef LOG_ENABLED
+extern struct icmpstat icmpstat;
+#endif
+
+#endif
diff --git a/slirp/if.c b/slirp/if.c
new file mode 100644
index 0000000..17f8a73
--- /dev/null
+++ b/slirp/if.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+int if_queued = 0; /* Number of packets queued so far */
+
+struct mbuf if_fastq; /* fast queue (for interactive data) */
+struct mbuf if_batchq; /* queue for non-interactive data */
+struct mbuf *next_m; /* Pointer to next mbuf to output */
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+static void
+ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
+{
+ ifm->ifs_next = ifmhead->ifs_next;
+ ifmhead->ifs_next = ifm;
+ ifm->ifs_prev = ifmhead;
+ ifm->ifs_next->ifs_prev = ifm;
+}
+
+static void
+ifs_remque(struct mbuf *ifm)
+{
+ ifm->ifs_prev->ifs_next = ifm->ifs_next;
+ ifm->ifs_next->ifs_prev = ifm->ifs_prev;
+}
+
+void
+if_init(void)
+{
+ if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq;
+ if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq;
+ // sl_compress_init(&comp_s);
+ next_m = &if_batchq;
+}
+
+#if 0
+/*
+ * This shouldn't be needed since the modem is blocking and
+ * we don't expect any signals, but what the hell..
+ */
+inline int
+writen(fd, bptr, n)
+ int fd;
+ char *bptr;
+ int n;
+{
+ int ret;
+ int total;
+
+ /* This should succeed most of the time */
+ ret = send(fd, bptr, n,0);
+ if (ret == n || ret <= 0)
+ return ret;
+
+ /* Didn't write everything, go into the loop */
+ total = ret;
+ while (n > total) {
+ ret = send(fd, bptr+total, n-total,0);
+ if (ret <= 0)
+ return ret;
+ total += ret;
+ }
+ return total;
+}
+
+/*
+ * if_input - read() the tty, do "top level" processing (ie: check for any escapes),
+ * and pass onto (*ttyp->if_input)
+ *
+ * XXXXX Any zeros arriving by themselves are NOT placed into the arriving packet.
+ */
+#define INBUFF_SIZE 2048 /* XXX */
+void
+if_input(ttyp)
+ struct ttys *ttyp;
+{
+ u_char if_inbuff[INBUFF_SIZE];
+ int if_n;
+
+ DEBUG_CALL("if_input");
+ DEBUG_ARG("ttyp = %lx", (long)ttyp);
+
+ if_n = recv(ttyp->fd, (char *)if_inbuff, INBUFF_SIZE,0);
+
+ DEBUG_MISC((dfd, " read %d bytes\n", if_n));
+
+ if (if_n <= 0) {
+ if (if_n == 0 || (errno != EINTR && errno != EAGAIN)) {
+ if (ttyp->up)
+ link_up--;
+ tty_detached(ttyp, 0);
+ }
+ return;
+ }
+ if (if_n == 1) {
+ if (*if_inbuff == '0') {
+ ttyp->ones = 0;
+ if (++ttyp->zeros >= 5)
+ slirp_exit(0);
+ return;
+ }
+ if (*if_inbuff == '1') {
+ ttyp->zeros = 0;
+ if (++ttyp->ones >= 5)
+ tty_detached(ttyp, 0);
+ return;
+ }
+ }
+ ttyp->ones = ttyp->zeros = 0;
+
+ (*ttyp->if_input)(ttyp, if_inbuff, if_n);
+}
+#endif
+
+/*
+ * if_output: Queue packet into an output queue.
+ * There are 2 output queue's, if_fastq and if_batchq.
+ * Each output queue is a doubly linked list of double linked lists
+ * of mbufs, each list belonging to one "session" (socket). This
+ * way, we can output packets fairly by sending one packet from each
+ * session, instead of all the packets from one session, then all packets
+ * from the next session, etc. Packets on the if_fastq get absolute
+ * priority, but if one session hogs the link, it gets "downgraded"
+ * to the batchq until it runs out of packets, then it'll return
+ * to the fastq (eg. if the user does an ls -alR in a telnet session,
+ * it'll temporarily get downgraded to the batchq)
+ */
+void
+if_output(struct socket *so, struct mbuf *ifm)
+{
+ struct mbuf *ifq;
+ int on_fastq = 1;
+
+ DEBUG_CALL("if_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ifm = %lx", (long)ifm);
+
+ /*
+ * First remove the mbuf from m_usedlist,
+ * since we're gonna use m_next and m_prev ourselves
+ * XXX Shouldn't need this, gotta change dtom() etc.
+ */
+ if (ifm->m_flags & M_USEDLIST) {
+ remque(ifm);
+ ifm->m_flags &= ~M_USEDLIST;
+ }
+
+ /*
+ * See if there's already a batchq list for this session.
+ * This can include an interactive session, which should go on fastq,
+ * but gets too greedy... hence it'll be downgraded from fastq to batchq.
+ * We mustn't put this packet back on the fastq (or we'll send it out of order)
+ * XXX add cache here?
+ */
+ for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) {
+ if (so == ifq->ifq_so) {
+ /* A match! */
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ }
+
+ /* No match, check which queue to put it on */
+ if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
+ ifq = if_fastq.ifq_prev;
+ on_fastq = 1;
+ /*
+ * Check if this packet is a part of the last
+ * packet's session
+ */
+ if (ifq->ifq_so == so) {
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ } else
+ ifq = if_batchq.ifq_prev;
+
+ /* Create a new doubly linked list for this session */
+ ifm->ifq_so = so;
+ ifs_init(ifm);
+ insque(ifm, ifq);
+
+diddit:
+ ++if_queued;
+
+ if (so) {
+ /* Update *_queued */
+ so->so_queued++;
+ so->so_nqueued++;
+ /*
+ * Check if the interactive session should be downgraded to
+ * the batchq. A session is downgraded if it has queued 6
+ * packets without pausing, and at least 3 of those packets
+ * have been sent over the link
+ * (XXX These are arbitrary numbers, probably not optimal..)
+ */
+ if (on_fastq && ((so->so_nqueued >= 6) &&
+ (so->so_nqueued - so->so_queued) >= 3)) {
+
+ /* Remove from current queue... */
+ remque(ifm->ifs_next);
+
+ /* ...And insert in the new. That'll teach ya! */
+ insque(ifm->ifs_next, &if_batchq);
+ }
+ }
+
+#ifndef FULL_BOLT
+ /*
+ * This prevents us from malloc()ing too many mbufs
+ */
+ if (link_up) {
+ /* if_start will check towrite */
+ if_start();
+ }
+#endif
+}
+
+/*
+ * Send a packet
+ * We choose a packet based on it's position in the output queues;
+ * If there are packets on the fastq, they are sent FIFO, before
+ * everything else. Otherwise we choose the first packet from the
+ * batchq and send it. the next packet chosen will be from the session
+ * after this one, then the session after that one, and so on.. So,
+ * for example, if there are 3 ftp session's fighting for bandwidth,
+ * one packet will be sent from the first session, then one packet
+ * from the second session, then one packet from the third, then back
+ * to the first, etc. etc.
+ */
+void
+if_start(void)
+{
+ struct mbuf *ifm, *ifqt;
+
+ DEBUG_CALL("if_start");
+
+ if (if_queued == 0)
+ return; /* Nothing to do */
+
+ again:
+ /* check if we can really output */
+ if (!slirp_can_output())
+ return;
+
+ /*
+ * See which queue to get next packet from
+ * If there's something in the fastq, select it immediately
+ */
+ if (if_fastq.ifq_next != &if_fastq) {
+ ifm = if_fastq.ifq_next;
+ } else {
+ /* Nothing on fastq, see if next_m is valid */
+ if (next_m != &if_batchq)
+ ifm = next_m;
+ else
+ ifm = if_batchq.ifq_next;
+
+ /* Set which packet to send on next iteration */
+ next_m = ifm->ifq_next;
+ }
+ /* Remove it from the queue */
+ ifqt = ifm->ifq_prev;
+ remque(ifm);
+ --if_queued;
+
+ /* If there are more packets for this session, re-queue them */
+ if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
+ insque(ifm->ifs_next, ifqt);
+ ifs_remque(ifm);
+ }
+
+ /* Update so_queued */
+ if (ifm->ifq_so) {
+ if (--ifm->ifq_so->so_queued == 0)
+ /* If there's no more queued, reset nqueued */
+ ifm->ifq_so->so_nqueued = 0;
+ }
+
+ /* Encapsulate the packet for sending */
+ if_encap((uint8_t *)ifm->m_data, ifm->m_len);
+
+ m_free(ifm);
+
+ if (if_queued)
+ goto again;
+}
diff --git a/slirp/if.h b/slirp/if.h
new file mode 100644
index 0000000..bed7152
--- /dev/null
+++ b/slirp/if.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IF_H_
+#define _IF_H_
+
+#define IF_COMPRESS 0x01 /* We want compression */
+#define IF_NOCOMPRESS 0x02 /* Do not do compression */
+#define IF_AUTOCOMP 0x04 /* Autodetect (default) */
+#define IF_NOCIDCOMP 0x08 /* CID compression */
+
+#define IF_MTU 1500
+#define IF_MRU 1500
+#define IF_COMP IF_AUTOCOMP /* Flags for compression */
+
+#if 0
+/*
+ * Set if_maxlinkhdr to 48 because it's 40 bytes for TCP/IP,
+ * and 8 bytes for PPP, but need to have it on an 8byte boundary
+ */
+#ifdef USE_PPP
+#define IF_MAXLINKHDR 48
+#else
+#define IF_MAXLINKHDR 40
+#endif
+#else
+ /* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
+#define IF_MAXLINKHDR (2 + 14 + 40)
+#endif
+
+extern int if_queued; /* Number of packets queued so far */
+
+extern struct mbuf if_fastq; /* fast queue (for interactive data) */
+extern struct mbuf if_batchq; /* queue for non-interactive data */
+extern struct mbuf *next_m;
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+#ifdef LOG_ENABLED
+/* Interface statistics */
+struct slirp_ifstats {
+ u_int out_pkts; /* Output packets */
+ u_int out_bytes; /* Output bytes */
+ u_int out_errpkts; /* Output Error Packets */
+ u_int out_errbytes; /* Output Error Bytes */
+ u_int in_pkts; /* Input packets */
+ u_int in_bytes; /* Input bytes */
+ u_int in_errpkts; /* Input Error Packets */
+ u_int in_errbytes; /* Input Error Bytes */
+
+ u_int bytes_saved; /* Number of bytes that compression "saved" */
+ /* ie: number of bytes that didn't need to be sent over the link
+ * because of compression */
+
+ u_int in_mbad; /* Bad incoming packets */
+};
+#endif
+
+#endif
diff --git a/slirp/ip.h b/slirp/ip.h
new file mode 100644
index 0000000..7a7a9b9
--- /dev/null
+++ b/slirp/ip.h
@@ -0,0 +1,293 @@
+/*
+ * 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. 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.1 (Berkeley) 6/10/93
+ * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
+ */
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#ifdef WORDS_BIGENDIAN
+# ifndef NTOHL
+# define NTOHL(d)
+# endif
+# ifndef NTOHS
+# define NTOHS(d)
+# endif
+# ifndef HTONL
+# define HTONL(d)
+# endif
+# ifndef HTONS
+# define HTONS(d)
+# endif
+#else
+# ifndef NTOHL
+# define NTOHL(d) ((d) = ntohl((d)))
+# endif
+# ifndef NTOHS
+# define NTOHS(d) ((d) = ntohs((u_int16_t)(d)))
+# endif
+# ifndef HTONL
+# define HTONL(d) ((d) = htonl((d)))
+# endif
+# ifndef HTONS
+# define HTONS(d) ((d) = htons((u_int16_t)(d)))
+# endif
+#endif
+
+typedef u_int32_t n_long; /* long as received from the net */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip {
+#ifdef WORDS_BIGENDIAN
+ u_int ip_v:4, /* version */
+ ip_hl:4; /* header length */
+#else
+ u_int ip_hl:4, /* header length */
+ ip_v:4; /* version */
+#endif
+ u_int8_t ip_tos; /* type of service */
+ u_int16_t ip_len; /* total length */
+ u_int16_t ip_id; /* identification */
+ u_int16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* don't fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ u_int8_t ip_ttl; /* time to live */
+ u_int8_t ip_p; /* protocol */
+ u_int16_t 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 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_int8_t ipt_code; /* IPOPT_TS */
+ u_int8_t ipt_len; /* size of structure (variable) */
+ u_int8_t ipt_ptr; /* index of current entry */
+#ifdef WORDS_BIGENDIAN
+ u_int ipt_oflw:4, /* overflow counter */
+ ipt_flg:4; /* flags, see below */
+#else
+ u_int ipt_flg:4, /* flags, see below */
+ ipt_oflw:4; /* overflow counter */
+#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 */
+
+#if SIZEOF_CHAR_P == 4
+struct mbuf_ptr {
+ struct mbuf *mptr;
+ uint32_t dummy;
+};
+#else
+struct mbuf_ptr {
+ struct mbuf *mptr;
+};
+#endif
+struct qlink {
+ void *next, *prev;
+};
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
+ u_int8_t ih_x1; /* (unused) */
+ u_int8_t ih_pr; /* protocol */
+ u_int16_t ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+} __attribute__((packed));
+
+/*
+ * 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.
+ * size 28 bytes
+ */
+struct ipq {
+ struct qlink frag_link; /* to ip headers of fragments */
+ struct qlink ip_link; /* to other reass headers */
+ u_int8_t ipq_ttl; /* time for reass q to live */
+ u_int8_t ipq_p; /* protocol of this fragment */
+ u_int16_t ipq_id; /* sequence id for reassembly */
+ struct in_addr ipq_src,ipq_dst;
+};
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_link must be at same offset as frag_link above
+ */
+struct ipasfrag {
+ struct qlink ipf_link;
+ struct ip ipf_ip;
+};
+
+#define ipf_off ipf_ip.ip_off
+#define ipf_tos ipf_ip.ip_tos
+#define ipf_len ipf_ip.ip_len
+#define ipf_next ipf_link.next
+#define ipf_prev ipf_link.prev
+
+/*
+ * 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 */
+ int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */
+};
+
+#ifdef LOG_ENABLED
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+
+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 successfully 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 */
+ u_long ips_unaligned; /* times the ip packet was not aligned */
+};
+
+extern struct ipstat ipstat;
+#endif
+
+extern struct ipq ipq; /* ip reass. queue */
+extern u_int16_t ip_id; /* ip packet ctr, for ids */
+
+#endif
diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c
new file mode 100644
index 0000000..61dcaf8
--- /dev/null
+++ b/slirp/ip_icmp.c
@@ -0,0 +1,366 @@
+/*
+ * 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. 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_icmp.c 8.2 (Berkeley) 1/4/94
+ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+#ifdef LOG_ENABLED
+struct icmpstat icmpstat;
+#endif
+
+/* The message sent when emulating PING */
+/* Be nice and tell them it's just a pseudo-ping packet */
+static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
+
+/* list of actions for icmp_error() on RX of an icmp message */
+static const int icmp_flush[19] = {
+/* ECHO REPLY (0) */ 0,
+ 1,
+ 1,
+/* DEST UNREACH (3) */ 1,
+/* SOURCE QUENCH (4)*/ 1,
+/* REDIRECT (5) */ 1,
+ 1,
+ 1,
+/* ECHO (8) */ 0,
+/* ROUTERADVERT (9) */ 1,
+/* ROUTERSOLICIT (10) */ 1,
+/* TIME EXCEEDED (11) */ 1,
+/* PARAMETER PROBLEM (12) */ 1,
+/* TIMESTAMP (13) */ 0,
+/* TIMESTAMP REPLY (14) */ 0,
+/* INFO (15) */ 0,
+/* INFO REPLY (16) */ 0,
+/* ADDR MASK (17) */ 0,
+/* ADDR MASK REPLY (18) */ 0
+};
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(struct mbuf *m, int hlen)
+{
+ register struct icmp *icp;
+ register struct ip *ip=mtod(m, struct ip *);
+ int icmplen=ip->ip_len;
+ /* int code; */
+
+ DEBUG_CALL("icmp_input");
+ DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ STAT(icmpstat.icps_received++);
+
+ /*
+ * Locate icmp structure in mbuf, and check
+ * that its not corrupted and of at least minimum length.
+ */
+ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
+ STAT(icmpstat.icps_tooshort++);
+ freeit:
+ m_freem(m);
+ goto end_error;
+ }
+
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ icp = mtod(m, struct icmp *);
+ if (cksum(m, icmplen)) {
+ STAT(icmpstat.icps_checksum++);
+ goto freeit;
+ }
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ /* icmpstat.icps_inhist[icp->icmp_type]++; */
+ /* code = icp->icmp_code; */
+
+ DEBUG_ARG("icmp_type = %d", icp->icmp_type);
+ switch (icp->icmp_type) {
+ case ICMP_ECHO:
+ icp->icmp_type = ICMP_ECHOREPLY;
+ ip->ip_len += hlen; /* since ip_input subtracts this */
+ if (ip->ip_dst.s_addr == alias_addr.s_addr) {
+ icmp_reflect(m);
+ } else {
+ struct socket *so;
+ struct sockaddr_in addr;
+ if ((so = socreate()) == NULL) goto freeit;
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ m_free(m);
+ goto end_error;
+ }
+ so->so_m = m;
+ so->so_faddr = ip->ip_dst;
+ so->so_fport = htons(7);
+ so->so_laddr = ip->ip_src;
+ so->so_lport = htons(9);
+ so->so_iptos = ip->ip_tos;
+ so->so_type = IPPROTO_ICMP;
+ so->so_state = SS_ISFCONNECTED;
+
+ /* Send the packet */
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ /* It's an alias */
+ switch(ntohl(so->so_faddr.s_addr) & 0xff) {
+ case CTL_DNS:
+ addr.sin_addr = dns_addr;
+ break;
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ } else {
+ addr.sin_addr = so->so_faddr;
+ }
+ addr.sin_port = so->so_fport;
+ if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
+ (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ udp_detach(so);
+ }
+ } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
+ break;
+ case ICMP_UNREACH:
+ /* XXX? report error? close socket? */
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TSTAMP:
+ case ICMP_MASKREQ:
+ case ICMP_REDIRECT:
+ STAT(icmpstat.icps_notsupp++);
+ m_freem(m);
+ break;
+
+ default:
+ STAT(icmpstat.icps_badtype++);
+ m_freem(m);
+ } /* swith */
+
+end_error:
+ /* m is m_free()'d xor put in a socket xor or given to ip_send */
+ return;
+}
+
+
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ * MUST NOT change this header information.
+ * MUST NOT reply to a multicast/broadcast IP address.
+ * MUST NOT reply to a multicast/broadcast MAC address.
+ * MUST reply to only the first fragment.
+ */
+/*
+ * Send ICMP_UNREACH back to the source regarding msrc.
+ * mbuf *msrc is used as a template, but is NOT m_free()'d.
+ * It is reported as the bad ip packet. The header should
+ * be fully correct and in host byte order.
+ * ICMP fragmentation is illegal. All machines must accept 576 bytes in one
+ * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
+ */
+
+#define ICMP_MAXDATALEN (IP_MSS-28)
+void
+icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message)
+{
+ unsigned hlen, shlen, s_ip_len;
+ register struct ip *ip;
+ register struct icmp *icp;
+ register struct mbuf *m;
+
+ DEBUG_CALL("icmp_error");
+ DEBUG_ARG("msrc = %lx", (long )msrc);
+ DEBUG_ARG("msrc_len = %d", msrc->m_len);
+
+ if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
+
+ /* check msrc */
+ if(!msrc) goto end_error;
+ ip = mtod(msrc, struct ip *);
+#ifdef DEBUG
+ { char bufa[20], bufb[20];
+ strcpy(bufa, inet_ntoa(ip->ip_src));
+ strcpy(bufb, inet_ntoa(ip->ip_dst));
+ DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb));
+ }
+#endif
+ if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */
+
+ shlen=ip->ip_hl << 2;
+ s_ip_len=ip->ip_len;
+ if(ip->ip_p == IPPROTO_ICMP) {
+ icp = (struct icmp *)((char *)ip + shlen);
+ /*
+ * Assume any unknown ICMP type is an error. This isn't
+ * specified by the RFC, but think about it..
+ */
+ if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error;
+ }
+
+ /* make a copy */
+ if(!(m=m_get())) goto end_error; /* get mbuf */
+ { int new_m_size;
+ new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN;
+ if(new_m_size>m->m_size) m_inc(m, new_m_size);
+ }
+ memcpy(m->m_data, msrc->m_data, msrc->m_len);
+ m->m_len = msrc->m_len; /* copy msrc to m */
+
+ /* make the header of the reply packet */
+ ip = mtod(m, struct ip *);
+ hlen= sizeof(struct ip ); /* no options in reply */
+
+ /* fill in icmp */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ icp = mtod(m, struct icmp *);
+
+ if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */
+ else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */
+ s_ip_len=ICMP_MAXDATALEN;
+
+ m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */
+
+ /* min. size = 8+sizeof(struct ip)+8 */
+
+ icp->icmp_type = type;
+ icp->icmp_code = code;
+ icp->icmp_id = 0;
+ icp->icmp_seq = 0;
+
+ memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */
+ HTONS(icp->icmp_ip.ip_len);
+ HTONS(icp->icmp_ip.ip_id);
+ HTONS(icp->icmp_ip.ip_off);
+
+#ifdef DEBUG
+ if(message) { /* DEBUG : append message to ICMP packet */
+ int message_len;
+ char *cpnt;
+ message_len=strlen(message);
+ if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN;
+ cpnt=(char *)m->m_data+m->m_len;
+ memcpy(cpnt, message, message_len);
+ m->m_len+=message_len;
+ }
+#endif
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, m->m_len);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len = m->m_len;
+
+ ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
+
+ ip->ip_ttl = MAXTTL;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_dst = ip->ip_src; /* ip adresses */
+ ip->ip_src = alias_addr;
+
+ (void ) ip_output((struct socket *)NULL, m);
+
+ STAT(icmpstat.icps_reflect++);
+
+end_error:
+ return;
+}
+#undef ICMP_MAXDATALEN
+
+/*
+ * Reflect the ip packet back to the source
+ */
+void
+icmp_reflect(struct mbuf *m)
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ int optlen = hlen - sizeof(struct ip );
+ register struct icmp *icp;
+
+ /*
+ * Send an icmp packet back to the ip level,
+ * after supplying a checksum.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ if (optlen > 0) {
+ /*
+ * Strip out original options by copying rest of first
+ * mbuf's data back, and adjust the IP length.
+ */
+ memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen,
+ (unsigned )(m->m_len - hlen));
+ hlen -= optlen;
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len -= optlen;
+ m->m_len -= optlen;
+ }
+
+ ip->ip_ttl = MAXTTL;
+ { /* swap */
+ struct in_addr icmp_dst;
+ icmp_dst = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = icmp_dst;
+ }
+
+ (void ) ip_output((struct socket *)NULL, m);
+
+ STAT(icmpstat.icps_reflect++);
+}
diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h
new file mode 100644
index 0000000..0330131
--- /dev/null
+++ b/slirp/ip_icmp.h
@@ -0,0 +1,161 @@
+/*
+ * 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. 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_icmp.h 8.1 (Berkeley) 6/10/93
+ * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
+ */
+
+#ifndef _NETINET_IP_ICMP_H_
+#define _NETINET_IP_ICMP_H_
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+typedef u_int32_t n_time;
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ u_char icmp_type; /* type of message, see below */
+ u_char icmp_code; /* type sub code */
+ u_short icmp_cksum; /* ones complement cksum of struct */
+ union {
+ u_char ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ u_short icd_id;
+ u_short icd_seq;
+ } ih_idseq;
+ int ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu {
+ u_short ipm_void;
+ u_short ipm_nextmtu;
+ } ih_pmtu;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+ union {
+ struct id_ts {
+ n_time its_otime;
+ n_time its_rtime;
+ n_time its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ uint32_t id_mask;
+ char id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+void icmp_input _P((struct mbuf *, int));
+void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message);
+void icmp_reflect _P((struct mbuf *));
+
+#endif
diff --git a/slirp/ip_input.c b/slirp/ip_input.c
new file mode 100644
index 0000000..c37412e
--- /dev/null
+++ b/slirp/ip_input.c
@@ -0,0 +1,718 @@
+/*
+ * 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. 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_input.c 8.2 (Berkeley) 1/4/94
+ * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <osdep.h>
+#include "ip_icmp.h"
+
+#ifdef LOG_ENABLED
+struct ipstat ipstat;
+#endif
+
+struct ipq ipq;
+
+static struct ip *ip_reass(register struct ip *ip,
+ register struct ipq *fp);
+static void ip_freef(struct ipq *fp);
+static void ip_enq(register struct ipasfrag *p,
+ register struct ipasfrag *prev);
+static void ip_deq(register struct ipasfrag *p);
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init(void)
+{
+ ipq.ip_link.next = ipq.ip_link.prev = &ipq.ip_link;
+ ip_id = tt.tv_sec & 0xffff;
+ udp_init();
+ tcp_init();
+}
+
+/*
+ * Ip input routine. Checksum and byte swap header. If fragmented
+ * try to reassemble. Process options. Pass to next level.
+ */
+void
+ip_input(struct mbuf *m)
+{
+ register struct ip *ip;
+ int hlen;
+
+ DEBUG_CALL("ip_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ STAT(ipstat.ips_total++);
+
+ if (m->m_len < sizeof (struct ip)) {
+ STAT(ipstat.ips_toosmall++);
+ return;
+ }
+
+ ip = mtod(m, struct ip *);
+
+ if (ip->ip_v != IPVERSION) {
+ STAT(ipstat.ips_badvers++);
+ goto bad;
+ }
+
+ hlen = ip->ip_hl << 2;
+ if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
+ STAT(ipstat.ips_badhlen++); /* or packet too short */
+ goto bad;
+ }
+
+ /* keep ip header intact for ICMP reply
+ * ip->ip_sum = cksum(m, hlen);
+ * if (ip->ip_sum) {
+ */
+ if(cksum(m,hlen)) {
+ STAT(ipstat.ips_badsum++);
+ goto bad;
+ }
+
+ /*
+ * Convert fields to host representation.
+ */
+ NTOHS(ip->ip_len);
+ if (ip->ip_len < hlen) {
+ STAT(ipstat.ips_badlen++);
+ goto bad;
+ }
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_len < ip->ip_len) {
+ STAT(ipstat.ips_tooshort++);
+ goto bad;
+ }
+
+ if (slirp_restrict) {
+ if (memcmp(&ip->ip_dst.s_addr, &special_addr, 3)) {
+ if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP)
+ goto bad;
+ } else {
+ int host = ntohl(ip->ip_dst.s_addr) & 0xff;
+ struct ex_list *ex_ptr;
+
+ if (host == 0xff)
+ goto bad;
+
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_addr == host)
+ break;
+
+ if (!ex_ptr)
+ goto bad;
+ }
+ }
+
+ /* Should drop packet if mbuf too long? hmmm... */
+ if (m->m_len > ip->ip_len)
+ m_adj(m, ip->ip_len - m->m_len);
+
+ /* check ip_ttl for a correct ICMP reply */
+ if(ip->ip_ttl==0 || ip->ip_ttl==1) {
+ icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
+ goto bad;
+ }
+
+ /*
+ * Process options and, if not destined for us,
+ * ship it on. ip_dooptions returns 1 when an
+ * error was detected (causing an icmp message
+ * to be sent and the original packet to be freed).
+ */
+/* We do no IP options */
+/* if (hlen > sizeof (struct ip) && ip_dooptions(m))
+ * goto next;
+ */
+ /*
+ * If offset or IP_MF are set, must reassemble.
+ * Otherwise, nothing need be done.
+ * (We could look in the reassembly queue to see
+ * if the packet was previously fragmented,
+ * but it's not worth the time; just let them time out.)
+ *
+ * XXX This should fail, don't fragment yet
+ */
+ if (ip->ip_off &~ IP_DF) {
+ register struct ipq *fp;
+ struct qlink *l;
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ for (l = ipq.ip_link.next; l != &ipq.ip_link; l = l->next) {
+ fp = container_of(l, struct ipq, ip_link);
+ if (ip->ip_id == fp->ipq_id &&
+ ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+ ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+ ip->ip_p == fp->ipq_p)
+ goto found;
+ }
+ fp = NULL;
+ found:
+
+ /*
+ * Adjust ip_len to not reflect header,
+ * set ip_mff if more fragments are expected,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ if (ip->ip_off & IP_MF)
+ ip->ip_tos |= 1;
+ else
+ ip->ip_tos &= ~1;
+
+ ip->ip_off <<= 3;
+
+ /*
+ * If datagram marked as having more fragments
+ * or if this is not the first fragment,
+ * attempt reassembly; if it succeeds, proceed.
+ */
+ if (ip->ip_tos & 1 || ip->ip_off) {
+ STAT(ipstat.ips_fragments++);
+ ip = ip_reass(ip, fp);
+ if (ip == NULL)
+ return;
+ STAT(ipstat.ips_reassembled++);
+ m = dtom(ip);
+ } else
+ if (fp)
+ ip_freef(fp);
+
+ } else
+ ip->ip_len -= hlen;
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ STAT(ipstat.ips_delivered++);
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcp_input(m, hlen, (struct socket *)NULL);
+ break;
+ case IPPROTO_UDP:
+ udp_input(m, hlen);
+ break;
+ case IPPROTO_ICMP:
+ icmp_input(m, hlen);
+ break;
+ default:
+ STAT(ipstat.ips_noproto++);
+ m_free(m);
+ }
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+#define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink)))
+#define fragtoip(P) ((struct ip*)(((char*)(P)) + sizeof(struct qlink)))
+/*
+ * Take incoming datagram fragment and try to
+ * reassemble it into whole datagram. If a chain for
+ * reassembly of this datagram already exists, then it
+ * is given as fp; otherwise have to make a chain.
+ */
+static struct ip *
+ip_reass(register struct ip *ip, register struct ipq *fp)
+{
+ register struct mbuf *m = dtom(ip);
+ register struct ipasfrag *q;
+ int hlen = ip->ip_hl << 2;
+ int i, next;
+
+ DEBUG_CALL("ip_reass");
+ DEBUG_ARG("ip = %lx", (long)ip);
+ DEBUG_ARG("fp = %lx", (long)fp);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ * Fragment m_data is concatenated.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ /*
+ * If first fragment to arrive, create a reassembly queue.
+ */
+ if (fp == NULL) {
+ struct mbuf *t;
+ if ((t = m_get()) == NULL) goto dropfrag;
+ fp = mtod(t, struct ipq *);
+ insque(&fp->ip_link, &ipq.ip_link);
+ fp->ipq_ttl = IPFRAGTTL;
+ fp->ipq_p = ip->ip_p;
+ fp->ipq_id = ip->ip_id;
+ fp->frag_link.next = fp->frag_link.prev = &fp->frag_link;
+ fp->ipq_src = ip->ip_src;
+ fp->ipq_dst = ip->ip_dst;
+ q = (struct ipasfrag *)fp;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link;
+ q = q->ipf_next)
+ if (q->ipf_off > ip->ip_off)
+ 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 (q->ipf_prev != &fp->frag_link) {
+ struct ipasfrag *pq = q->ipf_prev;
+ i = pq->ipf_off + pq->ipf_len - ip->ip_off;
+ if (i > 0) {
+ if (i >= ip->ip_len)
+ goto dropfrag;
+ m_adj(dtom(ip), i);
+ ip->ip_off += i;
+ ip->ip_len -= i;
+ }
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct ipasfrag*)&fp->frag_link &&
+ ip->ip_off + ip->ip_len > q->ipf_off) {
+ i = (ip->ip_off + ip->ip_len) - q->ipf_off;
+ if (i < q->ipf_len) {
+ q->ipf_len -= i;
+ q->ipf_off += i;
+ m_adj(dtom(q), i);
+ break;
+ }
+ q = q->ipf_next;
+ m_freem(dtom(q->ipf_prev));
+ ip_deq(q->ipf_prev);
+ }
+
+insert:
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ */
+ ip_enq(iptofrag(ip), q->ipf_prev);
+ next = 0;
+ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link;
+ q = q->ipf_next) {
+ if (q->ipf_off != next)
+ return NULL;
+ next += q->ipf_len;
+ }
+ if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1)
+ return NULL;
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+ q = fp->frag_link.next;
+ m = dtom(q);
+
+ q = (struct ipasfrag *) q->ipf_next;
+ while (q != (struct ipasfrag*)&fp->frag_link) {
+ struct mbuf *t = dtom(q);
+ q = (struct ipasfrag *) q->ipf_next;
+ m_cat(m, t);
+ }
+
+ /*
+ * Create header for new ip packet by
+ * modifying header of first packet;
+ * dequeue and discard fragment reassembly header.
+ * Make header visible.
+ */
+ q = fp->frag_link.next;
+
+ /*
+ * If the fragments concatenated to an mbuf that's
+ * bigger than the total size of the fragment, then and
+ * m_ext buffer was alloced. But fp->ipq_next points to
+ * the old buffer (in the mbuf), so we must point ip
+ * into the new buffer.
+ */
+ if (m->m_flags & M_EXT) {
+ int delta = (char *)q - m->m_dat;
+ q = (struct ipasfrag *)(m->m_ext + delta);
+ }
+
+ /* DEBUG_ARG("ip = %lx", (long)ip);
+ * ip=(struct ipasfrag *)m->m_data; */
+
+ ip = fragtoip(q);
+ ip->ip_len = next;
+ ip->ip_tos &= ~1;
+ ip->ip_src = fp->ipq_src;
+ ip->ip_dst = fp->ipq_dst;
+ remque(&fp->ip_link);
+ (void) m_free(dtom(fp));
+ m->m_len += (ip->ip_hl << 2);
+ m->m_data -= (ip->ip_hl << 2);
+
+ return ip;
+
+dropfrag:
+ STAT(ipstat.ips_fragdropped++);
+ m_freem(m);
+ return NULL;
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+static void
+ip_freef(struct ipq *fp)
+{
+ register struct ipasfrag *q, *p;
+
+ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) {
+ p = q->ipf_next;
+ ip_deq(q);
+ m_freem(dtom(q));
+ }
+ remque(&fp->ip_link);
+ (void) m_free(dtom(fp));
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+static void
+ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
+{
+ DEBUG_CALL("ip_enq");
+ DEBUG_ARG("prev = %lx", (long)prev);
+ p->ipf_prev = prev;
+ p->ipf_next = prev->ipf_next;
+ ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p;
+ prev->ipf_next = p;
+}
+
+/*
+ * To ip_enq as remque is to insque.
+ */
+static void
+ip_deq(register struct ipasfrag *p)
+{
+ ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
+ ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo(void)
+{
+ struct qlink *l;
+
+ DEBUG_CALL("ip_slowtimo");
+
+ l = ipq.ip_link.next;
+
+ if (l == NULL)
+ return;
+
+ while (l != &ipq.ip_link) {
+ struct ipq *fp = container_of(l, struct ipq, ip_link);
+ l = l->next;
+ if (--fp->ipq_ttl == 0) {
+ STAT(ipstat.ips_fragtimeout++);
+ ip_freef(fp);
+ }
+ }
+}
+
+/*
+ * Do option processing on a datagram,
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
+ */
+
+#ifdef notdef
+
+int
+ip_dooptions(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register u_char *cp;
+ register struct ip_timestamp *ipt;
+ register struct in_ifaddr *ia;
+/* int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; */
+ int opt, optlen, cnt, off, code, type, forward = 0;
+ struct in_addr *sin, dst;
+typedef u_int32_t n_time;
+ n_time ntime;
+
+ dst = ip->ip_dst;
+ cp = (u_char *)(ip + 1);
+ cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[IPOPT_OLEN];
+ if (optlen <= 0 || optlen > cnt) {
+ code = &cp[IPOPT_OLEN] - (u_char *)ip;
+ goto bad;
+ }
+ }
+ switch (opt) {
+
+ default:
+ break;
+
+ /*
+ * Source routing with record.
+ * Find interface with current destination address.
+ * If none on this machine then drop if strictly routed,
+ * or do nothing if loosely routed.
+ * Record interface address and bring up next address
+ * component. If strictly routed make sure next
+ * address is on directly accessible net.
+ */
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ ipaddr.sin_addr = ip->ip_dst;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithaddr((struct sockaddr *)&ipaddr);
+ if (ia == 0) {
+ if (opt == IPOPT_SSRR) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ /*
+ * Loose routing, and not at next destination
+ * yet; nothing to do except forward.
+ */
+ break;
+ }
+ off--; / * 0 origin * /
+ if (off > optlen - sizeof(struct in_addr)) {
+ /*
+ * End of source route. Should be for us.
+ */
+ save_rte(cp, ip->ip_src);
+ break;
+ }
+ /*
+ * locate outgoing interface
+ */
+ bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ if (opt == IPOPT_SSRR) {
+#define INA struct in_ifaddr *
+#define SA struct sockaddr *
+ if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+ ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+ } else
+ ia = ip_rtaddr(ipaddr.sin_addr);
+ if (ia == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ ip->ip_dst = ipaddr.sin_addr;
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ /*
+ * Let ip_intr's mcast routing check handle mcast pkts
+ */
+ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
+ break;
+
+ case IPOPT_RR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ /*
+ * If no space remains, ignore.
+ */
+ off--; * 0 origin *
+ if (off > optlen - sizeof(struct in_addr))
+ break;
+ bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ /*
+ * locate outgoing interface; if we're the destination,
+ * use the incoming interface (should be same).
+ */
+ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+ (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_HOST;
+ goto bad;
+ }
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS:
+ code = cp - (u_char *)ip;
+ ipt = (struct ip_timestamp *)cp;
+ if (ipt->ipt_len < 5)
+ goto bad;
+ if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
+ if (++ipt->ipt_oflw == 0)
+ goto bad;
+ break;
+ }
+ sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
+ switch (ipt->ipt_flg) {
+
+ case IPOPT_TS_TSONLY:
+ break;
+
+ case IPOPT_TS_TSANDADDR:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ ipaddr.sin_addr = dst;
+ ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
+ m->m_pkthdr.rcvif);
+ if (ia == 0)
+ continue;
+ bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
+ (caddr_t)sin, sizeof(struct in_addr));
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS_PRESPEC:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+ sizeof(struct in_addr));
+ if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+ continue;
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ default:
+ goto bad;
+ }
+ ntime = iptime();
+ bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+ sizeof(n_time));
+ ipt->ipt_ptr += sizeof(n_time);
+ }
+ }
+ if (forward) {
+ ip_forward(m, 1);
+ return (1);
+ }
+ }
+ }
+ return (0);
+bad:
+ /* ip->ip_len -= ip->ip_hl << 2; XXX icmp_error adds in hdr length */
+
+/* Not yet */
+ icmp_error(m, type, code, 0, 0);
+
+ STAT(ipstat.ips_badoptions++);
+ return (1);
+}
+
+#endif /* notdef */
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * (XXX) should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(register struct mbuf *m, struct mbuf *mopt)
+{
+ register int i;
+ struct ip *ip = mtod(m, struct ip *);
+ register caddr_t opts;
+ int olen;
+
+ olen = (ip->ip_hl<<2) - sizeof (struct ip);
+ opts = (caddr_t)(ip + 1);
+ i = m->m_len - (sizeof (struct ip) + olen);
+ memcpy(opts, opts + olen, (unsigned)i);
+ m->m_len -= olen;
+
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
diff --git a/slirp/ip_output.c b/slirp/ip_output.c
new file mode 100644
index 0000000..5bce520
--- /dev/null
+++ b/slirp/ip_output.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 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. 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_output.c 8.3 (Berkeley) 1/21/94
+ * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+u_int16_t ip_id;
+
+/* Number of packets queued before we start sending
+ * (to prevent allocing too many mbufs) */
+#define IF_THRESH 10
+
+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(struct socket *so, struct mbuf *m0)
+{
+ register struct ip *ip;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof(struct ip );
+ int len, off, error = 0;
+
+ DEBUG_CALL("ip_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m0 = %lx", (long)m0);
+
+ /* We do no options */
+/* if (opt) {
+ * m = ip_insertoptions(m, opt, &len);
+ * hlen = len;
+ * }
+ */
+ ip = mtod(m, struct ip *);
+ /*
+ * Fill in IP header.
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = htons(ip_id++);
+ ip->ip_hl = hlen >> 2;
+ STAT(ipstat.ips_localout++);
+
+ /*
+ * Verify that we have any chance at all of being able to queue
+ * the packet or packet fragments
+ */
+ /* XXX Hmmm... */
+/* if (if_queued > IF_THRESH && towrite <= 0) {
+ * error = ENOBUFS;
+ * goto bad;
+ * }
+ */
+
+ /*
+ * If small enough for interface, can just send directly.
+ */
+ if ((u_int16_t)ip->ip_len <= IF_MTU) {
+ ip->ip_len = htons((u_int16_t)ip->ip_len);
+ ip->ip_off = htons((u_int16_t)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+
+ if_output(so, m);
+ goto done;
+ }
+
+ /*
+ * Too large for interface; fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ if (ip->ip_off & IP_DF) {
+ error = -1;
+ STAT(ipstat.ips_cantfrag++);
+ goto bad;
+ }
+
+ len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */
+ if (len < 8) {
+ error = -1;
+ goto bad;
+ }
+
+ {
+ int mhlen, firstlen = len;
+ struct mbuf **mnext = &m->m_nextpkt;
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ mhlen = sizeof (struct ip);
+ for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
+ register struct ip *mhip;
+ m = m_get();
+ if (m == NULL) {
+ error = -1;
+ STAT(ipstat.ips_odropped++);
+ goto sendorfree;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ mhip = mtod(m, struct ip *);
+ *mhip = *ip;
+
+ /* No options */
+/* if (hlen > sizeof (struct ip)) {
+ * mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
+ * mhip->ip_hl = mhlen >> 2;
+ * }
+ */
+ m->m_len = mhlen;
+ mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
+ if (ip->ip_off & IP_MF)
+ mhip->ip_off |= IP_MF;
+ if (off + len >= (u_int16_t)ip->ip_len)
+ len = (u_int16_t)ip->ip_len - off;
+ else
+ mhip->ip_off |= IP_MF;
+ mhip->ip_len = htons((u_int16_t)(len + mhlen));
+
+ if (m_copy(m, m0, off, len) < 0) {
+ error = -1;
+ goto sendorfree;
+ }
+
+ mhip->ip_off = htons((u_int16_t)mhip->ip_off);
+ mhip->ip_sum = 0;
+ mhip->ip_sum = cksum(m, mhlen);
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ STAT(ipstat.ips_ofragments++);
+ }
+ /*
+ * Update first fragment by trimming what's been copied out
+ * and updating header, then send each fragment (in order).
+ */
+ m = m0;
+ m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len);
+ ip->ip_len = htons((u_int16_t)m->m_len);
+ ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+sendorfree:
+ for (m = m0; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ if (error == 0)
+ if_output(so, m);
+ else
+ m_freem(m);
+ }
+
+ if (error == 0)
+ STAT(ipstat.ips_fragmented++);
+ }
+
+done:
+ return (error);
+
+bad:
+ m_freem(m0);
+ goto done;
+}
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
new file mode 100644
index 0000000..d0df24b
--- /dev/null
+++ b/slirp/libslirp.h
@@ -0,0 +1,44 @@
+#ifndef _LIBSLIRP_H
+#define _LIBSLIRP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void slirp_init(int restricted, const char *special_ip);
+
+void slirp_select_fill(int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_input(const uint8_t *pkt, int pkt_len);
+
+/* you must provide the following functions: */
+int slirp_can_output(void);
+void slirp_output(const uint8_t *pkt, int pkt_len);
+
+void slirp_redir_loop(void (*func)(void *opaque, int is_udp,
+ struct in_addr *laddr, u_int lport,
+ struct in_addr *faddr, u_int fport),
+ void *opaque);
+int slirp_redir_rm(int is_udp, int host_port);
+int slirp_redir(int is_udp, int host_port,
+ struct in_addr guest_addr, int guest_port);
+int slirp_add_exec(int do_pty, const void *args, int addr_low_byte,
+ int guest_port);
+
+extern const char *tftp_prefix;
+extern char slirp_hostname[33];
+extern const char *bootp_filename;
+
+void slirp_stats(void);
+void slirp_socket_recv(int addr_low_byte, int guest_port, const uint8_t *buf,
+ int size);
+size_t slirp_socket_can_recv(int addr_low_byte, int guest_port);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/slirp/main.h b/slirp/main.h
new file mode 100644
index 0000000..ed51385
--- /dev/null
+++ b/slirp/main.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define TOWRITEMAX 512
+
+extern struct timeval tt;
+extern int link_up;
+extern int slirp_socket;
+extern int slirp_socket_unit;
+extern int slirp_socket_port;
+extern u_int32_t slirp_socket_addr;
+extern char *slirp_socket_passwd;
+extern int ctty_closed;
+
+/*
+ * Get the difference in 2 times from updtim()
+ * Allow for wraparound times, "just in case"
+ * x is the greater of the 2 (current time) and y is
+ * what it's being compared against.
+ */
+#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y)
+
+extern char *slirp_tty;
+extern char *exec_shell;
+extern u_int curtime;
+extern fd_set *global_readfds, *global_writefds, *global_xfds;
+extern struct in_addr ctl_addr;
+extern struct in_addr special_addr;
+extern struct in_addr alias_addr;
+extern struct in_addr our_addr;
+extern struct in_addr loopback_addr;
+extern struct in_addr dns_addr;
+extern char *username;
+extern char *socket_path;
+extern int towrite_max;
+extern int ppp_exit;
+extern int tcp_keepintvl;
+extern uint8_t client_ethaddr[6];
+extern const char *slirp_special_ip;
+extern int slirp_restrict;
+
+#define PROTO_SLIP 0x1
+#ifdef USE_PPP
+#define PROTO_PPP 0x2
+#endif
+
+void if_encap(const uint8_t *ip_data, int ip_data_len);
+ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);
diff --git a/slirp/mbuf.c b/slirp/mbuf.c
new file mode 100644
index 0000000..1d30a63
--- /dev/null
+++ b/slirp/mbuf.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/*
+ * mbuf's in SLiRP are much simpler than the real mbufs in
+ * FreeBSD. They are fixed size, determined by the MTU,
+ * so that one whole packet can fit. Mbuf's cannot be
+ * chained together. If there's more data than the mbuf
+ * could hold, an external malloced buffer is pointed to
+ * by m_ext (and the data pointers) and M_EXT is set in
+ * the flags
+ */
+
+#include <slirp.h>
+
+int mbuf_alloced = 0;
+struct mbuf m_freelist, m_usedlist;
+#define MBUF_THRESH 30
+int mbuf_max = 0;
+
+/*
+ * Find a nice value for msize
+ * XXX if_maxlinkhdr already in mtu
+ */
+#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + sizeof(struct m_hdr ) + 6)
+
+void
+m_init(void)
+{
+ m_freelist.m_next = m_freelist.m_prev = &m_freelist;
+ m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist;
+}
+
+/*
+ * Get an mbuf from the free list, if there are none
+ * malloc one
+ *
+ * Because fragmentation can occur if we alloc new mbufs and
+ * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
+ * which tells m_free to actually free() it
+ */
+struct mbuf *
+m_get(void)
+{
+ register struct mbuf *m;
+ int flags = 0;
+
+ DEBUG_CALL("m_get");
+
+ if (m_freelist.m_next == &m_freelist) {
+ m = (struct mbuf *)malloc(SLIRP_MSIZE);
+ if (m == NULL) goto end_error;
+ mbuf_alloced++;
+ if (mbuf_alloced > MBUF_THRESH)
+ flags = M_DOFREE;
+ if (mbuf_alloced > mbuf_max)
+ mbuf_max = mbuf_alloced;
+ } else {
+ m = m_freelist.m_next;
+ remque(m);
+ }
+
+ /* Insert it in the used list */
+ insque(m,&m_usedlist);
+ m->m_flags = (flags | M_USEDLIST);
+
+ /* Initialise it */
+ m->m_size = SLIRP_MSIZE - sizeof(struct m_hdr);
+ m->m_data = m->m_dat;
+ m->m_len = 0;
+ m->m_nextpkt = NULL;
+ m->m_prevpkt = NULL;
+end_error:
+ DEBUG_ARG("m = %lx", (long )m);
+ return m;
+}
+
+void
+m_free(struct mbuf *m)
+{
+
+ DEBUG_CALL("m_free");
+ DEBUG_ARG("m = %lx", (long )m);
+
+ if(m) {
+ /* Remove from m_usedlist */
+ if (m->m_flags & M_USEDLIST)
+ remque(m);
+
+ /* If it's M_EXT, free() it */
+ if (m->m_flags & M_EXT)
+ free(m->m_ext);
+
+ /*
+ * Either free() it or put it on the free list
+ */
+ if (m->m_flags & M_DOFREE) {
+ free(m);
+ mbuf_alloced--;
+ } else if ((m->m_flags & M_FREELIST) == 0) {
+ insque(m,&m_freelist);
+ m->m_flags = M_FREELIST; /* Clobber other flags */
+ }
+ } /* if(m) */
+}
+
+/*
+ * Copy data from one mbuf to the end of
+ * the other.. if result is too big for one mbuf, malloc()
+ * an M_EXT data segment
+ */
+void
+m_cat(struct mbuf *m, struct mbuf *n)
+{
+ /*
+ * If there's no room, realloc
+ */
+ if (M_FREEROOM(m) < n->m_len)
+ m_inc(m,m->m_size+MINCSIZE);
+
+ memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
+ m->m_len += n->m_len;
+
+ m_free(n);
+}
+
+
+/* make m size bytes large */
+void
+m_inc(struct mbuf *m, int size)
+{
+ int datasize;
+
+ /* some compiles throw up on gotos. This one we can fake. */
+ if(m->m_size>size) return;
+
+ if (m->m_flags & M_EXT) {
+ datasize = m->m_data - m->m_ext;
+ m->m_ext = (char *)realloc(m->m_ext,size);
+/* if (m->m_ext == NULL)
+ * return (struct mbuf *)NULL;
+ */
+ m->m_data = m->m_ext + datasize;
+ } else {
+ char *dat;
+ datasize = m->m_data - m->m_dat;
+ dat = (char *)malloc(size);
+/* if (dat == NULL)
+ * return (struct mbuf *)NULL;
+ */
+ memcpy(dat, m->m_dat, m->m_size);
+
+ m->m_ext = dat;
+ m->m_data = m->m_ext + datasize;
+ m->m_flags |= M_EXT;
+ }
+
+ m->m_size = size;
+
+}
+
+
+
+void
+m_adj(struct mbuf *m, int len)
+{
+ if (m == NULL)
+ return;
+ if (len >= 0) {
+ /* Trim from head */
+ m->m_data += len;
+ m->m_len -= len;
+ } else {
+ /* Trim from tail */
+ len = -len;
+ m->m_len -= len;
+ }
+}
+
+
+/*
+ * Copy len bytes from m, starting off bytes into n
+ */
+int
+m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
+{
+ if (len > M_FREEROOM(n))
+ return -1;
+
+ memcpy((n->m_data + n->m_len), (m->m_data + off), len);
+ n->m_len += len;
+ return 0;
+}
+
+
+/*
+ * Given a pointer into an mbuf, return the mbuf
+ * XXX This is a kludge, I should eliminate the need for it
+ * Fortunately, it's not used often
+ */
+struct mbuf *
+dtom(void *dat)
+{
+ struct mbuf *m;
+
+ DEBUG_CALL("dtom");
+ DEBUG_ARG("dat = %lx", (long )dat);
+
+ /* bug corrected for M_EXT buffers */
+ for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) {
+ if (m->m_flags & M_EXT) {
+ if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
+ return m;
+ } else {
+ if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) )
+ return m;
+ }
+ }
+
+ DEBUG_ERROR((dfd, "dtom failed"));
+
+ return (struct mbuf *)0;
+}
diff --git a/slirp/mbuf.h b/slirp/mbuf.h
new file mode 100644
index 0000000..5527373
--- /dev/null
+++ b/slirp/mbuf.h
@@ -0,0 +1,142 @@
+/*
+ * 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. 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.
+ *
+ * @(#)mbuf.h 8.3 (Berkeley) 1/21/94
+ * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp
+ */
+
+#ifndef _MBUF_H_
+#define _MBUF_H_
+
+#define m_freem m_free
+
+
+#define MINCSIZE 4096 /* Amount to increase mbuf if too small */
+
+/*
+ * Macros for type conversion
+ * mtod(m,t) - convert mbuf pointer to data pointer of correct type
+ * dtom(x) - convert data pointer within mbuf to mbuf pointer (XXX)
+ */
+#define mtod(m,t) ((t)(m)->m_data)
+/* #define dtom(x) ((struct mbuf *)((int)(x) & ~(M_SIZE-1))) */
+
+/* XXX About mbufs for slirp:
+ * Only one mbuf is ever used in a chain, for each "cell" of data.
+ * m_nextpkt points to the next packet, if fragmented.
+ * If the data is too large, the M_EXT is used, and a larger block
+ * is alloced. Therefore, m_free[m] must check for M_EXT and if set
+ * free the m_ext. This is inefficient memory-wise, but who cares.
+ */
+
+/* XXX should union some of these! */
+/* header at beginning of each mbuf: */
+struct m_hdr {
+ struct mbuf *mh_next; /* Linked list of mbufs */
+ struct mbuf *mh_prev;
+ struct mbuf *mh_nextpkt; /* Next packet in queue/record */
+ struct mbuf *mh_prevpkt; /* Flags aren't used in the output queue */
+ int mh_flags; /* Misc flags */
+
+ int mh_size; /* Size of data */
+ struct socket *mh_so;
+
+ caddr_t mh_data; /* Location of data */
+ int mh_len; /* Amount of data in this mbuf */
+};
+
+/*
+ * How much room is in the mbuf, from m_data to the end of the mbuf
+ */
+#define M_ROOM(m) ((m->m_flags & M_EXT)? \
+ (((m)->m_ext + (m)->m_size) - (m)->m_data) \
+ : \
+ (((m)->m_dat + (m)->m_size) - (m)->m_data))
+
+/*
+ * How much free room there is
+ */
+#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
+#define M_TRAILINGSPACE M_FREEROOM
+
+struct mbuf {
+ struct m_hdr m_hdr;
+ union M_dat {
+ char m_dat_[1]; /* ANSI don't like 0 sized arrays */
+ char *m_ext_;
+ } M_dat;
+};
+
+#define m_next m_hdr.mh_next
+#define m_prev m_hdr.mh_prev
+#define m_nextpkt m_hdr.mh_nextpkt
+#define m_prevpkt m_hdr.mh_prevpkt
+#define m_flags m_hdr.mh_flags
+#define m_len m_hdr.mh_len
+#define m_data m_hdr.mh_data
+#define m_size m_hdr.mh_size
+#define m_dat M_dat.m_dat_
+#define m_ext M_dat.m_ext_
+#define m_so m_hdr.mh_so
+
+#define ifq_prev m_prev
+#define ifq_next m_next
+#define ifs_prev m_prevpkt
+#define ifs_next m_nextpkt
+#define ifq_so m_so
+
+#define M_EXT 0x01 /* m_ext points to more (malloced) data */
+#define M_FREELIST 0x02 /* mbuf is on free list */
+#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */
+#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free()
+ * it rather than putting it on the free list */
+
+/*
+ * Mbuf statistics. XXX
+ */
+
+struct mbstat {
+ int mbs_alloced; /* Number of mbufs allocated */
+
+};
+
+extern struct mbstat mbstat;
+extern int mbuf_alloced;
+extern struct mbuf m_freelist, m_usedlist;
+extern int mbuf_max;
+
+void m_init _P((void));
+struct mbuf * m_get _P((void));
+void m_free _P((struct mbuf *));
+void m_cat _P((register struct mbuf *, register struct mbuf *));
+void m_inc _P((struct mbuf *, int));
+void m_adj _P((struct mbuf *, int));
+int m_copy _P((struct mbuf *, struct mbuf *, int, int));
+struct mbuf * dtom _P((void *));
+
+#endif
diff --git a/slirp/misc.c b/slirp/misc.c
new file mode 100644
index 0000000..1391d49
--- /dev/null
+++ b/slirp/misc.c
@@ -0,0 +1,907 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+u_int curtime, time_fasttimo, last_slowtimo;
+
+#if 0
+int x_port = -1;
+int x_display = 0;
+int x_screen = 0;
+
+int
+show_x(buff, inso)
+ char *buff;
+ struct socket *inso;
+{
+ if (x_port < 0) {
+ lprint("X Redir: X not being redirected.\r\n");
+ } else {
+ lprint("X Redir: In sh/bash/zsh/etc. type: DISPLAY=%s:%d.%d; export DISPLAY\r\n",
+ inet_ntoa(our_addr), x_port, x_screen);
+ lprint("X Redir: In csh/tcsh/etc. type: setenv DISPLAY %s:%d.%d\r\n",
+ inet_ntoa(our_addr), x_port, x_screen);
+ if (x_display)
+ lprint("X Redir: Redirecting to display %d\r\n", x_display);
+ }
+
+ return CFG_OK;
+}
+
+
+/*
+ * XXX Allow more than one X redirection?
+ */
+void
+redir_x(inaddr, start_port, display, screen)
+ u_int32_t inaddr;
+ int start_port;
+ int display;
+ int screen;
+{
+ int i;
+
+ if (x_port >= 0) {
+ lprint("X Redir: X already being redirected.\r\n");
+ show_x(0, 0);
+ } else {
+ for (i = 6001 + (start_port-1); i <= 6100; i++) {
+ if (solisten(htons(i), inaddr, htons(6000 + display), 0)) {
+ /* Success */
+ x_port = i - 6000;
+ x_display = display;
+ x_screen = screen;
+ show_x(0, 0);
+ return;
+ }
+ }
+ lprint("X Redir: Error: Couldn't redirect a port for X. Weird.\r\n");
+ }
+}
+#endif
+
+/*
+ * Get our IP address and put it in our_addr
+ */
+void
+getouraddr(void)
+{
+ char buff[256];
+ struct hostent *he = NULL;
+
+ if (gethostname(buff,256) == 0)
+ he = gethostbyname(buff);
+ if (he)
+ our_addr = *(struct in_addr *)he->h_addr;
+ if (our_addr.s_addr == 0)
+ our_addr.s_addr = loopback_addr.s_addr;
+}
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+inline void
+insque(void *a, void *b)
+{
+ register struct quehead *element = (struct quehead *) a;
+ register struct quehead *head = (struct quehead *) b;
+ element->qh_link = head->qh_link;
+ head->qh_link = (struct quehead *)element;
+ element->qh_rlink = (struct quehead *)head;
+ ((struct quehead *)(element->qh_link))->qh_rlink
+ = (struct quehead *)element;
+}
+
+inline void
+remque(void *a)
+{
+ register struct quehead *element = (struct quehead *) a;
+ ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
+ ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
+ element->qh_rlink = NULL;
+ /* element->qh_link = NULL; TCP FIN1 crashes if you do this. Why ? */
+}
+
+/* #endif */
+
+
+int
+add_exec(struct ex_list **ex_ptr, int do_pty, char *exec, int addr, int port)
+{
+ struct ex_list *tmp_ptr;
+
+ /* First, check if the port is "bound" */
+ for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
+ if (port == tmp_ptr->ex_fport && addr == tmp_ptr->ex_addr)
+ return -1;
+ }
+
+ tmp_ptr = *ex_ptr;
+ *ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
+ (*ex_ptr)->ex_fport = port;
+ (*ex_ptr)->ex_addr = addr;
+ (*ex_ptr)->ex_pty = do_pty;
+ (*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
+ (*ex_ptr)->ex_next = tmp_ptr;
+ return 0;
+}
+
+#ifndef HAVE_STRERROR
+
+/*
+ * For systems with no strerror
+ */
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(error)
+ int error;
+{
+ if (error < sys_nerr)
+ return sys_errlist[error];
+ else
+ return "Unknown error.";
+}
+
+#endif
+
+
+#ifdef _WIN32
+
+int
+fork_exec(struct socket *so, const char *ex, int do_pty)
+{
+ /* not implemented */
+ return 0;
+}
+
+#else
+
+#ifndef CONFIG_QEMU
+int
+slirp_openpty(amaster, aslave)
+ int *amaster, *aslave;
+{
+ register int master, slave;
+
+#ifdef HAVE_GRANTPT
+ char *ptr;
+
+ if ((master = open("/dev/ptmx", O_RDWR)) < 0 ||
+ grantpt(master) < 0 ||
+ unlockpt(master) < 0 ||
+ (ptr = ptsname(master)) == NULL) {
+ close(master);
+ return -1;
+ }
+
+ if ((slave = open(ptr, O_RDWR)) < 0 ||
+ ioctl(slave, I_PUSH, "ptem") < 0 ||
+ ioctl(slave, I_PUSH, "ldterm") < 0 ||
+ ioctl(slave, I_PUSH, "ttcompat") < 0) {
+ close(master);
+ close(slave);
+ return -1;
+ }
+
+ *amaster = master;
+ *aslave = slave;
+ return 0;
+
+#else
+
+ static char line[] = "/dev/ptyXX";
+ register const char *cp1, *cp2;
+
+ for (cp1 = "pqrsPQRS"; *cp1; cp1++) {
+ line[8] = *cp1;
+ for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) {
+ line[9] = *cp2;
+ if ((master = open(line, O_RDWR, 0)) == -1) {
+ if (errno == ENOENT)
+ return (-1); /* out of ptys */
+ } else {
+ line[5] = 't';
+ /* These will fail */
+ (void) chown(line, getuid(), 0);
+ (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
+#ifdef HAVE_REVOKE
+ (void) revoke(line);
+#endif
+ if ((slave = open(line, O_RDWR, 0)) != -1) {
+ *amaster = master;
+ *aslave = slave;
+ return 0;
+ }
+ (void) close(master);
+ line[5] = 'p';
+ }
+ }
+ }
+ errno = ENOENT; /* out of ptys */
+ return (-1);
+#endif
+}
+#endif
+
+/*
+ * XXX This is ugly
+ * We create and bind a socket, then fork off to another
+ * process, which connects to this socket, after which we
+ * exec the wanted program. If something (strange) happens,
+ * the accept() call could block us forever.
+ *
+ * do_pty = 0 Fork/exec inetd style
+ * do_pty = 1 Fork/exec using slirp.telnetd
+ * do_ptr = 2 Fork/exec using pty
+ */
+int
+fork_exec(struct socket *so, const char *ex, int do_pty)
+{
+ int s;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int opt;
+ int master = -1;
+ const char *argv[256];
+#if 0
+ char buff[256];
+#endif
+ /* don't want to clobber the original */
+ char *bptr;
+ const char *curarg;
+ int c, i, ret;
+
+ DEBUG_CALL("fork_exec");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ex = %lx", (long)ex);
+ DEBUG_ARG("do_pty = %lx", (long)do_pty);
+
+ if (do_pty == 2) {
+#if 0
+ if (slirp_openpty(&master, &s) == -1) {
+ lprint("Error: openpty failed: %s\n", strerror(errno));
+ return 0;
+ }
+#else
+ return 0;
+#endif
+ } else {
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
+ bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
+ listen(s, 1) < 0) {
+ lprint("Error: inet socket: %s\n", strerror(errno));
+ closesocket(s);
+
+ return 0;
+ }
+ }
+
+ switch(fork()) {
+ case -1:
+ lprint("Error: fork failed: %s\n", strerror(errno));
+ close(s);
+ if (do_pty == 2)
+ close(master);
+ return 0;
+
+ case 0:
+ /* Set the DISPLAY */
+ if (do_pty == 2) {
+ (void) close(master);
+#ifdef TIOCSCTTY /* XXXXX */
+ (void) setsid();
+ ioctl(s, TIOCSCTTY, (char *)NULL);
+#endif
+ } else {
+ getsockname(s, (struct sockaddr *)&addr, &addrlen);
+ close(s);
+ /*
+ * Connect to the socket
+ * XXX If any of these fail, we're in trouble!
+ */
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ addr.sin_addr = loopback_addr;
+ do {
+ ret = connect(s, (struct sockaddr *)&addr, addrlen);
+ } while (ret < 0 && errno == EINTR);
+ }
+
+#if 0
+ if (x_port >= 0) {
+#ifdef HAVE_SETENV
+ sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ setenv("DISPLAY", buff, 1);
+#else
+ sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ putenv(buff);
+#endif
+ }
+#endif
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ for (s = getdtablesize() - 1; s >= 3; s--)
+ close(s);
+
+ i = 0;
+ bptr = strdup(ex); /* No need to free() this */
+ if (do_pty == 1) {
+ /* Setup "slirp.telnetd -x" */
+ argv[i++] = "slirp.telnetd";
+ argv[i++] = "-x";
+ argv[i++] = bptr;
+ } else
+ do {
+ /* Change the string into argv[] */
+ curarg = bptr;
+ while (*bptr != ' ' && *bptr != (char)0)
+ bptr++;
+ c = *bptr;
+ *bptr++ = (char)0;
+ argv[i++] = strdup(curarg);
+ } while (c);
+
+ argv[i] = NULL;
+ execvp(argv[0], (char **)argv);
+
+ /* Ooops, failed, let's tell the user why */
+ {
+ char buff[256];
+
+ snprintf(buff, sizeof(buff),
+ "Error: execvp of %s failed: %s\n",
+ argv[0], strerror(errno));
+ write(2, buff, strlen(buff)+1);
+ }
+ close(0); close(1); close(2); /* XXX */
+ exit(1);
+
+ default:
+ if (do_pty == 2) {
+ close(s);
+ so->s = master;
+ } else {
+ /*
+ * XXX this could block us...
+ * XXX Should set a timer here, and if accept() doesn't
+ * return after X seconds, declare it a failure
+ * The only reason this will block forever is if socket()
+ * of connect() fail in the child process
+ */
+ do {
+ so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
+ } while (so->s < 0 && errno == EINTR);
+ closesocket(s);
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ }
+ fd_nonblock(so->s);
+
+ /* Append the telnet options now */
+ if (so->so_m != NULL && do_pty == 1) {
+ sbappend(so, so->so_m);
+ so->so_m = NULL;
+ }
+
+ return 1;
+ }
+}
+#endif
+
+#ifndef HAVE_STRDUP
+char *
+strdup(str)
+ const char *str;
+{
+ char *bptr;
+
+ bptr = (char *)malloc(strlen(str)+1);
+ strcpy(bptr, str);
+
+ return bptr;
+}
+#endif
+
+#if 0
+void
+snooze_hup(num)
+ int num;
+{
+ int s, ret;
+#ifndef NO_UNIX_SOCKETS
+ struct sockaddr_un sock_un;
+#endif
+ struct sockaddr_in sock_in;
+ char buff[256];
+
+ ret = -1;
+ if (slirp_socket_passwd) {
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ slirp_exit(1);
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_addr.s_addr = slirp_socket_addr;
+ sock_in.sin_port = htons(slirp_socket_port);
+ if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) != 0)
+ slirp_exit(1); /* just exit...*/
+ sprintf(buff, "kill %s:%d", slirp_socket_passwd, slirp_socket_unit);
+ write(s, buff, strlen(buff)+1);
+ }
+#ifndef NO_UNIX_SOCKETS
+ else {
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ slirp_exit(1);
+ sock_un.sun_family = AF_UNIX;
+ strcpy(sock_un.sun_path, socket_path);
+ if (connect(s, (struct sockaddr *)&sock_un,
+ sizeof(sock_un.sun_family) + sizeof(sock_un.sun_path)) != 0)
+ slirp_exit(1);
+ sprintf(buff, "kill none:%d", slirp_socket_unit);
+ write(s, buff, strlen(buff)+1);
+ }
+#endif
+ slirp_exit(0);
+}
+
+
+void
+snooze()
+{
+ sigset_t s;
+ int i;
+
+ /* Don't need our data anymore */
+ /* XXX This makes SunOS barf */
+/* brk(0); */
+
+ /* Close all fd's */
+ for (i = 255; i >= 0; i--)
+ close(i);
+
+ signal(SIGQUIT, slirp_exit);
+ signal(SIGHUP, snooze_hup);
+ sigemptyset(&s);
+
+ /* Wait for any signal */
+ sigsuspend(&s);
+
+ /* Just in case ... */
+ exit(255);
+}
+
+void
+relay(s)
+ int s;
+{
+ char buf[8192];
+ int n;
+ fd_set readfds;
+ struct ttys *ttyp;
+
+ /* Don't need our data anymore */
+ /* XXX This makes SunOS barf */
+/* brk(0); */
+
+ signal(SIGQUIT, slirp_exit);
+ signal(SIGHUP, slirp_exit);
+ signal(SIGINT, slirp_exit);
+ signal(SIGTERM, slirp_exit);
+
+ /* Fudge to get term_raw and term_restore to work */
+ if (NULL == (ttyp = tty_attach (0, slirp_tty))) {
+ lprint ("Error: tty_attach failed in misc.c:relay()\r\n");
+ slirp_exit (1);
+ }
+ ttyp->fd = 0;
+ ttyp->flags |= TTY_CTTY;
+ term_raw(ttyp);
+
+ while (1) {
+ FD_ZERO(&readfds);
+
+ FD_SET(0, &readfds);
+ FD_SET(s, &readfds);
+
+ n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
+
+ if (n <= 0)
+ slirp_exit(0);
+
+ if (FD_ISSET(0, &readfds)) {
+ n = read(0, buf, 8192);
+ if (n <= 0)
+ slirp_exit(0);
+ n = writen(s, buf, n);
+ if (n <= 0)
+ slirp_exit(0);
+ }
+
+ if (FD_ISSET(s, &readfds)) {
+ n = read(s, buf, 8192);
+ if (n <= 0)
+ slirp_exit(0);
+ n = writen(0, buf, n);
+ if (n <= 0)
+ slirp_exit(0);
+ }
+ }
+
+ /* Just in case.... */
+ exit(1);
+}
+#endif
+
+#ifdef CONFIG_QEMU
+#include "monitor.h"
+
+void lprint(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ monitor_vprintf(cur_mon, format, args);
+ va_end(args);
+}
+#else
+int (*lprint_print) _P((void *, const char *, va_list));
+char *lprint_ptr, *lprint_ptr2, **lprint_arg;
+
+void
+#ifdef __STDC__
+lprint(const char *format, ...)
+#else
+lprint(va_alist) va_dcl
+#endif
+{
+ va_list args;
+
+#ifdef __STDC__
+ va_start(args, format);
+#else
+ char *format;
+ va_start(args);
+ format = va_arg(args, char *);
+#endif
+#if 0
+ /* If we're printing to an sbuf, make sure there's enough room */
+ /* XXX +100? */
+ if (lprint_sb) {
+ if ((lprint_ptr - lprint_sb->sb_wptr) >=
+ (lprint_sb->sb_datalen - (strlen(format) + 100))) {
+ int deltaw = lprint_sb->sb_wptr - lprint_sb->sb_data;
+ int deltar = lprint_sb->sb_rptr - lprint_sb->sb_data;
+ int deltap = lprint_ptr - lprint_sb->sb_data;
+
+ lprint_sb->sb_data = (char *)realloc(lprint_sb->sb_data,
+ lprint_sb->sb_datalen + TCP_SNDSPACE);
+
+ /* Adjust all values */
+ lprint_sb->sb_wptr = lprint_sb->sb_data + deltaw;
+ lprint_sb->sb_rptr = lprint_sb->sb_data + deltar;
+ lprint_ptr = lprint_sb->sb_data + deltap;
+
+ lprint_sb->sb_datalen += TCP_SNDSPACE;
+ }
+ }
+#endif
+ if (lprint_print)
+ lprint_ptr += (*lprint_print)(*lprint_arg, format, args);
+
+ /* Check if they want output to be logged to file as well */
+ if (lfd) {
+ /*
+ * Remove \r's
+ * otherwise you'll get ^M all over the file
+ */
+ int len = strlen(format);
+ char *bptr1, *bptr2;
+
+ bptr1 = bptr2 = strdup(format);
+
+ while (len--) {
+ if (*bptr1 == '\r')
+ memcpy(bptr1, bptr1+1, len+1);
+ else
+ bptr1++;
+ }
+ vfprintf(lfd, bptr2, args);
+ free(bptr2);
+ }
+ va_end(args);
+}
+
+void
+add_emu(buff)
+ char *buff;
+{
+ u_int lport, fport;
+ u_int8_t tos = 0, emu = 0;
+ char buff1[256], buff2[256], buff4[128];
+ char *buff3 = buff4;
+ struct emu_t *emup;
+ struct socket *so;
+
+ if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) {
+ lprint("Error: Bad arguments\r\n");
+ return;
+ }
+
+ if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) {
+ lport = 0;
+ if (sscanf(buff1, "%d", &fport) != 1) {
+ lprint("Error: Bad first argument\r\n");
+ return;
+ }
+ }
+
+ if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) {
+ buff3 = 0;
+ if (sscanf(buff2, "%256s", buff1) != 1) {
+ lprint("Error: Bad second argument\r\n");
+ return;
+ }
+ }
+
+ if (buff3) {
+ if (strcmp(buff3, "lowdelay") == 0)
+ tos = IPTOS_LOWDELAY;
+ else if (strcmp(buff3, "throughput") == 0)
+ tos = IPTOS_THROUGHPUT;
+ else {
+ lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n");
+ return;
+ }
+ }
+
+ if (strcmp(buff1, "ftp") == 0)
+ emu = EMU_FTP;
+ else if (strcmp(buff1, "irc") == 0)
+ emu = EMU_IRC;
+ else if (strcmp(buff1, "none") == 0)
+ emu = EMU_NONE; /* ie: no emulation */
+ else {
+ lprint("Error: Unknown service\r\n");
+ return;
+ }
+
+ /* First, check that it isn't already emulated */
+ for (emup = tcpemu; emup; emup = emup->next) {
+ if (emup->lport == lport && emup->fport == fport) {
+ lprint("Error: port already emulated\r\n");
+ return;
+ }
+ }
+
+ /* link it */
+ emup = (struct emu_t *)malloc(sizeof (struct emu_t));
+ emup->lport = (u_int16_t)lport;
+ emup->fport = (u_int16_t)fport;
+ emup->tos = tos;
+ emup->emu = emu;
+ emup->next = tcpemu;
+ tcpemu = emup;
+
+ /* And finally, mark all current sessions, if any, as being emulated */
+ for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+ if ((lport && lport == ntohs(so->so_lport)) ||
+ (fport && fport == ntohs(so->so_fport))) {
+ if (emu)
+ so->so_emu = emu;
+ if (tos)
+ so->so_iptos = tos;
+ }
+ }
+
+ lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport);
+}
+#endif
+
+#ifdef BAD_SPRINTF
+
+#undef vsprintf
+#undef sprintf
+
+/*
+ * Some BSD-derived systems have a sprintf which returns char *
+ */
+
+int
+vsprintf_len(string, format, args)
+ char *string;
+ const char *format;
+ va_list args;
+{
+ vsprintf(string, format, args);
+ return strlen(string);
+}
+
+int
+#ifdef __STDC__
+sprintf_len(char *string, const char *format, ...)
+#else
+sprintf_len(va_alist) va_dcl
+#endif
+{
+ va_list args;
+#ifdef __STDC__
+ va_start(args, format);
+#else
+ char *string;
+ char *format;
+ va_start(args);
+ string = va_arg(args, char *);
+ format = va_arg(args, char *);
+#endif
+ vsprintf(string, format, args);
+ return strlen(string);
+}
+
+#endif
+
+void
+u_sleep(int usec)
+{
+ struct timeval t;
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+
+ t.tv_sec = 0;
+ t.tv_usec = usec * 1000;
+
+ select(0, &fdset, &fdset, &fdset, &t);
+}
+
+/*
+ * Set fd blocking and non-blocking
+ */
+
+void
+fd_nonblock(int fd)
+{
+#ifdef FIONBIO
+#ifdef _WIN32
+ unsigned long opt = 1;
+#else
+ int opt = 1;
+#endif
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+void
+fd_block(int fd)
+{
+#ifdef FIONBIO
+#ifdef _WIN32
+ unsigned long opt = 0;
+#else
+ int opt = 0;
+#endif
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt &= ~O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+
+#if 0
+/*
+ * invoke RSH
+ */
+int
+rsh_exec(so,ns, user, host, args)
+ struct socket *so;
+ struct socket *ns;
+ char *user;
+ char *host;
+ char *args;
+{
+ int fd[2];
+ int fd0[2];
+ int s;
+ char buff[256];
+
+ DEBUG_CALL("rsh_exec");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (pipe(fd)<0) {
+ lprint("Error: pipe failed: %s\n", strerror(errno));
+ return 0;
+ }
+/* #ifdef HAVE_SOCKETPAIR */
+#if 1
+ if (socketpair(PF_UNIX,SOCK_STREAM,0, fd0) == -1) {
+ close(fd[0]);
+ close(fd[1]);
+ lprint("Error: openpty failed: %s\n", strerror(errno));
+ return 0;
+ }
+#else
+ if (slirp_openpty(&fd0[0], &fd0[1]) == -1) {
+ close(fd[0]);
+ close(fd[1]);
+ lprint("Error: openpty failed: %s\n", strerror(errno));
+ return 0;
+ }
+#endif
+
+ switch(fork()) {
+ case -1:
+ lprint("Error: fork failed: %s\n", strerror(errno));
+ close(fd[0]);
+ close(fd[1]);
+ close(fd0[0]);
+ close(fd0[1]);
+ return 0;
+
+ case 0:
+ close(fd[0]);
+ close(fd0[0]);
+
+ /* Set the DISPLAY */
+ if (x_port >= 0) {
+#ifdef HAVE_SETENV
+ sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ setenv("DISPLAY", buff, 1);
+#else
+ sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
+ putenv(buff);
+#endif
+ }
+
+ dup2(fd0[1], 0);
+ dup2(fd0[1], 1);
+ dup2(fd[1], 2);
+ for (s = 3; s <= 255; s++)
+ close(s);
+
+ execlp("rsh","rsh","-l", user, host, args, NULL);
+
+ /* Ooops, failed, let's tell the user why */
+
+ sprintf(buff, "Error: execlp of %s failed: %s\n",
+ "rsh", strerror(errno));
+ write(2, buff, strlen(buff)+1);
+ close(0); close(1); close(2); /* XXX */
+ exit(1);
+
+ default:
+ close(fd[1]);
+ close(fd0[1]);
+ ns->s=fd[0];
+ so->s=fd0[0];
+
+ return 1;
+ }
+}
+#endif
diff --git a/slirp/misc.h b/slirp/misc.h
new file mode 100644
index 0000000..ab8e3a7
--- /dev/null
+++ b/slirp/misc.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+struct ex_list {
+ int ex_pty; /* Do we want a pty? */
+ int ex_addr; /* The last byte of the address */
+ int ex_fport; /* Port to telnet to */
+ const char *ex_exec; /* Command line of what to exec */
+ struct ex_list *ex_next;
+};
+
+extern struct ex_list *exec_list;
+extern u_int time_fasttimo, last_slowtimo;
+
+extern int (*lprint_print) _P((void *, const char *, va_list));
+extern char *lprint_ptr, *lprint_ptr2, **lprint_arg;
+extern struct sbuf *lprint_sb;
+
+#ifndef HAVE_STRDUP
+char *strdup _P((const char *));
+#endif
+
+void do_wait _P((int));
+
+#define EMU_NONE 0x0
+
+/* TCP emulations */
+#define EMU_CTL 0x1
+#define EMU_FTP 0x2
+#define EMU_KSH 0x3
+#define EMU_IRC 0x4
+#define EMU_REALAUDIO 0x5
+#define EMU_RLOGIN 0x6
+#define EMU_IDENT 0x7
+#define EMU_RSH 0x8
+
+#define EMU_NOCONNECT 0x10 /* Don't connect */
+
+/* UDP emulations */
+#define EMU_TALK 0x1
+#define EMU_NTALK 0x2
+#define EMU_CUSEEME 0x3
+
+struct tos_t {
+ u_int16_t lport;
+ u_int16_t fport;
+ u_int8_t tos;
+ u_int8_t emu;
+};
+
+struct emu_t {
+ u_int16_t lport;
+ u_int16_t fport;
+ u_int8_t tos;
+ u_int8_t emu;
+ struct emu_t *next;
+};
+
+#ifndef CONFIG_QEMU
+extern struct emu_t *tcpemu;
+#endif
+
+extern int x_port, x_server, x_display;
+
+int show_x _P((char *, struct socket *));
+void redir_x _P((u_int32_t, int, int, int));
+void getouraddr _P((void));
+void slirp_insque _P((void *, void *));
+void slirp_remque _P((void *));
+int add_exec _P((struct ex_list **, int, char *, int, int));
+int slirp_openpty _P((int *, int *));
+int fork_exec(struct socket *so, const char *ex, int do_pty);
+void snooze_hup _P((int));
+void snooze _P((void));
+void relay _P((int));
+void add_emu _P((char *));
+void u_sleep _P((int));
+void fd_nonblock _P((int));
+void fd_block _P((int));
+int rsh_exec _P((struct socket *, struct socket *, char *, char *, char *));
+
+#endif
diff --git a/slirp/sbuf.c b/slirp/sbuf.c
new file mode 100644
index 0000000..5375414
--- /dev/null
+++ b/slirp/sbuf.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+static void sbappendsb(struct sbuf *sb, struct mbuf *m);
+
+/* Done as a macro in socket.h */
+/* int
+ * sbspace(struct sockbuff *sb)
+ * {
+ * return SB_DATALEN - sb->sb_cc;
+ * }
+ */
+
+void
+sbfree(struct sbuf *sb)
+{
+ free(sb->sb_data);
+}
+
+void
+sbdrop(struct sbuf *sb, int num)
+{
+ /*
+ * We can only drop how much we have
+ * This should never succeed
+ */
+ if(num > sb->sb_cc)
+ num = sb->sb_cc;
+ sb->sb_cc -= num;
+ sb->sb_rptr += num;
+ if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_rptr -= sb->sb_datalen;
+
+}
+
+void
+sbreserve(struct sbuf *sb, int size)
+{
+ if (sb->sb_data) {
+ /* Already alloced, realloc if necessary */
+ if (sb->sb_datalen != size) {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+ } else {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+}
+
+/*
+ * Try and write() to the socket, whatever doesn't get written
+ * append to the buffer... for a host with a fast net connection,
+ * this prevents an unnecessary copy of the data
+ * (the socket is non-blocking, so we won't hang)
+ */
+void
+sbappend(struct socket *so, struct mbuf *m)
+{
+ int ret = 0;
+
+ DEBUG_CALL("sbappend");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m->m_len = %d", m->m_len);
+
+ /* Shouldn't happen, but... e.g. foreign host closes connection */
+ if (m->m_len <= 0) {
+ m_free(m);
+ return;
+ }
+
+ /*
+ * If there is urgent data, call sosendoob
+ * if not all was sent, sowrite will take care of the rest
+ * (The rest of this function is just an optimisation)
+ */
+ if (so->so_urgc) {
+ sbappendsb(&so->so_rcv, m);
+ m_free(m);
+ sosendoob(so);
+ return;
+ }
+
+ /*
+ * We only write if there's nothing in the buffer,
+ * ottherwise it'll arrive out of order, and hence corrupt
+ */
+ if (!so->so_rcv.sb_cc)
+ ret = slirp_send(so, m->m_data, m->m_len, 0);
+
+ if (ret <= 0) {
+ /*
+ * Nothing was written
+ * It's possible that the socket has closed, but
+ * we don't need to check because if it has closed,
+ * it will be detected in the normal way by soread()
+ */
+ sbappendsb(&so->so_rcv, m);
+ } else if (ret != m->m_len) {
+ /*
+ * Something was written, but not everything..
+ * sbappendsb the rest
+ */
+ m->m_len -= ret;
+ m->m_data += ret;
+ sbappendsb(&so->so_rcv, m);
+ } /* else */
+ /* Whatever happened, we free the mbuf */
+ m_free(m);
+}
+
+/*
+ * Copy the data from m into sb
+ * The caller is responsible to make sure there's enough room
+ */
+static void
+sbappendsb(struct sbuf *sb, struct mbuf *m)
+{
+ int len, n, nn;
+
+ len = m->m_len;
+
+ if (sb->sb_wptr < sb->sb_rptr) {
+ n = sb->sb_rptr - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ } else {
+ /* Do the right edge first */
+ n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ len -= n;
+ if (len) {
+ /* Now the left edge */
+ nn = sb->sb_rptr - sb->sb_data;
+ if (nn > len) nn = len;
+ memcpy(sb->sb_data,m->m_data+n,nn);
+ n += nn;
+ }
+ }
+
+ sb->sb_cc += n;
+ sb->sb_wptr += n;
+ if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_wptr -= sb->sb_datalen;
+}
+
+/*
+ * Copy data from sbuf to a normal, straight buffer
+ * Don't update the sbuf rptr, this will be
+ * done in sbdrop when the data is acked
+ */
+void
+sbcopy(struct sbuf *sb, int off, int len, char *to)
+{
+ char *from;
+
+ from = sb->sb_rptr + off;
+ if (from >= sb->sb_data + sb->sb_datalen)
+ from -= sb->sb_datalen;
+
+ if (from < sb->sb_wptr) {
+ if (len > sb->sb_cc) len = sb->sb_cc;
+ memcpy(to,from,len);
+ } else {
+ /* re-use off */
+ off = (sb->sb_data + sb->sb_datalen) - from;
+ if (off > len) off = len;
+ memcpy(to,from,off);
+ len -= off;
+ if (len)
+ memcpy(to+off,sb->sb_data,len);
+ }
+}
diff --git a/slirp/sbuf.h b/slirp/sbuf.h
new file mode 100644
index 0000000..a4f1036
--- /dev/null
+++ b/slirp/sbuf.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SBUF_H_
+#define _SBUF_H_
+
+#define sbflush(sb) sbdrop((sb),(sb)->sb_cc)
+#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc)
+
+struct sbuf {
+ u_int sb_cc; /* actual chars in buffer */
+ u_int sb_datalen; /* Length of data */
+ char *sb_wptr; /* write pointer. points to where the next
+ * bytes should be written in the sbuf */
+ char *sb_rptr; /* read pointer. points to where the next
+ * byte should be read from the sbuf */
+ char *sb_data; /* Actual data */
+};
+
+void sbfree _P((struct sbuf *));
+void sbdrop _P((struct sbuf *, int));
+void sbreserve _P((struct sbuf *, int));
+void sbappend _P((struct socket *, struct mbuf *));
+void sbcopy _P((struct sbuf *, int, int, char *));
+
+#endif
diff --git a/slirp/slirp.c b/slirp/slirp.c
new file mode 100644
index 0000000..30d4ee2
--- /dev/null
+++ b/slirp/slirp.c
@@ -0,0 +1,1086 @@
+/*
+ * libslirp glue
+ *
+ * Copyright (c) 2004-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "slirp.h"
+#include "hw/hw.h"
+
+/* host address */
+struct in_addr our_addr;
+/* host dns address */
+struct in_addr dns_addr;
+/* host loopback address */
+struct in_addr loopback_addr;
+
+/* address for slirp virtual addresses */
+struct in_addr special_addr;
+/* virtual address alias for host */
+struct in_addr alias_addr;
+
+static const uint8_t special_ethaddr[6] = {
+ 0x52, 0x54, 0x00, 0x12, 0x35, 0x00
+};
+
+/* ARP cache for the guest IP addresses (XXX: allow many entries) */
+uint8_t client_ethaddr[6];
+static struct in_addr client_ipaddr;
+
+static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 };
+
+const char *slirp_special_ip = CTL_SPECIAL;
+int slirp_restrict;
+static int do_slowtimo;
+int link_up;
+struct timeval tt;
+FILE *lfd;
+struct ex_list *exec_list;
+
+/* XXX: suppress those select globals */
+fd_set *global_readfds, *global_writefds, *global_xfds;
+
+char slirp_hostname[33];
+
+#ifdef _WIN32
+
+static int get_dns_addr(struct in_addr *pdns_addr)
+{
+ FIXED_INFO *FixedInfo=NULL;
+ ULONG BufLen;
+ DWORD ret;
+ IP_ADDR_STRING *pIPAddr;
+ struct in_addr tmp_addr;
+
+ FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
+ BufLen = sizeof(FIXED_INFO);
+
+ if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ FixedInfo = GlobalAlloc(GPTR, BufLen);
+ }
+
+ if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
+ printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return -1;
+ }
+
+ pIPAddr = &(FixedInfo->DnsServerList);
+ inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
+ *pdns_addr = tmp_addr;
+#if 0
+ printf( "DNS Servers:\n" );
+ printf( "DNS Addr:%s\n", pIPAddr->IpAddress.String );
+
+ pIPAddr = FixedInfo -> DnsServerList.Next;
+ while ( pIPAddr ) {
+ printf( "DNS Addr:%s\n", pIPAddr ->IpAddress.String );
+ pIPAddr = pIPAddr ->Next;
+ }
+#endif
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return 0;
+}
+
+#else
+
+static int get_dns_addr(struct in_addr *pdns_addr)
+{
+ char buff[512];
+ char buff2[257];
+ FILE *f;
+ int found = 0;
+ struct in_addr tmp_addr;
+
+ f = fopen("/etc/resolv.conf", "r");
+ if (!f)
+ return -1;
+
+#ifdef DEBUG
+ lprint("IP address of your DNS(s): ");
+#endif
+ while (fgets(buff, 512, f) != NULL) {
+ if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
+ if (!inet_aton(buff2, &tmp_addr))
+ continue;
+ if (tmp_addr.s_addr == loopback_addr.s_addr)
+ tmp_addr = our_addr;
+ /* If it's the first one, set it to dns_addr */
+ if (!found)
+ *pdns_addr = tmp_addr;
+#ifdef DEBUG
+ else
+ lprint(", ");
+#endif
+ if (++found > 3) {
+#ifdef DEBUG
+ lprint("(more)");
+#endif
+ break;
+ }
+#ifdef DEBUG
+ else
+ lprint("%s", inet_ntoa(tmp_addr));
+#endif
+ }
+ }
+ fclose(f);
+ if (!found)
+ return -1;
+ return 0;
+}
+
+#endif
+
+#ifdef _WIN32
+static void slirp_cleanup(void)
+{
+ WSACleanup();
+}
+#endif
+
+static void slirp_state_save(QEMUFile *f, void *opaque);
+static int slirp_state_load(QEMUFile *f, void *opaque, int version_id);
+
+void slirp_init(int restricted, const char *special_ip)
+{
+ // debug_init("/tmp/slirp.log", DEBUG_DEFAULT);
+
+#ifdef _WIN32
+ {
+ WSADATA Data;
+ WSAStartup(MAKEWORD(2,0), &Data);
+ atexit(slirp_cleanup);
+ }
+#endif
+
+ link_up = 1;
+ slirp_restrict = restricted;
+
+ if_init();
+ ip_init();
+
+ /* Initialise mbufs *after* setting the MTU */
+ m_init();
+
+ /* set default addresses */
+ inet_aton("127.0.0.1", &loopback_addr);
+
+ if (get_dns_addr(&dns_addr) < 0) {
+ dns_addr = loopback_addr;
+ fprintf (stderr, "Warning: No DNS servers found\n");
+ }
+
+ if (special_ip)
+ slirp_special_ip = special_ip;
+
+ inet_aton(slirp_special_ip, &special_addr);
+ alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS);
+ getouraddr();
+ register_savevm("slirp", 0, 1, slirp_state_save, slirp_state_load, NULL);
+}
+
+#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
+
+/*
+ * curtime kept to an accuracy of 1ms
+ */
+#ifdef _WIN32
+static void updtime(void)
+{
+ struct _timeb tb;
+
+ _ftime(&tb);
+ curtime = (u_int)tb.time * (u_int)1000;
+ curtime += (u_int)tb.millitm;
+}
+#else
+static void updtime(void)
+{
+ gettimeofday(&tt, NULL);
+
+ curtime = (u_int)tt.tv_sec * (u_int)1000;
+ curtime += (u_int)tt.tv_usec / (u_int)1000;
+
+ if ((tt.tv_usec % 1000) >= 500)
+ curtime++;
+}
+#endif
+
+void slirp_select_fill(int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+ struct socket *so, *so_next;
+ struct timeval timeout;
+ int nfds;
+ int tmp_time;
+
+ /* fail safe */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+
+ nfds = *pnfds;
+ /*
+ * First, TCP sockets
+ */
+ do_slowtimo = 0;
+ if (link_up) {
+ /*
+ * *_slowtimo needs calling if there are IP fragments
+ * in the fragment queue, or there are TCP connections active
+ */
+ do_slowtimo = ((tcb.so_next != &tcb) ||
+ (&ipq.ip_link != ipq.ip_link.next));
+
+ for (so = tcb.so_next; so != &tcb; so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if we need a tcp_fasttimo
+ */
+ if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
+ time_fasttimo = curtime; /* Flag when we want a fasttimo */
+
+ /*
+ * NOFDREF can include still connecting to local-host,
+ * newly socreated() sockets etc. Don't want to select these.
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Set for reading sockets which are accepting
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing sockets which are connecting
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing if we are connected, can send more, and
+ * we have something to send
+ */
+ if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ }
+
+ /*
+ * Set for reading (and urgent data) if we are connected, can
+ * receive more, and we have room for it XXX /2 ?
+ */
+ if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
+ FD_SET(so->s, readfds);
+ FD_SET(so->s, xfds);
+ UPD_NFDS(so->s);
+ }
+ }
+
+ /*
+ * UDP sockets
+ */
+ for (so = udb.so_next; so != &udb; so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ udp_detach(so);
+ continue;
+ } else
+ do_slowtimo = 1; /* Let socket expire */
+ }
+
+ /*
+ * When UDP packets are received from over the
+ * link, they're sendto()'d straight away, so
+ * no need for setting for writing
+ * Limit the number of packets queued by this session
+ * to 4. Note that even though we try and limit this
+ * to 4 packets, the session could have more queued
+ * if the packets needed to be fragmented
+ * (XXX <= 4 ?)
+ */
+ if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ }
+ }
+ }
+
+ /*
+ * Setup timeout to use minimum CPU usage, especially when idle
+ */
+
+ /*
+ * First, see the timeout needed by *timo
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = -1;
+ /*
+ * If a slowtimo is needed, set timeout to 500ms from the last
+ * slow timeout. If a fast timeout is needed, set timeout within
+ * 200ms of when it was requested.
+ */
+ if (do_slowtimo) {
+ /* XXX + 10000 because some select()'s aren't that accurate */
+ timeout.tv_usec = ((500 - (curtime - last_slowtimo)) * 1000) + 10000;
+ if (timeout.tv_usec < 0)
+ timeout.tv_usec = 0;
+ else if (timeout.tv_usec > 510000)
+ timeout.tv_usec = 510000;
+
+ /* Can only fasttimo if we also slowtimo */
+ if (time_fasttimo) {
+ tmp_time = (200 - (curtime - time_fasttimo)) * 1000;
+ if (tmp_time < 0)
+ tmp_time = 0;
+
+ /* Choose the smallest of the 2 */
+ if (tmp_time < timeout.tv_usec)
+ timeout.tv_usec = (u_int)tmp_time;
+ }
+ }
+ *pnfds = nfds;
+}
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+ struct socket *so, *so_next;
+ int ret;
+
+ global_readfds = readfds;
+ global_writefds = writefds;
+ global_xfds = xfds;
+
+ /* Update time */
+ updtime();
+
+ /*
+ * See if anything has timed out
+ */
+ if (link_up) {
+ if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+ tcp_fasttimo();
+ time_fasttimo = 0;
+ }
+ if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+ ip_slowtimo();
+ tcp_slowtimo();
+ last_slowtimo = curtime;
+ }
+ }
+
+ /*
+ * Check sockets
+ */
+ if (link_up) {
+ /*
+ * Check TCP sockets
+ */
+ for (so = tcb.so_next; so != &tcb; so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * FD_ISSET is meaningless on these sockets
+ * (and they can crash the program)
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Check for URG data
+ * This will soread as well, so no need to
+ * test for readfds below if this succeeds
+ */
+ if (FD_ISSET(so->s, xfds))
+ sorecvoob(so);
+ /*
+ * Check sockets for reading
+ */
+ else if (FD_ISSET(so->s, readfds)) {
+ /*
+ * Check for incoming connections
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ tcp_connect(so);
+ continue;
+ } /* else */
+ ret = soread(so);
+
+ /* Output it if we read something */
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+ }
+
+ /*
+ * Check sockets for writing
+ */
+ if (FD_ISSET(so->s, writefds)) {
+ /*
+ * Check for non-blocking, still-connecting sockets
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ /* Connected */
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ ret = send(so->s, (const void *) &ret, 0, 0);
+ if (ret < 0) {
+ /* XXXXX Must fix, zero bytes is a NOP */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+ }
+ /* else so->so_state &= ~SS_ISFCONNECTING; */
+
+ /*
+ * Continue tcp_input
+ */
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ /* continue; */
+ } else
+ ret = sowrite(so);
+ /*
+ * XXXXX If we wrote something (a lot), there
+ * could be a need for a window update.
+ * In the worst case, the remote will send
+ * a window probe to get things going again
+ */
+ }
+
+ /*
+ * Probe a still-connecting, non-blocking socket
+ * to check if it's still alive
+ */
+#ifdef PROBE_CONN
+ if (so->so_state & SS_ISFCONNECTING) {
+ ret = recv(so->s, (char *)&ret, 0,0);
+
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue; /* Still connecting, continue */
+
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+
+ /* tcp_input will take care of it */
+ } else {
+ ret = send(so->s, &ret, 0,0);
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+ } else
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ }
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
+ } /* SS_ISFCONNECTING */
+#endif
+ }
+
+ /*
+ * Now UDP sockets.
+ * Incoming packets are sent straight away, they're not buffered.
+ * Incoming UDP data isn't buffered either.
+ */
+ for (so = udb.so_next; so != &udb; so = so_next) {
+ so_next = so->so_next;
+
+ if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+ sorecvfrom(so);
+ }
+ }
+ }
+
+ /*
+ * See if we can start outputting
+ */
+ if (if_queued && link_up)
+ if_start();
+
+ /* clear global file descriptor sets.
+ * these reside on the stack in vl.c
+ * so they're unusable if we're not in
+ * slirp_select_fill or slirp_select_poll.
+ */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+}
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+
+struct ethhdr
+{
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+};
+
+static void arp_input(const uint8_t *pkt, int pkt_len)
+{
+ struct ethhdr *eh = (struct ethhdr *)pkt;
+ struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
+ uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
+ struct ethhdr *reh = (struct ethhdr *)arp_reply;
+ struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
+ int ar_op;
+ struct ex_list *ex_ptr;
+
+ ar_op = ntohs(ah->ar_op);
+ switch(ar_op) {
+ case ARPOP_REQUEST:
+ if (!memcmp(ah->ar_tip, &special_addr, 3)) {
+ if (ah->ar_tip[3] == CTL_DNS || ah->ar_tip[3] == CTL_ALIAS)
+ goto arp_ok;
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_addr == ah->ar_tip[3])
+ goto arp_ok;
+ }
+ return;
+ arp_ok:
+ /* XXX: make an ARP request to have the client address */
+ memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
+
+ /* ARP request for alias/dns mac address */
+ memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
+ reh->h_source[5] = ah->ar_tip[3];
+ reh->h_proto = htons(ETH_P_ARP);
+
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REPLY);
+ memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
+ memcpy(rah->ar_sip, ah->ar_tip, 4);
+ memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
+ memcpy(rah->ar_tip, ah->ar_sip, 4);
+ slirp_output(arp_reply, sizeof(arp_reply));
+ }
+ break;
+ case ARPOP_REPLY:
+ /* reply to request of client mac address ? */
+ if (!memcmp(client_ethaddr, zero_ethaddr, ETH_ALEN) &&
+ !memcmp(ah->ar_sip, &client_ipaddr.s_addr, 4)) {
+ memcpy(client_ethaddr, ah->ar_sha, ETH_ALEN);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void slirp_input(const uint8_t *pkt, int pkt_len)
+{
+ struct mbuf *m;
+ int proto;
+
+ if (pkt_len < ETH_HLEN)
+ return;
+
+ proto = ntohs(*(uint16_t *)(pkt + 12));
+ switch(proto) {
+ case ETH_P_ARP:
+ arp_input(pkt, pkt_len);
+ break;
+ case ETH_P_IP:
+ m = m_get();
+ if (!m)
+ return;
+ /* Note: we add to align the IP header */
+ if (M_FREEROOM(m) < pkt_len + 2) {
+ m_inc(m, pkt_len + 2);
+ }
+ m->m_len = pkt_len + 2;
+ memcpy(m->m_data + 2, pkt, pkt_len);
+
+ m->m_data += 2 + ETH_HLEN;
+ m->m_len -= 2 + ETH_HLEN;
+
+ ip_input(m);
+ break;
+ default:
+ break;
+ }
+}
+
+/* output the IP packet to the ethernet device */
+void if_encap(const uint8_t *ip_data, int ip_data_len)
+{
+ uint8_t buf[1600];
+ struct ethhdr *eh = (struct ethhdr *)buf;
+
+ if (ip_data_len + ETH_HLEN > sizeof(buf))
+ return;
+
+ if (!memcmp(client_ethaddr, zero_ethaddr, ETH_ALEN)) {
+ uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
+ struct ethhdr *reh = (struct ethhdr *)arp_req;
+ struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
+ const struct ip *iph = (const struct ip *)ip_data;
+
+ /* If the client addr is not known, there is no point in
+ sending the packet to it. Normally the sender should have
+ done an ARP request to get its MAC address. Here we do it
+ in place of sending the packet and we hope that the sender
+ will retry sending its packet. */
+ memset(reh->h_dest, 0xff, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
+ reh->h_source[5] = CTL_ALIAS;
+ reh->h_proto = htons(ETH_P_ARP);
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REQUEST);
+ /* source hw addr */
+ memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 1);
+ rah->ar_sha[5] = CTL_ALIAS;
+ /* source IP */
+ memcpy(rah->ar_sip, &alias_addr, 4);
+ /* target hw addr (none) */
+ memset(rah->ar_tha, 0, ETH_ALEN);
+ /* target IP */
+ memcpy(rah->ar_tip, &iph->ip_dst, 4);
+ client_ipaddr = iph->ip_dst;
+ slirp_output(arp_req, sizeof(arp_req));
+ } else {
+ memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
+ memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
+ /* XXX: not correct */
+ eh->h_source[5] = CTL_ALIAS;
+ eh->h_proto = htons(ETH_P_IP);
+ memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
+ slirp_output(buf, ip_data_len + ETH_HLEN);
+ }
+}
+
+static void _slirp_redir_loop(void (*func)(void *opaque, int is_udp,
+ struct in_addr *laddr, u_int lport,
+ struct in_addr *faddr, u_int fport),
+ void *opaque, int is_udp)
+{
+ struct socket *head = (is_udp ? &udb : &tcb);
+ struct socket *so;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ func(opaque, is_udp,
+ &so->so_laddr, ntohs(so->so_lport),
+ &so->so_faddr, ntohs(so->so_fport));
+ }
+}
+
+void slirp_redir_loop(void (*func)(void *opaque, int is_udp,
+ struct in_addr *laddr, u_int lport,
+ struct in_addr *faddr, u_int fport),
+ void *opaque)
+{
+ _slirp_redir_loop(func, opaque, 0);
+ _slirp_redir_loop(func, opaque, 1);
+}
+
+/* Unlistens a redirection
+ *
+ * Return value: number of redirs removed */
+int slirp_redir_rm(int is_udp, int host_port)
+{
+ struct socket *so;
+ struct socket *head = (is_udp ? &udb : &tcb);
+ int fport = htons(host_port);
+ int n = 0;
+
+ loop_again:
+ for (so = head->so_next; so != head; so = so->so_next) {
+ if (so->so_fport == fport) {
+ close(so->s);
+ sofree(so);
+ n++;
+ goto loop_again;
+ }
+ }
+
+ return n;
+}
+
+int slirp_redir(int is_udp, int host_port,
+ struct in_addr guest_addr, int guest_port)
+{
+ if (is_udp) {
+ if (!udp_listen(htons(host_port), guest_addr.s_addr,
+ htons(guest_port), 0))
+ return -1;
+ } else {
+ if (!solisten(htons(host_port), guest_addr.s_addr,
+ htons(guest_port), 0))
+ return -1;
+ }
+ return 0;
+}
+
+int slirp_add_exec(int do_pty, const void *args, int addr_low_byte,
+ int guest_port)
+{
+ return add_exec(&exec_list, do_pty, (char *)args,
+ addr_low_byte, htons(guest_port));
+}
+
+ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
+{
+ if (so->s == -1 && so->extra) {
+ qemu_chr_write(so->extra, buf, len);
+ return len;
+ }
+
+ return send(so->s, buf, len, flags);
+}
+
+static struct socket *slirp_find_ctl_socket(int addr_low_byte, int guest_port)
+{
+ struct socket *so;
+
+ for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) ==
+ special_addr.s_addr
+ && (ntohl(so->so_faddr.s_addr) & 0xff) ==
+ addr_low_byte
+ && htons(so->so_fport) == guest_port)
+ return so;
+ }
+
+ return NULL;
+}
+
+size_t slirp_socket_can_recv(int addr_low_byte, int guest_port)
+{
+ struct iovec iov[2];
+ struct socket *so;
+
+ if (!link_up)
+ return 0;
+
+ so = slirp_find_ctl_socket(addr_low_byte, guest_port);
+
+ if (!so || so->so_state & SS_NOFDREF)
+ return 0;
+
+ if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
+ return 0;
+
+ return sopreprbuf(so, iov, NULL);
+}
+
+void slirp_socket_recv(int addr_low_byte, int guest_port, const uint8_t *buf,
+ int size)
+{
+ int ret;
+ struct socket *so = slirp_find_ctl_socket(addr_low_byte, guest_port);
+
+ if (!so)
+ return;
+
+ ret = soreadbuf(so, (const char *)buf, size);
+
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+}
+
+static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp)
+{
+ int i;
+
+ qemu_put_sbe16(f, tp->t_state);
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ qemu_put_sbe16(f, tp->t_timer[i]);
+ qemu_put_sbe16(f, tp->t_rxtshift);
+ qemu_put_sbe16(f, tp->t_rxtcur);
+ qemu_put_sbe16(f, tp->t_dupacks);
+ qemu_put_be16(f, tp->t_maxseg);
+ qemu_put_sbyte(f, tp->t_force);
+ qemu_put_be16(f, tp->t_flags);
+ qemu_put_be32(f, tp->snd_una);
+ qemu_put_be32(f, tp->snd_nxt);
+ qemu_put_be32(f, tp->snd_up);
+ qemu_put_be32(f, tp->snd_wl1);
+ qemu_put_be32(f, tp->snd_wl2);
+ qemu_put_be32(f, tp->iss);
+ qemu_put_be32(f, tp->snd_wnd);
+ qemu_put_be32(f, tp->rcv_wnd);
+ qemu_put_be32(f, tp->rcv_nxt);
+ qemu_put_be32(f, tp->rcv_up);
+ qemu_put_be32(f, tp->irs);
+ qemu_put_be32(f, tp->rcv_adv);
+ qemu_put_be32(f, tp->snd_max);
+ qemu_put_be32(f, tp->snd_cwnd);
+ qemu_put_be32(f, tp->snd_ssthresh);
+ qemu_put_sbe16(f, tp->t_idle);
+ qemu_put_sbe16(f, tp->t_rtt);
+ qemu_put_be32(f, tp->t_rtseq);
+ qemu_put_sbe16(f, tp->t_srtt);
+ qemu_put_sbe16(f, tp->t_rttvar);
+ qemu_put_be16(f, tp->t_rttmin);
+ qemu_put_be32(f, tp->max_sndwnd);
+ qemu_put_byte(f, tp->t_oobflags);
+ qemu_put_byte(f, tp->t_iobc);
+ qemu_put_sbe16(f, tp->t_softerror);
+ qemu_put_byte(f, tp->snd_scale);
+ qemu_put_byte(f, tp->rcv_scale);
+ qemu_put_byte(f, tp->request_r_scale);
+ qemu_put_byte(f, tp->requested_s_scale);
+ qemu_put_be32(f, tp->ts_recent);
+ qemu_put_be32(f, tp->ts_recent_age);
+ qemu_put_be32(f, tp->last_ack_sent);
+}
+
+static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
+{
+ uint32_t off;
+
+ qemu_put_be32(f, sbuf->sb_cc);
+ qemu_put_be32(f, sbuf->sb_datalen);
+ off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data);
+ qemu_put_sbe32(f, off);
+ off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data);
+ qemu_put_sbe32(f, off);
+ qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
+}
+
+static void slirp_socket_save(QEMUFile *f, struct socket *so)
+{
+ qemu_put_be32(f, so->so_urgc);
+ qemu_put_be32(f, so->so_faddr.s_addr);
+ qemu_put_be32(f, so->so_laddr.s_addr);
+ qemu_put_be16(f, so->so_fport);
+ qemu_put_be16(f, so->so_lport);
+ qemu_put_byte(f, so->so_iptos);
+ qemu_put_byte(f, so->so_emu);
+ qemu_put_byte(f, so->so_type);
+ qemu_put_be32(f, so->so_state);
+ slirp_sbuf_save(f, &so->so_rcv);
+ slirp_sbuf_save(f, &so->so_snd);
+ slirp_tcp_save(f, so->so_tcpcb);
+}
+
+static void slirp_state_save(QEMUFile *f, void *opaque)
+{
+ struct ex_list *ex_ptr;
+
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_pty == 3) {
+ struct socket *so;
+ so = slirp_find_ctl_socket(ex_ptr->ex_addr, ntohs(ex_ptr->ex_fport));
+ if (!so)
+ continue;
+
+ qemu_put_byte(f, 42);
+ slirp_socket_save(f, so);
+ }
+ qemu_put_byte(f, 0);
+}
+
+static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp)
+{
+ int i;
+
+ tp->t_state = qemu_get_sbe16(f);
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = qemu_get_sbe16(f);
+ tp->t_rxtshift = qemu_get_sbe16(f);
+ tp->t_rxtcur = qemu_get_sbe16(f);
+ tp->t_dupacks = qemu_get_sbe16(f);
+ tp->t_maxseg = qemu_get_be16(f);
+ tp->t_force = qemu_get_sbyte(f);
+ tp->t_flags = qemu_get_be16(f);
+ tp->snd_una = qemu_get_be32(f);
+ tp->snd_nxt = qemu_get_be32(f);
+ tp->snd_up = qemu_get_be32(f);
+ tp->snd_wl1 = qemu_get_be32(f);
+ tp->snd_wl2 = qemu_get_be32(f);
+ tp->iss = qemu_get_be32(f);
+ tp->snd_wnd = qemu_get_be32(f);
+ tp->rcv_wnd = qemu_get_be32(f);
+ tp->rcv_nxt = qemu_get_be32(f);
+ tp->rcv_up = qemu_get_be32(f);
+ tp->irs = qemu_get_be32(f);
+ tp->rcv_adv = qemu_get_be32(f);
+ tp->snd_max = qemu_get_be32(f);
+ tp->snd_cwnd = qemu_get_be32(f);
+ tp->snd_ssthresh = qemu_get_be32(f);
+ tp->t_idle = qemu_get_sbe16(f);
+ tp->t_rtt = qemu_get_sbe16(f);
+ tp->t_rtseq = qemu_get_be32(f);
+ tp->t_srtt = qemu_get_sbe16(f);
+ tp->t_rttvar = qemu_get_sbe16(f);
+ tp->t_rttmin = qemu_get_be16(f);
+ tp->max_sndwnd = qemu_get_be32(f);
+ tp->t_oobflags = qemu_get_byte(f);
+ tp->t_iobc = qemu_get_byte(f);
+ tp->t_softerror = qemu_get_sbe16(f);
+ tp->snd_scale = qemu_get_byte(f);
+ tp->rcv_scale = qemu_get_byte(f);
+ tp->request_r_scale = qemu_get_byte(f);
+ tp->requested_s_scale = qemu_get_byte(f);
+ tp->ts_recent = qemu_get_be32(f);
+ tp->ts_recent_age = qemu_get_be32(f);
+ tp->last_ack_sent = qemu_get_be32(f);
+ tcp_template(tp);
+}
+
+static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
+{
+ uint32_t off, sb_cc, sb_datalen;
+
+ sb_cc = qemu_get_be32(f);
+ sb_datalen = qemu_get_be32(f);
+
+ sbreserve(sbuf, sb_datalen);
+
+ if (sbuf->sb_datalen != sb_datalen)
+ return -ENOMEM;
+
+ sbuf->sb_cc = sb_cc;
+
+ off = qemu_get_sbe32(f);
+ sbuf->sb_wptr = sbuf->sb_data + off;
+ off = qemu_get_sbe32(f);
+ sbuf->sb_rptr = sbuf->sb_data + off;
+ qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
+
+ return 0;
+}
+
+static int slirp_socket_load(QEMUFile *f, struct socket *so)
+{
+ if (tcp_attach(so) < 0)
+ return -ENOMEM;
+
+ so->so_urgc = qemu_get_be32(f);
+ so->so_faddr.s_addr = qemu_get_be32(f);
+ so->so_laddr.s_addr = qemu_get_be32(f);
+ so->so_fport = qemu_get_be16(f);
+ so->so_lport = qemu_get_be16(f);
+ so->so_iptos = qemu_get_byte(f);
+ so->so_emu = qemu_get_byte(f);
+ so->so_type = qemu_get_byte(f);
+ so->so_state = qemu_get_be32(f);
+ if (slirp_sbuf_load(f, &so->so_rcv) < 0)
+ return -ENOMEM;
+ if (slirp_sbuf_load(f, &so->so_snd) < 0)
+ return -ENOMEM;
+ slirp_tcp_load(f, so->so_tcpcb);
+
+ return 0;
+}
+
+static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
+{
+ struct ex_list *ex_ptr;
+ int r;
+
+ while ((r = qemu_get_byte(f))) {
+ int ret;
+ struct socket *so = socreate();
+
+ if (!so)
+ return -ENOMEM;
+
+ ret = slirp_socket_load(f, so);
+
+ if (ret < 0)
+ return ret;
+
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) != special_addr.s_addr)
+ return -EINVAL;
+
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_pty == 3 &&
+ (ntohl(so->so_faddr.s_addr) & 0xff) == ex_ptr->ex_addr &&
+ so->so_fport == ex_ptr->ex_fport)
+ break;
+
+ if (!ex_ptr)
+ return -EINVAL;
+
+ so->extra = (void *)ex_ptr->ex_exec;
+ }
+
+ return 0;
+}
diff --git a/slirp/slirp.h b/slirp/slirp.h
new file mode 100644
index 0000000..8309fe0
--- /dev/null
+++ b/slirp/slirp.h
@@ -0,0 +1,333 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define CONFIG_QEMU
+
+//#define DEBUG 1
+
+// Uncomment the following line to enable SLIRP statistics printing in Qemu
+//#define LOG_ENABLED
+
+#ifdef LOG_ENABLED
+#define STAT(expr) expr
+#else
+#define STAT(expr) do { } while(0)
+#endif
+
+#ifndef CONFIG_QEMU
+#include "version.h"
+#endif
+#include "config-host.h"
+#include "slirp_config.h"
+
+#ifdef _WIN32
+# include <inttypes.h>
+
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+typedef char *caddr_t;
+
+# include <windows.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <sys/timeb.h>
+# include <iphlpapi.h>
+
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# define EINPROGRESS WSAEINPROGRESS
+# define ENOTCONN WSAENOTCONN
+# define EHOSTUNREACH WSAEHOSTUNREACH
+# define ENETUNREACH WSAENETUNREACH
+# define ECONNREFUSED WSAECONNREFUSED
+#else
+# define ioctlsocket ioctl
+# define closesocket(s) close(s)
+# define O_BINARY 0
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+#include <sys/time.h>
+
+#ifdef NEED_TYPEDEFS
+typedef char int8_t;
+typedef unsigned char u_int8_t;
+
+# if SIZEOF_SHORT == 2
+ typedef short int16_t;
+ typedef unsigned short u_int16_t;
+# else
+# if SIZEOF_INT == 2
+ typedef int int16_t;
+ typedef unsigned int u_int16_t;
+# else
+ #error Cannot find a type with sizeof() == 2
+# endif
+# endif
+
+# if SIZEOF_SHORT == 4
+ typedef short int32_t;
+ typedef unsigned short u_int32_t;
+# else
+# if SIZEOF_INT == 4
+ typedef int int32_t;
+ typedef unsigned int u_int32_t;
+# else
+ #error Cannot find a type with sizeof() == 4
+# endif
+# endif
+#endif /* NEED_TYPEDEFS */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_MEMMOVE
+#define memmove(x, y, z) bcopy(y, x, z)
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/uio.h>
+#endif
+
+#undef _P
+#ifndef NO_PROTOTYPES
+# define _P(x) x
+#else
+# define _P(x) ()
+#endif
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef GETTIMEOFDAY_ONE_ARG
+#define gettimeofday(x, y) gettimeofday(x)
+#endif
+
+/* Systems lacking strdup() definition in <string.h>. */
+#if defined(ultrix)
+char *strdup _P((const char *));
+#endif
+
+/* Systems lacking malloc() definition in <stdlib.h>. */
+#if defined(ultrix) || defined(hcx)
+void *malloc _P((size_t arg));
+void free _P((void *ptr));
+#endif
+
+#ifndef HAVE_INET_ATON
+int inet_aton _P((const char *cp, struct in_addr *ia));
+#endif
+
+#include <fcntl.h>
+#ifndef NO_UNIX_SOCKETS
+#include <sys/un.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H)
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#ifdef USE_PPP
+#include <ppp/slirppp.h>
+#endif
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <sys/stat.h>
+
+/* Avoid conflicting with the libc insque() and remque(), which
+ have different prototypes. */
+#define insque slirp_insque
+#define remque slirp_remque
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#endif
+
+#include "debug.h"
+
+#include "ip.h"
+#include "tcp.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "udp.h"
+#include "icmp_var.h"
+#include "mbuf.h"
+#include "sbuf.h"
+#include "socket.h"
+#include "if.h"
+#include "main.h"
+#include "misc.h"
+#include "ctl.h"
+#ifdef USE_PPP
+#include "ppp/pppd.h"
+#include "ppp/ppp.h"
+#endif
+
+#include "bootp.h"
+#include "tftp.h"
+#include "libslirp.h"
+
+extern struct ttys *ttys_unit[MAX_INTERFACES];
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef FULL_BOLT
+void if_start _P((void));
+#else
+void if_start _P((struct ttys *));
+#endif
+
+#ifdef BAD_SPRINTF
+# define vsprintf vsprintf_len
+# define sprintf sprintf_len
+ extern int vsprintf_len _P((char *, const char *, va_list));
+ extern int sprintf_len _P((char *, const char *, ...));
+#endif
+
+#ifdef DECLARE_SPRINTF
+# ifndef BAD_SPRINTF
+ extern int vsprintf _P((char *, const char *, va_list));
+# endif
+ extern int vfprintf _P((FILE *, const char *, va_list));
+#endif
+
+#ifndef HAVE_STRERROR
+ extern char *strerror _P((int error));
+#endif
+
+#ifndef HAVE_INDEX
+ char *index _P((const char *, int));
+#endif
+
+#ifndef HAVE_GETHOSTID
+ long gethostid _P((void));
+#endif
+
+void lprint _P((const char *, ...));
+
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+
+#define DEFAULT_BAUD 115200
+
+#define SO_OPTIONS DO_KEEPALIVE
+#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL)
+
+/* cksum.c */
+int cksum(struct mbuf *m, int len);
+
+/* if.c */
+void if_init _P((void));
+void if_output _P((struct socket *, struct mbuf *));
+
+/* ip_input.c */
+void ip_init _P((void));
+void ip_input _P((struct mbuf *));
+void ip_slowtimo _P((void));
+void ip_stripoptions _P((register struct mbuf *, struct mbuf *));
+
+/* ip_output.c */
+int ip_output _P((struct socket *, struct mbuf *));
+
+/* tcp_input.c */
+void tcp_input _P((register struct mbuf *, int, struct socket *));
+int tcp_mss _P((register struct tcpcb *, u_int));
+
+/* tcp_output.c */
+int tcp_output _P((register struct tcpcb *));
+void tcp_setpersist _P((register struct tcpcb *));
+
+/* tcp_subr.c */
+void tcp_init _P((void));
+void tcp_template _P((struct tcpcb *));
+void tcp_respond _P((struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int));
+struct tcpcb * tcp_newtcpcb _P((struct socket *));
+struct tcpcb * tcp_close _P((register struct tcpcb *));
+void tcp_sockclosed _P((struct tcpcb *));
+int tcp_fconnect _P((struct socket *));
+void tcp_connect _P((struct socket *));
+int tcp_attach _P((struct socket *));
+u_int8_t tcp_tos _P((struct socket *));
+int tcp_emu _P((struct socket *, struct mbuf *));
+int tcp_ctl _P((struct socket *));
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
+
+#ifdef USE_PPP
+#define MIN_MRU MINMRU
+#define MAX_MRU MAXMRU
+#else
+#define MIN_MRU 128
+#define MAX_MRU 16384
+#endif
+
+#ifndef _WIN32
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifdef _WIN32
+#undef errno
+#define errno (WSAGetLastError())
+#endif
+
+#endif
diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h
new file mode 100644
index 0000000..dbc8dfd
--- /dev/null
+++ b/slirp/slirp_config.h
@@ -0,0 +1,206 @@
+/*
+ * User definable configuration options
+ */
+
+/* Undefine if you don't want talk emulation */
+#undef EMULATE_TALK
+
+/* Define if you want the connection to be probed */
+/* XXX Not working yet, so ignore this for now */
+#undef PROBE_CONN
+
+/* Define to 1 if you want KEEPALIVE timers */
+#define DO_KEEPALIVE 0
+
+/* Define to MAX interfaces you expect to use at once */
+/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
+/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
+#define MAX_INTERFACES 1
+#define MAX_PPP_INTERFACES 1
+
+/* Define if you want slirp's socket in /tmp */
+/* XXXXXX Do this in ./configure */
+#undef USE_TMPSOCKET
+
+/* Define if you want slirp to use cfsetXspeed() on the terminal */
+#undef DO_CFSETSPEED
+
+/* Define this if you want slirp to write to the tty as fast as it can */
+/* This should only be set if you are using load-balancing, slirp does a */
+/* pretty good job on single modems already, and seting this will make */
+/* interactive sessions less responsive */
+/* XXXXX Talk about having fast modem as unit 0 */
+#undef FULL_BOLT
+
+/*
+ * Define if you want slirp to use less CPU
+ * You will notice a small lag in interactive sessions, but it's not that bad
+ * Things like Netscape/ftp/etc. are completely unaffected
+ * This is mainly for sysadmins who have many slirp users
+ */
+#undef USE_LOWCPU
+
+/* Define this if your compiler doesn't like prototypes */
+#ifndef __STDC__
+#define NO_PROTOTYPES
+#endif
+
+/*********************************************************/
+/*
+ * Autoconf defined configuration options
+ * You shouldn't need to touch any of these
+ */
+
+/* Ignore this */
+#undef DUMMY_PPP
+
+/* Define if you have unistd.h */
+#define HAVE_UNISTD_H
+
+/* Define if you have stdlib.h */
+#define HAVE_STDLIB_H
+
+/* Define if you have sys/ioctl.h */
+#undef HAVE_SYS_IOCTL_H
+#ifndef _WIN32
+#define HAVE_SYS_IOCTL_H
+#endif
+
+/* Define if you have sys/filio.h */
+#undef HAVE_SYS_FILIO_H
+#ifdef __APPLE__
+#define HAVE_SYS_FILIO_H
+#endif
+
+/* Define if you have strerror */
+#define HAVE_STRERROR
+
+/* Define if you have strdup() */
+#define HAVE_STRDUP
+
+/* Define according to how time.h should be included */
+#define TIME_WITH_SYS_TIME 0
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have sys/bitypes.h */
+#undef HAVE_SYS_BITYPES_H
+
+/* Define if the machine is big endian */
+//#undef WORDS_BIGENDIAN
+
+/* Define if your sprintf returns char * instead of int */
+#undef BAD_SPRINTF
+
+/* Define if you have readv */
+#undef HAVE_READV
+
+/* Define if iovec needs to be declared */
+#undef DECLARE_IOVEC
+#ifdef _WIN32
+#define DECLARE_IOVEC
+#endif
+
+/* Define if a declaration of sprintf/fprintf is needed */
+#undef DECLARE_SPRINTF
+
+/* Define if you have a POSIX.1 sys/wait.h */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have sys/select.h */
+#undef HAVE_SYS_SELECT_H
+#ifndef _WIN32
+#define HAVE_SYS_SELECT_H
+#endif
+
+/* Define if you have strings.h */
+#define HAVE_STRING_H
+
+/* Define if you have arpa/inet.h */
+#undef HAVE_ARPA_INET_H
+#ifndef _WIN32
+#define HAVE_ARPA_INET_H
+#endif
+
+/* Define if you have sys/signal.h */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have sys/stropts.h */
+#undef HAVE_SYS_STROPTS_H
+
+/* Define to whatever your compiler thinks inline should be */
+//#define inline inline
+
+/* Define to whatever your compiler thinks const should be */
+//#define const const
+
+/* Define if your compiler doesn't like prototypes */
+#undef NO_PROTOTYPES
+
+/* Define if you don't have u_int32_t etc. typedef'd */
+#undef NEED_TYPEDEFS
+#ifdef __sun__
+#define NEED_TYPEDEFS
+#endif
+
+/* Define to sizeof(char) */
+#define SIZEOF_CHAR 1
+
+/* Define to sizeof(short) */
+#define SIZEOF_SHORT 2
+
+/* Define to sizeof(int) */
+#define SIZEOF_INT 4
+
+/* Define to sizeof(char *) */
+#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
+
+/* Define if you have random() */
+#undef HAVE_RANDOM
+
+/* Define if you have srandom() */
+#undef HAVE_SRANDOM
+
+/* Define if you have inet_aton */
+#undef HAVE_INET_ATON
+#ifndef _WIN32
+#define HAVE_INET_ATON
+#endif
+
+/* Define if you have setenv */
+#undef HAVE_SETENV
+
+/* Define if you have index() */
+#define HAVE_INDEX
+
+/* Define if you have bcmp() */
+#undef HAVE_BCMP
+
+/* Define if you have drand48 */
+#undef HAVE_DRAND48
+
+/* Define if you have memmove */
+#define HAVE_MEMMOVE
+
+/* Define if you have gethostid */
+#define HAVE_GETHOSTID
+
+/* Define if you DON'T have unix-domain sockets */
+#undef NO_UNIX_SOCKETS
+#ifdef _WIN32
+#define NO_UNIX_SOCKETS
+#endif
+
+/* Define if gettimeofday only takes one argument */
+#undef GETTIMEOFDAY_ONE_ARG
+
+/* Define if you have revoke() */
+#undef HAVE_REVOKE
+
+/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
+#undef HAVE_GRANTPT
+
+/* Define if you have fchmod */
+#undef HAVE_FCHMOD
+
+/* Define if you have <sys/type32.h> */
+#undef HAVE_SYS_TYPES32_H
diff --git a/slirp/socket.c b/slirp/socket.c
new file mode 100644
index 0000000..82d026c
--- /dev/null
+++ b/slirp/socket.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "qemu-common.h"
+#include <slirp.h>
+#include "ip_icmp.h"
+#ifdef __sun__
+#include <sys/filio.h>
+#endif
+
+static void sofcantrcvmore(struct socket *so);
+static void sofcantsendmore(struct socket *so);
+
+#if 0
+static void
+so_init()
+{
+ /* Nothing yet */
+}
+#endif
+
+struct socket *
+solookup(struct socket *head, struct in_addr laddr, u_int lport,
+ struct in_addr faddr, u_int fport)
+{
+ struct socket *so;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ if (so->so_lport == lport &&
+ so->so_laddr.s_addr == laddr.s_addr &&
+ so->so_faddr.s_addr == faddr.s_addr &&
+ so->so_fport == fport)
+ break;
+ }
+
+ if (so == head)
+ return (struct socket *)NULL;
+ return so;
+
+}
+
+/*
+ * Create a new socket, initialise the fields
+ * It is the responsibility of the caller to
+ * insque() it into the correct linked-list
+ */
+struct socket *
+socreate(void)
+{
+ struct socket *so;
+
+ so = (struct socket *)malloc(sizeof(struct socket));
+ if(so) {
+ memset(so, 0, sizeof(struct socket));
+ so->so_state = SS_NOFDREF;
+ so->s = -1;
+ }
+ return(so);
+}
+
+/*
+ * remque and free a socket, clobber cache
+ */
+void
+sofree(struct socket *so)
+{
+ if (so->so_emu==EMU_RSH && so->extra) {
+ sofree(so->extra);
+ so->extra=NULL;
+ }
+ if (so == tcp_last_so)
+ tcp_last_so = &tcb;
+ else if (so == udp_last_so)
+ udp_last_so = &udb;
+
+ m_free(so->so_m);
+
+ if(so->so_next && so->so_prev)
+ remque(so); /* crashes if so is not in a queue */
+
+ free(so);
+}
+
+size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np)
+{
+ int n, lss, total;
+ struct sbuf *sb = &so->so_snd;
+ int len = sb->sb_datalen - sb->sb_cc;
+ int mss = so->so_tcpcb->t_maxseg;
+
+ DEBUG_CALL("sopreprbuf");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ len = sb->sb_datalen - sb->sb_cc;
+
+ if (len <= 0)
+ return 0;
+
+ iov[0].iov_base = sb->sb_wptr;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ if (sb->sb_wptr < sb->sb_rptr) {
+ iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_rptr - sb->sb_data;
+ if(iov[1].iov_len > len)
+ iov[1].iov_len = len;
+ total = iov[0].iov_len + iov[1].iov_len;
+ if (total > mss) {
+ lss = total%mss;
+ if (iov[1].iov_len > lss) {
+ iov[1].iov_len -= lss;
+ n = 2;
+ } else {
+ lss -= iov[1].iov_len;
+ iov[0].iov_len -= lss;
+ n = 1;
+ }
+ } else
+ n = 2;
+ } else {
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ }
+ }
+ if (np)
+ *np = n;
+
+ return iov[0].iov_len + (n - 1) * iov[1].iov_len;
+}
+
+/*
+ * Read from so's socket into sb_snd, updating all relevant sbuf fields
+ * NOTE: This will only be called if it is select()ed for reading, so
+ * a read() of 0 (or less) means it's disconnected
+ */
+int
+soread(struct socket *so)
+{
+ int n, nn;
+ struct sbuf *sb = &so->so_snd;
+ struct iovec iov[2];
+
+ DEBUG_CALL("soread");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+ sopreprbuf(so, iov, &n);
+
+#ifdef HAVE_READV
+ nn = readv(so->s, (struct iovec *)iov, n);
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#else
+ nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ if (nn <= 0) {
+ if (nn < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+ else {
+ DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno)));
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+ }
+
+#ifndef HAVE_READV
+ /*
+ * If there was no error, try and read the second time round
+ * We read again if n = 2 (ie, there's another part of the buffer)
+ * and we read as much as we could in the first read
+ * We don't test for <= 0 this time, because there legitimately
+ * might not be any more data (since the socket is non-blocking),
+ * a close will be detected on next iteration.
+ * A return of -1 wont (shouldn't) happen, since it didn't happen above
+ */
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#endif
+
+ /* Update fields */
+ sb->sb_cc += nn;
+ sb->sb_wptr += nn;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return nn;
+}
+
+int soreadbuf(struct socket *so, const char *buf, int size)
+{
+ int n, nn, copy = size;
+ struct sbuf *sb = &so->so_snd;
+ struct iovec iov[2];
+
+ DEBUG_CALL("soreadbuf");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+ if (sopreprbuf(so, iov, &n) < size)
+ goto err;
+
+ nn = MIN(iov[0].iov_len, copy);
+ memcpy(iov[0].iov_base, buf, nn);
+
+ copy -= nn;
+ buf += nn;
+
+ if (copy == 0)
+ goto done;
+
+ memcpy(iov[1].iov_base, buf, copy);
+
+done:
+ /* Update fields */
+ sb->sb_cc += size;
+ sb->sb_wptr += size;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return size;
+err:
+
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ fprintf(stderr, "soreadbuf buffer to small");
+ return -1;
+}
+
+/*
+ * Get urgent data
+ *
+ * When the socket is created, we set it SO_OOBINLINE,
+ * so when OOB data arrives, we soread() it and everything
+ * in the send buffer is sent as urgent data
+ */
+void
+sorecvoob(struct socket *so)
+{
+ struct tcpcb *tp = sototcpcb(so);
+
+ DEBUG_CALL("sorecvoob");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ /*
+ * We take a guess at how much urgent data has arrived.
+ * In most situations, when urgent data arrives, the next
+ * read() should get all the urgent data. This guess will
+ * be wrong however if more data arrives just after the
+ * urgent data, or the read() doesn't return all the
+ * urgent data.
+ */
+ soread(so);
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ tcp_output(tp);
+ tp->t_force = 0;
+}
+
+/*
+ * Send urgent data
+ * There's a lot duplicated code here, but...
+ */
+int
+sosendoob(struct socket *so)
+{
+ struct sbuf *sb = &so->so_rcv;
+ char buff[2048]; /* XXX Shouldn't be sending more oob data than this */
+
+ int n, len;
+
+ DEBUG_CALL("sosendoob");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
+
+ if (so->so_urgc > 2048)
+ so->so_urgc = 2048; /* XXXX */
+
+ if (sb->sb_rptr < sb->sb_wptr) {
+ /* We can send it directly */
+ n = slirp_send(so, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+ so->so_urgc -= n;
+
+ DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ } else {
+ /*
+ * Since there's no sendv or sendtov like writev,
+ * we must copy all data to a linear buffer then
+ * send it all
+ */
+ len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (len > so->so_urgc) len = so->so_urgc;
+ memcpy(buff, sb->sb_rptr, len);
+ so->so_urgc -= len;
+ if (so->so_urgc) {
+ n = sb->sb_wptr - sb->sb_data;
+ if (n > so->so_urgc) n = so->so_urgc;
+ memcpy((buff + len), sb->sb_data, n);
+ so->so_urgc -= n;
+ len += n;
+ }
+ n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+#ifdef DEBUG
+ if (n != len)
+ DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
+#endif
+ DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ }
+
+ sb->sb_cc -= n;
+ sb->sb_rptr += n;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ return n;
+}
+
+/*
+ * Write data from so_rcv to so's socket,
+ * updating all sbuf field as necessary
+ */
+int
+sowrite(struct socket *so)
+{
+ int n,nn;
+ struct sbuf *sb = &so->so_rcv;
+ int len = sb->sb_cc;
+ struct iovec iov[2];
+
+ DEBUG_CALL("sowrite");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_urgc) {
+ sosendoob(so);
+ if (sb->sb_cc == 0)
+ return 0;
+ }
+
+ /*
+ * No need to check if there's something to write,
+ * sowrite wouldn't have been called otherwise
+ */
+
+ len = sb->sb_cc;
+
+ iov[0].iov_base = sb->sb_rptr;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ if (sb->sb_rptr < sb->sb_wptr) {
+ iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_wptr - sb->sb_data;
+ if (iov[1].iov_len > len) iov[1].iov_len = len;
+ n = 2;
+ } else
+ n = 1;
+ }
+ /* Check if there's urgent data to send, and if so, send it */
+
+#ifdef HAVE_READV
+ nn = writev(so->s, (const struct iovec *)iov, n);
+
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#else
+ nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ /* This should never happen, but people tell me it does *shrug* */
+ if (nn < 0 && (errno == EAGAIN || errno == EINTR))
+ return 0;
+
+ if (nn <= 0) {
+ DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n",
+ so->so_state, errno));
+ sofcantsendmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+
+#ifndef HAVE_READV
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#endif
+
+ /* Update sbuf */
+ sb->sb_cc -= nn;
+ sb->sb_rptr += nn;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ /*
+ * If in DRAIN mode, and there's no more data, set
+ * it CANTSENDMORE
+ */
+ if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
+ sofcantsendmore(so);
+
+ return nn;
+}
+
+/*
+ * recvfrom() a UDP socket
+ */
+void
+sorecvfrom(struct socket *so)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+
+ DEBUG_CALL("sorecvfrom");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
+ char buff[256];
+ int len;
+
+ len = recvfrom(so->s, buff, 256, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ /* XXX Check if reply is "correct"? */
+
+ if(len == -1 || len == 0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ } else {
+ icmp_reflect(so->so_m);
+ so->so_m = NULL; /* Don't m_free() it again! */
+ }
+ /* No need for this socket anymore, udp_detach it */
+ udp_detach(so);
+ } else { /* A "normal" UDP packet */
+ struct mbuf *m;
+ int len;
+#ifdef _WIN32
+ unsigned long n;
+#else
+ int n;
+#endif
+
+ if (!(m = m_get())) return;
+ m->m_data += IF_MAXLINKHDR;
+
+ /*
+ * XXX Shouldn't FIONREAD packets destined for port 53,
+ * but I don't know the max packet size for DNS lookups
+ */
+ len = M_FREEROOM(m);
+ /* if (so->so_fport != htons(53)) { */
+ ioctlsocket(so->s, FIONREAD, &n);
+
+ if (n > len) {
+ n = (m->m_data - m->m_dat) + m->m_len + n + 1;
+ m_inc(m, n);
+ len = M_FREEROOM(m);
+ }
+ /* } */
+
+ m->m_len = recvfrom(so->s, m->m_data, len, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
+ m->m_len, errno,strerror(errno)));
+ if(m->m_len<0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ m_free(m);
+ } else {
+ /*
+ * Hack: domain name lookup will be used the most for UDP,
+ * and since they'll only be used once there's no need
+ * for the 4 minute (or whatever) timeout... So we time them
+ * out much quicker (10 seconds for now...)
+ */
+ if (so->so_expire) {
+ if (so->so_fport == htons(53))
+ so->so_expire = curtime + SO_EXPIREFAST;
+ else
+ so->so_expire = curtime + SO_EXPIRE;
+ }
+
+ /* if (m->m_len == len) {
+ * m_inc(m, MINCSIZE);
+ * m->m_len = 0;
+ * }
+ */
+
+ /*
+ * If this packet was destined for CTL_ADDR,
+ * make it look like that's where it came from, done by udp_output
+ */
+ udp_output(so, m, &addr);
+ } /* rx error */
+ } /* if ping packet */
+}
+
+/*
+ * sendto() a socket
+ */
+int
+sosendto(struct socket *so, struct mbuf *m)
+{
+ int ret;
+ struct sockaddr_in addr;
+
+ DEBUG_CALL("sosendto");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ /* It's an alias */
+ switch(ntohl(so->so_faddr.s_addr) & 0xff) {
+ case CTL_DNS:
+ addr.sin_addr = dns_addr;
+ break;
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+
+ /* Don't care what port we get */
+ ret = sendto(so->s, m->m_data, m->m_len, 0,
+ (struct sockaddr *)&addr, sizeof (struct sockaddr));
+ if (ret < 0)
+ return -1;
+
+ /*
+ * Kill the socket if there's no reply in 4 minutes,
+ * but only if it's an expirable socket
+ */
+ if (so->so_expire)
+ so->so_expire = curtime + SO_EXPIRE;
+ so->so_state = SS_ISFCONNECTED; /* So that it gets select()ed */
+ return 0;
+}
+
+/*
+ * XXX This should really be tcp_listen
+ */
+struct socket *
+solisten(u_int port, u_int32_t laddr, u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ int s, opt = 1;
+ socklen_t addrlen = sizeof(addr);
+
+ DEBUG_CALL("solisten");
+ DEBUG_ARG("port = %d", port);
+ DEBUG_ARG("laddr = %x", laddr);
+ DEBUG_ARG("lport = %d", lport);
+ DEBUG_ARG("flags = %x", flags);
+
+ if ((so = socreate()) == NULL) {
+ /* free(so); Not sofree() ??? free(NULL) == NOP */
+ return NULL;
+ }
+
+ /* Don't tcp_attach... we don't need so_snd nor so_rcv */
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
+ free(so);
+ return NULL;
+ }
+ insque(so,&tcb);
+
+ /*
+ * SS_FACCEPTONCE sockets must time out.
+ */
+ if (flags & SS_FACCEPTONCE)
+ so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
+
+ so->so_state = (SS_FACCEPTCONN|flags);
+ so->so_lport = lport; /* Kept in network format */
+ so->so_laddr.s_addr = laddr; /* Ditto */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = port;
+
+ if (((s = socket(AF_INET,SOCK_STREAM,0)) < 0) ||
+ (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) ||
+ (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) ||
+ (listen(s,1) < 0)) {
+ int tmperrno = errno; /* Don't clobber the real reason we failed */
+
+ close(s);
+ sofree(so);
+ /* Restore the real errno */
+#ifdef _WIN32
+ WSASetLastError(tmperrno);
+#else
+ errno = tmperrno;
+#endif
+ return NULL;
+ }
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+
+ getsockname(s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->s = s;
+ return so;
+}
+
+#if 0
+/*
+ * Data is available in so_rcv
+ * Just write() the data to the socket
+ * XXX not yet...
+ */
+static void
+sorwakeup(so)
+ struct socket *so;
+{
+/* sowrite(so); */
+/* FD_CLR(so->s,&writefds); */
+}
+
+/*
+ * Data has been freed in so_snd
+ * We have room for a read() if we want to
+ * For now, don't read, it'll be done in the main loop
+ */
+static void
+sowwakeup(so)
+ struct socket *so;
+{
+ /* Nothing, yet */
+}
+#endif
+
+/*
+ * Various session state calls
+ * XXX Should be #define's
+ * The socket state stuff needs work, these often get call 2 or 3
+ * times each when only 1 was needed
+ */
+void
+soisfconnecting(struct socket *so)
+{
+ so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
+ SS_FCANTSENDMORE|SS_FWDRAIN);
+ so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
+}
+
+void
+soisfconnected(struct socket *so)
+{
+ so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
+ so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
+}
+
+static void
+sofcantrcvmore(struct socket *so)
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,0);
+ if(global_writefds) {
+ FD_CLR(so->s,global_writefds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTSENDMORE)
+ so->so_state = SS_NOFDREF; /* Don't select it */ /* XXX close() here as well? */
+ else
+ so->so_state |= SS_FCANTRCVMORE;
+}
+
+static void
+sofcantsendmore(struct socket *so)
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,1); /* send FIN to fhost */
+ if (global_readfds) {
+ FD_CLR(so->s,global_readfds);
+ }
+ if (global_xfds) {
+ FD_CLR(so->s,global_xfds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTRCVMORE)
+ so->so_state = SS_NOFDREF; /* as above */
+ else
+ so->so_state |= SS_FCANTSENDMORE;
+}
+
+void
+soisfdisconnected(struct socket *so)
+{
+/* so->so_state &= ~(SS_ISFCONNECTING|SS_ISFCONNECTED); */
+/* close(so->s); */
+/* so->so_state = SS_ISFDISCONNECTED; */
+ /*
+ * XXX Do nothing ... ?
+ */
+}
+
+/*
+ * Set write drain mode
+ * Set CANTSENDMORE once all data has been write()n
+ */
+void
+sofwdrain(struct socket *so)
+{
+ if (so->so_rcv.sb_cc)
+ so->so_state |= SS_FWDRAIN;
+ else
+ sofcantsendmore(so);
+}
diff --git a/slirp/socket.h b/slirp/socket.h
new file mode 100644
index 0000000..f5adaba
--- /dev/null
+++ b/slirp/socket.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/* MINE */
+
+#ifndef _SLIRP_SOCKET_H_
+#define _SLIRP_SOCKET_H_
+
+#define SO_EXPIRE 240000
+#define SO_EXPIREFAST 10000
+
+/*
+ * Our socket structure
+ */
+
+struct socket {
+ struct socket *so_next,*so_prev; /* For a linked list of sockets */
+
+ int s; /* The actual socket */
+
+ /* XXX union these with not-yet-used sbuf params */
+ struct mbuf *so_m; /* Pointer to the original SYN packet,
+ * for non-blocking connect()'s, and
+ * PING reply's */
+ struct tcpiphdr *so_ti; /* Pointer to the original ti within
+ * so_mconn, for non-blocking connections */
+ int so_urgc;
+ struct in_addr so_faddr; /* foreign host table entry */
+ struct in_addr so_laddr; /* local host table entry */
+ u_int16_t so_fport; /* foreign port */
+ u_int16_t so_lport; /* local port */
+
+ u_int8_t so_iptos; /* Type of service */
+ u_int8_t so_emu; /* Is the socket emulated? */
+
+ u_char so_type; /* Type of socket, UDP or TCP */
+ int so_state; /* internal state flags SS_*, below */
+
+ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
+ u_int so_expire; /* When the socket will expire */
+
+ int so_queued; /* Number of packets queued from this socket */
+ int so_nqueued; /* Number of packets queued in a row
+ * Used to determine when to "downgrade" a session
+ * from fastq to batchq */
+
+ struct sbuf so_rcv; /* Receive buffer */
+ struct sbuf so_snd; /* Send buffer */
+ void * extra; /* Extra pointer */
+};
+
+
+/*
+ * Socket state bits. (peer means the host on the Internet,
+ * local host means the host on the other end of the modem)
+ */
+#define SS_NOFDREF 0x001 /* No fd reference */
+
+#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */
+#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */
+#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */
+#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */
+/* #define SS_ISFDISCONNECTED 0x020*/ /* Socket has disconnected from peer, in 2MSL state */
+#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */
+
+#define SS_CTL 0x080
+#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */
+#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */
+
+extern struct socket tcb;
+
+struct socket * solookup _P((struct socket *, struct in_addr, u_int, struct in_addr, u_int));
+struct socket * socreate _P((void));
+void sofree _P((struct socket *));
+int soread _P((struct socket *));
+void sorecvoob _P((struct socket *));
+int sosendoob _P((struct socket *));
+int sowrite _P((struct socket *));
+void sorecvfrom _P((struct socket *));
+int sosendto _P((struct socket *, struct mbuf *));
+struct socket * solisten _P((u_int, u_int32_t, u_int, int));
+void soisfconnecting _P((register struct socket *));
+void soisfconnected _P((register struct socket *));
+void soisfdisconnected _P((struct socket *));
+void sofwdrain _P((struct socket *));
+struct iovec; /* For win32 */
+size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
+int soreadbuf(struct socket *so, const char *buf, int size);
+
+#endif /* _SOCKET_H_ */
diff --git a/slirp/tcp.h b/slirp/tcp.h
new file mode 100644
index 0000000..4057032
--- /dev/null
+++ b/slirp/tcp.h
@@ -0,0 +1,171 @@
+/*
+ * 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. 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.h 8.1 (Berkeley) 6/10/93
+ * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
+ */
+
+#ifndef _TCP_H_
+#define _TCP_H_
+
+typedef u_int32_t tcp_seq;
+
+#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
+#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
+
+extern struct socket *tcp_last_so;
+
+#define TCP_SNDSPACE 8192
+#define TCP_RCVSPACE 8192
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+ u_int16_t th_sport; /* source port */
+ u_int16_t th_dport; /* destination port */
+ tcp_seq th_seq; /* sequence number */
+ tcp_seq th_ack; /* acknowledgement number */
+#ifdef WORDS_BIGENDIAN
+ u_int th_off:4, /* data offset */
+ th_x2:4; /* (unused) */
+#else
+ u_int th_x2:4, /* (unused) */
+ th_off:4; /* data offset */
+#endif
+ u_int8_t th_flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ u_int16_t th_win; /* window */
+ u_int16_t th_sum; /* checksum */
+ u_int16_t th_urp; /* urgent pointer */
+};
+
+#include "tcp_var.h"
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WINDOW 3
+#define TCPOLEN_WINDOW 3
+#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
+#define TCPOLEN_SACK_PERMITTED 2
+#define TCPOPT_SACK 5 /* Experimental */
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ *
+ * We make this 1460 because we only care about Ethernet in the qemu context.
+ */
+#define TCP_MSS 1460
+
+#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ *
+ * We don't use the system headers on unix because we have conflicting
+ * local structures. We can't avoid the system definitions on Windows,
+ * so we undefine them.
+ */
+#undef TCP_NODELAY
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#undef TCP_MAXSEG
+/* #define TCP_MAXSEG 0x02 */ /* set maximum segment size */
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES 11
+
+#define TCPS_CLOSED 0 /* closed */
+#define TCPS_LISTEN 1 /* listening for connection */
+#define TCPS_SYN_SENT 2 /* active, have sent syn */
+#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED 4 /* established */
+#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
+#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
+#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
+#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
+
+/*
+ * 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
+
+#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
+
+extern tcp_seq tcp_iss; /* tcp initial send seq # */
+
+extern const char * const tcpstates[];
+
+#endif
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
new file mode 100644
index 0000000..effedfc
--- /dev/null
+++ b/slirp/tcp_input.c
@@ -0,0 +1,1725 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * 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. 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.5 (Berkeley) 4/10/94
+ * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+struct socket tcb;
+
+#define TCPREXMTTHRESH 3
+struct socket *tcp_last_so = &tcb;
+
+tcp_seq tcp_iss; /* tcp initial send seq # */
+
+#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).
+ */
+#ifdef TCP_ACK_HACK
+#define TCP_REASS(tp, ti, m, so, flags) {\
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ tcpfrag_list_empty(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) {\
+ if (ti->ti_flags & TH_PUSH) \
+ tp->t_flags |= TF_ACKNOW; \
+ else \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ STAT(tcpstat.tcps_rcvpack++); \
+ STAT(tcpstat.tcps_rcvbyte += (ti)->ti_len); \
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend((so), (m)); \
+ } else \
+ sbappend((so), (m)); \
+/* sorwakeup(so); */ \
+ } else {\
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#else
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ tcpfrag_list_empty(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ STAT(tcpstat.tcps_rcvpack++); \
+ STAT(tcpstat.tcps_rcvbyte += (ti)->ti_len); \
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend(so, (m)); \
+ } else \
+ sbappend((so), (m)); \
+/* sorwakeup(so); */ \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#endif
+static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt,
+ struct tcpiphdr *ti);
+static void tcp_xmit_timer(register struct tcpcb *tp, int rtt);
+
+static int
+tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti,
+ struct mbuf *m)
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_socket;
+ int flags;
+
+ /*
+ * Call with ti==NULL after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == NULL)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp);
+ q = tcpiphdr_next(q))
+ 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 (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) {
+ register int i;
+ q = tcpiphdr_prev(q);
+ /* 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) {
+ STAT(tcpstat.tcps_rcvduppack++);
+ STAT(tcpstat.tcps_rcvdupbyte += ti->ti_len);
+ m_freem(m);
+ /*
+ * Try to present any queued data
+ * at the left window edge to the user.
+ * This is needed after the 3-WHS
+ * completes.
+ */
+ goto present; /* ??? */
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = tcpiphdr_next(q);
+ }
+ STAT(tcpstat.tcps_rcvoopack++);
+ STAT(tcpstat.tcps_rcvoobyte += ti->ti_len);
+ ti->ti_mbuf = m;
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (!tcpfrag_list_end(q, 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(q->ti_mbuf, i);
+ break;
+ }
+ q = tcpiphdr_next(q);
+ m = tcpiphdr_prev(q)->ti_mbuf;
+ remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q)));
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (!TCPS_HAVEESTABLISHED(tp->t_state))
+ return (0);
+ ti = tcpfrag_list_first(tp);
+ if (tcpfrag_list_end(ti, 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(tcpiphdr2qlink(ti));
+ m = ti->ti_mbuf;
+ ti = tcpiphdr_next(ti);
+/* if (so->so_state & SS_FCANTRCVMORE) */
+ if (so->so_state & SS_FCANTSENDMORE)
+ m_freem(m);
+ else {
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, 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(struct mbuf *m, int iphlen, struct socket *inso)
+{
+ struct ip save_ip, *ip;
+ register struct tcpiphdr *ti;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, tlen, off;
+ register struct tcpcb *tp = NULL;
+ register int tiflags;
+ struct socket *so = NULL;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+/* int dropsocket = 0; */
+ int iss = 0;
+ u_long tiwin;
+ int ret;
+/* int ts_present = 0; */
+ struct ex_list *ex_ptr;
+
+ DEBUG_CALL("tcp_input");
+ DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n",
+ (long )m, iphlen, (long )inso ));
+
+ /*
+ * If called with m == 0, then we're continuing the connect
+ */
+ if (m == NULL) {
+ so = inso;
+
+ /* Re-set a few variables */
+ tp = sototcpcb(so);
+ m = so->so_m;
+ so->so_m = NULL;
+ ti = so->so_ti;
+ tiwin = ti->ti_win;
+ tiflags = ti->ti_flags;
+
+ goto cont_conn;
+ }
+
+
+ STAT(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);
+ iphlen=sizeof(struct ip );
+ }
+ /* XXX Check if too short */
+
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ ip=mtod(m, struct ip *);
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen;
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+ memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ ti->ti_x1 = 0;
+ ti->ti_len = htons((u_int16_t)tlen);
+ len = sizeof(struct ip ) + tlen;
+ /* keep checksum for ICMP reply
+ * ti->ti_sum = cksum(m, len);
+ * if (ti->ti_sum) { */
+ if(cksum(m, len)) {
+ STAT(tcpstat.tcps_rcvbadsum++);
+ goto drop;
+ }
+
+ /*
+ * 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) {
+ STAT(tcpstat.tcps_rcvbadoff++);
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + 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_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
+ * (ti->ti_flags & TH_SYN) == 0) {
+ * ts_present = 1;
+ * ts_val = ntohl(*(u_int32_t *)(optp + 4));
+ * ts_ecr = ntohl(*(u_int32_t *)(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);
+
+ /*
+ * 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);
+
+ if (slirp_restrict) {
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_fport == ti->ti_dport &&
+ (ntohl(ti->ti_dst.s_addr) & 0xff) == ex_ptr->ex_addr)
+ break;
+
+ if (!ex_ptr)
+ goto drop;
+ }
+ /*
+ * Locate pcb for segment.
+ */
+findso:
+ so = tcp_last_so;
+ if (so->so_fport != ti->ti_dport ||
+ so->so_lport != ti->ti_sport ||
+ so->so_laddr.s_addr != ti->ti_src.s_addr ||
+ so->so_faddr.s_addr != ti->ti_dst.s_addr) {
+ so = solookup(&tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport);
+ if (so)
+ tcp_last_so = so;
+ STAT(tcpstat.tcps_socachemiss++);
+ }
+
+ /*
+ * 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.
+ *
+ * state == CLOSED means we've done socreate() but haven't
+ * attached it to a protocol yet...
+ *
+ * XXX If a TCB does not exist, and the TH_SYN flag is
+ * the only flag set, then create a session, mark it
+ * as if it was LISTENING, and continue...
+ */
+ if (so == NULL) {
+ if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN)
+ goto dropwithreset;
+
+ if ((so = socreate()) == NULL)
+ goto dropwithreset;
+ if (tcp_attach(so) < 0) {
+ free(so); /* Not sofree (if it failed, it's not insqued) */
+ goto dropwithreset;
+ }
+
+ sbreserve(&so->so_snd, TCP_SNDSPACE);
+ sbreserve(&so->so_rcv, TCP_RCVSPACE);
+
+ /* tcp_last_so = so; */ /* XXX ? */
+ /* tp = sototcpcb(so); */
+
+ so->so_laddr = ti->ti_src;
+ so->so_lport = ti->ti_sport;
+ so->so_faddr = ti->ti_dst;
+ so->so_fport = ti->ti_dport;
+
+ if ((so->so_iptos = tcp_tos(so)) == 0)
+ so->so_iptos = ((struct ip *)ti)->ip_tos;
+
+ tp = sototcpcb(so);
+ tp->t_state = TCPS_LISTEN;
+ }
+
+ /*
+ * If this is a still-connecting socket, this probably
+ * a retransmit of the SYN. Whether it's a retransmit SYN
+ * or something else, we nuke it.
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ goto drop;
+
+ tp = sototcpcb(so);
+
+ /* XXX Should never fail */
+ if (tp == NULL)
+ 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;
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ if (SO_OPTIONS)
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
+ else
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
+
+ /*
+ * 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, (u_char *)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.
+ *
+ * XXX Some of these tests are not needed
+ * eg: the tiwin == tp->snd_wnd prevents many more
+ * predictions.. with no *real* advantage..
+ */
+ 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.
+ */
+ STAT(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;
+ STAT(tcpstat.tcps_rcvackpack++);
+ STAT(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;
+
+ /*
+ * There's room in so_snd, sowwakup will read()
+ * from the socket if we can
+ */
+/* if (so->so_snd.sb_flags & SB_NOTIFY)
+ * sowwakeup(so);
+ */
+ /*
+ * This is called because sowwakeup might have
+ * put data into so_snd. Since we don't so sowwakeup,
+ * we don't need this.. XXX???
+ */
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tcpfrag_list_empty(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.
+ */
+ STAT(tcpstat.tcps_preddat++);
+ tp->rcv_nxt += ti->ti_len;
+ STAT(tcpstat.tcps_rcvpack++);
+ STAT(tcpstat.tcps_rcvbyte += ti->ti_len);
+ /*
+ * Add data to socket buffer.
+ */
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+
+ /*
+ * XXX This is called when data arrives. Later, check
+ * if we can actually write() to the socket
+ * XXX Need to check? It's be NON_BLOCKING
+ */
+/* sorwakeup(so); */
+
+ /*
+ * If this is a short packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * It is better to not delay acks at all to maximize
+ * TCP throughput. See RFC 2581.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(tp);
+ return;
+ }
+ } /* header prediction */
+ /*
+ * 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: {
+
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+
+ /*
+ * This has way too many gotos...
+ * But a bit of spaghetti code never hurt anybody :)
+ */
+
+ /*
+ * If this is destined for the control address, then flag to
+ * tcp_ctl once connected, otherwise connect
+ */
+ if ((so->so_faddr.s_addr&htonl(0xffffff00)) == special_addr.s_addr) {
+ int lastbyte=ntohl(so->so_faddr.s_addr) & 0xff;
+ if (lastbyte!=CTL_ALIAS && lastbyte!=CTL_DNS) {
+#if 0
+ if(lastbyte==CTL_CMD || lastbyte==CTL_EXEC) {
+ /* Command or exec adress */
+ so->so_state |= SS_CTL;
+ } else
+#endif
+ {
+ /* May be an add exec */
+ for(ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if(ex_ptr->ex_fport == so->so_fport &&
+ lastbyte == ex_ptr->ex_addr) {
+ so->so_state |= SS_CTL;
+ break;
+ }
+ }
+ }
+ if(so->so_state & SS_CTL) goto cont_input;
+ }
+ /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */
+ }
+
+ if (so->so_emu & EMU_NOCONNECT) {
+ so->so_emu &= ~EMU_NOCONNECT;
+ goto cont_input;
+ }
+
+ if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
+ u_char code=ICMP_UNREACH_NET;
+ DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n",
+ errno,strerror(errno)));
+ if(errno == ECONNREFUSED) {
+ /* ACK the SYN, send RST to refuse the connection */
+ tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ } else {
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ HTONL(ti->ti_seq); /* restore tcp header */
+ HTONL(ti->ti_ack);
+ HTONS(ti->ti_win);
+ HTONS(ti->ti_urp);
+ m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ *ip=save_ip;
+ icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
+ }
+ tp = tcp_close(tp);
+ m_free(m);
+ } else {
+ /*
+ * Haven't connected yet, save the current mbuf
+ * and ti, and return
+ * XXX Some OS's don't tell us whether the connect()
+ * succeeded or not. So we must time it out.
+ */
+ so->so_m = m;
+ so->so_ti = ti;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ }
+ return;
+
+ cont_conn:
+ /* m==NULL
+ * Check if the connect succeeded
+ */
+ if (so->so_state & SS_NOFDREF) {
+ tp = tcp_close(tp);
+ goto dropwithreset;
+ }
+ cont_input:
+ tcp_template(tp);
+
+ if (optp)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+ /* , */
+ /* &ts_present, &ts_val, &ts_ecr); */
+
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ 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;
+ STAT(tcpstat.tcps_accepts++);
+ goto trimthenstep6;
+ } /* case TCPS_LISTEN */
+
+ /*
+ * 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,0); /* XXX Check t_softerror! */
+ 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)) {
+ STAT(tcpstat.tcps_connects++);
+ soisfconnected(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;
+ STAT(tcpstat.tcps_rcvpackafterwin++);
+ STAT(tcpstat.tcps_rcvbyteafterwin += todrop);
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ } /* switch tp->t_state */
+ /*
+ * 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--;
+ }
+ /*
+ * Following if statement from Stevens, vol. 2, p. 960.
+ */
+ if (todrop > ti->ti_len
+ || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) {
+ /*
+ * Any valid FIN must be to the left of the window.
+ * At this point the FIN must be a duplicate or out
+ * of sequence; drop it.
+ */
+ tiflags &= ~TH_FIN;
+
+ /*
+ * Send an ACK to resynchronize and drop any data.
+ * But keep on processing for RST or ACK.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ todrop = ti->ti_len;
+ STAT(tcpstat.tcps_rcvduppack++);
+ STAT(tcpstat.tcps_rcvdupbyte += todrop);
+ } else {
+ STAT(tcpstat.tcps_rcvpartduppack++);
+ STAT(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);
+ STAT(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) {
+ STAT(tcpstat.tcps_rcvpackafterwin++);
+ if (todrop >= ti->ti_len) {
+ STAT(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->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findso;
+ }
+ /*
+ * 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;
+ STAT(tcpstat.tcps_rcvwinprobe++);
+ } else
+ goto dropafterack;
+ } else
+ STAT(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;
+ STAT(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,0);
+ 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. una<=ack<=max
+ */
+ case TCPS_SYN_RECEIVED:
+
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ STAT(tcpstat.tcps_connects++);
+ tp->t_state = TCPS_ESTABLISHED;
+ /*
+ * The sent SYN is ack'ed with our sequence number +1
+ * The first data byte already in the buffer will get
+ * lost if no correction is made. This is only needed for
+ * SS_CTL since the buffer is empty otherwise.
+ * tp->snd_una++; or:
+ */
+ tp->snd_una=ti->ti_ack;
+ if (so->so_state & SS_CTL) {
+ /* So tcp_ctl reports the right state */
+ ret = tcp_ctl(so);
+ if (ret == 1) {
+ soisfconnected(so);
+ so->so_state &= ~SS_CTL; /* success XXX */
+ } else if (ret == 2) {
+ so->so_state = SS_NOFDREF; /* CTL_CMD */
+ } else {
+ needoutput = 1;
+ tp->t_state = TCPS_FIN_WAIT_1;
+ }
+ } else {
+ soisfconnected(so);
+ }
+
+ /* 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;
+ /* Avoid ack processing; snd_una==ti_ack => dup ack */
+ goto synrx_to_est;
+ /* 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) {
+ STAT(tcpstat.tcps_rcvdupack++);
+ DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n",
+ (long )m, (long )so));
+ /*
+ * 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
+ * threshold 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;
+ }
+ synrx_to_est:
+ /*
+ * 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)) {
+ STAT(tcpstat.tcps_rcvacktoomuch++);
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+ STAT(tcpstat.tcps_rcvackpack++);
+ STAT(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^2 / 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;
+ }
+ /*
+ * XXX sowwakup is called when data is acked and there's room for
+ * for more data... it should read() the socket
+ */
+/* 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_FCANTRCVMORE) {
+ soisfdisconnected(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;
+ soisfdisconnected(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;
+ }
+ } /* switch(tp->t_state) */
+
+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)
+ STAT(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 > so->so_rcv.sb_datalen) {
+ ti->ti_urp = 0;
+ tiflags &= ~TH_URG;
+ goto dodata;
+ }
+ /*
+ * 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_urgc = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt); /* -1; */
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+
+ }
+ } 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:
+
+ /*
+ * 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_datalen - (tp->rcv_adv - tp->rcv_nxt);
+ } else {
+ m_free(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) {
+ /*
+ * If we receive a FIN we can't send more data,
+ * set it SS_FDRAIN
+ * Shutdown the socket if there is no rx data in the
+ * buffer.
+ * soread() is called on completion of shutdown() and
+ * will got to TCPS_LAST_ACK, and use tcp_output()
+ * to send the FIN.
+ */
+/* sofcantrcvmore(so); */
+ sofwdrain(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:
+ if(so->so_emu == EMU_CTL) /* no shutdown on socket */
+ tp->t_state = TCPS_LAST_ACK;
+ else
+ 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;
+ soisfdisconnected(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 this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * See above.
+ */
+/* if (ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg) {
+ */
+/* if ((ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg &&
+ * (so->so_iptos & IPTOS_LOWDELAY) == 0) ||
+ * ((so->so_iptos & IPTOS_LOWDELAY) &&
+ * ((struct tcpiphdr_2 *)ti)->first_char == (char)27)) {
+ */
+ if (ti->ti_len && (unsigned)ti->ti_len <= 5 &&
+ ((struct tcpiphdr_2 *)ti)->first_char == (char)27) {
+ tp->t_flags |= TF_ACKNOW;
+ }
+
+ /*
+ * 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:
+ /* reuses m if m!=NULL, m_free() unnecessary */
+ 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);
+ }
+
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ m_free(m);
+
+ return;
+}
+
+ /* , ts_present, ts_val, ts_ecr) */
+/* int *ts_present;
+ * u_int32_t *ts_val, *ts_ecr;
+ */
+static void
+tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
+{
+ u_int16_t mss;
+ int opt, optlen;
+
+ DEBUG_CALL("tcp_dooptions");
+ DEBUG_ARGS((dfd," tp = %lx cnt=%i \n", (long )tp, cnt));
+
+ 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;
+ memcpy((char *) &mss, (char *) cp + 2, 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;
+ * memcpy((char *) ts_val, (char *)cp + 2, sizeof(*ts_val));
+ * NTOHL(*ts_val);
+ * memcpy((char *) ts_ecr, (char *)cp + 6, 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.
+ */
+
+#ifdef notdef
+
+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;
+ memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next; /* XXX WRONG! Fix it! */
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+#endif /* notdef */
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+
+static void
+tcp_xmit_timer(register struct tcpcb *tp, int rtt)
+{
+ register short delta;
+
+ DEBUG_CALL("tcp_xmit_timer");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("rtt = %d", rtt);
+
+ STAT(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),
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+
+ /*
+ * 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(struct tcpcb *tp, u_int offer)
+{
+ struct socket *so = tp->t_socket;
+ int mss;
+
+ DEBUG_CALL("tcp_mss");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("offer = %d", offer);
+
+ mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr);
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32);
+ if (mss < tp->t_maxseg || offer != 0)
+ tp->t_maxseg = mss;
+
+ tp->snd_cwnd = mss;
+
+ sbreserve(&so->so_snd, TCP_SNDSPACE + ((TCP_SNDSPACE % mss) ?
+ (mss - (TCP_SNDSPACE % mss)) :
+ 0));
+ sbreserve(&so->so_rcv, TCP_RCVSPACE + ((TCP_RCVSPACE % mss) ?
+ (mss - (TCP_RCVSPACE % mss)) :
+ 0));
+
+ DEBUG_MISC((dfd, " returning mss = %d\n", mss));
+
+ return mss;
+}
diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c
new file mode 100644
index 0000000..9ed50f5
--- /dev/null
+++ b/slirp/tcp_output.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 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. 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.3 (Berkeley) 12/30/93
+ * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/*
+ * Since this is only used in "stats socket", we give meaning
+ * names instead of the REAL names
+ */
+const char * const tcpstates[] = {
+/* "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", */
+ "REDIRECT", "LISTEN", "SYN_SENT", "SYN_RCVD",
+ "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
+ "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
+};
+
+static const u_char tcp_outflags[TCP_NSTATES] = {
+ TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
+ TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
+ TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+
+
+#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(struct tcpcb *tp)
+{
+ register struct socket *so = tp->t_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;
+
+ DEBUG_CALL("tcp_output");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /*
+ * 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];
+
+ DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
+
+ /*
+ * 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 ((1 || 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 && tp->max_sndwnd > 0)
+ 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_datalen)
+ 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.
+ */
+ STAT(tcpstat.tcps_didnuttin++);
+
+ 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_int16_t mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = htons((u_int16_t) tcp_mss(tp, 0));
+ memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
+ optlen = 4;
+
+/* if ((tp->t_flags & TF_REQ_SCALE) &&
+ * ((flags & TH_ACK) == 0 ||
+ * (tp->t_flags & TF_RCVD_SCALE))) {
+ * *((u_int32_t *) (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_int32_t *lp = (u_int32_t *)(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;
+ }
+
+ /*
+ * 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)
+ STAT(tcpstat.tcps_sndprobe++);
+ else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+ STAT(tcpstat.tcps_sndrexmitpack++);
+ STAT(tcpstat.tcps_sndrexmitbyte += len);
+ } else {
+ STAT(tcpstat.tcps_sndpack++);
+ STAT(tcpstat.tcps_sndbyte += len);
+ }
+
+ m = m_get();
+ if (m == NULL) {
+/* error = ENOBUFS; */
+ error = 1;
+ goto out;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ m->m_len = hdrlen;
+
+ /*
+ * This will always succeed, since we make sure our mbufs
+ * are big enough to hold one MSS packet + header + ... etc.
+ */
+/* if (len <= MHLEN - hdrlen - max_linkhdr) { */
+
+ sbcopy(&so->so_snd, 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)
+ * len = 0;
+ * }
+ */
+ /*
+ * 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)
+ STAT(tcpstat.tcps_sndacks++);
+ else if (flags & (TH_SYN|TH_FIN|TH_RST))
+ STAT(tcpstat.tcps_sndctrl++);
+ else if (SEQ_GT(tp->snd_up, tp->snd_una))
+ STAT(tcpstat.tcps_sndurg++);
+ else
+ STAT(tcpstat.tcps_sndwinup++);
+
+ m = m_get();
+ if (m == NULL) {
+/* error = ENOBUFS; */
+ error = 1;
+ goto out;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ m->m_len = hdrlen;
+ }
+
+ ti = mtod(m, struct tcpiphdr *);
+
+ memcpy((caddr_t)ti, &tp->t_template, 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) {
+ memcpy((caddr_t)(ti + 1), (caddr_t)opt, 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_datalen / 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_int16_t) (win>>tp->rcv_scale));
+
+ if (SEQ_GT(tp->snd_up, tp->snd_una)) {
+ ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+#ifdef notdef
+ if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
+ ti->ti_urp = htons((u_int16_t)(tp->snd_up - tp->snd_nxt));
+#endif
+ 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_int16_t)(sizeof (struct tcphdr) +
+ optlen + len));
+ ti->ti_sum = 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;
+ STAT(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;
+
+ /*
+ * 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_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+
+ {
+
+ ((struct ip *)ti)->ip_len = m->m_len;
+
+ ((struct ip *)ti)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ti)->ip_tos = so->so_iptos;
+
+/* #if BSD >= 43 */
+ /* Don't do IP options... */
+/* error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+ * so->so_options & SO_DONTROUTE, 0);
+ */
+ error = ip_output(so, m);
+
+/* #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);
+ }
+ STAT(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(struct tcpcb *tp)
+{
+ int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+/* if (tp->t_timer[TCPT_REXMT])
+ * panic("tcp_output REXMT");
+ */
+ /*
+ * Start/restart persistence 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/slirp/tcp_subr.c b/slirp/tcp_subr.c
new file mode 100644
index 0000000..9d020a6
--- /dev/null
+++ b/slirp/tcp_subr.c
@@ -0,0 +1,1311 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 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. 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.1 (Berkeley) 6/10/93
+ * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/* patchable/settable parameters for tcp */
+/* Don't do rfc1323 performance enhancements */
+#define TCP_DO_RFC1323 0
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init(void)
+{
+ tcp_iss = 1; /* wrong */
+ tcb.so_next = tcb.so_prev = &tcb;
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+/* struct tcpiphdr * */
+void
+tcp_template(struct tcpcb *tp)
+{
+ struct socket *so = tp->t_socket;
+ register struct tcpiphdr *n = &tp->t_template;
+
+ n->ti_mbuf = NULL;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = so->so_faddr;
+ n->ti_dst = so->so_laddr;
+ n->ti_sport = so->so_fport;
+ n->ti_dport = so->so_lport;
+
+ 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;
+}
+
+/*
+ * 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(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
+ tcp_seq ack, tcp_seq seq, int flags)
+{
+ register int tlen;
+ int win = 0;
+
+ DEBUG_CALL("tcp_respond");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("ti = %lx", (long)ti);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("ack = %u", ack);
+ DEBUG_ARG("seq = %u", seq);
+ DEBUG_ARG("flags = %x", flags);
+
+ if (tp)
+ win = sbspace(&tp->t_socket->so_rcv);
+ if (m == NULL) {
+ if ((m = m_get()) == NULL)
+ return;
+#ifdef TCP_COMPAT_42
+ tlen = 1;
+#else
+ tlen = 0;
+#endif
+ m->m_data += IF_MAXLINKHDR;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ /*
+ * ti points into m so the next line is just making
+ * the mbuf point to ti
+ */
+ 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_int32_t);
+ xchg(ti->ti_dport, ti->ti_sport, u_int16_t);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+
+ ti->ti_mbuf = NULL;
+ 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_int16_t) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((u_int16_t)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+
+ if(flags & TH_RST)
+ ((struct ip *)ti)->ip_ttl = MAXTTL;
+ else
+ ((struct ip *)ti)->ip_ttl = IPDEFTTL;
+
+ (void) ip_output((struct socket *)0, m);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(struct socket *so)
+{
+ register struct tcpcb *tp;
+
+ tp = (struct tcpcb *)malloc(sizeof(*tp));
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+
+ memset((char *) tp, 0, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
+ tp->t_maxseg = TCP_MSS;
+
+ tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_socket = so;
+
+ /*
+ * 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 = TCPTV_SRTTDFLT << 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;
+ tp->t_state = TCPS_CLOSED;
+
+ so->so_tcpcb = 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(struct tcpcb *tp, int err)
+{
+/* tcp_drop(tp, errno)
+ register struct tcpcb *tp;
+ int errno;
+{
+*/
+
+ DEBUG_CALL("tcp_drop");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("errno = %d", errno);
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ STAT(tcpstat.tcps_drops++);
+ } else
+ STAT(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(struct tcpcb *tp)
+{
+ register struct tcpiphdr *t;
+ struct socket *so = tp->t_socket;
+ register struct mbuf *m;
+
+ DEBUG_CALL("tcp_close");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /* free the reassembly queue, if any */
+ t = tcpfrag_list_first(tp);
+ while (!tcpfrag_list_end(t, tp)) {
+ t = tcpiphdr_next(t);
+ m = tcpiphdr_prev(t)->ti_mbuf;
+ remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
+ m_freem(m);
+ }
+ /* It's static */
+/* if (tp->t_template)
+ * (void) m_free(dtom(tp->t_template));
+ */
+/* free(tp, M_PCB); */
+ free(tp);
+ so->so_tcpcb = NULL;
+ soisfdisconnected(so);
+ /* clobber input socket cache if we're closing the cached connection */
+ if (so == tcp_last_so)
+ tcp_last_so = &tcb;
+ closesocket(so->s);
+ sbfree(&so->so_rcv);
+ sbfree(&so->so_snd);
+ sofree(so);
+ STAT(tcpstat.tcps_closed++);
+ return ((struct tcpcb *)0);
+}
+
+#ifdef notdef
+void
+tcp_drain()
+{
+ /* XXX */
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment. We will gradually open it again as we proceed.
+ */
+void
+tcp_quench(i, errno)
+
+ int errno;
+{
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if (tp)
+ tp->snd_cwnd = tp->t_maxseg;
+}
+
+#endif /* notdef */
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+
+/*
+ * 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.
+ */
+void
+tcp_sockclosed(struct tcpcb *tp)
+{
+
+ DEBUG_CALL("tcp_sockclosed");
+ DEBUG_ARG("tp = %lx", (long)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;
+ }
+/* soisfdisconnecting(tp->t_socket); */
+ if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
+ soisfdisconnected(tp->t_socket);
+ if (tp)
+ tcp_output(tp);
+}
+
+/*
+ * Connect to a host on the Internet
+ * Called by tcp_input
+ * Only do a connect, the tcp fields will be set in tcp_input
+ * return 0 if there's a result of the connect,
+ * else return -1 means we're still connecting
+ * The return value is almost always -1 since the socket is
+ * nonblocking. Connect returns after the SYN is sent, and does
+ * not wait for ACK+SYN.
+ */
+int tcp_fconnect(struct socket *so)
+{
+ int ret=0;
+
+ DEBUG_CALL("tcp_fconnect");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if( (ret=so->s=socket(AF_INET,SOCK_STREAM,0)) >= 0) {
+ int opt, s=so->s;
+ struct sockaddr_in addr;
+
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt ));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt ));
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ /* It's an alias */
+ switch(ntohl(so->so_faddr.s_addr) & 0xff) {
+ case CTL_DNS:
+ addr.sin_addr = dns_addr;
+ break;
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
+ "addr.sin_addr.s_addr=%.16s\n",
+ ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+ /* We don't care what port we get */
+ ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
+
+ /*
+ * If it's not in progress, it failed, so we just return 0,
+ * without clearing SS_NOFDREF
+ */
+ soisfconnecting(so);
+ }
+
+ return(ret);
+}
+
+/*
+ * Accept the socket and connect to the local-host
+ *
+ * We have a problem. The correct thing to do would be
+ * to first connect to the local-host, and only if the
+ * connection is accepted, then do an accept() here.
+ * But, a) we need to know who's trying to connect
+ * to the socket to be able to SYN the local-host, and
+ * b) we are already connected to the foreign host by
+ * the time it gets to accept(), so... We simply accept
+ * here and SYN the local-host.
+ */
+void
+tcp_connect(struct socket *inso)
+{
+ struct socket *so;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct tcpcb *tp;
+ int s, opt;
+
+ DEBUG_CALL("tcp_connect");
+ DEBUG_ARG("inso = %lx", (long)inso);
+
+ /*
+ * If it's an SS_ACCEPTONCE socket, no need to socreate()
+ * another socket, just use the accept() socket.
+ */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ /* FACCEPTONCE already have a tcpcb */
+ so = inso;
+ } else {
+ if ((so = socreate()) == NULL) {
+ /* If it failed, get rid of the pending connection */
+ closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
+ return;
+ }
+ if (tcp_attach(so) < 0) {
+ free(so); /* NOT sofree */
+ return;
+ }
+ so->so_laddr = inso->so_laddr;
+ so->so_lport = inso->so_lport;
+ }
+
+ (void) tcp_mss(sototcpcb(so), 0);
+
+ if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) {
+ tcp_close(sototcpcb(so)); /* This will sofree() as well */
+ return;
+ }
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int));
+
+ so->so_fport = addr.sin_port;
+ so->so_faddr = addr.sin_addr;
+ /* Translate connections from localhost to the real hostname */
+ if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+
+ /* Close the accept() socket, set right state */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ closesocket(so->s); /* If we only accept once, close the accept() socket */
+ so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
+ /* if it's not FACCEPTONCE, it's already NOFDREF */
+ }
+ so->s = s;
+
+ so->so_iptos = tcp_tos(so);
+ tp = sototcpcb(so);
+
+ tcp_template(tp);
+
+ /* 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); */ /* NOFDREF used instead */
+ STAT(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/2;
+ tcp_sendseqinit(tp);
+ tcp_output(tp);
+}
+
+/*
+ * Attach a TCPCB to a socket.
+ */
+int
+tcp_attach(struct socket *so)
+{
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
+ return -1;
+
+ insque(so, &tcb);
+
+ return 0;
+}
+
+/*
+ * Set the socket's type of service field
+ */
+static const struct tos_t tcptos[] = {
+ {0, 20, IPTOS_THROUGHPUT, 0}, /* ftp data */
+ {21, 21, IPTOS_LOWDELAY, EMU_FTP}, /* ftp control */
+ {0, 23, IPTOS_LOWDELAY, 0}, /* telnet */
+ {0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */
+ {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */
+ {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */
+ {0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */
+ {0, 543, IPTOS_LOWDELAY, 0}, /* klogin */
+ {0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */
+ {0, 6668, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC undernet */
+ {0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */
+ {0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */
+ {0, 0, 0, 0}
+};
+
+#ifdef CONFIG_QEMU
+static
+#endif
+struct emu_t *tcpemu = NULL;
+
+/*
+ * Return TOS according to the above table
+ */
+u_int8_t
+tcp_tos(struct socket *so)
+{
+ int i = 0;
+ struct emu_t *emup;
+
+ while(tcptos[i].tos) {
+ if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) ||
+ (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) {
+ so->so_emu = tcptos[i].emu;
+ return tcptos[i].tos;
+ }
+ i++;
+ }
+
+ /* Nope, lets see if there's a user-added one */
+ for (emup = tcpemu; emup; emup = emup->next) {
+ if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) ||
+ (emup->lport && (ntohs(so->so_lport) == emup->lport))) {
+ so->so_emu = emup->emu;
+ return emup->tos;
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+int do_echo = -1;
+#endif
+
+/*
+ * Emulate programs that try and connect to us
+ * This includes ftp (the data connection is
+ * initiated by the server) and IRC (DCC CHAT and
+ * DCC SEND) for now
+ *
+ * NOTE: It's possible to crash SLiRP by sending it
+ * unstandard strings to emulate... if this is a problem,
+ * more checks are needed here
+ *
+ * XXX Assumes the whole command came in one packet
+ *
+ * XXX Some ftp clients will have their TOS set to
+ * LOWDELAY and so Nagel will kick in. Because of this,
+ * we'll get the first letter, followed by the rest, so
+ * we simply scan for ORT instead of PORT...
+ * DCC doesn't have this problem because there's other stuff
+ * in the packet before the DCC command.
+ *
+ * Return 1 if the mbuf m is still valid and should be
+ * sbappend()ed
+ *
+ * NOTE: if you return 0 you MUST m_free() the mbuf!
+ */
+int
+tcp_emu(struct socket *so, struct mbuf *m)
+{
+ u_int n1, n2, n3, n4, n5, n6;
+ char buff[257];
+ u_int32_t laddr;
+ u_int lport;
+ char *bptr;
+
+ DEBUG_CALL("tcp_emu");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ switch(so->so_emu) {
+ int x, i;
+
+ case EMU_IDENT:
+ /*
+ * Identification protocol as per rfc-1413
+ */
+
+ {
+ struct socket *tmpso;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m->m_data[m->m_len] = 0; /* NULL terminate */
+ if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) {
+ if (sscanf(so_rcv->sb_data, "%u%*[ ,]%u", &n1, &n2) == 2) {
+ HTONS(n1);
+ HTONS(n2);
+ /* n2 is the one on our host */
+ for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) {
+ if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr &&
+ tmpso->so_lport == n2 &&
+ tmpso->so_faddr.s_addr == so->so_faddr.s_addr &&
+ tmpso->so_fport == n1) {
+ if (getsockname(tmpso->s,
+ (struct sockaddr *)&addr, &addrlen) == 0)
+ n2 = ntohs(addr.sin_port);
+ break;
+ }
+ }
+ }
+ so_rcv->sb_cc = snprintf(so_rcv->sb_data,
+ so_rcv->sb_datalen,
+ "%d,%d\r\n", n1, n2);
+ so_rcv->sb_rptr = so_rcv->sb_data;
+ so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
+ }
+ m_free(m);
+ return 0;
+ }
+
+#if 0
+ case EMU_RLOGIN:
+ /*
+ * Rlogin emulation
+ * First we accumulate all the initial option negotiation,
+ * then fork_exec() rlogin according to the options
+ */
+ {
+ int i, i2, n;
+ char *ptr;
+ char args[100];
+ char term[100];
+ struct sbuf *so_snd = &so->so_snd;
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ /* First check if they have a priveladged port, or too much data has arrived */
+ if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 ||
+ (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) {
+ memcpy(so_snd->sb_wptr, "Permission denied\n", 18);
+ so_snd->sb_wptr += 18;
+ so_snd->sb_cc += 18;
+ tcp_sockclosed(sototcpcb(so));
+ m_free(m);
+ return 0;
+ }
+
+ /* Append the current data */
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m_free(m);
+
+ /*
+ * Check if we have all the initial options,
+ * and build argument list to rlogin while we're here
+ */
+ n = 0;
+ ptr = so_rcv->sb_data;
+ args[0] = 0;
+ term[0] = 0;
+ while (ptr < so_rcv->sb_wptr) {
+ if (*ptr++ == 0) {
+ n++;
+ if (n == 2) {
+ sprintf(args, "rlogin -l %s %s",
+ ptr, inet_ntoa(so->so_faddr));
+ } else if (n == 3) {
+ i2 = so_rcv->sb_wptr - ptr;
+ for (i = 0; i < i2; i++) {
+ if (ptr[i] == '/') {
+ ptr[i] = 0;
+#ifdef HAVE_SETENV
+ sprintf(term, "%s", ptr);
+#else
+ sprintf(term, "TERM=%s", ptr);
+#endif
+ ptr[i] = '/';
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (n != 4)
+ return 0;
+
+ /* We have it, set our term variable and fork_exec() */
+#ifdef HAVE_SETENV
+ setenv("TERM", term, 1);
+#else
+ putenv(term);
+#endif
+ fork_exec(so, args, 2);
+ term[0] = 0;
+ so->so_emu = 0;
+
+ /* And finally, send the client a 0 character */
+ so_snd->sb_wptr[0] = 0;
+ so_snd->sb_wptr++;
+ so_snd->sb_cc++;
+
+ return 0;
+ }
+
+ case EMU_RSH:
+ /*
+ * rsh emulation
+ * First we accumulate all the initial option negotiation,
+ * then rsh_exec() rsh according to the options
+ */
+ {
+ int n;
+ char *ptr;
+ char *user;
+ char *args;
+ struct sbuf *so_snd = &so->so_snd;
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ /* First check if they have a priveladged port, or too much data has arrived */
+ if (ntohs(so->so_lport) > 1023 || ntohs(so->so_lport) < 512 ||
+ (m->m_len + so_rcv->sb_wptr) > (so_rcv->sb_data + so_rcv->sb_datalen)) {
+ memcpy(so_snd->sb_wptr, "Permission denied\n", 18);
+ so_snd->sb_wptr += 18;
+ so_snd->sb_cc += 18;
+ tcp_sockclosed(sototcpcb(so));
+ m_free(m);
+ return 0;
+ }
+
+ /* Append the current data */
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m_free(m);
+
+ /*
+ * Check if we have all the initial options,
+ * and build argument list to rlogin while we're here
+ */
+ n = 0;
+ ptr = so_rcv->sb_data;
+ user="";
+ args="";
+ if (so->extra==NULL) {
+ struct socket *ns;
+ struct tcpcb* tp;
+ int port=atoi(ptr);
+ if (port <= 0) return 0;
+ if (port > 1023 || port < 512) {
+ memcpy(so_snd->sb_wptr, "Permission denied\n", 18);
+ so_snd->sb_wptr += 18;
+ so_snd->sb_cc += 18;
+ tcp_sockclosed(sototcpcb(so));
+ return 0;
+ }
+ if ((ns=socreate()) == NULL)
+ return 0;
+ if (tcp_attach(ns)<0) {
+ free(ns);
+ return 0;
+ }
+
+ ns->so_laddr=so->so_laddr;
+ ns->so_lport=htons(port);
+
+ (void) tcp_mss(sototcpcb(ns), 0);
+
+ ns->so_faddr=so->so_faddr;
+ ns->so_fport=htons(IPPORT_RESERVED-1); /* Use a fake port. */
+
+ if (ns->so_faddr.s_addr == 0 ||
+ ns->so_faddr.s_addr == loopback_addr.s_addr)
+ ns->so_faddr = alias_addr;
+
+ ns->so_iptos = tcp_tos(ns);
+ tp = sototcpcb(ns);
+
+ tcp_template(tp);
+
+ /* 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++;
+ */
+
+ /*soisfconnecting(ns);*/
+
+ STAT(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/2;
+ tcp_sendseqinit(tp);
+ tcp_output(tp);
+ so->extra=ns;
+ }
+ while (ptr < so_rcv->sb_wptr) {
+ if (*ptr++ == 0) {
+ n++;
+ if (n == 2) {
+ user=ptr;
+ } else if (n == 3) {
+ args=ptr;
+ }
+ }
+ }
+
+ if (n != 4)
+ return 0;
+
+ rsh_exec(so,so->extra, user, inet_ntoa(so->so_faddr), args);
+ so->so_emu = 0;
+ so->extra=NULL;
+
+ /* And finally, send the client a 0 character */
+ so_snd->sb_wptr[0] = 0;
+ so_snd->sb_wptr++;
+ so_snd->sb_cc++;
+
+ return 0;
+ }
+
+ case EMU_CTL:
+ {
+ int num;
+ struct sbuf *so_snd = &so->so_snd;
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ /*
+ * If there is binary data here, we save it in so->so_m
+ */
+ if (!so->so_m) {
+ int rxlen;
+ char *rxdata;
+ rxdata=mtod(m, char *);
+ for (rxlen=m->m_len; rxlen; rxlen--) {
+ if (*rxdata++ & 0x80) {
+ so->so_m = m;
+ return 0;
+ }
+ }
+ } /* if(so->so_m==NULL) */
+
+ /*
+ * Append the line
+ */
+ sbappendsb(so_rcv, m);
+
+ /* To avoid going over the edge of the buffer, we reset it */
+ if (so_snd->sb_cc == 0)
+ so_snd->sb_wptr = so_snd->sb_rptr = so_snd->sb_data;
+
+ /*
+ * A bit of a hack:
+ * If the first packet we get here is 1 byte long, then it
+ * was done in telnet character mode, therefore we must echo
+ * the characters as they come. Otherwise, we echo nothing,
+ * because in linemode, the line is already echoed
+ * XXX two or more control connections won't work
+ */
+ if (do_echo == -1) {
+ if (m->m_len == 1) do_echo = 1;
+ else do_echo = 0;
+ }
+ if (do_echo) {
+ sbappendsb(so_snd, m);
+ m_free(m);
+ tcp_output(sototcpcb(so)); /* XXX */
+ } else
+ m_free(m);
+
+ num = 0;
+ while (num < so->so_rcv.sb_cc) {
+ if (*(so->so_rcv.sb_rptr + num) == '\n' ||
+ *(so->so_rcv.sb_rptr + num) == '\r') {
+ int n;
+
+ *(so_rcv->sb_rptr + num) = 0;
+ if (ctl_password && !ctl_password_ok) {
+ /* Need a password */
+ if (sscanf(so_rcv->sb_rptr, "pass %256s", buff) == 1) {
+ if (strcmp(buff, ctl_password) == 0) {
+ ctl_password_ok = 1;
+ n = sprintf(so_snd->sb_wptr,
+ "Password OK.\r\n");
+ goto do_prompt;
+ }
+ }
+ n = sprintf(so_snd->sb_wptr,
+ "Error: Password required, log on with \"pass PASSWORD\"\r\n");
+ goto do_prompt;
+ }
+ cfg_quitting = 0;
+ n = do_config(so_rcv->sb_rptr, so, PRN_SPRINTF);
+ if (!cfg_quitting) {
+ /* Register the printed data */
+do_prompt:
+ so_snd->sb_cc += n;
+ so_snd->sb_wptr += n;
+ /* Add prompt */
+ n = sprintf(so_snd->sb_wptr, "Slirp> ");
+ so_snd->sb_cc += n;
+ so_snd->sb_wptr += n;
+ }
+ /* Drop so_rcv data */
+ so_rcv->sb_cc = 0;
+ so_rcv->sb_wptr = so_rcv->sb_rptr = so_rcv->sb_data;
+ tcp_output(sototcpcb(so)); /* Send the reply */
+ }
+ num++;
+ }
+ return 0;
+ }
+#endif
+ case EMU_FTP: /* ftp */
+ *(m->m_data+m->m_len) = 0; /* NUL terminate for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {
+ /*
+ * Need to emulate the PORT command
+ */
+ x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
+ "ORT %d,%d,%d,%d,%d,%d\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+ return 1;
+ } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
+ /*
+ * Need to emulate the PASV response
+ */
+ x = sscanf(bptr, "27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
+ "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+
+ return 1;
+ }
+
+ return 1;
+
+ case EMU_KSH:
+ /*
+ * The kshell (Kerberos rsh) and shell services both pass
+ * a local port port number to carry signals to the server
+ * and stderr to the client. It is passed at the beginning
+ * of the connection as a NUL-terminated decimal ASCII string.
+ */
+ so->so_emu = 0;
+ for (lport = 0, i = 0; i < m->m_len-1; ++i) {
+ if (m->m_data[i] < '0' || m->m_data[i] > '9')
+ return 1; /* invalid number */
+ lport *= 10;
+ lport += m->m_data[i] - '0';
+ }
+ if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
+ (so = solisten(0, so->so_laddr.s_addr, htons(lport), SS_FACCEPTONCE)) != NULL)
+ m->m_len = snprintf(m->m_data, m->m_hdr.mh_size, "%d",
+ ntohs(so->so_fport)) + 1;
+ return 1;
+
+ case EMU_IRC:
+ /*
+ * Need to emulate DCC CHAT, DCC SEND and DCC MOVE
+ */
+ *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)
+ return 1;
+
+ /* The %256s is for the broken mIRC */
+ if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {
+ if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC CHAT chat %lu %u%c\n",
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), 1);
+ } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC SEND %s %lu %u %u%c\n", buff,
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = solisten(0, htonl(laddr), htons(lport), SS_FACCEPTONCE)) == NULL)
+ return 1;
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC MOVE %s %lu %u %u%c\n", buff,
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ }
+ return 1;
+
+ case EMU_REALAUDIO:
+ /*
+ * RealAudio emulation - JP. We must try to parse the incoming
+ * data and try to find the two characters that contain the
+ * port number. Then we redirect an udp port and replace the
+ * number with the real port we got.
+ *
+ * The 1.0 beta versions of the player are not supported
+ * any more.
+ *
+ * A typical packet for player version 1.0 (release version):
+ *
+ * 0000:50 4E 41 00 05
+ * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 .....×..gælÜc..P
+ * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH
+ * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v
+ * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB
+ *
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * second packet. This time we received five bytes first and
+ * then the rest. You never know how many bytes you get.
+ *
+ * A typical packet for player version 2.0 (beta):
+ *
+ * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA...........Á.
+ * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .guxõc..Win2.0.0
+ * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/
+ * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas
+ * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B
+ *
+ * Port number 0x1BC1 is found at offset 0x0d.
+ *
+ * This is just a horrible switch statement. Variable ra tells
+ * us where we're going.
+ */
+
+ bptr = m->m_data;
+ while (bptr < m->m_data + m->m_len) {
+ u_short p;
+ static int ra = 0;
+ char ra_tbl[4];
+
+ ra_tbl[0] = 0x50;
+ ra_tbl[1] = 0x4e;
+ ra_tbl[2] = 0x41;
+ ra_tbl[3] = 0;
+
+ switch (ra) {
+ case 0:
+ case 2:
+ case 3:
+ if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 1:
+ /*
+ * We may get 0x50 several times, ignore them
+ */
+ if (*bptr == 0x50) {
+ ra = 1;
+ bptr++;
+ continue;
+ } else if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 4:
+ /*
+ * skip version number
+ */
+ bptr++;
+ break;
+
+ case 5:
+ /*
+ * The difference between versions 1.0 and
+ * 2.0 is here. For future versions of
+ * the player this may need to be modified.
+ */
+ if (*(bptr + 1) == 0x02)
+ bptr += 8;
+ else
+ bptr += 4;
+ break;
+
+ case 6:
+ /* This is the field containing the port
+ * number that RA-player is listening to.
+ */
+ lport = (((u_char*)bptr)[0] << 8)
+ + ((u_char *)bptr)[1];
+ if (lport < 6970)
+ lport += 256; /* don't know why */
+ if (lport < 6970 || lport > 7170)
+ return 1; /* failed */
+
+ /* try to get udp port between 6970 - 7170 */
+ for (p = 6970; p < 7071; p++) {
+ if (udp_listen( htons(p),
+ so->so_laddr.s_addr,
+ htons(lport),
+ SS_FACCEPTONCE)) {
+ break;
+ }
+ }
+ if (p == 7071)
+ p = 0;
+ *(u_char *)bptr++ = (p >> 8) & 0xff;
+ *(u_char *)bptr++ = p & 0xff;
+ ra = 0;
+ return 1; /* port redirected, we're done */
+ break;
+
+ default:
+ ra = 0;
+ }
+ ra++;
+ }
+ return 1;
+
+ default:
+ /* Ooops, not emulated, won't call tcp_emu again */
+ so->so_emu = 0;
+ return 1;
+ }
+}
+
+/*
+ * Do misc. config of SLiRP while its running.
+ * Return 0 if this connections is to be closed, 1 otherwise,
+ * return 2 if this is a command-line connection
+ */
+int
+tcp_ctl(struct socket *so)
+{
+ struct sbuf *sb = &so->so_snd;
+ int command;
+ struct ex_list *ex_ptr;
+ int do_pty;
+ // struct socket *tmpso;
+
+ DEBUG_CALL("tcp_ctl");
+ DEBUG_ARG("so = %lx", (long )so);
+
+#if 0
+ /*
+ * Check if they're authorised
+ */
+ if (ctl_addr.s_addr && (ctl_addr.s_addr == -1 || (so->so_laddr.s_addr != ctl_addr.s_addr))) {
+ sb->sb_cc = sprintf(sb->sb_wptr,"Error: Permission denied.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return 0;
+ }
+#endif
+ command = (ntohl(so->so_faddr.s_addr) & 0xff);
+
+ switch(command) {
+ default: /* Check for exec's */
+
+ /*
+ * Check if it's pty_exec
+ */
+ for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_fport == so->so_fport &&
+ command == ex_ptr->ex_addr) {
+ if (ex_ptr->ex_pty == 3) {
+ so->s = -1;
+ so->extra = (void *)ex_ptr->ex_exec;
+ return 1;
+ }
+ do_pty = ex_ptr->ex_pty;
+ goto do_exec;
+ }
+ }
+
+ /*
+ * Nothing bound..
+ */
+ /* tcp_fconnect(so); */
+
+ /* FALLTHROUGH */
+ case CTL_ALIAS:
+ sb->sb_cc = snprintf(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data),
+ "Error: No application configured.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return(0);
+
+ do_exec:
+ DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec));
+ return(fork_exec(so, ex_ptr->ex_exec, do_pty));
+
+#if 0
+ case CTL_CMD:
+ for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) {
+ if (tmpso->so_emu == EMU_CTL &&
+ !(tmpso->so_tcpcb?
+ (tmpso->so_tcpcb->t_state & (TCPS_TIME_WAIT|TCPS_LAST_ACK))
+ :0)) {
+ /* Ooops, control connection already active */
+ sb->sb_cc = sprintf(sb->sb_wptr,"Sorry, already connected.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return 0;
+ }
+ }
+ so->so_emu = EMU_CTL;
+ ctl_password_ok = 0;
+ sb->sb_cc = sprintf(sb->sb_wptr, "Slirp command-line ready (type \"help\" for help).\r\nSlirp> ");
+ sb->sb_wptr += sb->sb_cc;
+ do_echo=-1;
+ return(2);
+#endif
+ }
+}
diff --git a/slirp/tcp_timer.c b/slirp/tcp_timer.c
new file mode 100644
index 0000000..9215eb2
--- /dev/null
+++ b/slirp/tcp_timer.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 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. 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.1 (Berkeley) 6/10/93
+ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
+ */
+
+#include <slirp.h>
+
+#ifdef LOG_ENABLED
+struct tcpstat tcpstat; /* tcp statistics */
+#endif
+
+u_int32_t tcp_now; /* for RFC 1323 timestamps */
+
+static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
+
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo(void)
+{
+ register struct socket *so;
+ register struct tcpcb *tp;
+
+ DEBUG_CALL("tcp_fasttimo");
+
+ so = tcb.so_next;
+ if (so)
+ for (; so != &tcb; so = so->so_next)
+ if ((tp = (struct tcpcb *)so->so_tcpcb) &&
+ (tp->t_flags & TF_DELACK)) {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ STAT(tcpstat.tcps_delack++);
+ (void) tcp_output(tp);
+ }
+}
+
+/*
+ * 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(void)
+{
+ register struct socket *ip, *ipnxt;
+ register struct tcpcb *tp;
+ register int i;
+
+ DEBUG_CALL("tcp_slowtimo");
+
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = tcb.so_next;
+ if (ip == 0)
+ return;
+ for (; ip != &tcb; ip = ipnxt) {
+ ipnxt = ip->so_next;
+ tp = sototcpcb(ip);
+ if (tp == 0)
+ continue;
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+ tcp_timers(tp,i);
+ if (ipnxt->so_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 = 0; /* XXX */
+#endif
+ tcp_now++; /* for timestamps */
+}
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(struct tcpcb *tp)
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * TCP timer processing.
+ */
+static struct tcpcb *
+tcp_timers(register struct tcpcb *tp, int timer)
+{
+ register int rexmt;
+
+ DEBUG_CALL("tcp_timers");
+
+ 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] = TCPTV_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:
+
+ /*
+ * XXXXX If a packet has timed out, then remove all the queued
+ * packets for that session.
+ */
+
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ /*
+ * This is a hack to suit our terminal server here at the uni of canberra
+ * since they have trouble with zeroes... It usually lets them through
+ * unharmed, but under some conditions, it'll eat the zeros. If we
+ * keep retransmitting it, it'll keep eating the zeroes, so we keep
+ * retransmitting, and eventually the connection dies...
+ * (this only happens on incoming data)
+ *
+ * So, if we were gonna drop the connection from too many retransmits,
+ * don't... instead halve the t_maxseg, which might break up the NULLs and
+ * let them through
+ *
+ * *sigh*
+ */
+
+ tp->t_maxseg >>= 1;
+ if (tp->t_maxseg < 32) {
+ /*
+ * We tried our best, now the connection must die!
+ */
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ STAT(tcpstat.tcps_timeoutdrop++);
+ tp = tcp_drop(tp, tp->t_softerror);
+ /* tp->t_softerror : ETIMEDOUT); */ /* XXX */
+ return (tp); /* XXX */
+ }
+
+ /*
+ * Set rxtshift to 6, which is still at the maximum
+ * backoff time
+ */
+ tp->t_rxtshift = 6;
+ }
+ STAT(tcpstat.tcps_rexmttimeo++);
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+ 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 threshold size.
+ * For a threshold, 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 threshold
+ * 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;
+
+ /*
+ * Persistence timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ STAT(tcpstat.tcps_persisttimeo++);
+ 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:
+ STAT(tcpstat.tcps_keeptimeo++);
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+
+/* if (tp->t_socket->so_options & SO_KEEPALIVE && */
+ if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) {
+ if (tp->t_idle >= TCPTV_KEEP_IDLE + 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.
+ */
+ STAT(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] = TCPTV_KEEPINTVL;
+ } else
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
+ break;
+
+ dropit:
+ STAT(tcpstat.tcps_keepdrops++);
+ tp = tcp_drop(tp, 0); /* ETIMEDOUT); */
+ break;
+ }
+
+ return (tp);
+}
diff --git a/slirp/tcp_timer.h b/slirp/tcp_timer.h
new file mode 100644
index 0000000..791ee49
--- /dev/null
+++ b/slirp/tcp_timer.h
@@ -0,0 +1,133 @@
+/*
+ * 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. 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.h 8.1 (Berkeley) 6/10/93
+ * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
+ */
+
+#ifndef _TCP_TIMER_H_
+#define _TCP_TIMER_H_
+
+/*
+ * Definitions of the TCP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define TCPT_NTIMERS 4
+
+#define TCPT_REXMT 0 /* retransmit */
+#define TCPT_PERSIST 1 /* retransmit persistence */
+#define TCPT_KEEP 2 /* keep alive */
+#define TCPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection. Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection. We force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */
+
+#define TCPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
+#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
+#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEPCNT 8 /* max probes before drop */
+
+#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+/* #define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) */ /* max allowable REXMT value */
+#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+
+#ifdef TCPTIMERS
+char *tcptimers[] =
+ { "REXMT", "PERSIST", "KEEP", "2MSL" };
+#endif
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((tv) < (tvmin)) \
+ (tv) = (tvmin); \
+ else if ((tv) > (tvmax)) \
+ (tv) = (tvmax); \
+}
+
+extern const int tcp_backoff[];
+
+struct tcpcb;
+
+void tcp_fasttimo _P((void));
+void tcp_slowtimo _P((void));
+void tcp_canceltimers _P((struct tcpcb *));
+
+#endif
diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h
new file mode 100644
index 0000000..d4af1c8
--- /dev/null
+++ b/slirp/tcp_var.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * 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. 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.3 (Berkeley) 4/10/94
+ * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
+ */
+
+#ifndef _TCP_VAR_H_
+#define _TCP_VAR_H_
+
+#include "tcpip.h"
+#include "tcp_timer.h"
+
+/*
+ * 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 */
+
+ /* Make it static for now */
+/* struct tcpiphdr *t_template; / * skeletal packet for transmit */
+ struct tcpiphdr t_template;
+
+ struct socket *t_socket; /* back pointer to socket */
+/*
+ * 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_int32_t snd_wnd; /* send window */
+/* receive sequence variables */
+ u_int32_t 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_int32_t snd_cwnd; /* congestion-controlled window */
+ u_int32_t snd_ssthresh; /* snd_cwnd size threshold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ 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_int32_t 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_int32_t ts_recent; /* timestamp echo data */
+ u_int32_t ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+
+};
+
+#define sototcpcb(so) ((so)->so_tcpcb)
+
+/*
+ * 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)
+
+#ifdef LOG_ENABLED
+/*
+ * 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_socachemiss; /* tcp_last_so misses */
+ u_long tcps_didnuttin; /* Times tcp_output didn't do anything XXX */
+};
+
+extern struct tcpstat tcpstat; /* tcp statistics */
+#endif
+
+extern u_int32_t tcp_now; /* for RFC 1323 timestamps */
+
+#endif
diff --git a/slirp/tcpip.h b/slirp/tcpip.h
new file mode 100644
index 0000000..7974ce3
--- /dev/null
+++ b/slirp/tcpip.h
@@ -0,0 +1,77 @@
+/*
+ * 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. 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.
+ *
+ * @(#)tcpip.h 8.1 (Berkeley) 6/10/93
+ * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
+ */
+
+#ifndef _TCPIP_H_
+#define _TCPIP_H_
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr {
+ struct ipovly ti_i; /* overlaid ip structure */
+ struct tcphdr ti_t; /* tcp header */
+};
+#define ti_mbuf ti_i.ih_mbuf.mptr
+#define ti_x1 ti_i.ih_x1
+#define ti_pr ti_i.ih_pr
+#define ti_len ti_i.ih_len
+#define ti_src ti_i.ih_src
+#define ti_dst ti_i.ih_dst
+#define ti_sport ti_t.th_sport
+#define ti_dport ti_t.th_dport
+#define ti_seq ti_t.th_seq
+#define ti_ack ti_t.th_ack
+#define ti_x2 ti_t.th_x2
+#define ti_off ti_t.th_off
+#define ti_flags ti_t.th_flags
+#define ti_win ti_t.th_win
+#define ti_sum ti_t.th_sum
+#define ti_urp ti_t.th_urp
+
+#define tcpiphdr2qlink(T) ((struct qlink*)(((char*)(T)) - sizeof(struct qlink)))
+#define qlink2tcpiphdr(Q) ((struct tcpiphdr*)(((char*)(Q)) + sizeof(struct qlink)))
+#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next)
+#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev)
+#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next)
+#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
+#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
+
+/*
+ * Just a clean way to get to the first byte
+ * of the packet
+ */
+struct tcpiphdr_2 {
+ struct tcpiphdr dummy;
+ char first_char;
+};
+
+#endif
diff --git a/slirp/tftp.c b/slirp/tftp.c
new file mode 100644
index 0000000..4ad5504
--- /dev/null
+++ b/slirp/tftp.c
@@ -0,0 +1,425 @@
+/*
+ * tftp.c - a simple, read-only tftp server for qemu
+ *
+ * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <slirp.h>
+#include "qemu-common.h" // for pstrcpy
+
+struct tftp_session {
+ int in_use;
+ unsigned char filename[TFTP_FILENAME_MAX];
+
+ struct in_addr client_ip;
+ u_int16_t client_port;
+
+ int timestamp;
+};
+
+static struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+
+const char *tftp_prefix;
+
+static void tftp_session_update(struct tftp_session *spt)
+{
+ spt->timestamp = curtime;
+ spt->in_use = 1;
+}
+
+static void tftp_session_terminate(struct tftp_session *spt)
+{
+ spt->in_use = 0;
+}
+
+static int tftp_session_allocate(struct tftp_t *tp)
+{
+ struct tftp_session *spt;
+ int k;
+
+ for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+ spt = &tftp_sessions[k];
+
+ if (!spt->in_use)
+ goto found;
+
+ /* sessions time out after 5 inactive seconds */
+ if ((int)(curtime - spt->timestamp) > 5000)
+ goto found;
+ }
+
+ return -1;
+
+ found:
+ memset(spt, 0, sizeof(*spt));
+ memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
+ spt->client_port = tp->udp.uh_sport;
+
+ tftp_session_update(spt);
+
+ return k;
+}
+
+static int tftp_session_find(struct tftp_t *tp)
+{
+ struct tftp_session *spt;
+ int k;
+
+ for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+ spt = &tftp_sessions[k];
+
+ if (spt->in_use) {
+ if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
+ if (spt->client_port == tp->udp.uh_sport) {
+ return k;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
+ u_int8_t *buf, int len)
+{
+ int fd;
+ int bytes_read = 0;
+ char buffer[1024];
+ int n;
+
+ n = snprintf(buffer, sizeof(buffer), "%s/%s",
+ tftp_prefix, spt->filename);
+ if (n >= sizeof(buffer))
+ return -1;
+
+ fd = open(buffer, O_RDONLY | O_BINARY);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (len) {
+ lseek(fd, block_nr * 512, SEEK_SET);
+
+ bytes_read = read(fd, buf, len);
+ }
+
+ close(fd);
+
+ return bytes_read;
+}
+
+static int tftp_send_oack(struct tftp_session *spt,
+ const char *key, uint32_t value,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int n = 0;
+
+ m = m_get();
+
+ if (!m)
+ return -1;
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_OACK);
+ n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
+ key) + 1;
+ n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
+ value) + 1;
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ m->m_len = sizeof(struct tftp_t) - 514 + n -
+ sizeof(struct ip) - sizeof(struct udphdr);
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ return 0;
+}
+
+
+
+static int tftp_send_error(struct tftp_session *spt,
+ u_int16_t errorcode, const char *msg,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int nobytes;
+
+ m = m_get();
+
+ if (!m) {
+ return -1;
+ }
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_ERROR);
+ tp->x.tp_error.tp_error_code = htons(errorcode);
+ pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ nobytes = 2;
+
+ m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ tftp_session_terminate(spt);
+
+ return 0;
+}
+
+static int tftp_send_data(struct tftp_session *spt,
+ u_int16_t block_nr,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int nobytes;
+
+ if (block_nr < 1) {
+ return -1;
+ }
+
+ m = m_get();
+
+ if (!m) {
+ return -1;
+ }
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_DATA);
+ tp->x.tp_data.tp_block_nr = htons(block_nr);
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
+
+ if (nobytes < 0) {
+ m_free(m);
+
+ /* send "file not found" error back */
+
+ tftp_send_error(spt, 1, "File not found", tp);
+
+ return -1;
+ }
+
+ m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ if (nobytes == 512) {
+ tftp_session_update(spt);
+ }
+ else {
+ tftp_session_terminate(spt);
+ }
+
+ return 0;
+}
+
+static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
+{
+ struct tftp_session *spt;
+ int s, k, n;
+ u_int8_t *src, *dst;
+
+ s = tftp_session_allocate(tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ spt = &tftp_sessions[s];
+
+ src = tp->x.tp_buf;
+ dst = spt->filename;
+ n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
+
+ /* get name */
+
+ for (k = 0; k < n; k++) {
+ if (k < TFTP_FILENAME_MAX) {
+ dst[k] = src[k];
+ }
+ else {
+ return;
+ }
+
+ if (src[k] == '\0') {
+ break;
+ }
+ }
+
+ if (k >= n) {
+ return;
+ }
+
+ k++;
+
+ /* check mode */
+ if ((n - k) < 6) {
+ return;
+ }
+
+ if (memcmp(&src[k], "octet\0", 6) != 0) {
+ tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
+ return;
+ }
+
+ k += 6; /* skipping octet */
+
+ /* do sanity checks on the filename */
+
+ if ((spt->filename[0] != '/')
+ || (spt->filename[strlen((char *)spt->filename) - 1] == '/')
+ || strstr((char *)spt->filename, "/../")) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ /* only allow exported prefixes */
+
+ if (!tftp_prefix) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ /* check if the file exists */
+
+ if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
+ tftp_send_error(spt, 1, "File not found", tp);
+ return;
+ }
+
+ if (src[n - 1] != 0) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ while (k < n) {
+ const char *key, *value;
+
+ key = (char *)src + k;
+ k += strlen(key) + 1;
+
+ if (k >= n) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ value = (char *)src + k;
+ k += strlen(value) + 1;
+
+ if (strcmp(key, "tsize") == 0) {
+ int tsize = atoi(value);
+ struct stat stat_p;
+
+ if (tsize == 0 && tftp_prefix) {
+ char buffer[1024];
+ int len;
+
+ len = snprintf(buffer, sizeof(buffer), "%s/%s",
+ tftp_prefix, spt->filename);
+
+ if (stat(buffer, &stat_p) == 0)
+ tsize = stat_p.st_size;
+ else {
+ tftp_send_error(spt, 1, "File not found", tp);
+ return;
+ }
+ }
+
+ tftp_send_oack(spt, "tsize", tsize, tp);
+ }
+ }
+
+ tftp_send_data(spt, 1, tp);
+}
+
+static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
+{
+ int s;
+
+ s = tftp_session_find(tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ if (tftp_send_data(&tftp_sessions[s],
+ ntohs(tp->x.tp_data.tp_block_nr) + 1,
+ tp) < 0) {
+ return;
+ }
+}
+
+void tftp_input(struct mbuf *m)
+{
+ struct tftp_t *tp = (struct tftp_t *)m->m_data;
+
+ switch(ntohs(tp->tp_op)) {
+ case TFTP_RRQ:
+ tftp_handle_rrq(tp, m->m_len);
+ break;
+
+ case TFTP_ACK:
+ tftp_handle_ack(tp, m->m_len);
+ break;
+ }
+}
diff --git a/slirp/tftp.h b/slirp/tftp.h
new file mode 100644
index 0000000..8f2675e
--- /dev/null
+++ b/slirp/tftp.h
@@ -0,0 +1,33 @@
+/* tftp defines */
+
+#define TFTP_SESSIONS_MAX 3
+
+#define TFTP_SERVER 69
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+#define TFTP_FILENAME_MAX 512
+
+struct tftp_t {
+ struct ip ip;
+ struct udphdr udp;
+ u_int16_t tp_op;
+ union {
+ struct {
+ u_int16_t tp_block_nr;
+ u_int8_t tp_buf[512];
+ } tp_data;
+ struct {
+ u_int16_t tp_error_code;
+ u_int8_t tp_msg[512];
+ } tp_error;
+ u_int8_t tp_buf[512 + 2];
+ } x;
+};
+
+void tftp_input(struct mbuf *m);
diff --git a/slirp/udp.c b/slirp/udp.c
new file mode 100644
index 0000000..11e78cd
--- /dev/null
+++ b/slirp/udp.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 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. 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.4 (Berkeley) 1/21/94
+ * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+#ifdef LOG_ENABLED
+struct udpstat udpstat;
+#endif
+
+struct socket udb;
+
+static u_int8_t udp_tos(struct socket *so);
+static void udp_emu(struct socket *so, struct mbuf *m);
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
+#ifndef COMPAT_42
+#define UDPCKSUM 1
+#else
+#define UDPCKSUM 0 /* XXX */
+#endif
+
+struct socket *udp_last_so = &udb;
+
+void
+udp_init(void)
+{
+ udb.so_next = udb.so_prev = &udb;
+}
+/* m->m_data points at ip packet header
+ * m->m_len length ip packet
+ * ip->ip_len length data (IPDU)
+ */
+void
+udp_input(register struct mbuf *m, int iphlen)
+{
+ register struct ip *ip;
+ register struct udphdr *uh;
+/* struct mbuf *opts = 0;*/
+ int len;
+ struct ip save_ip;
+ struct socket *so;
+
+ DEBUG_CALL("udp_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("iphlen = %d", iphlen);
+
+ STAT(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 *);
+ 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_int16_t)uh->uh_ulen);
+
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ STAT(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;
+ save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (UDPCKSUM && uh->uh_sum) {
+ memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr));
+ ((struct ipovly *)ip)->ih_x1 = 0;
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+ /* keep uh_sum for ICMP reply
+ * uh->uh_sum = cksum(m, len + sizeof (struct ip));
+ * if (uh->uh_sum) {
+ */
+ if(cksum(m, len + sizeof(struct ip))) {
+ STAT(udpstat.udps_badsum++);
+ goto bad;
+ }
+ }
+
+ /*
+ * handle DHCP/BOOTP
+ */
+ if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
+ bootp_input(m);
+ goto bad;
+ }
+
+ if (slirp_restrict)
+ goto bad;
+
+ /*
+ * handle TFTP
+ */
+ if (ntohs(uh->uh_dport) == TFTP_SERVER) {
+ tftp_input(m);
+ goto bad;
+ }
+
+ /*
+ * Locate pcb for datagram.
+ */
+ so = udp_last_so;
+ if (so->so_lport != uh->uh_sport ||
+ so->so_laddr.s_addr != ip->ip_src.s_addr) {
+ struct socket *tmp;
+
+ for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) {
+ if (tmp->so_lport == uh->uh_sport &&
+ tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
+ tmp->so_faddr.s_addr = ip->ip_dst.s_addr;
+ tmp->so_fport = uh->uh_dport;
+ so = tmp;
+ break;
+ }
+ }
+ if (tmp == &udb) {
+ so = NULL;
+ } else {
+ STAT(udpstat.udpps_pcbcachemiss++);
+ udp_last_so = so;
+ }
+ }
+
+ if (so == NULL) {
+ /*
+ * If there's no socket for this packet,
+ * create one
+ */
+ if ((so = socreate()) == NULL) goto bad;
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ goto bad;
+ }
+
+ /*
+ * Setup fields
+ */
+ /* udp_last_so = so; */
+ so->so_laddr = ip->ip_src;
+ so->so_lport = uh->uh_sport;
+
+ if ((so->so_iptos = udp_tos(so)) == 0)
+ so->so_iptos = ip->ip_tos;
+
+ /*
+ * XXXXX Here, check if it's in udpexec_list,
+ * and if it is, do the fork_exec() etc.
+ */
+ }
+
+ so->so_faddr = ip->ip_dst; /* XXX */
+ so->so_fport = uh->uh_dport; /* XXX */
+
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+
+ /*
+ * Now we sendto() the packet.
+ */
+ if (so->so_emu)
+ udp_emu(so, m);
+
+ if(sosendto(so,m) == -1) {
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ }
+
+ m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
+
+ /* restore the orig mbuf packet */
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ so->so_m=m; /* ICMP backup */
+
+ return;
+bad:
+ m_freem(m);
+ /* if (opts) m_freem(opts); */
+ return;
+}
+
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos)
+{
+ register struct udpiphdr *ui;
+ int error = 0;
+
+ DEBUG_CALL("udp_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
+ DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
+
+ /*
+ * Adjust for header
+ */
+ m->m_data -= sizeof(struct udpiphdr);
+ m->m_len += sizeof(struct udpiphdr);
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */
+ /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
+ ui->ui_src = saddr->sin_addr;
+ ui->ui_dst = daddr->sin_addr;
+ ui->ui_sport = saddr->sin_port;
+ ui->ui_dport = daddr->sin_port;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if (UDPCKSUM) {
+ if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0)
+ ui->ui_sum = 0xffff;
+ }
+ ((struct ip *)ui)->ip_len = m->m_len;
+
+ ((struct ip *)ui)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ui)->ip_tos = iptos;
+
+ STAT(udpstat.udps_opackets++);
+
+ error = ip_output(so, m);
+
+ return (error);
+}
+
+int udp_output(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *addr)
+
+{
+ struct sockaddr_in saddr, daddr;
+
+ saddr = *addr;
+ if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr) {
+ if ((so->so_faddr.s_addr & htonl(0x000000ff)) == htonl(0xff))
+ saddr.sin_addr.s_addr = alias_addr.s_addr;
+ else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
+ (ntohl(so->so_faddr.s_addr) & 0xff) != CTL_ALIAS)
+ saddr.sin_addr.s_addr = so->so_faddr.s_addr;
+ }
+ daddr.sin_addr = so->so_laddr;
+ daddr.sin_port = so->so_lport;
+
+ return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
+}
+
+int
+udp_attach(struct socket *so)
+{
+ struct sockaddr_in addr;
+
+ if((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) {
+ /*
+ * Here, we bind() the socket. Although not really needed
+ * (sendto() on an unbound socket will bind it), it's done
+ * here so that emulation of ytalk etc. don't have to do it
+ */
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if(bind(so->s, (struct sockaddr *)&addr, sizeof(addr))<0) {
+ int lasterrno=errno;
+ closesocket(so->s);
+ so->s=-1;
+#ifdef _WIN32
+ WSASetLastError(lasterrno);
+#else
+ errno=lasterrno;
+#endif
+ } else {
+ /* success, insert in queue */
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so,&udb);
+ }
+ }
+ return(so->s);
+}
+
+void
+udp_detach(struct socket *so)
+{
+ closesocket(so->s);
+ /* if (so->so_m) m_free(so->so_m); done by sofree */
+
+ sofree(so);
+}
+
+static const struct tos_t udptos[] = {
+ {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
+ {517, 517, IPTOS_LOWDELAY, EMU_TALK}, /* talk */
+ {518, 518, IPTOS_LOWDELAY, EMU_NTALK}, /* ntalk */
+ {0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME}, /* Cu-Seeme */
+ {0, 0, 0, 0}
+};
+
+static u_int8_t
+udp_tos(struct socket *so)
+{
+ int i = 0;
+
+ while(udptos[i].tos) {
+ if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
+ (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
+ so->so_emu = udptos[i].emu;
+ return udptos[i].tos;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+#ifdef EMULATE_TALK
+#include "talkd.h"
+#endif
+
+/*
+ * Here, talk/ytalk/ntalk requests must be emulated
+ */
+static void
+udp_emu(struct socket *so, struct mbuf *m)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+#ifdef EMULATE_TALK
+ CTL_MSG_OLD *omsg;
+ CTL_MSG *nmsg;
+ char buff[sizeof(CTL_MSG)];
+ u_char type;
+
+struct talk_request {
+ struct talk_request *next;
+ struct socket *udp_so;
+ struct socket *tcp_so;
+} *req;
+
+ static struct talk_request *req_tbl = 0;
+
+#endif
+
+struct cu_header {
+ uint16_t d_family; // destination family
+ uint16_t d_port; // destination port
+ uint32_t d_addr; // destination address
+ uint16_t s_family; // source family
+ uint16_t s_port; // source port
+ uint32_t so_addr; // source address
+ uint32_t seqn; // sequence number
+ uint16_t message; // message
+ uint16_t data_type; // data type
+ uint16_t pkt_len; // packet length
+} *cu_head;
+
+ switch(so->so_emu) {
+
+#ifdef EMULATE_TALK
+ case EMU_TALK:
+ case EMU_NTALK:
+ /*
+ * Talk emulation. We always change the ctl_addr to get
+ * some answers from the daemon. When an ANNOUNCE comes,
+ * we send LEAVE_INVITE to the local daemons. Also when a
+ * DELETE comes, we send copies to the local daemons.
+ */
+ if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0)
+ return;
+
+#define IS_OLD (so->so_emu == EMU_TALK)
+
+#define COPY_MSG(dest, src) { dest->type = src->type; \
+ dest->id_num = src->id_num; \
+ dest->pid = src->pid; \
+ dest->addr = src->addr; \
+ dest->ctl_addr = src->ctl_addr; \
+ memcpy(&dest->l_name, &src->l_name, NAME_SIZE_OLD); \
+ memcpy(&dest->r_name, &src->r_name, NAME_SIZE_OLD); \
+ memcpy(&dest->r_tty, &src->r_tty, TTY_SIZE); }
+
+#define OTOSIN(ptr, field) ((struct sockaddr_in *)&ptr->field)
+/* old_sockaddr to sockaddr_in */
+
+
+ if (IS_OLD) { /* old talk */
+ omsg = mtod(m, CTL_MSG_OLD*);
+ nmsg = (CTL_MSG *) buff;
+ type = omsg->type;
+ OTOSIN(omsg, ctl_addr)->sin_port = addr.sin_port;
+ OTOSIN(omsg, ctl_addr)->sin_addr = our_addr;
+ pstrcpy(omsg->l_name, NAME_SIZE_OLD, getlogin());
+ } else { /* new talk */
+ omsg = (CTL_MSG_OLD *) buff;
+ nmsg = mtod(m, CTL_MSG *);
+ type = nmsg->type;
+ OTOSIN(nmsg, ctl_addr)->sin_port = addr.sin_port;
+ OTOSIN(nmsg, ctl_addr)->sin_addr = our_addr;
+ pstrcpy(nmsg->l_name, NAME_SIZE_OLD, getlogin());
+ }
+
+ if (type == LOOK_UP)
+ return; /* for LOOK_UP this is enough */
+
+ if (IS_OLD) { /* make a copy of the message */
+ COPY_MSG(nmsg, omsg);
+ nmsg->vers = 1;
+ nmsg->answer = 0;
+ } else
+ COPY_MSG(omsg, nmsg);
+
+ /*
+ * If if is an ANNOUNCE message, we go through the
+ * request table to see if a tcp port has already
+ * been redirected for this socket. If not, we solisten()
+ * a new socket and add this entry to the table.
+ * The port number of the tcp socket and our IP
+ * are put to the addr field of the message structures.
+ * Then a LEAVE_INVITE is sent to both local daemon
+ * ports, 517 and 518. This is why we have two copies
+ * of the message, one in old talk and one in new talk
+ * format.
+ */
+
+ if (type == ANNOUNCE) {
+ int s;
+ u_short temp_port;
+
+ for(req = req_tbl; req; req = req->next)
+ if (so == req->udp_so)
+ break; /* found it */
+
+ if (!req) { /* no entry for so, create new */
+ req = (struct talk_request *)
+ malloc(sizeof(struct talk_request));
+ req->udp_so = so;
+ req->tcp_so = solisten(0,
+ OTOSIN(omsg, addr)->sin_addr.s_addr,
+ OTOSIN(omsg, addr)->sin_port,
+ SS_FACCEPTONCE);
+ req->next = req_tbl;
+ req_tbl = req;
+ }
+
+ /* replace port number in addr field */
+ addrlen = sizeof(addr);
+ getsockname(req->tcp_so->s,
+ (struct sockaddr *) &addr,
+ &addrlen);
+ OTOSIN(omsg, addr)->sin_port = addr.sin_port;
+ OTOSIN(omsg, addr)->sin_addr = our_addr;
+ OTOSIN(nmsg, addr)->sin_port = addr.sin_port;
+ OTOSIN(nmsg, addr)->sin_addr = our_addr;
+
+ /* send LEAVE_INVITEs */
+ temp_port = OTOSIN(omsg, ctl_addr)->sin_port;
+ OTOSIN(omsg, ctl_addr)->sin_port = 0;
+ OTOSIN(nmsg, ctl_addr)->sin_port = 0;
+ omsg->type = nmsg->type = LEAVE_INVITE;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ addr.sin_addr = our_addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(517);
+ sendto(s, (char *)omsg, sizeof(*omsg), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ addr.sin_port = htons(518);
+ sendto(s, (char *)nmsg, sizeof(*nmsg), 0,
+ (struct sockaddr *) &addr, sizeof(addr));
+ closesocket(s) ;
+
+ omsg->type = nmsg->type = ANNOUNCE;
+ OTOSIN(omsg, ctl_addr)->sin_port = temp_port;
+ OTOSIN(nmsg, ctl_addr)->sin_port = temp_port;
+ }
+
+ /*
+ * If it is a DELETE message, we send a copy to the
+ * local daemons. Then we delete the entry corresponding
+ * to our socket from the request table.
+ */
+
+ if (type == DELETE) {
+ struct talk_request *temp_req, *req_next;
+ int s;
+ u_short temp_port;
+
+ temp_port = OTOSIN(omsg, ctl_addr)->sin_port;
+ OTOSIN(omsg, ctl_addr)->sin_port = 0;
+ OTOSIN(nmsg, ctl_addr)->sin_port = 0;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ addr.sin_addr = our_addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(517);
+ sendto(s, (char *)omsg, sizeof(*omsg), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ addr.sin_port = htons(518);
+ sendto(s, (char *)nmsg, sizeof(*nmsg), 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ closesocket(s);
+
+ OTOSIN(omsg, ctl_addr)->sin_port = temp_port;
+ OTOSIN(nmsg, ctl_addr)->sin_port = temp_port;
+
+ /* delete table entry */
+ if (so == req_tbl->udp_so) {
+ temp_req = req_tbl;
+ req_tbl = req_tbl->next;
+ free(temp_req);
+ } else {
+ temp_req = req_tbl;
+ for(req = req_tbl->next; req; req = req_next) {
+ req_next = req->next;
+ if (so == req->udp_so) {
+ temp_req->next = req_next;
+ free(req);
+ break;
+ } else {
+ temp_req = req;
+ }
+ }
+ }
+ }
+
+ return;
+#endif
+
+ case EMU_CUSEEME:
+
+ /*
+ * Cu-SeeMe emulation.
+ * Hopefully the packet is more that 16 bytes long. We don't
+ * do any other tests, just replace the address and port
+ * fields.
+ */
+ if (m->m_len >= sizeof (*cu_head)) {
+ if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0)
+ return;
+ cu_head = mtod(m, struct cu_header *);
+ cu_head->s_port = addr.sin_port;
+ cu_head->so_addr = our_addr.s_addr;
+ }
+
+ return;
+ }
+}
+
+struct socket *
+udp_listen(u_int port, u_int32_t laddr, u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1;
+
+ if ((so = socreate()) == NULL) {
+ free(so);
+ return NULL;
+ }
+ so->s = socket(AF_INET,SOCK_DGRAM,0);
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so,&udb);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = port;
+
+ if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
+ udp_detach(so);
+ return NULL;
+ }
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+/* setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); */
+
+ getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->so_lport = lport;
+ so->so_laddr.s_addr = laddr;
+ if (flags != SS_FACCEPTONCE)
+ so->so_expire = 0;
+
+ so->so_state = SS_ISFCONNECTED;
+
+ return so;
+}
diff --git a/slirp/udp.h b/slirp/udp.h
new file mode 100644
index 0000000..51a07a2
--- /dev/null
+++ b/slirp/udp.h
@@ -0,0 +1,108 @@
+/*
+ * 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. 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.h 8.1 (Berkeley) 6/10/93
+ * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
+ */
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#define UDP_TTL 0x60
+#define UDP_UDPDATALEN 16192
+
+extern struct socket *udp_last_so;
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ u_int16_t uh_sport; /* source port */
+ u_int16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ u_int16_t uh_sum; /* udp checksum */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_mbuf ui_i.ih_mbuf.mptr
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+#ifdef LOG_ENABLED
+struct udpstat {
+ /* input statistics: */
+ u_long udps_ipackets; /* total input packets */
+ u_long udps_hdrops; /* packet shorter than header */
+ u_long udps_badsum; /* checksum error */
+ u_long udps_badlen; /* data length larger than packet */
+ u_long udps_noport; /* no socket on port */
+ u_long udps_noportbcast; /* of above, arrived as broadcast */
+ u_long udps_fullsock; /* not delivered, input socket full */
+ u_long udpps_pcbcachemiss; /* input packets missing pcb cache */
+ /* output statistics: */
+ u_long udps_opackets; /* total output packets */
+};
+#endif
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
+#define UDPCTL_MAXID 2
+
+#ifdef LOG_ENABLED
+extern struct udpstat udpstat;
+#endif
+
+extern struct socket udb;
+struct mbuf;
+
+void udp_init _P((void));
+void udp_input _P((register struct mbuf *, int));
+int udp_output _P((struct socket *, struct mbuf *, struct sockaddr_in *));
+int udp_attach _P((struct socket *));
+void udp_detach _P((struct socket *));
+struct socket * udp_listen _P((u_int, u_int32_t, u_int, int));
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos);
+#endif