slirp: Initial import of QEMU slirp
This version of slirp is a fork from QEMU Version 2.4.0.1 slirp implementation Taken from commit id of 474590efc51f262fb5d81ca417d510cb7fb7a914 in the qemu repository git://repo.or.cz/qemu/ar7.git Thanks to Fabrice Bellard.
This commit is contained in:
parent
6bf92de3db
commit
e4cf98c4c0
41 changed files with 11407 additions and 0 deletions
61
slirp/COPYRIGHT
Normal file
61
slirp/COPYRIGHT
Normal file
|
@ -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.
|
3
slirp/Makefile.objs
Normal file
3
slirp/Makefile.objs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o
|
||||||
|
common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
|
||||||
|
common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o
|
14
slirp/README
Normal file
14
slirp/README
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
October 6, 2015
|
||||||
|
|
||||||
|
This version of slirp is a fork from QEMU Version 2.4.0.1 slirp implementation
|
||||||
|
Taken from commit 474590efc51f262fb5d81ca417d510cb7fb7a914
|
||||||
|
in the qemu repository git://repo.or.cz/qemu/ar7.git
|
||||||
|
|
||||||
|
The goal being to absolutely mininize changes from the base qemu provided code
|
||||||
|
so that the more active changes being done by the qemu folks can easily be
|
||||||
|
adopted in the future by directly replacing the revised modules. This is
|
||||||
|
achieved by careful manipulation of the include files provided in the
|
||||||
|
slirp/qemu directory so that things are stubbed out locally and the minimal
|
||||||
|
capabilities needed for sim_ether integration are provided.
|
||||||
|
|
||||||
|
Thanks to Fabrice Bellard.
|
89
slirp/arp_table.c
Normal file
89
slirp/arp_table.c
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* ARP table
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 AdaCore
|
||||||
|
*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN])
|
||||||
|
{
|
||||||
|
const uint32_t broadcast_addr =
|
||||||
|
~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr;
|
||||||
|
ArpTable *arptbl = &slirp->arp_table;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DEBUG_CALL("arp_table_add");
|
||||||
|
DEBUG_ARG("ip = 0x%x", ip_addr);
|
||||||
|
DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
ethaddr[0], ethaddr[1], ethaddr[2],
|
||||||
|
ethaddr[3], ethaddr[4], ethaddr[5]));
|
||||||
|
|
||||||
|
if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
|
||||||
|
/* Do not register broadcast addresses */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search for an entry */
|
||||||
|
for (i = 0; i < ARP_TABLE_SIZE; i++) {
|
||||||
|
if (arptbl->table[i].ar_sip == ip_addr) {
|
||||||
|
/* Update the entry */
|
||||||
|
memcpy(arptbl->table[i].ar_sha, ethaddr, ETH_ALEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No entry found, create a new one */
|
||||||
|
arptbl->table[arptbl->next_victim].ar_sip = ip_addr;
|
||||||
|
memcpy(arptbl->table[arptbl->next_victim].ar_sha, ethaddr, ETH_ALEN);
|
||||||
|
arptbl->next_victim = (arptbl->next_victim + 1) % ARP_TABLE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
|
||||||
|
uint8_t out_ethaddr[ETH_ALEN])
|
||||||
|
{
|
||||||
|
const uint32_t broadcast_addr =
|
||||||
|
~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr;
|
||||||
|
ArpTable *arptbl = &slirp->arp_table;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DEBUG_CALL("arp_table_search");
|
||||||
|
DEBUG_ARG("ip = 0x%x", ip_addr);
|
||||||
|
|
||||||
|
/* If broadcast address */
|
||||||
|
if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
|
||||||
|
/* return Ethernet broadcast address */
|
||||||
|
memset(out_ethaddr, 0xff, ETH_ALEN);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARP_TABLE_SIZE; i++) {
|
||||||
|
if (arptbl->table[i].ar_sip == ip_addr) {
|
||||||
|
memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN);
|
||||||
|
DEBUG_ARGS((dfd, " found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
out_ethaddr[0], out_ethaddr[1], out_ethaddr[2],
|
||||||
|
out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
332
slirp/bootp.c
Normal file
332
slirp/bootp.c
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
/*
|
||||||
|
* 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 LEASE_TIME (24 * 3600)
|
||||||
|
|
||||||
|
static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define DPRINTF(fmt, ...) \
|
||||||
|
do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(fmt, ...) do{}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr,
|
||||||
|
const uint8_t *macaddr)
|
||||||
|
{
|
||||||
|
BOOTPClient *bc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
|
||||||
|
bc = &slirp->bootp_clients[i];
|
||||||
|
if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
found:
|
||||||
|
bc = &slirp->bootp_clients[i];
|
||||||
|
bc->allocated = 1;
|
||||||
|
paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
|
||||||
|
return bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr,
|
||||||
|
const uint8_t *macaddr)
|
||||||
|
{
|
||||||
|
uint32_t req_addr = ntohl(paddr->s_addr);
|
||||||
|
uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr);
|
||||||
|
BOOTPClient *bc;
|
||||||
|
|
||||||
|
if (req_addr >= dhcp_addr &&
|
||||||
|
req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) {
|
||||||
|
bc = &slirp->bootp_clients[req_addr - dhcp_addr];
|
||||||
|
if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
|
||||||
|
bc->allocated = 1;
|
||||||
|
return bc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr,
|
||||||
|
const uint8_t *macaddr)
|
||||||
|
{
|
||||||
|
BOOTPClient *bc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
|
||||||
|
if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6))
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
found:
|
||||||
|
bc = &slirp->bootp_clients[i];
|
||||||
|
bc->allocated = 1;
|
||||||
|
paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
|
||||||
|
return bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
|
||||||
|
struct in_addr *preq_addr)
|
||||||
|
{
|
||||||
|
const uint8_t *p, *p_end;
|
||||||
|
int len, tag;
|
||||||
|
|
||||||
|
*pmsg_type = 0;
|
||||||
|
preq_addr->s_addr = htonl(0L);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
memcpy(&(preq_addr->s_addr), p, 4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) &&
|
||||||
|
bp->bp_ciaddr.s_addr) {
|
||||||
|
memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
|
||||||
|
{
|
||||||
|
BOOTPClient *bc = NULL;
|
||||||
|
struct mbuf *m;
|
||||||
|
struct bootp_t *rbp;
|
||||||
|
struct sockaddr_in saddr, daddr;
|
||||||
|
struct in_addr preq_addr;
|
||||||
|
int dhcp_msg_type, val;
|
||||||
|
uint8_t *q;
|
||||||
|
uint8_t client_ethaddr[ETH_ALEN];
|
||||||
|
|
||||||
|
/* 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.s_addr != htonl(0L))
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Get client's hardware address from bootp request */
|
||||||
|
memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN);
|
||||||
|
|
||||||
|
m = m_get(slirp);
|
||||||
|
if (!m) {
|
||||||
|
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.s_addr != htonl(0L)) {
|
||||||
|
bc = request_addr(slirp, &preq_addr, client_ethaddr);
|
||||||
|
if (bc) {
|
||||||
|
daddr.sin_addr = preq_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bc) {
|
||||||
|
new_addr:
|
||||||
|
bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr);
|
||||||
|
if (!bc) {
|
||||||
|
DPRINTF("no address left\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(bc->macaddr, client_ethaddr, ETH_ALEN);
|
||||||
|
} else if (preq_addr.s_addr != htonl(0L)) {
|
||||||
|
bc = request_addr(slirp, &preq_addr, client_ethaddr);
|
||||||
|
if (bc) {
|
||||||
|
daddr.sin_addr = preq_addr;
|
||||||
|
memcpy(bc->macaddr, client_ethaddr, ETH_ALEN);
|
||||||
|
} else {
|
||||||
|
/* DHCPNAKs should be sent to broadcast */
|
||||||
|
daddr.sin_addr.s_addr = 0xffffffff;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bc = find_addr(slirp, &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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update ARP table for this IP address */
|
||||||
|
arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr);
|
||||||
|
|
||||||
|
saddr.sin_addr = slirp->vhost_addr;
|
||||||
|
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, ETH_ALEN);
|
||||||
|
|
||||||
|
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 (slirp->bootp_filename)
|
||||||
|
snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
|
||||||
|
slirp->bootp_filename);
|
||||||
|
|
||||||
|
*q++ = RFC2132_SRV_ID;
|
||||||
|
*q++ = 4;
|
||||||
|
memcpy(q, &saddr.sin_addr, 4);
|
||||||
|
q += 4;
|
||||||
|
|
||||||
|
*q++ = RFC1533_NETMASK;
|
||||||
|
*q++ = 4;
|
||||||
|
memcpy(q, &slirp->vnetwork_mask, 4);
|
||||||
|
q += 4;
|
||||||
|
|
||||||
|
if (!slirp->restricted) {
|
||||||
|
*q++ = RFC1533_GATEWAY;
|
||||||
|
*q++ = 4;
|
||||||
|
memcpy(q, &saddr.sin_addr, 4);
|
||||||
|
q += 4;
|
||||||
|
|
||||||
|
*q++ = RFC1533_DNS;
|
||||||
|
*q++ = 4;
|
||||||
|
memcpy(q, &slirp->vnameserver_addr, 4);
|
||||||
|
q += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
*q++ = RFC2132_LEASE_TIME;
|
||||||
|
*q++ = 4;
|
||||||
|
val = htonl(LEASE_TIME);
|
||||||
|
memcpy(q, &val, 4);
|
||||||
|
q += 4;
|
||||||
|
|
||||||
|
if (*slirp->client_hostname) {
|
||||||
|
val = strlen(slirp->client_hostname);
|
||||||
|
*q++ = RFC1533_HOSTNAME;
|
||||||
|
*q++ = val;
|
||||||
|
memcpy(q, slirp->client_hostname, val);
|
||||||
|
q += val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slirp->vdnssearch) {
|
||||||
|
size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend);
|
||||||
|
val = slirp->vdnssearch_len;
|
||||||
|
if (val + 1 > spaceleft) {
|
||||||
|
g_warning("DHCP packet size exceeded, "
|
||||||
|
"omitting domain-search option.");
|
||||||
|
} else {
|
||||||
|
memcpy(q, slirp->vdnssearch, 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(m->slirp, bp);
|
||||||
|
}
|
||||||
|
}
|
126
slirp/bootp.h
Normal file
126
slirp/bootp.h
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/* bootp/dhcp defines */
|
||||||
|
#ifndef SLIRP_BOOTP_H
|
||||||
|
#define SLIRP_BOOTP_H 1
|
||||||
|
|
||||||
|
#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];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t allocated;
|
||||||
|
uint8_t macaddr[6];
|
||||||
|
} BOOTPClient;
|
||||||
|
|
||||||
|
#define NB_BOOTP_CLIENTS 16
|
||||||
|
|
||||||
|
void bootp_input(struct mbuf *m);
|
||||||
|
|
||||||
|
#endif
|
139
slirp/cksum.c
Normal file
139
slirp/cksum.c
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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]; \
|
||||||
|
(void)ADDCARRY(sum);}
|
||||||
|
|
||||||
|
int cksum(struct mbuf *m, int len)
|
||||||
|
{
|
||||||
|
register uint16_t *w;
|
||||||
|
register int sum = 0;
|
||||||
|
register int mlen = 0;
|
||||||
|
int byte_swapped = 0;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint8_t c[2];
|
||||||
|
uint16_t s;
|
||||||
|
} s_util;
|
||||||
|
union {
|
||||||
|
uint16_t s[2];
|
||||||
|
uint32_t l;
|
||||||
|
} l_util;
|
||||||
|
|
||||||
|
if (m->m_len == 0)
|
||||||
|
goto cont;
|
||||||
|
w = mtod(m, uint16_t *);
|
||||||
|
|
||||||
|
mlen = m->m_len;
|
||||||
|
|
||||||
|
if (len < mlen)
|
||||||
|
mlen = len;
|
||||||
|
#ifdef DEBUG
|
||||||
|
len -= mlen;
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Force to even boundary.
|
||||||
|
*/
|
||||||
|
if ((1 & (uintptr_t)w) && (mlen > 0)) {
|
||||||
|
REDUCE;
|
||||||
|
sum <<= 8;
|
||||||
|
s_util.c[0] = *(uint8_t *)w;
|
||||||
|
w = (uint16_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;
|
||||||
|
if (mlen == -1) {
|
||||||
|
s_util.c[1] = *(uint8_t *)w;
|
||||||
|
sum += s_util.s;
|
||||||
|
mlen = 0;
|
||||||
|
} else
|
||||||
|
|
||||||
|
mlen = -1;
|
||||||
|
} else if (mlen == -1)
|
||||||
|
s_util.c[0] = *(uint8_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);
|
||||||
|
}
|
34
slirp/debug.h
Normal file
34
slirp/debug.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995 Danny Gasparovski.
|
||||||
|
*
|
||||||
|
* Please read the file COPYRIGHT for the
|
||||||
|
* terms and conditions of the copyright.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define DEBUG 1
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
#define DBG_CALL 0x1
|
||||||
|
#define DBG_MISC 0x2
|
||||||
|
#define DBG_ERROR 0x4
|
||||||
|
|
||||||
|
#define dfd stderr
|
||||||
|
|
||||||
|
extern int slirp_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
|
314
slirp/dnssearch.c
Normal file
314
slirp/dnssearch.c
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
/*
|
||||||
|
* Domain search option for DHCP (RFC 3397)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 Klaus Stengel
|
||||||
|
*
|
||||||
|
* 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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "slirp.h"
|
||||||
|
|
||||||
|
static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119;
|
||||||
|
static const uint8_t MAX_OPT_LEN = 255;
|
||||||
|
static const uint8_t OPT_HEADER_LEN = 2;
|
||||||
|
static const uint8_t REFERENCE_LEN = 2;
|
||||||
|
|
||||||
|
struct compact_domain;
|
||||||
|
|
||||||
|
typedef struct compact_domain {
|
||||||
|
struct compact_domain *self;
|
||||||
|
struct compact_domain *refdom;
|
||||||
|
uint8_t *labels;
|
||||||
|
size_t len;
|
||||||
|
size_t common_octets;
|
||||||
|
} CompactDomain;
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
domain_suffix_diffoff(const CompactDomain *a, const CompactDomain *b)
|
||||||
|
{
|
||||||
|
size_t la = a->len, lb = b->len;
|
||||||
|
uint8_t *da = a->labels + la, *db = b->labels + lb;
|
||||||
|
size_t i, lm = (la < lb) ? la : lb;
|
||||||
|
|
||||||
|
for (i = 0; i < lm; i++) {
|
||||||
|
da--; db--;
|
||||||
|
if (*da != *db) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int domain_suffix_ord(const void *cva, const void *cvb)
|
||||||
|
{
|
||||||
|
const CompactDomain *a = cva, *b = cvb;
|
||||||
|
size_t la = a->len, lb = b->len;
|
||||||
|
size_t doff = domain_suffix_diffoff(a, b);
|
||||||
|
uint8_t ca = a->labels[la - doff];
|
||||||
|
uint8_t cb = b->labels[lb - doff];
|
||||||
|
|
||||||
|
if (ca < cb) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ca > cb) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (la < lb) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (la > lb) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t domain_common_label(CompactDomain *a, CompactDomain *b)
|
||||||
|
{
|
||||||
|
size_t res, doff = domain_suffix_diffoff(a, b);
|
||||||
|
uint8_t *first_eq_pos = a->labels + (a->len - doff);
|
||||||
|
uint8_t *label = a->labels;
|
||||||
|
|
||||||
|
while (*label && label < first_eq_pos) {
|
||||||
|
label += *label + 1;
|
||||||
|
}
|
||||||
|
res = a->len - (label - a->labels);
|
||||||
|
/* only report if it can help to reduce the packet size */
|
||||||
|
return (res > REFERENCE_LEN) ? res : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void domain_fixup_order(CompactDomain *cd, size_t n)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
CompactDomain *cur = cd + i, *next = cd[i].self;
|
||||||
|
|
||||||
|
while (!cur->common_octets) {
|
||||||
|
CompactDomain *tmp = next->self; /* backup target value */
|
||||||
|
|
||||||
|
next->self = cur;
|
||||||
|
cur->common_octets++;
|
||||||
|
|
||||||
|
cur = next;
|
||||||
|
next = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void domain_mklabels(CompactDomain *cd, const char *input)
|
||||||
|
{
|
||||||
|
uint8_t *len_marker = cd->labels;
|
||||||
|
uint8_t *output = len_marker; /* pre-incremented */
|
||||||
|
const char *in = input;
|
||||||
|
char cur_chr;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (cd->len == 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
cd->len++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
cur_chr = *in++;
|
||||||
|
if (cur_chr == '.' || cur_chr == '\0') {
|
||||||
|
len = output - len_marker;
|
||||||
|
if ((len == 0 && cur_chr == '.') || len >= 64) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
*len_marker = len;
|
||||||
|
|
||||||
|
output++;
|
||||||
|
len_marker = output;
|
||||||
|
} else {
|
||||||
|
output++;
|
||||||
|
*output = cur_chr;
|
||||||
|
}
|
||||||
|
} while (cur_chr != '\0');
|
||||||
|
|
||||||
|
/* ensure proper zero-termination */
|
||||||
|
if (len != 0) {
|
||||||
|
*len_marker = 0;
|
||||||
|
cd->len++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
g_warning("failed to parse domain name '%s'\n", input);
|
||||||
|
cd->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
domain_mkxrefs(CompactDomain *doms, CompactDomain *last, size_t depth)
|
||||||
|
{
|
||||||
|
CompactDomain *i = doms, *target = doms;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (i->labels < target->labels) {
|
||||||
|
target = i;
|
||||||
|
}
|
||||||
|
} while (i++ != last);
|
||||||
|
|
||||||
|
for (i = doms; i != last; i++) {
|
||||||
|
CompactDomain *group_last;
|
||||||
|
size_t next_depth;
|
||||||
|
|
||||||
|
if (i->common_octets == depth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_depth = -1;
|
||||||
|
for (group_last = i; group_last != last; group_last++) {
|
||||||
|
size_t co = group_last->common_octets;
|
||||||
|
if (co <= depth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (co < next_depth) {
|
||||||
|
next_depth = co;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
domain_mkxrefs(i, group_last, next_depth);
|
||||||
|
|
||||||
|
i = group_last;
|
||||||
|
if (i == last) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = doms;
|
||||||
|
do {
|
||||||
|
if (i != target && i->refdom == NULL) {
|
||||||
|
i->refdom = target;
|
||||||
|
i->common_octets = depth;
|
||||||
|
}
|
||||||
|
} while (i++ != last);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t domain_compactify(CompactDomain *domains, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *start = domains->self->labels, *outptr = start;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
CompactDomain *cd = domains[i].self;
|
||||||
|
CompactDomain *rd = cd->refdom;
|
||||||
|
|
||||||
|
if (rd != NULL) {
|
||||||
|
size_t moff = (rd->labels - start)
|
||||||
|
+ (rd->len - cd->common_octets);
|
||||||
|
if (moff < 0x3FFFu) {
|
||||||
|
cd->len -= cd->common_octets - 2;
|
||||||
|
cd->labels[cd->len - 1] = moff & 0xFFu;
|
||||||
|
cd->labels[cd->len - 2] = 0xC0u | (moff >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cd->labels != outptr) {
|
||||||
|
memmove(outptr, cd->labels, cd->len);
|
||||||
|
cd->labels = outptr;
|
||||||
|
}
|
||||||
|
outptr += cd->len;
|
||||||
|
}
|
||||||
|
return outptr - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
int translate_dnssearch(Slirp *s, const char **names)
|
||||||
|
{
|
||||||
|
size_t blocks, bsrc_start, bsrc_end, bdst_start;
|
||||||
|
size_t i, num_domains, memreq = 0;
|
||||||
|
uint8_t *result = NULL, *outptr;
|
||||||
|
CompactDomain *domains = NULL;
|
||||||
|
const char **nameptr = names;
|
||||||
|
|
||||||
|
while (*nameptr != NULL) {
|
||||||
|
nameptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_domains = nameptr - names;
|
||||||
|
if (num_domains == 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
domains = g_malloc(num_domains * sizeof(*domains));
|
||||||
|
|
||||||
|
for (i = 0; i < num_domains; i++) {
|
||||||
|
size_t nlen = strlen(names[i]);
|
||||||
|
memreq += nlen + 2; /* 1 zero octet + 1 label length octet */
|
||||||
|
domains[i].self = domains + i;
|
||||||
|
domains[i].len = nlen;
|
||||||
|
domains[i].common_octets = 0;
|
||||||
|
domains[i].refdom = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reserve extra 2 header bytes for each 255 bytes of output */
|
||||||
|
memreq += ((memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN) * OPT_HEADER_LEN;
|
||||||
|
result = g_malloc(memreq * sizeof(*result));
|
||||||
|
|
||||||
|
outptr = result;
|
||||||
|
for (i = 0; i < num_domains; i++) {
|
||||||
|
domains[i].labels = outptr;
|
||||||
|
domain_mklabels(domains + i, names[i]);
|
||||||
|
outptr += domains[i].len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outptr == result) {
|
||||||
|
g_free(domains);
|
||||||
|
g_free(result);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(domains, num_domains, sizeof(*domains), domain_suffix_ord);
|
||||||
|
domain_fixup_order(domains, num_domains);
|
||||||
|
|
||||||
|
for (i = 1; i < num_domains; i++) {
|
||||||
|
size_t cl = domain_common_label(domains + i - 1, domains + i);
|
||||||
|
domains[i - 1].common_octets = cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain_mkxrefs(domains, domains + num_domains - 1, 0);
|
||||||
|
memreq = domain_compactify(domains, num_domains);
|
||||||
|
|
||||||
|
blocks = (memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN;
|
||||||
|
bsrc_end = memreq;
|
||||||
|
bsrc_start = (blocks - 1) * MAX_OPT_LEN;
|
||||||
|
bdst_start = bsrc_start + blocks * OPT_HEADER_LEN;
|
||||||
|
memreq += blocks * OPT_HEADER_LEN;
|
||||||
|
|
||||||
|
while (blocks--) {
|
||||||
|
size_t len = bsrc_end - bsrc_start;
|
||||||
|
memmove(result + bdst_start, result + bsrc_start, len);
|
||||||
|
result[bdst_start - 2] = RFC3397_OPT_DOMAIN_SEARCH;
|
||||||
|
result[bdst_start - 1] = len;
|
||||||
|
bsrc_end = bsrc_start;
|
||||||
|
bsrc_start -= MAX_OPT_LEN;
|
||||||
|
bdst_start -= MAX_OPT_LEN + OPT_HEADER_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(domains);
|
||||||
|
s->vdnssearch = result;
|
||||||
|
s->vdnssearch_len = memreq;
|
||||||
|
return 0;
|
||||||
|
}
|
237
slirp/if.c
Normal file
237
slirp/if.c
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995 Danny Gasparovski.
|
||||||
|
*
|
||||||
|
* Please read the file COPYRIGHT for the
|
||||||
|
* terms and conditions of the copyright.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <slirp.h>
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
|
||||||
|
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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
|
||||||
|
slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
|
||||||
|
slirp->next_m = &slirp->if_batchq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = ifm->slirp;
|
||||||
|
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 = slirp->if_batchq.ifq_prev; ifq && ifq != &slirp->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 = slirp->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 = slirp->if_batchq.ifq_prev;
|
||||||
|
/* Set next_m if the queue was empty so far */
|
||||||
|
if (slirp->next_m == &slirp->if_batchq) {
|
||||||
|
slirp->next_m = ifm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a new doubly linked list for this session */
|
||||||
|
ifm->ifq_so = so;
|
||||||
|
ifs_init(ifm);
|
||||||
|
insque(ifm, ifq);
|
||||||
|
|
||||||
|
diddit:
|
||||||
|
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, &slirp->if_batchq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FULL_BOLT
|
||||||
|
/*
|
||||||
|
* This prevents us from malloc()ing too many mbufs
|
||||||
|
*/
|
||||||
|
if_start(ifm->slirp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a packet
|
||||||
|
* We choose a packet based on its 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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||||
|
bool from_batchq, next_from_batchq;
|
||||||
|
struct mbuf *ifm, *ifm_next, *ifqt;
|
||||||
|
|
||||||
|
DEBUG_CALL("if_start");
|
||||||
|
|
||||||
|
if (slirp->if_start_busy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
slirp->if_start_busy = true;
|
||||||
|
|
||||||
|
if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
|
||||||
|
ifm_next = slirp->if_fastq.ifq_next;
|
||||||
|
next_from_batchq = false;
|
||||||
|
} else if (slirp->next_m != &slirp->if_batchq) {
|
||||||
|
/* Nothing on fastq, pick up from batchq via next_m */
|
||||||
|
ifm_next = slirp->next_m;
|
||||||
|
next_from_batchq = true;
|
||||||
|
} else {
|
||||||
|
ifm_next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ifm_next) {
|
||||||
|
ifm = ifm_next;
|
||||||
|
from_batchq = next_from_batchq;
|
||||||
|
|
||||||
|
ifm_next = ifm->ifq_next;
|
||||||
|
if (ifm_next == &slirp->if_fastq) {
|
||||||
|
/* No more packets in fastq, switch to batchq */
|
||||||
|
ifm_next = slirp->next_m;
|
||||||
|
next_from_batchq = true;
|
||||||
|
}
|
||||||
|
if (ifm_next == &slirp->if_batchq) {
|
||||||
|
/* end of batchq */
|
||||||
|
ifm_next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to send packet unless it already expired */
|
||||||
|
if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) {
|
||||||
|
/* Packet is delayed due to pending ARP resolution */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifm == slirp->next_m) {
|
||||||
|
/* Set which packet to send on next iteration */
|
||||||
|
slirp->next_m = ifm->ifq_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove it from the queue */
|
||||||
|
ifqt = ifm->ifq_prev;
|
||||||
|
remque(ifm);
|
||||||
|
|
||||||
|
/* If there are more packets for this session, re-queue them */
|
||||||
|
if (ifm->ifs_next != ifm) {
|
||||||
|
struct mbuf *next = ifm->ifs_next;
|
||||||
|
|
||||||
|
insque(next, ifqt);
|
||||||
|
ifs_remque(ifm);
|
||||||
|
|
||||||
|
if (!from_batchq) {
|
||||||
|
/* Next packet in fastq is from the same session */
|
||||||
|
ifm_next = next;
|
||||||
|
next_from_batchq = false;
|
||||||
|
} else if (slirp->next_m == &slirp->if_batchq) {
|
||||||
|
/* Set next_m and ifm_next if the session packet is now the
|
||||||
|
* only one on batchq */
|
||||||
|
slirp->next_m = ifm_next = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update so_queued */
|
||||||
|
if (ifm->ifq_so && --ifm->ifq_so->so_queued == 0) {
|
||||||
|
/* If there's no more queued, reset nqueued */
|
||||||
|
ifm->ifq_so->so_nqueued = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_free(ifm);
|
||||||
|
}
|
||||||
|
|
||||||
|
slirp->if_start_busy = false;
|
||||||
|
}
|
23
slirp/if.h
Normal file
23
slirp/if.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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 */
|
||||||
|
|
||||||
|
/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
|
||||||
|
#define IF_MAXLINKHDR (2 + 14 + 40)
|
||||||
|
|
||||||
|
#endif
|
249
slirp/ip.h
Normal file
249
slirp/ip.h
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
/*
|
||||||
|
* 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 HOST_WORDS_BIGENDIAN
|
||||||
|
# undef NTOHL
|
||||||
|
# undef NTOHS
|
||||||
|
# undef HTONL
|
||||||
|
# undef HTONS
|
||||||
|
# define NTOHL(d)
|
||||||
|
# define NTOHS(d)
|
||||||
|
# define HTONL(d)
|
||||||
|
# define HTONS(d)
|
||||||
|
#else
|
||||||
|
# ifndef NTOHL
|
||||||
|
# define NTOHL(d) ((d) = ntohl((d)))
|
||||||
|
# endif
|
||||||
|
# ifndef NTOHS
|
||||||
|
# define NTOHS(d) ((d) = ntohs((uint16_t)(d)))
|
||||||
|
# endif
|
||||||
|
# ifndef HTONL
|
||||||
|
# define HTONL(d) ((d) = htonl((d)))
|
||||||
|
# endif
|
||||||
|
# ifndef HTONS
|
||||||
|
# define HTONS(d) ((d) = htons((uint16_t)(d)))
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef uint32_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 HOST_WORDS_BIGENDIAN
|
||||||
|
uint8_t ip_v:4, /* version */
|
||||||
|
ip_hl:4; /* header length */
|
||||||
|
#else
|
||||||
|
uint8_t ip_hl:4, /* header length */
|
||||||
|
ip_v:4; /* version */
|
||||||
|
#endif
|
||||||
|
uint8_t ip_tos; /* type of service */
|
||||||
|
uint16_t ip_len; /* total length */
|
||||||
|
uint16_t ip_id; /* identification */
|
||||||
|
uint16_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 */
|
||||||
|
uint8_t ip_ttl; /* time to live */
|
||||||
|
uint8_t ip_p; /* protocol */
|
||||||
|
uint16_t ip_sum; /* checksum */
|
||||||
|
struct in_addr ip_src,ip_dst; /* source and dest address */
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
#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 {
|
||||||
|
uint8_t ipt_code; /* IPOPT_TS */
|
||||||
|
uint8_t ipt_len; /* size of structure (variable) */
|
||||||
|
uint8_t ipt_ptr; /* index of current entry */
|
||||||
|
#ifdef HOST_WORDS_BIGENDIAN
|
||||||
|
uint8_t ipt_oflw:4, /* overflow counter */
|
||||||
|
ipt_flg:4; /* flags, see below */
|
||||||
|
#else
|
||||||
|
uint8_t 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;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
#else
|
||||||
|
struct mbuf_ptr {
|
||||||
|
struct mbuf *mptr;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
#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 */
|
||||||
|
uint8_t ih_x1; /* (unused) */
|
||||||
|
uint8_t ih_pr; /* protocol */
|
||||||
|
uint16_t ih_len; /* protocol length */
|
||||||
|
struct in_addr ih_src; /* source internet address */
|
||||||
|
struct in_addr ih_dst; /* destination internet address */
|
||||||
|
} QEMU_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 */
|
||||||
|
uint8_t ipq_ttl; /* time for reass q to live */
|
||||||
|
uint8_t ipq_p; /* protocol of this fragment */
|
||||||
|
uint16_t ipq_id; /* sequence id for reassembly */
|
||||||
|
struct in_addr ipq_src,ipq_dst;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
#endif
|
450
slirp/ip_icmp.c
Normal file
450
slirp/ip_icmp.c
Normal file
|
@ -0,0 +1,450 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
void icmp_init(Slirp *slirp)
|
||||||
|
{
|
||||||
|
slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp;
|
||||||
|
slirp->icmp_last_so = &slirp->icmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void icmp_cleanup(Slirp *slirp)
|
||||||
|
{
|
||||||
|
while (slirp->icmp.so_next != &slirp->icmp) {
|
||||||
|
icmp_detach(slirp->icmp.so_next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
|
||||||
|
{
|
||||||
|
struct ip *ip = mtod(m, struct ip *);
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||||
|
if (so->s == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
so->so_m = m;
|
||||||
|
so->so_faddr = ip->ip_dst;
|
||||||
|
so->so_laddr = ip->ip_src;
|
||||||
|
so->so_iptos = ip->ip_tos;
|
||||||
|
so->so_type = IPPROTO_ICMP;
|
||||||
|
so->so_state = SS_ISFCONNECTED;
|
||||||
|
so->so_expire = curtime + SO_EXPIRE;
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr = so->so_faddr;
|
||||||
|
|
||||||
|
insque(so, &so->slirp->icmp);
|
||||||
|
|
||||||
|
if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0,
|
||||||
|
(struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||||
|
DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n",
|
||||||
|
errno, strerror(errno)));
|
||||||
|
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
|
||||||
|
icmp_detach(so);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void icmp_detach(struct socket *so)
|
||||||
|
{
|
||||||
|
closesocket(so->s);
|
||||||
|
sofree(so);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
Slirp *slirp = m->slirp;
|
||||||
|
|
||||||
|
DEBUG_CALL("icmp_input");
|
||||||
|
DEBUG_ARG("m = %lx", (long )m);
|
||||||
|
DEBUG_ARG("m_len = %d", m->m_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
||||||
|
freeit:
|
||||||
|
m_free(m);
|
||||||
|
goto end_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m->m_len -= hlen;
|
||||||
|
m->m_data += hlen;
|
||||||
|
icp = mtod(m, struct icmp *);
|
||||||
|
if (cksum(m, icmplen)) {
|
||||||
|
goto freeit;
|
||||||
|
}
|
||||||
|
m->m_len += hlen;
|
||||||
|
m->m_data -= hlen;
|
||||||
|
|
||||||
|
DEBUG_ARG("icmp_type = %d", icp->icmp_type);
|
||||||
|
switch (icp->icmp_type) {
|
||||||
|
case ICMP_ECHO:
|
||||||
|
ip->ip_len += hlen; /* since ip_input subtracts this */
|
||||||
|
if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
|
||||||
|
icmp_reflect(m);
|
||||||
|
} else if (slirp->restricted) {
|
||||||
|
goto freeit;
|
||||||
|
} else {
|
||||||
|
struct socket *so;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
if ((so = socreate(slirp)) == NULL) goto freeit;
|
||||||
|
if (icmp_send(so, m, hlen) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 & slirp->vnetwork_mask.s_addr) ==
|
||||||
|
slirp->vnetwork_addr.s_addr) {
|
||||||
|
/* It's an alias */
|
||||||
|
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
|
||||||
|
if (get_dns_addr(&addr.sin_addr) < 0)
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
} else {
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
}
|
||||||
|
} 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:
|
||||||
|
m_free(m);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
m_free(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 */
|
||||||
|
|
||||||
|
/* Do not reply to source-only IPs */
|
||||||
|
if ((ip->ip_src.s_addr & htonl(~(0xf << 28))) == 0) {
|
||||||
|
goto end_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
m = m_get(msrc->slirp);
|
||||||
|
if (!m) {
|
||||||
|
goto end_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ 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 addresses */
|
||||||
|
ip->ip_src = m->slirp->vhost_addr;
|
||||||
|
|
||||||
|
(void ) ip_output((struct socket *)NULL, m);
|
||||||
|
|
||||||
|
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_type = ICMP_ECHOREPLY;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void icmp_receive(struct socket *so)
|
||||||
|
{
|
||||||
|
struct mbuf *m = so->so_m;
|
||||||
|
struct ip *ip = mtod(m, struct ip *);
|
||||||
|
int hlen = ip->ip_hl << 2;
|
||||||
|
u_char error_code;
|
||||||
|
struct icmp *icp;
|
||||||
|
int id, len;
|
||||||
|
|
||||||
|
m->m_data += hlen;
|
||||||
|
m->m_len -= hlen;
|
||||||
|
icp = mtod(m, struct icmp *);
|
||||||
|
|
||||||
|
id = icp->icmp_id;
|
||||||
|
len = qemu_recv(so->s, icp, m->m_len, 0);
|
||||||
|
icp->icmp_id = id;
|
||||||
|
|
||||||
|
m->m_data -= hlen;
|
||||||
|
m->m_len += hlen;
|
||||||
|
|
||||||
|
if (len == -1 || len == 0) {
|
||||||
|
if (errno == ENETUNREACH) {
|
||||||
|
error_code = ICMP_UNREACH_NET;
|
||||||
|
} else {
|
||||||
|
error_code = ICMP_UNREACH_HOST;
|
||||||
|
}
|
||||||
|
DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno,
|
||||||
|
strerror(errno)));
|
||||||
|
icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
|
||||||
|
} else {
|
||||||
|
icmp_reflect(so->so_m);
|
||||||
|
so->so_m = NULL; /* Don't m_free() it again! */
|
||||||
|
}
|
||||||
|
icmp_detach(so);
|
||||||
|
}
|
165
slirp/ip_icmp.h
Normal file
165
slirp/ip_icmp.h
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* 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 uint32_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 ensure that the
|
||||||
|
* packet is large enough 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_init(Slirp *slirp);
|
||||||
|
void icmp_cleanup(Slirp *slirp);
|
||||||
|
void icmp_input(struct mbuf *, int);
|
||||||
|
void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
|
||||||
|
const char *message);
|
||||||
|
void icmp_reflect(struct mbuf *);
|
||||||
|
void icmp_receive(struct socket *so);
|
||||||
|
void icmp_detach(struct socket *so);
|
||||||
|
|
||||||
|
#endif
|
668
slirp/ip_input.c
Normal file
668
slirp/ip_input.c
Normal file
|
@ -0,0 +1,668 @@
|
||||||
|
/*
|
||||||
|
* 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 <qemu/osdep.h>
|
||||||
|
#include "ip_icmp.h"
|
||||||
|
|
||||||
|
static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp);
|
||||||
|
static void ip_freef(Slirp *slirp, 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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;
|
||||||
|
udp_init(slirp);
|
||||||
|
tcp_init(slirp);
|
||||||
|
icmp_init(slirp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ip_cleanup(Slirp *slirp)
|
||||||
|
{
|
||||||
|
udp_cleanup(slirp);
|
||||||
|
tcp_cleanup(slirp);
|
||||||
|
icmp_cleanup(slirp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = m->slirp;
|
||||||
|
register struct ip *ip;
|
||||||
|
int hlen;
|
||||||
|
|
||||||
|
DEBUG_CALL("ip_input");
|
||||||
|
DEBUG_ARG("m = %lx", (long)m);
|
||||||
|
DEBUG_ARG("m_len = %d", m->m_len);
|
||||||
|
|
||||||
|
if (m->m_len < sizeof (struct ip)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = mtod(m, struct ip *);
|
||||||
|
|
||||||
|
if (ip->ip_v != IPVERSION) {
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
hlen = ip->ip_hl << 2;
|
||||||
|
if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
|
||||||
|
goto bad; /* or packet too short */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* keep ip header intact for ICMP reply
|
||||||
|
* ip->ip_sum = cksum(m, hlen);
|
||||||
|
* if (ip->ip_sum) {
|
||||||
|
*/
|
||||||
|
if(cksum(m,hlen)) {
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert fields to host representation.
|
||||||
|
*/
|
||||||
|
NTOHS(ip->ip_len);
|
||||||
|
if (ip->ip_len < hlen) {
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = slirp->ipq.ip_link.next; l != &slirp->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) {
|
||||||
|
ip = ip_reass(slirp, ip, fp);
|
||||||
|
if (ip == NULL)
|
||||||
|
return;
|
||||||
|
m = dtom(slirp, ip);
|
||||||
|
} else
|
||||||
|
if (fp)
|
||||||
|
ip_freef(slirp, fp);
|
||||||
|
|
||||||
|
} else
|
||||||
|
ip->ip_len -= hlen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch out to protocol's input routine.
|
||||||
|
*/
|
||||||
|
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:
|
||||||
|
m_free(m);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
bad:
|
||||||
|
m_free(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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(Slirp *slirp, struct ip *ip, struct ipq *fp)
|
||||||
|
{
|
||||||
|
register struct mbuf *m = dtom(slirp, 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 = m_get(slirp);
|
||||||
|
|
||||||
|
if (t == NULL) {
|
||||||
|
goto dropfrag;
|
||||||
|
}
|
||||||
|
fp = mtod(t, struct ipq *);
|
||||||
|
insque(&fp->ip_link, &slirp->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(slirp, 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(slirp, q), i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
q = q->ipf_next;
|
||||||
|
m_free(dtom(slirp, 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(slirp, q);
|
||||||
|
|
||||||
|
q = (struct ipasfrag *) q->ipf_next;
|
||||||
|
while (q != (struct ipasfrag*)&fp->frag_link) {
|
||||||
|
struct mbuf *t = dtom(slirp, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(slirp, fp));
|
||||||
|
m->m_len += (ip->ip_hl << 2);
|
||||||
|
m->m_data -= (ip->ip_hl << 2);
|
||||||
|
|
||||||
|
return ip;
|
||||||
|
|
||||||
|
dropfrag:
|
||||||
|
m_free(m);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a fragment reassembly header and all
|
||||||
|
* associated datagrams.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ip_freef(Slirp *slirp, 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_free(dtom(slirp, q));
|
||||||
|
}
|
||||||
|
remque(&fp->ip_link);
|
||||||
|
(void) m_free(dtom(slirp, 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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
struct qlink *l;
|
||||||
|
|
||||||
|
DEBUG_CALL("ip_slowtimo");
|
||||||
|
|
||||||
|
l = slirp->ipq.ip_link.next;
|
||||||
|
|
||||||
|
if (l == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (l != &slirp->ipq.ip_link) {
|
||||||
|
struct ipq *fp = container_of(l, struct ipq, ip_link);
|
||||||
|
l = l->next;
|
||||||
|
if (--fp->ipq_ttl == 0) {
|
||||||
|
ip_freef(slirp, 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, forward = 0;
|
||||||
|
struct in_addr *sin, dst;
|
||||||
|
typedef uint32_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:
|
||||||
|
icmp_error(m, type, code, 0, 0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
172
slirp/ip_output.c
Normal file
172
slirp/ip_output.c
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = m0->slirp;
|
||||||
|
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);
|
||||||
|
|
||||||
|
ip = mtod(m, struct ip *);
|
||||||
|
/*
|
||||||
|
* Fill in IP header.
|
||||||
|
*/
|
||||||
|
ip->ip_v = IPVERSION;
|
||||||
|
ip->ip_off &= IP_DF;
|
||||||
|
ip->ip_id = htons(slirp->ip_id++);
|
||||||
|
ip->ip_hl = hlen >> 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If small enough for interface, can just send directly.
|
||||||
|
*/
|
||||||
|
if ((uint16_t)ip->ip_len <= IF_MTU) {
|
||||||
|
ip->ip_len = htons((uint16_t)ip->ip_len);
|
||||||
|
ip->ip_off = htons((uint16_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;
|
||||||
|
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 < (uint16_t)ip->ip_len; off += len) {
|
||||||
|
register struct ip *mhip;
|
||||||
|
m = m_get(slirp);
|
||||||
|
if (m == NULL) {
|
||||||
|
error = -1;
|
||||||
|
goto sendorfree;
|
||||||
|
}
|
||||||
|
m->m_data += IF_MAXLINKHDR;
|
||||||
|
mhip = mtod(m, struct ip *);
|
||||||
|
*mhip = *ip;
|
||||||
|
|
||||||
|
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 >= (uint16_t)ip->ip_len)
|
||||||
|
len = (uint16_t)ip->ip_len - off;
|
||||||
|
else
|
||||||
|
mhip->ip_off |= IP_MF;
|
||||||
|
mhip->ip_len = htons((uint16_t)(len + mhlen));
|
||||||
|
|
||||||
|
if (m_copy(m, m0, off, len) < 0) {
|
||||||
|
error = -1;
|
||||||
|
goto sendorfree;
|
||||||
|
}
|
||||||
|
|
||||||
|
mhip->ip_off = htons((uint16_t)mhip->ip_off);
|
||||||
|
mhip->ip_sum = 0;
|
||||||
|
mhip->ip_sum = cksum(m, mhlen);
|
||||||
|
*mnext = m;
|
||||||
|
mnext = &m->m_nextpkt;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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 - (uint16_t)ip->ip_len);
|
||||||
|
ip->ip_len = htons((uint16_t)m->m_len);
|
||||||
|
ip->ip_off = htons((uint16_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_free(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
bad:
|
||||||
|
m_free(m0);
|
||||||
|
goto done;
|
||||||
|
}
|
43
slirp/libslirp.h
Normal file
43
slirp/libslirp.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef _LIBSLIRP_H
|
||||||
|
#define _LIBSLIRP_H
|
||||||
|
|
||||||
|
#include "qemu-common.h"
|
||||||
|
|
||||||
|
struct Slirp;
|
||||||
|
typedef struct Slirp Slirp;
|
||||||
|
|
||||||
|
int get_dns_addr(struct in_addr *pdns_addr);
|
||||||
|
|
||||||
|
Slirp *slirp_init(int restricted, struct in_addr vnetwork,
|
||||||
|
struct in_addr vnetmask, struct in_addr vhost,
|
||||||
|
const char *vhostname, const char *tftp_path,
|
||||||
|
const char *bootfile, struct in_addr vdhcp_start,
|
||||||
|
struct in_addr vnameserver, const char **vdnssearch,
|
||||||
|
void *opaque);
|
||||||
|
void slirp_cleanup(Slirp *slirp);
|
||||||
|
|
||||||
|
void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout);
|
||||||
|
|
||||||
|
void slirp_pollfds_poll(GArray *pollfds, int select_error);
|
||||||
|
|
||||||
|
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
|
||||||
|
|
||||||
|
/* you must provide the following functions: */
|
||||||
|
void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len);
|
||||||
|
|
||||||
|
int slirp_add_hostfwd(Slirp *slirp, int is_udp,
|
||||||
|
struct in_addr host_addr, int host_port,
|
||||||
|
struct in_addr guest_addr, int guest_port);
|
||||||
|
int slirp_remove_hostfwd(Slirp *slirp, int is_udp,
|
||||||
|
struct in_addr host_addr, int host_port);
|
||||||
|
int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
|
||||||
|
struct in_addr *guest_addr, int guest_port);
|
||||||
|
|
||||||
|
void slirp_connection_info(Slirp *slirp, Monitor *mon);
|
||||||
|
|
||||||
|
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||||
|
int guest_port, const uint8_t *buf, int size);
|
||||||
|
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
|
||||||
|
int guest_port);
|
||||||
|
|
||||||
|
#endif
|
50
slirp/main.h
Normal file
50
slirp/main.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995 Danny Gasparovski.
|
||||||
|
*
|
||||||
|
* Please read the file COPYRIGHT for the
|
||||||
|
* terms and conditions of the copyright.
|
||||||
|
*/
|
||||||
|
#ifndef SLIRP_MAIN_H
|
||||||
|
#define SLIRP_MAIN_H 1
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_SELECT_H
|
||||||
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TOWRITEMAX 512
|
||||||
|
|
||||||
|
extern int slirp_socket;
|
||||||
|
extern int slirp_socket_unit;
|
||||||
|
extern int slirp_socket_port;
|
||||||
|
extern uint32_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 struct in_addr loopback_addr;
|
||||||
|
extern unsigned long loopback_mask;
|
||||||
|
extern char *username;
|
||||||
|
extern char *socket_path;
|
||||||
|
extern int towrite_max;
|
||||||
|
extern int ppp_exit;
|
||||||
|
extern int tcp_keepintvl;
|
||||||
|
|
||||||
|
#define PROTO_SLIP 0x1
|
||||||
|
#ifdef USE_PPP
|
||||||
|
#define PROTO_PPP 0x2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int if_encap(Slirp *slirp, struct mbuf *ifm);
|
||||||
|
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);
|
||||||
|
|
||||||
|
#endif
|
241
slirp/mbuf.c
Normal file
241
slirp/mbuf.c
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
#define MBUF_THRESH 30
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a nice value for msize
|
||||||
|
* XXX if_maxlinkhdr already in mtu
|
||||||
|
*/
|
||||||
|
#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
|
||||||
|
|
||||||
|
void
|
||||||
|
m_init(Slirp *slirp)
|
||||||
|
{
|
||||||
|
slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist;
|
||||||
|
slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_cleanup(Slirp *slirp)
|
||||||
|
{
|
||||||
|
struct mbuf *m, *next;
|
||||||
|
|
||||||
|
m = slirp->m_usedlist.m_next;
|
||||||
|
while (m != &slirp->m_usedlist) {
|
||||||
|
next = m->m_next;
|
||||||
|
if (m->m_flags & M_EXT) {
|
||||||
|
free(m->m_ext);
|
||||||
|
}
|
||||||
|
free(m);
|
||||||
|
m = next;
|
||||||
|
}
|
||||||
|
m = slirp->m_freelist.m_next;
|
||||||
|
while (m != &slirp->m_freelist) {
|
||||||
|
next = m->m_next;
|
||||||
|
free(m);
|
||||||
|
m = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
register struct mbuf *m;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
DEBUG_CALL("m_get");
|
||||||
|
|
||||||
|
if (slirp->m_freelist.m_next == &slirp->m_freelist) {
|
||||||
|
m = (struct mbuf *)malloc(SLIRP_MSIZE);
|
||||||
|
if (m == NULL) goto end_error;
|
||||||
|
slirp->mbuf_alloced++;
|
||||||
|
if (slirp->mbuf_alloced > MBUF_THRESH)
|
||||||
|
flags = M_DOFREE;
|
||||||
|
m->slirp = slirp;
|
||||||
|
} else {
|
||||||
|
m = slirp->m_freelist.m_next;
|
||||||
|
remque(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert it in the used list */
|
||||||
|
insque(m,&slirp->m_usedlist);
|
||||||
|
m->m_flags = (flags | M_USEDLIST);
|
||||||
|
|
||||||
|
/* Initialise it */
|
||||||
|
m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
|
||||||
|
m->m_data = m->m_dat;
|
||||||
|
m->m_len = 0;
|
||||||
|
m->m_nextpkt = NULL;
|
||||||
|
m->m_prevpkt = NULL;
|
||||||
|
m->arp_requested = false;
|
||||||
|
m->expiration_date = (uint64_t)-1;
|
||||||
|
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) {
|
||||||
|
m->slirp->mbuf_alloced--;
|
||||||
|
free(m);
|
||||||
|
} else if ((m->m_flags & M_FREELIST) == 0) {
|
||||||
|
insque(m,&m->slirp->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);
|
||||||
|
m->m_data = m->m_ext + datasize;
|
||||||
|
} else {
|
||||||
|
char *dat;
|
||||||
|
datasize = m->m_data - m->m_dat;
|
||||||
|
dat = (char *)malloc(size);
|
||||||
|
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(Slirp *slirp, void *dat)
|
||||||
|
{
|
||||||
|
struct mbuf *m;
|
||||||
|
|
||||||
|
DEBUG_CALL("dtom");
|
||||||
|
DEBUG_ARG("dat = %lx", (long )dat);
|
||||||
|
|
||||||
|
/* bug corrected for M_EXT buffers */
|
||||||
|
for (m = slirp->m_usedlist.m_next; m != &slirp->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;
|
||||||
|
}
|
118
slirp/mbuf.h
Normal file
118
slirp/mbuf.h
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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 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
|
||||||
|
*/
|
||||||
|
#define mtod(m,t) ((t)(m)->m_data)
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
/* XXX should union some of these! */
|
||||||
|
/* header at beginning of each mbuf: */
|
||||||
|
struct mbuf *m_next; /* Linked list of mbufs */
|
||||||
|
struct mbuf *m_prev;
|
||||||
|
struct mbuf *m_nextpkt; /* Next packet in queue/record */
|
||||||
|
struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */
|
||||||
|
int m_flags; /* Misc flags */
|
||||||
|
|
||||||
|
int m_size; /* Size of data */
|
||||||
|
struct socket *m_so;
|
||||||
|
|
||||||
|
caddr_t m_data; /* Location of data */
|
||||||
|
int m_len; /* Amount of data in this mbuf */
|
||||||
|
|
||||||
|
Slirp *slirp;
|
||||||
|
bool arp_requested;
|
||||||
|
uint64_t expiration_date;
|
||||||
|
/* start of dynamic buffer area, must be last element */
|
||||||
|
union {
|
||||||
|
char m_dat[1]; /* ANSI don't like 0 sized arrays */
|
||||||
|
char *m_ext;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
void m_init(Slirp *);
|
||||||
|
void m_cleanup(Slirp *slirp);
|
||||||
|
struct mbuf * m_get(Slirp *);
|
||||||
|
void m_free(struct mbuf *);
|
||||||
|
void m_cat(register struct mbuf *, register struct mbuf *);
|
||||||
|
void m_inc(struct mbuf *, int);
|
||||||
|
void m_adj(struct mbuf *, int);
|
||||||
|
int m_copy(struct mbuf *, struct mbuf *, int, int);
|
||||||
|
struct mbuf * dtom(Slirp *, void *);
|
||||||
|
|
||||||
|
static inline void ifs_init(struct mbuf *ifm)
|
||||||
|
{
|
||||||
|
ifm->ifs_next = ifm->ifs_prev = ifm;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
319
slirp/misc.c
Normal file
319
slirp/misc.c
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995 Danny Gasparovski.
|
||||||
|
*
|
||||||
|
* Please read the file COPYRIGHT for the
|
||||||
|
* terms and conditions of the copyright.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <slirp.h>
|
||||||
|
#include <libslirp.h>
|
||||||
|
|
||||||
|
#include "monitor/monitor.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_exec(struct ex_list **ex_ptr, int do_pty, char *exec,
|
||||||
|
struct in_addr 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.s_addr == tmp_ptr->ex_addr.s_addr)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_ptr = *ex_ptr;
|
||||||
|
*ex_ptr = g_new(struct ex_list, 1);
|
||||||
|
(*ex_ptr)->ex_fport = port;
|
||||||
|
(*ex_ptr)->ex_addr = addr;
|
||||||
|
(*ex_ptr)->ex_pty = do_pty;
|
||||||
|
(*ex_ptr)->ex_exec = (do_pty == 3) ? exec : g_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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
const char *argv[256];
|
||||||
|
/* don't want to clobber the original */
|
||||||
|
char *bptr;
|
||||||
|
const char *curarg;
|
||||||
|
int c, i, ret;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = 0;
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
|
if ((s = qemu_socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
|
||||||
|
bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
|
||||||
|
listen(s, 1) < 0) {
|
||||||
|
error_report("Error: inet socket: %s", strerror(errno));
|
||||||
|
closesocket(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
switch(pid) {
|
||||||
|
case -1:
|
||||||
|
error_report("Error: fork failed: %s", strerror(errno));
|
||||||
|
close(s);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
/* Set the DISPLAY */
|
||||||
|
getsockname(s, (struct sockaddr *)&addr, &addrlen);
|
||||||
|
close(s);
|
||||||
|
/*
|
||||||
|
* Connect to the socket
|
||||||
|
* XXX If any of these fail, we're in trouble!
|
||||||
|
*/
|
||||||
|
s = qemu_socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
do {
|
||||||
|
ret = connect(s, (struct sockaddr *)&addr, addrlen);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
dup2(s, 0);
|
||||||
|
dup2(s, 1);
|
||||||
|
dup2(s, 2);
|
||||||
|
for (s = getdtablesize() - 1; s >= 3; s--)
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
bptr = g_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++] = g_strdup(curarg);
|
||||||
|
} while (c);
|
||||||
|
|
||||||
|
argv[i] = NULL;
|
||||||
|
execvp(argv[0], (char **)argv);
|
||||||
|
|
||||||
|
/* Ooops, failed, let's tell the user why */
|
||||||
|
fprintf(stderr, "Error: execvp of %s failed: %s\n",
|
||||||
|
argv[0], strerror(errno));
|
||||||
|
close(0); close(1); close(2); /* XXX */
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
default:
|
||||||
|
qemu_add_child_watch(pid);
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
socket_set_fast_reuse(so->s);
|
||||||
|
opt = 1;
|
||||||
|
qemu_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
|
||||||
|
qemu_set_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
|
||||||
|
|
||||||
|
void slirp_connection_info(Slirp *slirp, Monitor *mon)
|
||||||
|
{
|
||||||
|
const char * const tcpstates[] = {
|
||||||
|
[TCPS_CLOSED] = "CLOSED",
|
||||||
|
[TCPS_LISTEN] = "LISTEN",
|
||||||
|
[TCPS_SYN_SENT] = "SYN_SENT",
|
||||||
|
[TCPS_SYN_RECEIVED] = "SYN_RCVD",
|
||||||
|
[TCPS_ESTABLISHED] = "ESTABLISHED",
|
||||||
|
[TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
|
||||||
|
[TCPS_FIN_WAIT_1] = "FIN_WAIT_1",
|
||||||
|
[TCPS_CLOSING] = "CLOSING",
|
||||||
|
[TCPS_LAST_ACK] = "LAST_ACK",
|
||||||
|
[TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
|
||||||
|
[TCPS_TIME_WAIT] = "TIME_WAIT",
|
||||||
|
};
|
||||||
|
struct in_addr dst_addr;
|
||||||
|
struct sockaddr_in src;
|
||||||
|
socklen_t src_len;
|
||||||
|
uint16_t dst_port;
|
||||||
|
struct socket *so;
|
||||||
|
const char *state;
|
||||||
|
char buf[20];
|
||||||
|
|
||||||
|
monitor_printf(mon, " Protocol[State] FD Source Address Port "
|
||||||
|
"Dest. Address Port RecvQ SendQ\n");
|
||||||
|
|
||||||
|
for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
|
||||||
|
if (so->so_state & SS_HOSTFWD) {
|
||||||
|
state = "HOST_FORWARD";
|
||||||
|
} else if (so->so_tcpcb) {
|
||||||
|
state = tcpstates[so->so_tcpcb->t_state];
|
||||||
|
} else {
|
||||||
|
state = "NONE";
|
||||||
|
}
|
||||||
|
if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
|
||||||
|
src_len = sizeof(src);
|
||||||
|
getsockname(so->s, (struct sockaddr *)&src, &src_len);
|
||||||
|
dst_addr = so->so_laddr;
|
||||||
|
dst_port = so->so_lport;
|
||||||
|
} else {
|
||||||
|
src.sin_addr = so->so_laddr;
|
||||||
|
src.sin_port = so->so_lport;
|
||||||
|
dst_addr = so->so_faddr;
|
||||||
|
dst_port = so->so_fport;
|
||||||
|
}
|
||||||
|
snprintf(buf, sizeof(buf), " TCP[%s]", state);
|
||||||
|
monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s,
|
||||||
|
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
|
||||||
|
ntohs(src.sin_port));
|
||||||
|
monitor_printf(mon, "%15s %5d %5d %5d\n",
|
||||||
|
inet_ntoa(dst_addr), ntohs(dst_port),
|
||||||
|
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
|
||||||
|
if (so->so_state & SS_HOSTFWD) {
|
||||||
|
snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]");
|
||||||
|
src_len = sizeof(src);
|
||||||
|
getsockname(so->s, (struct sockaddr *)&src, &src_len);
|
||||||
|
dst_addr = so->so_laddr;
|
||||||
|
dst_port = so->so_lport;
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), " UDP[%d sec]",
|
||||||
|
(so->so_expire - curtime) / 1000);
|
||||||
|
src.sin_addr = so->so_laddr;
|
||||||
|
src.sin_port = so->so_lport;
|
||||||
|
dst_addr = so->so_faddr;
|
||||||
|
dst_port = so->so_fport;
|
||||||
|
}
|
||||||
|
monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s,
|
||||||
|
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
|
||||||
|
ntohs(src.sin_port));
|
||||||
|
monitor_printf(mon, "%15s %5d %5d %5d\n",
|
||||||
|
inet_ntoa(dst_addr), ntohs(dst_port),
|
||||||
|
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) {
|
||||||
|
snprintf(buf, sizeof(buf), " ICMP[%d sec]",
|
||||||
|
(so->so_expire - curtime) / 1000);
|
||||||
|
src.sin_addr = so->so_laddr;
|
||||||
|
dst_addr = so->so_faddr;
|
||||||
|
monitor_printf(mon, "%-19s %3d %15s - ", buf, so->s,
|
||||||
|
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*");
|
||||||
|
monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr),
|
||||||
|
so->so_rcv.sb_cc, so->so_snd.sb_cc);
|
||||||
|
}
|
||||||
|
}
|
53
slirp/misc.h
Normal file
53
slirp/misc.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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? */
|
||||||
|
struct in_addr ex_addr; /* Server address */
|
||||||
|
int ex_fport; /* Port to telnet to */
|
||||||
|
const char *ex_exec; /* Command line of what to exec */
|
||||||
|
struct ex_list *ex_next;
|
||||||
|
};
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
struct tos_t {
|
||||||
|
uint16_t lport;
|
||||||
|
uint16_t fport;
|
||||||
|
uint8_t tos;
|
||||||
|
uint8_t emu;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct emu_t {
|
||||||
|
uint16_t lport;
|
||||||
|
uint16_t fport;
|
||||||
|
uint8_t tos;
|
||||||
|
uint8_t emu;
|
||||||
|
struct emu_t *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void slirp_insque(void *, void *);
|
||||||
|
void slirp_remque(void *);
|
||||||
|
int add_exec(struct ex_list **, int, char *, struct in_addr, int);
|
||||||
|
int fork_exec(struct socket *so, const char *ex, int do_pty);
|
||||||
|
|
||||||
|
#endif
|
187
slirp/sbuf.c
Normal file
187
slirp/sbuf.c
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995 Danny Gasparovski.
|
||||||
|
*
|
||||||
|
* Please read the file COPYRIGHT for the
|
||||||
|
* terms and conditions of the copyright.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <slirp.h>
|
||||||
|
#include <qemu/main-loop.h>
|
||||||
|
|
||||||
|
static void sbappendsb(struct sbuf *sb, struct mbuf *m);
|
||||||
|
|
||||||
|
void
|
||||||
|
sbfree(struct sbuf *sb)
|
||||||
|
{
|
||||||
|
free(sb->sb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sbdrop(struct sbuf *sb, int num)
|
||||||
|
{
|
||||||
|
int limit = sb->sb_datalen / 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
if (sb->sb_cc < limit && sb->sb_cc + num >= limit) {
|
||||||
|
qemu_notify_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
30
slirp/sbuf.h
Normal file
30
slirp/sbuf.h
Normal file
|
@ -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(struct sbuf *);
|
||||||
|
void sbdrop(struct sbuf *, int);
|
||||||
|
void sbreserve(struct sbuf *, int);
|
||||||
|
void sbappend(struct socket *, struct mbuf *);
|
||||||
|
void sbcopy(struct sbuf *, int, int, char *);
|
||||||
|
|
||||||
|
#endif
|
1207
slirp/slirp.c
Normal file
1207
slirp/slirp.c
Normal file
File diff suppressed because it is too large
Load diff
361
slirp/slirp.h
Normal file
361
slirp/slirp.h
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
#ifndef __COMMON_H__
|
||||||
|
#define __COMMON_H__
|
||||||
|
|
||||||
|
#include "config-host.h"
|
||||||
|
#include "slirp_config.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <inttypes.h>
|
||||||
|
|
||||||
|
typedef char *caddr_t;
|
||||||
|
|
||||||
|
# include <windows.h>
|
||||||
|
# include <winsock2.h>
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
# include <sys/timeb.h>
|
||||||
|
# include <iphlpapi.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define ioctlsocket ioctl
|
||||||
|
# define closesocket(s) close(s)
|
||||||
|
# if !defined(__HAIKU__)
|
||||||
|
# define O_BINARY 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_BITYPES_H
|
||||||
|
# include <sys/bitypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Systems lacking strdup() definition in <string.h>. */
|
||||||
|
#if defined(ultrix)
|
||||||
|
char *strdup(const char *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Systems lacking malloc() definition in <stdlib.h>. */
|
||||||
|
#if defined(ultrix) || defined(hcx)
|
||||||
|
void *malloc(size_t arg);
|
||||||
|
void free(void *ptr);
|
||||||
|
#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 "qemu/queue.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
|
||||||
|
#include "libslirp.h"
|
||||||
|
#include "ip.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "tcp_timer.h"
|
||||||
|
#include "tcp_var.h"
|
||||||
|
#include "tcpip.h"
|
||||||
|
#include "udp.h"
|
||||||
|
#include "ip_icmp.h"
|
||||||
|
#include "mbuf.h"
|
||||||
|
#include "sbuf.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "if.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#ifdef USE_PPP
|
||||||
|
#include "ppp/pppd.h"
|
||||||
|
#include "ppp/ppp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "bootp.h"
|
||||||
|
#include "tftp.h"
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
uint32_t ar_sip; /* sender IP address */
|
||||||
|
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
|
||||||
|
uint32_t ar_tip; /* target IP address */
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
#define ARP_TABLE_SIZE 16
|
||||||
|
|
||||||
|
typedef struct ArpTable {
|
||||||
|
struct arphdr table[ARP_TABLE_SIZE];
|
||||||
|
int next_victim;
|
||||||
|
} ArpTable;
|
||||||
|
|
||||||
|
void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]);
|
||||||
|
|
||||||
|
bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
|
||||||
|
uint8_t out_ethaddr[ETH_ALEN]);
|
||||||
|
|
||||||
|
struct Slirp {
|
||||||
|
QTAILQ_ENTRY(Slirp) entry;
|
||||||
|
u_int time_fasttimo;
|
||||||
|
u_int last_slowtimo;
|
||||||
|
bool do_slowtimo;
|
||||||
|
|
||||||
|
/* virtual network configuration */
|
||||||
|
struct in_addr vnetwork_addr;
|
||||||
|
struct in_addr vnetwork_mask;
|
||||||
|
struct in_addr vhost_addr;
|
||||||
|
struct in_addr vdhcp_startaddr;
|
||||||
|
struct in_addr vnameserver_addr;
|
||||||
|
|
||||||
|
struct in_addr client_ipaddr;
|
||||||
|
char client_hostname[33];
|
||||||
|
|
||||||
|
int restricted;
|
||||||
|
struct ex_list *exec_list;
|
||||||
|
|
||||||
|
/* mbuf states */
|
||||||
|
struct mbuf m_freelist, m_usedlist;
|
||||||
|
int mbuf_alloced;
|
||||||
|
|
||||||
|
/* if states */
|
||||||
|
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 */
|
||||||
|
bool if_start_busy; /* avoid if_start recursion */
|
||||||
|
|
||||||
|
/* ip states */
|
||||||
|
struct ipq ipq; /* ip reass. queue */
|
||||||
|
uint16_t ip_id; /* ip packet ctr, for ids */
|
||||||
|
|
||||||
|
/* bootp/dhcp states */
|
||||||
|
BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
|
||||||
|
char *bootp_filename;
|
||||||
|
size_t vdnssearch_len;
|
||||||
|
uint8_t *vdnssearch;
|
||||||
|
|
||||||
|
/* tcp states */
|
||||||
|
struct socket tcb;
|
||||||
|
struct socket *tcp_last_so;
|
||||||
|
tcp_seq tcp_iss; /* tcp initial send seq # */
|
||||||
|
uint32_t tcp_now; /* for RFC 1323 timestamps */
|
||||||
|
|
||||||
|
/* udp states */
|
||||||
|
struct socket udb;
|
||||||
|
struct socket *udp_last_so;
|
||||||
|
|
||||||
|
/* icmp states */
|
||||||
|
struct socket icmp;
|
||||||
|
struct socket *icmp_last_so;
|
||||||
|
|
||||||
|
/* tftp states */
|
||||||
|
char *tftp_prefix;
|
||||||
|
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
|
||||||
|
|
||||||
|
ArpTable arp_table;
|
||||||
|
|
||||||
|
void *opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Slirp *slirp_instance;
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL (void *)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FULL_BOLT
|
||||||
|
void if_start(Slirp *);
|
||||||
|
#else
|
||||||
|
void if_start(struct ttys *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_STRERROR
|
||||||
|
char *strerror(int error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_INDEX
|
||||||
|
char *index(const char *, int);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_GETHOSTID
|
||||||
|
long gethostid(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEFAULT_BAUD 115200
|
||||||
|
|
||||||
|
#define SO_OPTIONS DO_KEEPALIVE
|
||||||
|
#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL)
|
||||||
|
|
||||||
|
/* dnssearch.c */
|
||||||
|
int translate_dnssearch(Slirp *s, const char ** names);
|
||||||
|
|
||||||
|
/* cksum.c */
|
||||||
|
int cksum(struct mbuf *m, int len);
|
||||||
|
|
||||||
|
/* if.c */
|
||||||
|
void if_init(Slirp *);
|
||||||
|
void if_output(struct socket *, struct mbuf *);
|
||||||
|
|
||||||
|
/* ip_input.c */
|
||||||
|
void ip_init(Slirp *);
|
||||||
|
void ip_cleanup(Slirp *);
|
||||||
|
void ip_input(struct mbuf *);
|
||||||
|
void ip_slowtimo(Slirp *);
|
||||||
|
void ip_stripoptions(register struct mbuf *, struct mbuf *);
|
||||||
|
|
||||||
|
/* ip_output.c */
|
||||||
|
int ip_output(struct socket *, struct mbuf *);
|
||||||
|
|
||||||
|
/* tcp_input.c */
|
||||||
|
void tcp_input(register struct mbuf *, int, struct socket *);
|
||||||
|
int tcp_mss(register struct tcpcb *, u_int);
|
||||||
|
|
||||||
|
/* tcp_output.c */
|
||||||
|
int tcp_output(register struct tcpcb *);
|
||||||
|
void tcp_setpersist(register struct tcpcb *);
|
||||||
|
|
||||||
|
/* tcp_subr.c */
|
||||||
|
void tcp_init(Slirp *);
|
||||||
|
void tcp_cleanup(Slirp *);
|
||||||
|
void tcp_template(struct tcpcb *);
|
||||||
|
void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
|
||||||
|
struct tcpcb * tcp_newtcpcb(struct socket *);
|
||||||
|
struct tcpcb * tcp_close(register struct tcpcb *);
|
||||||
|
void tcp_sockclosed(struct tcpcb *);
|
||||||
|
int tcp_fconnect(struct socket *);
|
||||||
|
void tcp_connect(struct socket *);
|
||||||
|
int tcp_attach(struct socket *);
|
||||||
|
uint8_t tcp_tos(struct socket *);
|
||||||
|
int tcp_emu(struct socket *, struct mbuf *);
|
||||||
|
int tcp_ctl(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
|
185
slirp/slirp_config.h
Normal file
185
slirp/slirp_config.h
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* User definable configuration options
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 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 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 HOST_WORDS_BIGENDIAN
|
||||||
|
|
||||||
|
/* 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 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 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 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
|
720
slirp/socket.c
Normal file
720
slirp/socket.c
Normal file
|
@ -0,0 +1,720 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
so->slirp = slirp;
|
||||||
|
so->pollfds_idx = -1;
|
||||||
|
}
|
||||||
|
return(so);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* remque and free a socket, clobber cache
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sofree(struct socket *so)
|
||||||
|
{
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
|
||||||
|
if (so->so_emu==EMU_RSH && so->extra) {
|
||||||
|
sofree(so->extra);
|
||||||
|
so->extra=NULL;
|
||||||
|
}
|
||||||
|
if (so == slirp->tcp_last_so) {
|
||||||
|
slirp->tcp_last_so = &slirp->tcb;
|
||||||
|
} else if (so == slirp->udp_last_so) {
|
||||||
|
slirp->udp_last_so = &slirp->udb;
|
||||||
|
} else if (so == slirp->icmp_last_so) {
|
||||||
|
slirp->icmp_last_so = &slirp->icmp;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 = qemu_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 = qemu_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
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
m = m_get(so->slirp);
|
||||||
|
if (!m) {
|
||||||
|
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 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
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 & slirp->vnetwork_mask.s_addr) ==
|
||||||
|
slirp->vnetwork_addr.s_addr) {
|
||||||
|
/* It's an alias */
|
||||||
|
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
|
||||||
|
if (get_dns_addr(&addr.sin_addr) < 0)
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
} else {
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
}
|
||||||
|
} 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_PERSISTENT_MASK;
|
||||||
|
so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Listen for incoming TCP connections
|
||||||
|
*/
|
||||||
|
struct socket *
|
||||||
|
tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
|
||||||
|
u_int lport, int flags)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
struct socket *so;
|
||||||
|
int s, opt = 1;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
memset(&addr, 0, addrlen);
|
||||||
|
|
||||||
|
DEBUG_CALL("tcp_listen");
|
||||||
|
DEBUG_ARG("haddr = %x", haddr);
|
||||||
|
DEBUG_ARG("hport = %d", hport);
|
||||||
|
DEBUG_ARG("laddr = %x", laddr);
|
||||||
|
DEBUG_ARG("lport = %d", lport);
|
||||||
|
DEBUG_ARG("flags = %x", flags);
|
||||||
|
|
||||||
|
so = socreate(slirp);
|
||||||
|
if (!so) {
|
||||||
|
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, &slirp->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_PERSISTENT_MASK;
|
||||||
|
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 = haddr;
|
||||||
|
addr.sin_port = hport;
|
||||||
|
|
||||||
|
if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) < 0) ||
|
||||||
|
(socket_set_fast_reuse(s) < 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;
|
||||||
|
}
|
||||||
|
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &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 = slirp->vhost_addr;
|
||||||
|
else
|
||||||
|
so->so_faddr = addr.sin_addr;
|
||||||
|
|
||||||
|
so->s = s;
|
||||||
|
return so;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
so->so_state &= ~(SS_ISFCONNECTING);
|
||||||
|
if (so->so_state & SS_FCANTSENDMORE) {
|
||||||
|
so->so_state &= SS_PERSISTENT_MASK;
|
||||||
|
so->so_state |= SS_NOFDREF; /* Don't select it */
|
||||||
|
} 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 */
|
||||||
|
}
|
||||||
|
so->so_state &= ~(SS_ISFCONNECTING);
|
||||||
|
if (so->so_state & SS_FCANTRCVMORE) {
|
||||||
|
so->so_state &= SS_PERSISTENT_MASK;
|
||||||
|
so->so_state |= SS_NOFDREF; /* as above */
|
||||||
|
} else {
|
||||||
|
so->so_state |= SS_FCANTSENDMORE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
97
slirp/socket.h
Normal file
97
slirp/socket.h
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995 Danny Gasparovski.
|
||||||
|
*
|
||||||
|
* Please read the file COPYRIGHT for the
|
||||||
|
* terms and conditions of the copyright.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
int pollfds_idx; /* GPollFD GArray index */
|
||||||
|
|
||||||
|
Slirp *slirp; /* managing slirp instance */
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
uint16_t so_fport; /* foreign port */
|
||||||
|
uint16_t so_lport; /* local port */
|
||||||
|
|
||||||
|
uint8_t so_iptos; /* Type of service */
|
||||||
|
uint8_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_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 */
|
||||||
|
|
||||||
|
#define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */
|
||||||
|
#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
|
||||||
|
#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */
|
||||||
|
|
||||||
|
struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int);
|
||||||
|
struct socket * socreate(Slirp *);
|
||||||
|
void sofree(struct socket *);
|
||||||
|
int soread(struct socket *);
|
||||||
|
void sorecvoob(struct socket *);
|
||||||
|
int sosendoob(struct socket *);
|
||||||
|
int sowrite(struct socket *);
|
||||||
|
void sorecvfrom(struct socket *);
|
||||||
|
int sosendto(struct socket *, struct mbuf *);
|
||||||
|
struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
|
||||||
|
int);
|
||||||
|
void soisfconnecting(register struct socket *);
|
||||||
|
void soisfconnected(register struct socket *);
|
||||||
|
void sofwdrain(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_ */
|
176
slirp/tcp.h
Normal file
176
slirp/tcp.h
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* 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 uint32_t tcp_seq;
|
||||||
|
|
||||||
|
#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
|
||||||
|
#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
|
||||||
|
|
||||||
|
#define TCP_SNDSPACE 8192
|
||||||
|
#define TCP_RCVSPACE 8192
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TCP header.
|
||||||
|
* Per RFC 793, September, 1981.
|
||||||
|
*/
|
||||||
|
#define tcphdr slirp_tcphdr
|
||||||
|
struct tcphdr {
|
||||||
|
uint16_t th_sport; /* source port */
|
||||||
|
uint16_t th_dport; /* destination port */
|
||||||
|
tcp_seq th_seq; /* sequence number */
|
||||||
|
tcp_seq th_ack; /* acknowledgement number */
|
||||||
|
#ifdef HOST_WORDS_BIGENDIAN
|
||||||
|
uint8_t th_off:4, /* data offset */
|
||||||
|
th_x2:4; /* (unused) */
|
||||||
|
#else
|
||||||
|
uint8_t th_x2:4, /* (unused) */
|
||||||
|
th_off:4; /* data offset */
|
||||||
|
#endif
|
||||||
|
uint8_t th_flags;
|
||||||
|
uint16_t th_win; /* window */
|
||||||
|
uint16_t th_sum; /* checksum */
|
||||||
|
uint16_t th_urp; /* urgent pointer */
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "tcp_var.h"
|
||||||
|
|
||||||
|
#ifndef TH_FIN
|
||||||
|
#define TH_FIN 0x01
|
||||||
|
#define TH_SYN 0x02
|
||||||
|
#define TH_RST 0x04
|
||||||
|
#define TH_PUSH 0x08
|
||||||
|
#define TH_ACK 0x10
|
||||||
|
#define TH_URG 0x20
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TCPOPT_EOL
|
||||||
|
#define TCPOPT_EOL 0
|
||||||
|
#define TCPOPT_NOP 1
|
||||||
|
#define TCPOPT_MAXSEG 2
|
||||||
|
#define TCPOPT_WINDOW 3
|
||||||
|
#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
|
||||||
|
#define TCPOPT_SACK 5 /* Experimental */
|
||||||
|
#define TCPOPT_TIMESTAMP 8
|
||||||
|
|
||||||
|
#define TCPOPT_TSTAMP_HDR \
|
||||||
|
(TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TCPOLEN_MAXSEG
|
||||||
|
#define TCPOLEN_MAXSEG 4
|
||||||
|
#define TCPOLEN_WINDOW 3
|
||||||
|
#define TCPOLEN_SACK_PERMITTED 2
|
||||||
|
#define TCPOLEN_TIMESTAMP 10
|
||||||
|
#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#undef TCP_MSS
|
||||||
|
#define TCP_MSS 1460
|
||||||
|
|
||||||
|
#undef TCP_MAXWIN
|
||||||
|
#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
|
||||||
|
|
||||||
|
#undef TCP_MAX_WINSHIFT
|
||||||
|
#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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
||||||
|
|
||||||
|
#endif
|
1496
slirp/tcp_input.c
Normal file
1496
slirp/tcp_input.c
Normal file
File diff suppressed because it is too large
Load diff
493
slirp/tcp_output.c
Normal file
493
slirp/tcp_output.c
Normal file
|
@ -0,0 +1,493 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#undef MAX_TCPOPTLEN
|
||||||
|
#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.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
uint16_t mss;
|
||||||
|
|
||||||
|
opt[0] = TCPOPT_MAXSEG;
|
||||||
|
opt[1] = 4;
|
||||||
|
mss = htons((uint16_t) tcp_mss(tp, 0));
|
||||||
|
memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
|
||||||
|
optlen = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
m = m_get(so->slirp);
|
||||||
|
if (m == NULL) {
|
||||||
|
error = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
m->m_data += IF_MAXLINKHDR;
|
||||||
|
m->m_len = hdrlen;
|
||||||
|
|
||||||
|
sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
|
||||||
|
m->m_len += len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
m = m_get(so->slirp);
|
||||||
|
if (m == NULL) {
|
||||||
|
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((uint16_t) (win>>tp->rcv_scale));
|
||||||
|
|
||||||
|
if (SEQ_GT(tp->snd_up, tp->snd_una)) {
|
||||||
|
ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq)));
|
||||||
|
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((uint16_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
error = ip_output(so, m);
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
out:
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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++;
|
||||||
|
}
|
926
slirp/tcp_subr.c
Normal file
926
slirp/tcp_subr.c
Normal file
|
@ -0,0 +1,926 @@
|
||||||
|
/*
|
||||||
|
* 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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
slirp->tcp_iss = 1; /* wrong */
|
||||||
|
slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb;
|
||||||
|
slirp->tcp_last_so = &slirp->tcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcp_cleanup(Slirp *slirp)
|
||||||
|
{
|
||||||
|
while (slirp->tcb.so_next != &slirp->tcb) {
|
||||||
|
tcp_close(sototcpcb(slirp->tcb.so_next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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 = %p", tp);
|
||||||
|
DEBUG_ARG("ti = %p", ti);
|
||||||
|
DEBUG_ARG("m = %p", 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 (!tp || (m = m_get(tp->t_socket->slirp)) == NULL)
|
||||||
|
return;
|
||||||
|
tlen = 0;
|
||||||
|
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, uint32_t);
|
||||||
|
xchg(ti->ti_dport, ti->ti_sport, uint16_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((uint16_t) (win >> tp->rcv_scale));
|
||||||
|
else
|
||||||
|
ti->ti_win = htons((uint16_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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
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_free(m);
|
||||||
|
}
|
||||||
|
free(tp);
|
||||||
|
so->so_tcpcb = NULL;
|
||||||
|
/* clobber input socket cache if we're closing the cached connection */
|
||||||
|
if (so == slirp->tcp_last_so)
|
||||||
|
slirp->tcp_last_so = &slirp->tcb;
|
||||||
|
closesocket(so->s);
|
||||||
|
sbfree(&so->so_rcv);
|
||||||
|
sbfree(&so->so_snd);
|
||||||
|
sofree(so);
|
||||||
|
return ((struct tcpcb *)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
int ret=0;
|
||||||
|
|
||||||
|
DEBUG_CALL("tcp_fconnect");
|
||||||
|
DEBUG_ARG("so = %lx", (long )so);
|
||||||
|
|
||||||
|
if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) {
|
||||||
|
int opt, s=so->s;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
qemu_set_nonblock(s);
|
||||||
|
socket_set_fast_reuse(s);
|
||||||
|
opt = 1;
|
||||||
|
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
|
||||||
|
slirp->vnetwork_addr.s_addr) {
|
||||||
|
/* It's an alias */
|
||||||
|
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
|
||||||
|
if (get_dns_addr(&addr.sin_addr) < 0)
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
} else {
|
||||||
|
addr.sin_addr = loopback_addr;
|
||||||
|
}
|
||||||
|
} 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = inso->slirp;
|
||||||
|
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 {
|
||||||
|
so = socreate(slirp);
|
||||||
|
if (so == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_mss(sototcpcb(so), 0);
|
||||||
|
|
||||||
|
s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);
|
||||||
|
if (s < 0) {
|
||||||
|
tcp_close(sototcpcb(so)); /* This will sofree() as well */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qemu_set_nonblock(s);
|
||||||
|
socket_set_fast_reuse(s);
|
||||||
|
opt = 1;
|
||||||
|
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
|
||||||
|
socket_set_nodelay(s);
|
||||||
|
|
||||||
|
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_mask) ==
|
||||||
|
(loopback_addr.s_addr & loopback_mask)) {
|
||||||
|
so->so_faddr = slirp->vhost_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the accept() socket, set right state */
|
||||||
|
if (inso->so_state & SS_FACCEPTONCE) {
|
||||||
|
/* If we only accept once, close the accept() socket */
|
||||||
|
closesocket(so->s);
|
||||||
|
|
||||||
|
/* Don't select it yet, even though we have an FD */
|
||||||
|
/* if it's not FACCEPTONCE, it's already NOFDREF */
|
||||||
|
so->so_state = SS_NOFDREF;
|
||||||
|
}
|
||||||
|
so->s = s;
|
||||||
|
so->so_state |= SS_INCOMING;
|
||||||
|
|
||||||
|
so->so_iptos = tcp_tos(so);
|
||||||
|
tp = sototcpcb(so);
|
||||||
|
|
||||||
|
tcp_template(tp);
|
||||||
|
|
||||||
|
tp->t_state = TCPS_SYN_SENT;
|
||||||
|
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
|
||||||
|
tp->iss = slirp->tcp_iss;
|
||||||
|
slirp->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, &so->slirp->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}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct emu_t *tcpemu = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return TOS according to the above table
|
||||||
|
*/
|
||||||
|
uint8_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
u_int n1, n2, n3, n4, n5, n6;
|
||||||
|
char buff[257];
|
||||||
|
uint32_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 = slirp->tcb.so_next;
|
||||||
|
tmpso != &slirp->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = tcp_listen(slirp, INADDR_ANY, 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_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 = tcp_listen(slirp, INADDR_ANY, 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_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 = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
|
||||||
|
htons(lport), SS_FACCEPTONCE)) != NULL)
|
||||||
|
m->m_len = snprintf(m->m_data, m->m_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 = tcp_listen(slirp, INADDR_ANY, 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_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 = tcp_listen(slirp, INADDR_ANY, 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_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 = tcp_listen(slirp, INADDR_ANY, 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_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(slirp, INADDR_ANY,
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
struct sbuf *sb = &so->so_snd;
|
||||||
|
struct ex_list *ex_ptr;
|
||||||
|
int do_pty;
|
||||||
|
|
||||||
|
DEBUG_CALL("tcp_ctl");
|
||||||
|
DEBUG_ARG("so = %lx", (long )so);
|
||||||
|
|
||||||
|
if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
|
||||||
|
/* Check if it's pty_exec */
|
||||||
|
for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
|
||||||
|
if (ex_ptr->ex_fport == so->so_fport &&
|
||||||
|
so->so_faddr.s_addr == ex_ptr->ex_addr.s_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;
|
||||||
|
DEBUG_MISC((dfd, " executing %s\n", ex_ptr->ex_exec));
|
||||||
|
return fork_exec(so, ex_ptr->ex_exec, do_pty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
292
slirp/tcp_timer.c
Normal file
292
slirp/tcp_timer.c
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fast timeout routine for processing delayed acks
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
tcp_fasttimo(Slirp *slirp)
|
||||||
|
{
|
||||||
|
register struct socket *so;
|
||||||
|
register struct tcpcb *tp;
|
||||||
|
|
||||||
|
DEBUG_CALL("tcp_fasttimo");
|
||||||
|
|
||||||
|
so = slirp->tcb.so_next;
|
||||||
|
if (so)
|
||||||
|
for (; so != &slirp->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;
|
||||||
|
(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(Slirp *slirp)
|
||||||
|
{
|
||||||
|
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 = slirp->tcb.so_next;
|
||||||
|
if (ip == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (; ip != &slirp->tcb; ip = ipnxt) {
|
||||||
|
ipnxt = ip->so_next;
|
||||||
|
tp = sototcpcb(ip);
|
||||||
|
if (tp == NULL) {
|
||||||
|
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:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
slirp->tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
|
||||||
|
slirp->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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
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:
|
||||||
|
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:
|
||||||
|
if (tp->t_state < TCPS_ESTABLISHED)
|
||||||
|
goto dropit;
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
|
||||||
|
tp->rcv_nxt, tp->snd_una - 1, 0);
|
||||||
|
tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
|
||||||
|
} else
|
||||||
|
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
dropit:
|
||||||
|
tp = tcp_drop(tp, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tp);
|
||||||
|
}
|
127
slirp/tcp_timer.h
Normal file
127
slirp/tcp_timer.h
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* 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 ( 12*PR_SLOWHZ) /* max allowable REXMT value */
|
||||||
|
|
||||||
|
#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
|
||||||
|
|
||||||
|
#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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(Slirp *);
|
||||||
|
void tcp_slowtimo(Slirp *);
|
||||||
|
void tcp_canceltimers(struct tcpcb *);
|
||||||
|
|
||||||
|
#endif
|
161
slirp/tcp_var.h
Normal file
161
slirp/tcp_var.h
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* 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 */
|
||||||
|
|
||||||
|
struct tcpiphdr t_template; /* static skeletal packet for xmit */
|
||||||
|
|
||||||
|
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 */
|
||||||
|
uint32_t snd_wnd; /* send window */
|
||||||
|
/* receive sequence variables */
|
||||||
|
uint32_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) */
|
||||||
|
uint32_t snd_cwnd; /* congestion-controlled window */
|
||||||
|
uint32_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 */
|
||||||
|
uint32_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;
|
||||||
|
uint32_t ts_recent; /* timestamp echo data */
|
||||||
|
uint32_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)
|
||||||
|
|
||||||
|
#endif
|
77
slirp/tcpip.h
Normal file
77
slirp/tcpip.h
Normal file
|
@ -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
|
442
slirp/tftp.c
Normal file
442
slirp/tftp.c
Normal file
|
@ -0,0 +1,442 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
static inline int tftp_session_in_use(struct tftp_session *spt)
|
||||||
|
{
|
||||||
|
return (spt->slirp != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tftp_session_update(struct tftp_session *spt)
|
||||||
|
{
|
||||||
|
spt->timestamp = curtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tftp_session_terminate(struct tftp_session *spt)
|
||||||
|
{
|
||||||
|
if (spt->fd >= 0) {
|
||||||
|
close(spt->fd);
|
||||||
|
spt->fd = -1;
|
||||||
|
}
|
||||||
|
g_free(spt->filename);
|
||||||
|
spt->slirp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
|
||||||
|
{
|
||||||
|
struct tftp_session *spt;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
|
||||||
|
spt = &slirp->tftp_sessions[k];
|
||||||
|
|
||||||
|
if (!tftp_session_in_use(spt))
|
||||||
|
goto found;
|
||||||
|
|
||||||
|
/* sessions time out after 5 inactive seconds */
|
||||||
|
if ((int)(curtime - spt->timestamp) > 5000) {
|
||||||
|
tftp_session_terminate(spt);
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
found:
|
||||||
|
memset(spt, 0, sizeof(*spt));
|
||||||
|
memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
|
||||||
|
spt->fd = -1;
|
||||||
|
spt->client_port = tp->udp.uh_sport;
|
||||||
|
spt->slirp = slirp;
|
||||||
|
|
||||||
|
tftp_session_update(spt);
|
||||||
|
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
|
||||||
|
{
|
||||||
|
struct tftp_session *spt;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
|
||||||
|
spt = &slirp->tftp_sessions[k];
|
||||||
|
|
||||||
|
if (tftp_session_in_use(spt)) {
|
||||||
|
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, uint32_t block_nr,
|
||||||
|
uint8_t *buf, int len)
|
||||||
|
{
|
||||||
|
int bytes_read = 0;
|
||||||
|
|
||||||
|
if (spt->fd < 0) {
|
||||||
|
spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spt->fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
lseek(spt->fd, block_nr * 512, SEEK_SET);
|
||||||
|
|
||||||
|
bytes_read = read(spt->fd, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tftp_send_oack(struct tftp_session *spt,
|
||||||
|
const char *keys[], uint32_t values[], int nb,
|
||||||
|
struct tftp_t *recv_tp)
|
||||||
|
{
|
||||||
|
struct sockaddr_in saddr, daddr;
|
||||||
|
struct mbuf *m;
|
||||||
|
struct tftp_t *tp;
|
||||||
|
int i, n = 0;
|
||||||
|
|
||||||
|
m = m_get(spt->slirp);
|
||||||
|
|
||||||
|
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);
|
||||||
|
for (i = 0; i < nb; i++) {
|
||||||
|
n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
|
||||||
|
keys[i]) + 1;
|
||||||
|
n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
|
||||||
|
values[i]) + 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 void tftp_send_error(struct tftp_session *spt,
|
||||||
|
uint16_t errorcode, const char *msg,
|
||||||
|
struct tftp_t *recv_tp)
|
||||||
|
{
|
||||||
|
struct sockaddr_in saddr, daddr;
|
||||||
|
struct mbuf *m;
|
||||||
|
struct tftp_t *tp;
|
||||||
|
|
||||||
|
m = m_get(spt->slirp);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
out:
|
||||||
|
tftp_session_terminate(spt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tftp_send_next_block(struct tftp_session *spt,
|
||||||
|
struct tftp_t *recv_tp)
|
||||||
|
{
|
||||||
|
struct sockaddr_in saddr, daddr;
|
||||||
|
struct mbuf *m;
|
||||||
|
struct tftp_t *tp;
|
||||||
|
int nobytes;
|
||||||
|
|
||||||
|
m = m_get(spt->slirp);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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((spt->block_nr + 1) & 0xffff);
|
||||||
|
|
||||||
|
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, spt->block_nr, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
spt->block_nr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
|
||||||
|
{
|
||||||
|
struct tftp_session *spt;
|
||||||
|
int s, k;
|
||||||
|
size_t prefix_len;
|
||||||
|
char *req_fname;
|
||||||
|
const char *option_name[2];
|
||||||
|
uint32_t option_value[2];
|
||||||
|
int nb_options = 0;
|
||||||
|
|
||||||
|
/* check if a session already exists and if so terminate it */
|
||||||
|
s = tftp_session_find(slirp, tp);
|
||||||
|
if (s >= 0) {
|
||||||
|
tftp_session_terminate(&slirp->tftp_sessions[s]);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = tftp_session_allocate(slirp, tp);
|
||||||
|
|
||||||
|
if (s < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spt = &slirp->tftp_sessions[s];
|
||||||
|
|
||||||
|
/* unspecified prefix means service disabled */
|
||||||
|
if (!slirp->tftp_prefix) {
|
||||||
|
tftp_send_error(spt, 2, "Access violation", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip header fields */
|
||||||
|
k = 0;
|
||||||
|
pktlen -= offsetof(struct tftp_t, x.tp_buf);
|
||||||
|
|
||||||
|
/* prepend tftp_prefix */
|
||||||
|
prefix_len = strlen(slirp->tftp_prefix);
|
||||||
|
spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
|
||||||
|
memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
|
||||||
|
spt->filename[prefix_len] = '/';
|
||||||
|
|
||||||
|
/* get name */
|
||||||
|
req_fname = spt->filename + prefix_len + 1;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
|
||||||
|
tftp_send_error(spt, 2, "Access violation", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req_fname[k] = tp->x.tp_buf[k];
|
||||||
|
if (req_fname[k++] == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check mode */
|
||||||
|
if ((pktlen - k) < 6) {
|
||||||
|
tftp_send_error(spt, 2, "Access violation", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) {
|
||||||
|
tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
k += 6; /* skipping octet */
|
||||||
|
|
||||||
|
/* do sanity checks on the filename */
|
||||||
|
if (!strncmp(req_fname, "../", 3) ||
|
||||||
|
req_fname[strlen(req_fname) - 1] == '/' ||
|
||||||
|
strstr(req_fname, "/../")) {
|
||||||
|
tftp_send_error(spt, 2, "Access violation", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if the file exists */
|
||||||
|
if (tftp_read_data(spt, 0, NULL, 0) < 0) {
|
||||||
|
tftp_send_error(spt, 1, "File not found", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tp->x.tp_buf[pktlen - 1] != 0) {
|
||||||
|
tftp_send_error(spt, 2, "Access violation", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) {
|
||||||
|
const char *key, *value;
|
||||||
|
|
||||||
|
key = &tp->x.tp_buf[k];
|
||||||
|
k += strlen(key) + 1;
|
||||||
|
|
||||||
|
if (k >= pktlen) {
|
||||||
|
tftp_send_error(spt, 2, "Access violation", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = &tp->x.tp_buf[k];
|
||||||
|
k += strlen(value) + 1;
|
||||||
|
|
||||||
|
if (strcasecmp(key, "tsize") == 0) {
|
||||||
|
int tsize = atoi(value);
|
||||||
|
struct stat stat_p;
|
||||||
|
|
||||||
|
if (tsize == 0) {
|
||||||
|
if (stat(spt->filename, &stat_p) == 0)
|
||||||
|
tsize = stat_p.st_size;
|
||||||
|
else {
|
||||||
|
tftp_send_error(spt, 1, "File not found", tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
option_name[nb_options] = "tsize";
|
||||||
|
option_value[nb_options] = tsize;
|
||||||
|
nb_options++;
|
||||||
|
} else if (strcasecmp(key, "blksize") == 0) {
|
||||||
|
int blksize = atoi(value);
|
||||||
|
|
||||||
|
/* If blksize option is bigger than what we will
|
||||||
|
* emit, accept the option with our packet size.
|
||||||
|
* Otherwise, simply do as we didn't see the option.
|
||||||
|
*/
|
||||||
|
if (blksize >= 512) {
|
||||||
|
option_name[nb_options] = "blksize";
|
||||||
|
option_value[nb_options] = 512;
|
||||||
|
nb_options++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb_options > 0) {
|
||||||
|
assert(nb_options <= ARRAY_SIZE(option_name));
|
||||||
|
tftp_send_oack(spt, option_name, option_value, nb_options, tp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spt->block_nr = 0;
|
||||||
|
tftp_send_next_block(spt, tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = tftp_session_find(slirp, tp);
|
||||||
|
|
||||||
|
if (s < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tftp_send_next_block(&slirp->tftp_sessions[s], tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = tftp_session_find(slirp, tp);
|
||||||
|
|
||||||
|
if (s < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tftp_session_terminate(&slirp->tftp_sessions[s]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(m->slirp, tp, m->m_len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TFTP_ACK:
|
||||||
|
tftp_handle_ack(m->slirp, tp, m->m_len);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TFTP_ERROR:
|
||||||
|
tftp_handle_error(m->slirp, tp, m->m_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
49
slirp/tftp.h
Normal file
49
slirp/tftp.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/* tftp defines */
|
||||||
|
#ifndef SLIRP_TFTP_H
|
||||||
|
#define SLIRP_TFTP_H 1
|
||||||
|
|
||||||
|
#define TFTP_SESSIONS_MAX 20
|
||||||
|
|
||||||
|
#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;
|
||||||
|
uint16_t tp_op;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t tp_block_nr;
|
||||||
|
uint8_t tp_buf[512];
|
||||||
|
} tp_data;
|
||||||
|
struct {
|
||||||
|
uint16_t tp_error_code;
|
||||||
|
uint8_t tp_msg[512];
|
||||||
|
} tp_error;
|
||||||
|
char tp_buf[512 + 2];
|
||||||
|
} x;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tftp_session {
|
||||||
|
Slirp *slirp;
|
||||||
|
char *filename;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
struct in_addr client_ip;
|
||||||
|
uint16_t client_port;
|
||||||
|
uint32_t block_nr;
|
||||||
|
|
||||||
|
int timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
void tftp_input(struct mbuf *m);
|
||||||
|
|
||||||
|
#endif
|
394
slirp/udp.c
Normal file
394
slirp/udp.c
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
static uint8_t udp_tos(struct socket *so);
|
||||||
|
|
||||||
|
void
|
||||||
|
udp_init(Slirp *slirp)
|
||||||
|
{
|
||||||
|
slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb;
|
||||||
|
slirp->udp_last_so = &slirp->udb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void udp_cleanup(Slirp *slirp)
|
||||||
|
{
|
||||||
|
while (slirp->udb.so_next != &slirp->udb) {
|
||||||
|
udp_detach(slirp->udb.so_next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
Slirp *slirp = m->slirp;
|
||||||
|
register struct ip *ip;
|
||||||
|
register struct udphdr *uh;
|
||||||
|
int len;
|
||||||
|
struct ip save_ip;
|
||||||
|
struct socket *so;
|
||||||
|
|
||||||
|
DEBUG_CALL("udp_input");
|
||||||
|
DEBUG_ARG("m = %lx", (long)m);
|
||||||
|
DEBUG_ARG("iphlen = %d", iphlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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((uint16_t)uh->uh_ulen);
|
||||||
|
|
||||||
|
if (ip->ip_len != len) {
|
||||||
|
if (len > ip->ip_len) {
|
||||||
|
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 (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;
|
||||||
|
if(cksum(m, len + sizeof(struct ip))) {
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handle DHCP/BOOTP
|
||||||
|
*/
|
||||||
|
if (ntohs(uh->uh_dport) == BOOTP_SERVER &&
|
||||||
|
(ip->ip_dst.s_addr == slirp->vhost_addr.s_addr ||
|
||||||
|
ip->ip_dst.s_addr == 0xffffffff)) {
|
||||||
|
bootp_input(m);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handle TFTP
|
||||||
|
*/
|
||||||
|
if (ntohs(uh->uh_dport) == TFTP_SERVER &&
|
||||||
|
ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
|
||||||
|
tftp_input(m);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slirp->restricted) {
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Locate pcb for datagram.
|
||||||
|
*/
|
||||||
|
so = slirp->udp_last_so;
|
||||||
|
if (so == &slirp->udb || so->so_lport != uh->uh_sport ||
|
||||||
|
so->so_laddr.s_addr != ip->ip_src.s_addr) {
|
||||||
|
struct socket *tmp;
|
||||||
|
|
||||||
|
for (tmp = slirp->udb.so_next; tmp != &slirp->udb;
|
||||||
|
tmp = tmp->so_next) {
|
||||||
|
if (tmp->so_lport == uh->uh_sport &&
|
||||||
|
tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
|
||||||
|
so = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp == &slirp->udb) {
|
||||||
|
so = NULL;
|
||||||
|
} else {
|
||||||
|
slirp->udp_last_so = so;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (so == NULL) {
|
||||||
|
/*
|
||||||
|
* If there's no socket for this packet,
|
||||||
|
* create one
|
||||||
|
*/
|
||||||
|
so = socreate(slirp);
|
||||||
|
if (!so) {
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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(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_free(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
/* 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 ((ui->ui_sum = cksum(m, 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;
|
||||||
|
|
||||||
|
error = ip_output(so, m);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp_output(struct socket *so, struct mbuf *m,
|
||||||
|
struct sockaddr_in *addr)
|
||||||
|
|
||||||
|
{
|
||||||
|
Slirp *slirp = so->slirp;
|
||||||
|
struct sockaddr_in saddr, daddr;
|
||||||
|
|
||||||
|
saddr = *addr;
|
||||||
|
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
|
||||||
|
slirp->vnetwork_addr.s_addr) {
|
||||||
|
uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
|
||||||
|
|
||||||
|
if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
|
||||||
|
saddr.sin_addr = slirp->vhost_addr;
|
||||||
|
} else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
|
||||||
|
so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
|
||||||
|
saddr.sin_addr = so->so_faddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) {
|
||||||
|
so->so_expire = curtime + SO_EXPIRE;
|
||||||
|
insque(so, &so->slirp->udb);
|
||||||
|
}
|
||||||
|
return(so->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
udp_detach(struct socket *so)
|
||||||
|
{
|
||||||
|
closesocket(so->s);
|
||||||
|
sofree(so);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct tos_t udptos[] = {
|
||||||
|
{0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct socket *
|
||||||
|
udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
|
||||||
|
u_int lport, int flags)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
struct socket *so;
|
||||||
|
socklen_t addrlen = sizeof(struct sockaddr_in);
|
||||||
|
|
||||||
|
so = socreate(slirp);
|
||||||
|
if (!so) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
so->s = qemu_socket(AF_INET,SOCK_DGRAM,0);
|
||||||
|
so->so_expire = curtime + SO_EXPIRE;
|
||||||
|
insque(so, &slirp->udb);
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = haddr;
|
||||||
|
addr.sin_port = hport;
|
||||||
|
|
||||||
|
if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
|
||||||
|
udp_detach(so);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
socket_set_fast_reuse(so->s);
|
||||||
|
|
||||||
|
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 = slirp->vhost_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_PERSISTENT_MASK;
|
||||||
|
so->so_state |= SS_ISFCONNECTED | flags;
|
||||||
|
|
||||||
|
return so;
|
||||||
|
}
|
87
slirp/udp.h
Normal file
87
slirp/udp.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Udp protocol header.
|
||||||
|
* Per RFC 768, September, 1981.
|
||||||
|
*/
|
||||||
|
struct udphdr {
|
||||||
|
uint16_t uh_sport; /* source port */
|
||||||
|
uint16_t uh_dport; /* destination port */
|
||||||
|
int16_t uh_ulen; /* udp length */
|
||||||
|
uint16_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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Names for UDP sysctl objects
|
||||||
|
*/
|
||||||
|
#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
|
||||||
|
#define UDPCTL_MAXID 2
|
||||||
|
|
||||||
|
struct mbuf;
|
||||||
|
|
||||||
|
void udp_init(Slirp *);
|
||||||
|
void udp_cleanup(Slirp *);
|
||||||
|
void udp_input(register struct mbuf *, int);
|
||||||
|
int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
|
||||||
|
int udp_attach(struct socket *);
|
||||||
|
void udp_detach(struct socket *);
|
||||||
|
struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
|
||||||
|
int);
|
||||||
|
int udp_output2(struct socket *so, struct mbuf *m,
|
||||||
|
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
|
||||||
|
int iptos);
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue