diff --git a/src/sys/init/main.c b/src/sys/init/main.c index 357308b..370d1d9 100644 --- a/src/sys/init/main.c +++ b/src/sys/init/main.c @@ -24,6 +24,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $Log$ + Revision 1.1.1.1 2004/04/15 12:06:45 reddawg + UbixOS v1.0 + Revision 1.107 2004/04/13 16:36:33 reddawg Changed our copyright, it is all now under a BSD-Style license @@ -132,19 +135,19 @@ } enableUbixFS(); fdcInit(); - initHardDisk(); + //initHardDisk(); if (mount(0x0,0x0,0x0,"sys","rw") != 0x0) { kprintf("Problem Mounting sys Mount Point\n"); } - if (mount(0x1,0x0,0x0,"hd","rw") != 0x0) { + if (mount(0x0,0x1,0x0,"tmp","rw") != 0x0) { kprintf("Problem Mounting hd Mount Point\n"); } kprintf("Free Pages: [%i]\n",freePages); kprintf("MemoryMap: [0x%X]\n",vmmMemoryMap); kprintf("Starting Os\n"); - netInit(); + //netInit(); execThread(idleTask,(uInt32)(kmalloc(0x2000,sysID)+0x2000),0x0,"Idle Thread"); - execFile("shell@sys",0x0,0x0,0x0); + execFile("init@sys",0x0,0x0,0x0); irqEnable(0x0); sched(); return(0x0); diff --git a/src/sys/net/core/Makefile b/src/sys/net/core/Makefile new file mode 100644 index 0000000..29a2da8 --- /dev/null +++ b/src/sys/net/core/Makefile @@ -0,0 +1,27 @@ +# (C) 2002 The UbixOS Project +# $Id$ + +# Include Global 'Source' Options +include ../../../Makefile.inc +include ../../Makefile.inc + +# Objects +OBJS = inet.o mem.o memp.o netif.o pbuf.o stats.o sys.o tcp.o tcp_input.o tcp_output.o udp.o ip.o icmp.o ip_addr.o + +all: $(OBJS) + +# Compile Types +.cc.o: + $(CXX) ${CFLAGS} -fno-exceptions -DNOBOOL -Wall -fomit-frame-pointer -O -I../../include -c -o $@ $< +.cc.s: + $(CXX) ${CFLAGS} -fno-exceptions -DNOBOOL -Wall -fomit-frame-pointer -O -I../../include -S -o $@ $< +.c.o: + $(CC) ${CFLAGS} -Wall -fomit-frame-pointer -O -I../../include -c -o $@ $< +.c.s: + $(CC) ${CFLAGS} -Wall -fomit-frame-pointer -O -I../../include -S -o $@ $< +.S.o: + $(CC) ${CFLAGS} -Wall -fomit-frame-pointer -c -o $@ $< + +# Clean up the junk +clean: + $(REMOVE) $(OBJS) diff --git a/src/sys/net/core/icmp.c b/src/sys/net/core/icmp.c new file mode 100644 index 0000000..754ccc6 --- /dev/null +++ b/src/sys/net/core/icmp.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include + +#include "net/debug.h" + +#include "net/ipv4/icmp.h" +#include "net/ipv4/inet.h" +#include "net/ipv4/ip.h" +#include "net/def.h" + +#include "net/stats.h" + +/*-----------------------------------------------------------------------------------*/ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + unsigned char type; + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + struct ip_addr tmpaddr; + uInt16 hlen; + +#ifdef ICMP_STATS + ++stats.icmp.recv; +#endif /* ICMP_STATS */ + + + iphdr = p->payload; + hlen = IPH_HL(iphdr) * 4/sizeof(uInt8); + pbuf_header(p, -hlen); + + type = *((uInt8 *)p->payload); + + switch(type) { + case ICMP_ECHO: + if(ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) || + ip_addr_ismulticast(&iphdr->dest)) { + DEBUGF(ICMP_DEBUG, ("Smurf.\n")); +#ifdef ICMP_STATS + ++stats.icmp.err; +#endif /* ICMP_STATS */ + pbuf_free(p); + return; + } + DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + DEBUGF(DEMO_DEBUG, ("Pong!\n")); + if(p->tot_len < sizeof(struct icmp_echo_hdr)) { + DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + pbuf_free(p); +#ifdef ICMP_STATS + ++stats.icmp.lenerr; +#endif /* ICMP_STATS */ + + return; + } + iecho = p->payload; + if(inet_chksum_pbuf(p) != 0) { + DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); +#ifdef ICMP_STATS + ++stats.icmp.chkerr; +#endif /* ICMP_STATS */ + return; + } + tmpaddr.addr = iphdr->src.addr; + iphdr->src.addr = iphdr->dest.addr; + iphdr->dest.addr = tmpaddr.addr; + ICMPH_TYPE_SET(iecho, ICMP_ER); + /* adjust the checksum */ + if(iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) { + iecho->chksum += htons(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP_ECHO << 8); + } +#ifdef ICMP_STATS + ++stats.icmp.xmit; +#endif /* ICMP_STATS */ + + pbuf_header(p, hlen); + ip_output_if(p, &(iphdr->src), IP_HDRINCL, + IPH_TTL(iphdr), IP_PROTO_ICMP, inp); + break; + default: + DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type not supported.\n")); +#ifdef ICMP_STATS + ++stats.icmp.proterr; + ++stats.icmp.drop; +#endif /* ICMP_STATS */ + } + pbuf_free(p); +} +/*-----------------------------------------------------------------------------------*/ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_dur_hdr *idur; + + q = pbuf_alloc(PBUF_TRANSPORT, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + + iphdr = p->payload; + + idur = q->payload; + ICMPH_TYPE_SET(idur, ICMP_DUR); + ICMPH_CODE_SET(idur, t); + + bcopy(p->payload, (char *)q->payload + 8, IP_HLEN + 8); + + /* calculate checksum */ + idur->chksum = 0; + idur->chksum = inet_chksum(idur, q->len); +#ifdef ICMP_STATS + ++stats.icmp.xmit; +#endif /* ICMP_STATS */ + + ip_output(q, NULL, &(iphdr->src), + ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} +/*-----------------------------------------------------------------------------------*/ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_te_hdr *tehdr; + + q = pbuf_alloc(PBUF_TRANSPORT, 8 + IP_HLEN + 8, PBUF_RAM); + + iphdr = p->payload; +#if ICMP_DEBUG + DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(&(iphdr->src)); + DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(&(iphdr->dest)); + DEBUGF(ICMP_DEBUG, ("\n")); +#endif /* ICMP_DEBNUG */ + + tehdr = q->payload; + ICMPH_TYPE_SET(tehdr, ICMP_TE); + ICMPH_CODE_SET(tehdr, t); + + /* copy fields from original packet */ + bcopy((char *)p->payload, (char *)q->payload + 8, IP_HLEN + 8); + + /* calculate checksum */ + tehdr->chksum = 0; + tehdr->chksum = inet_chksum(tehdr, q->len); +#ifdef ICMP_STATS + ++stats.icmp.xmit; +#endif /* ICMP_STATS */ + ip_output(q, NULL, &(iphdr->src), + ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + + + + + + + + diff --git a/src/sys/net/core/inet.c b/src/sys/net/core/inet.c new file mode 100644 index 0000000..462d73d --- /dev/null +++ b/src/sys/net/core/inet.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* inet.c + * + * Functions common to all TCP/IP modules, such as the Internet checksum and the + * byte order functions. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/arch.h" + +#include "net/def.h" + +#include "net/ipv4/inet.h" + + +/*-----------------------------------------------------------------------------------*/ +/* chksum: + * + * Sums up all 16 bit words in a memory portion. Also includes any odd byte. + * This function is used by the other checksum functions. + * + * For now, this is not optimized. Must be optimized for the particular processor + * arcitecture on which it is to run. Preferebly coded in assembler. + */ +/*-----------------------------------------------------------------------------------*/ +static uInt32 +chksum(void *dataptr, int len) +{ + uInt32 acc; + + for(acc = 0; len > 1; len -= 2) { + acc += *((uInt16 *)dataptr)++; + } + + /* add up any odd byte */ + if(len == 1) { + acc += htons((uInt16)((*(uInt8 *)dataptr) & 0xff) << 8); + DEBUGF(INET_DEBUG, ("inet: chksum: odd byte %d\n", *(uInt8 *)dataptr)); + } + + return acc; +} +/*-----------------------------------------------------------------------------------*/ +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ +/*-----------------------------------------------------------------------------------*/ +uInt16 +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + uInt8 proto, uInt16 proto_len) +{ + uInt32 acc; + struct pbuf *q; + uInt8 swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if(q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + } + + if(swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + acc += (src->addr & 0xffff); + acc += ((src->addr >> 16) & 0xffff); + acc += (dest->addr & 0xffff); + acc += ((dest->addr >> 16) & 0xffff); + acc += (uInt32)htons((uInt16)proto); + acc += (uInt32)htons(proto_len); + + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} +/*-----------------------------------------------------------------------------------*/ +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ +/*-----------------------------------------------------------------------------------*/ +uInt16 +inet_chksum(void *dataptr, uInt16 len) +{ + uInt32 acc; + + acc = chksum(dataptr, len); + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} +/*-----------------------------------------------------------------------------------*/ +uInt16 +inet_chksum_pbuf(struct pbuf *p) +{ + uInt32 acc; + struct pbuf *q; + uInt8 swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if(q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); + } + } + + if(swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + return ~(acc & 0xffff); +} + + +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/inet6.c b/src/sys/net/core/inet6.c new file mode 100644 index 0000000..463b1c7 --- /dev/null +++ b/src/sys/net/core/inet6.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* inet6.c + * + * Functions common to all TCP/IP modules, such as the Internet checksum and the + * byte order functions. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include "net/debug.h" + +#include "net/def.h" +#include "net/ipv6/inet.h" + + +/*-----------------------------------------------------------------------------------*/ +/* chksum: + * + * Sums up all 16 bit words in a memory portion. Also includes any odd byte. + * This function is used by the other checksum functions. + * + * For now, this is not optimized. Must be optimized for the particular processor + * arcitecture on which it is to run. Preferebly coded in assembler. + */ +/*-----------------------------------------------------------------------------------*/ +static u32_t +chksum(void *dataptr, u16_t len) +{ + u16_t *sdataptr = dataptr; + u32_t acc; + + + for(acc = 0; len > 1; len -= 2) { + acc += *sdataptr++; + } + + /* add up any odd byte */ + if(len == 1) { + acc += htons((u16_t)(*(u8_t *)dataptr) << 8); + } + + return acc; + +} +/*-----------------------------------------------------------------------------------*/ +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ +/*-----------------------------------------------------------------------------------*/ +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped, i; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if(q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + } + + if(swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + + for(i = 0; i < 8; i++) { + acc += ((u16_t *)src->addr)[i] & 0xffff; + acc += ((u16_t *)dest->addr)[i] & 0xffff; + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + } + acc += (u16_t)htons((u16_t)proto); + acc += ((u16_t *)&proto_len)[0] & 0xffff; + acc += ((u16_t *)&proto_len)[1] & 0xffff; + + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} +/*-----------------------------------------------------------------------------------*/ +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ +/*-----------------------------------------------------------------------------------*/ +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + u32_t acc, sum; + + acc = chksum(dataptr, len); + sum = (acc & 0xffff) + (acc >> 16); + sum += (sum >> 16); + return ~(sum & 0xffff); +} +/*-----------------------------------------------------------------------------------*/ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while(acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if(q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); + } + } + + if(swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + return ~(acc & 0xffff); +} +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/ip.c b/src/sys/net/core/ip.c new file mode 100644 index 0000000..96deb94 --- /dev/null +++ b/src/sys/net/core/ip.c @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + + +/*-----------------------------------------------------------------------------------*/ +/* ip.c + * + * This is the code for the IP layer. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/def.h" +#include "net/mem.h" +#include "net/ipv4/ip.h" +#include "net/ipv4/inet.h" +#include "net/netif.h" +#include "net/ipv4/icmp.h" +#include "net/udp.h" +#include "net/tcp.h" + +#include "net/stats.h" + +#include "net/arch/perf.h" + +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ + +/*-----------------------------------------------------------------------------------*/ +/* ip_init: + * + * Initializes the IP layer. + */ +/*-----------------------------------------------------------------------------------*/ +void +ip_init(void) +{ +} +/*-----------------------------------------------------------------------------------*/ +/* ip_lookup: + * + * An experimental feature that will be changed in future versions. Do + * not depend on it yet... + */ +/*-----------------------------------------------------------------------------------*/ +#ifdef LWIP_DEBUG +uInt8 +ip_lookup(void *header, struct netif *inp) +{ + struct ip_hdr *iphdr; + + iphdr = header; + + /* Refuse anything that isn't IPv4. */ + if(IPH_V(iphdr) != 4) { + return 0; + } + + /* Immediately accept/decline packets that are fragments or has + options. */ +#if IP_REASSEMBLY == 0 + /* if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { + return 0; + }*/ +#endif /* IP_REASSEMBLY == 0 */ + +#if IP_OPTIONS == 0 + if(IPH_HL(iphdr) != 5) { + return 0; + } +#endif /* IP_OPTIONS == 0 */ + + switch(IPH_PROTO(iphdr)) { +#if LWIP_UDP > 0 + case IP_PROTO_UDP: + return udp_lookup(iphdr, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP > 0 + case IP_PROTO_TCP: + return 1; +#endif /* LWIP_TCP */ + case IP_PROTO_ICMP: + return 1; + break; + default: + return 0; + } +} +#endif /* LWIP_DEBUG */ +/*-----------------------------------------------------------------------------------*/ +/* ip_route: + * + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + */ +/*-----------------------------------------------------------------------------------*/ +struct netif * +ip_route(struct ip_addr *dest) +{ + struct netif *netif; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if(ip_addr_maskcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + return netif; + } + } + + return netif_default; +} +#if IP_FORWARD +/*-----------------------------------------------------------------------------------*/ +/* ip_forward: + * + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + */ +/*-----------------------------------------------------------------------------------*/ +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + static struct netif *netif; + + PERF_START; + + if((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { + + DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%lx found\n", + iphdr->dest.addr)); + + return; + } + + /* Don't forward packets onto the same network interface on which + they arrived. */ + if(netif == inp) { + DEBUGF(IP_DEBUG, ("ip_forward: not forward packets back on incoming interface.\n")); + + return; + } + + /* Decrement TTL and send ICMP if ttl == 0. */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + if(IPH_TTL(iphdr) == 0) { + /* Don't send ICMP messages in response to ICMP messages */ + if(IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } + return; + } + + /* Incremental update of the IP checksum. */ + if(IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100)); + } + + DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%lx\n", + iphdr->dest.addr)); + +#ifdef IP_STATS + ++stats.ip.fw; + ++stats.ip.xmit; +#endif /* IP_STATS */ + + PERF_STOP("ip_forward"); + + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); +} +#endif /* IP_FORWARD */ +/*-----------------------------------------------------------------------------------*/ +/* ip_reass: + * + * Tries to reassemble a fragmented IP packet. + */ +/*-----------------------------------------------------------------------------------*/ +#define IP_REASSEMBLY 0 +#define IP_REASS_BUFSIZE 5760 +#define IP_REASS_MAXAGE 10 + +#if IP_REASSEMBLY +static uInt8 ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE]; +static uInt8 ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8)]; +static const uInt8 bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01}; +static uInt16 ip_reasslen; +static uInt8 ip_reassflags; +#define IP_REASS_FLAG_LASTFRAG 0x01 +static uInt8 ip_reasstmr; + +static struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *q; + struct ip_hdr *fraghdr, *iphdr; + uInt16 offset, len; + uInt16 i; + + iphdr = (struct ip_hdr *)ip_reassbuf; + fraghdr = (struct ip_hdr *)p->payload; + + /* If ip_reasstmr is zero, no packet is present in the buffer, so we + write the IP header of the fragment into the reassembly + buffer. The timer is updated with the maximum age. */ + if(ip_reasstmr == 0) { + DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n")); + bcopy(fraghdr, iphdr, IP_HLEN); + ip_reasstmr = IP_REASS_MAXAGE; + ip_reassflags = 0; + /* Clear the bitmap. */ + bzero(ip_reassbitmap, sizeof(ip_reassbitmap)); + } + + /* Check if the incoming fragment matches the one currently present + in the reasembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if(ip_addr_cmp(&iphdr->src, &fraghdr->src) && + ip_addr_cmp(&iphdr->dest, &fraghdr->dest) && + IPH_ID(iphdr) == IPH_ID(fraghdr)) { + DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching old packet\n")); + /* Find out the offset in the reassembly buffer where we should + copy the fragment. */ + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if(offset > IP_REASS_BUFSIZE || + offset + len > IP_REASS_BUFSIZE) { + DEBUGF(IP_REASS_DEBUG, ("ip_reass: fragment outside of buffer (%d:%d/%d).\n", + offset, offset + len, IP_REASS_BUFSIZE)); + ip_reasstmr = 0; + goto nullreturn; + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + DEBUGF(IP_REASS_DEBUG, ("ip_reass: copying with offset %d into %d:%d\n", + offset, IP_HLEN + offset, IP_HLEN + offset + len)); + bcopy((uInt8 *)fraghdr + IPH_HL(fraghdr) * 4, + &ip_reassbuf[IP_HLEN + offset], len); + + /* Update the bitmap. */ + if(offset / (8 * 8) == (offset + len) / (8 * 8)) { + DEBUGF(IP_REASS_DEBUG, ("ip_reass: updating single byte in bitmap.\n")); + /* If the two endpoints are in the same byte, we only update + that byte. */ + ip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7] & + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } else { + /* If the two endpoints are in different bytes, we update the + bytes in the endpoints and fill the stuff inbetween with + 0xff. */ + ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8 ) & 7]; + DEBUGF(IP_REASS_DEBUG, ("ip_reass: updating many bytes in bitmap (%d:%d).\n", + 1 + offset / (8 * 8), (offset + len) / (8 * 8))); + for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { + ip_reassbitmap[i] = 0xff; + } + ip_reassbitmap[(offset + len) / (8 * 8)] |= ~bitmap_bits[((offset + len) / 8 ) & 7]; + } + + /* If this fragment has the More Fragments flag set to zero, we + know that this is the last fragment, so we can calculate the + size of the entire packet. We also set the + IP_REASS_FLAG_LASTFRAG flag to indicate that we have received + the final fragment. */ + + if((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) { + ip_reassflags |= IP_REASS_FLAG_LASTFRAG; + ip_reasslen = offset + len; + DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, total len %d\n", ip_reasslen)); + } + + /* Finally, we check if we have a full packet in the buffer. We do + this by checking if we have the last fragment and if all bits + in the bitmap are set. */ + if(ip_reassflags & IP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last byte in + the bitmap. */ + for(i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) { + if(ip_reassbitmap[i] != 0xff) { + DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, bitmap %d/%d failed (%x)\n", i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i])); + goto nullreturn; + } + } + /* Check the last byte in the bitmap. It should contain just the + right amount of bits. */ + if(ip_reassbitmap[ip_reasslen / (8 * 8)] != + (uInt8)~bitmap_bits[ip_reasslen / 8 & 7]) { + DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, bitmap %d didn't contain %x (%x)\n", + ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7], + ip_reassbitmap[ip_reasslen / (8 * 8)])); + goto nullreturn; + } + + /* Pretend to be a "normal" (i.e., not fragmented) IP packet + from now on. */ + IPH_OFFSET_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + /* If we have come this far, we have a full packet in the + buffer, so we allocate a pbuf and copy the packet into it. We + also reset the timer. */ + ip_reasstmr = 0; + pbuf_free(p); + p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL); + if(p != NULL) { + i = 0; + for(q = p; q != NULL; q = q->next) { + /* Copy enough bytes to fill this pbuf in the chain. The + avaliable data in the pbuf is given by the q->len + variable. */ + DEBUGF(IP_REASS_DEBUG, ("ip_reass: bcopy from %p (%d) to %p, %d bytes\n", + &ip_reassbuf[i], i, q->payload, q->len > ip_reasslen - i? ip_reasslen - i: q->len)); + bcopy(&ip_reassbuf[i], q->payload, + q->len > ip_reasslen - i? ip_reasslen - i: q->len); + i += q->len; + } + } + DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", p)); + return p; + } + } + + nullreturn: + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ +/*-----------------------------------------------------------------------------------*/ +/* ip_input: + * + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + */ +/*-----------------------------------------------------------------------------------*/ +err_t +ip_input(struct pbuf *p, struct netif *inp) { + static struct ip_hdr *iphdr; + static struct netif *netif; + static uInt8 hl; + + + +#ifdef IP_STATS + ++stats.ip.recv; +#endif /* IP_STATS */ + + /* identify the IP header */ + iphdr = p->payload; + if(IPH_V(iphdr) != 4) { + DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number %d\n", IPH_V(iphdr))); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + pbuf_free(p); +#ifdef IP_STATS + ++stats.ip.err; + ++stats.ip.drop; +#endif /* IP_STATS */ + return ERR_OK; + } + + hl = IPH_HL(iphdr); + + if(hl * 4 > p->len) { + DEBUGF(IP_DEBUG, ("IP packet dropped due to too short packet %d\n", p->len)); + + pbuf_free(p); +#ifdef IP_STATS + ++stats.ip.lenerr; + ++stats.ip.drop; +#endif /* IP_STATS */ + return ERR_OK; + } + + /* verify checksum */ + if(inet_chksum(iphdr, hl * 4) != 0) { + + DEBUGF(IP_DEBUG, ("IP packet dropped due to failing checksum 0x%x\n", inet_chksum(iphdr, hl * 4))); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + pbuf_free(p); +#ifdef IP_STATS + ++stats.ip.chkerr; + ++stats.ip.drop; +#endif /* IP_STATS */ + return ERR_OK; + } + + /* Trim pbuf. This should have been done at the netif layer, + but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, ntohs(IPH_LEN(iphdr))); + + /* is this packet for us? */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + + DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%lx netif->ip_addr 0x%lx (0x%lx, 0x%lx, 0x%lx)\n", + iphdr->dest.addr, netif->ip_addr.addr, + iphdr->dest.addr & netif->netmask.addr, + netif->ip_addr.addr & netif->netmask.addr, + iphdr->dest.addr & ~(netif->netmask.addr))); + + if(ip_addr_isany(&(netif->ip_addr)) || + ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || + (ip_addr_isbroadcast(&(iphdr->dest), &(netif->netmask)) && + ip_addr_maskcmp(&(iphdr->dest), &(netif->ip_addr), &(netif->netmask))) || + ip_addr_cmp(&(iphdr->dest), IP_ADDR_BROADCAST)) { + break; + } + } + +#if LWIP_DHCP + /* If a DHCP packet has arrived on the interface, we pass it up the + stack regardless of destination IP address. The reason is that + DHCP replies are sent to the IP adress that will be given to this + node (as recommended by RFC 1542 section 3.1.1, referred by RFC + 2131). */ + if(IPH_PROTO(iphdr) == IP_PROTO_UDP && + ((struct udp_hdr *)((uInt8 *)iphdr + IPH_HL(iphdr) * 4/sizeof(uInt8)))->src == + DHCP_SERVER_PORT) { + netif = inp; + } +#endif /* LWIP_DHCP */ + + if(netif == NULL) { + /* packet not for us, route or discard */ + DEBUGF(IP_DEBUG, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + if(!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask))) { + ip_forward(p, iphdr, inp); + } +#endif /* IP_FORWARD */ + pbuf_free(p); + return ERR_OK; + } + +#if IP_REASSEMBLY + if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { + p = ip_reass(p); + if(p == NULL) { + return ERR_OK; + } + iphdr = p->payload; + } +#else /* IP_REASSEMBLY */ + if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { + pbuf_free(p); + DEBUGF(IP_DEBUG, ("IP packet dropped since it was fragmented (0x%x).\n", + ntohs(IPH_OFFSET(iphdr)))); +#ifdef IP_STATS + ++stats.ip.opterr; + ++stats.ip.drop; +#endif /* IP_STATS */ + return ERR_OK; + } +#endif /* IP_REASSEMBLY */ + +#if IP_OPTIONS == 0 + if(hl * 4 > IP_HLEN) { + DEBUGF(IP_DEBUG, ("IP packet dropped since there were IP options.\n")); + + pbuf_free(p); +#ifdef IP_STATS + ++stats.ip.opterr; + ++stats.ip.drop; +#endif /* IP_STATS */ + return ERR_OK; + } +#endif /* IP_OPTIONS == 0 */ + + + /* send to upper layers */ +#if IP_DEBUG + DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + DEBUGF(IP_DEBUG, ("ip_input: p->len %d p->tot_len %d\n", p->len, p->tot_len)); +#endif /* IP_DEBUG */ + + switch(IPH_PROTO(iphdr)) { +#if LWIP_UDP > 0 + case IP_PROTO_UDP: + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP > 0 + case IP_PROTO_TCP: + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ + case IP_PROTO_ICMP: + icmp_input(p, inp); + break; + default: + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if(!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask)) && + !ip_addr_ismulticast(&(iphdr->dest))) { + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } + pbuf_free(p); + + DEBUGF(IP_DEBUG, ("Unsupported transportation protocol %d\n", IPH_PROTO(iphdr))); + +#ifdef IP_STATS + ++stats.ip.proterr; + ++stats.ip.drop; +#endif /* IP_STATS */ + + } + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* ip_output_if: + * + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + */ +/*-----------------------------------------------------------------------------------*/ +err_t +ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + uInt8 ttl, + uInt8 proto, struct netif *netif) +{ + static struct ip_hdr *iphdr; + static uInt16 ip_id = 0; + + + + if(dest != IP_HDRINCL) { + if(pbuf_header(p, IP_HLEN)) { + DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n")); + +#ifdef IP_STATS + ++stats.ip.err; +#endif /* IP_STATS */ + pbuf_free(p); + return ERR_BUF; + } + + iphdr = p->payload; + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); + + ip_addr_set(&(iphdr->dest), dest); + + IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, 0); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_OFFSET_SET(iphdr, htons(IP_DF)); + IPH_ID_SET(iphdr, htons(++ip_id)); + + if(ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + } else { + iphdr = p->payload; + dest = &(iphdr->dest); + } + +#ifdef IP_STATS + stats.ip.xmit++; +#endif /* IP_STATS */ + DEBUGF(IP_DEBUG, ("ip_output_if: %c%c ", netif->name[0], netif->name[1])); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + + return netif->output(netif, p, dest); +} +/*-----------------------------------------------------------------------------------*/ +/* ip_output: + * + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + */ +/*-----------------------------------------------------------------------------------*/ +err_t +ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + uInt8 ttl, uInt8 proto) +{ + static struct netif *netif; + + + if((netif = ip_route(dest)) == NULL) { + DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%lx\n", dest->addr)); + +#ifdef IP_STATS + ++stats.ip.rterr; +#endif /* IP_STATS */ + pbuf_free(p); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, proto, netif); +} +/*-----------------------------------------------------------------------------------*/ +#if IP_DEBUG +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = p->payload; + uInt8 *payload; + + payload = (uInt8 *)iphdr + IP_HLEN/sizeof(uInt8); + + DEBUGF(IP_DEBUG, ("IP header:\n")); + DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(IP_DEBUG, ("|%2d |%2d | %2d | %4d | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(IP_DEBUG, ("| %5d |%d%d%d| %4d | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(IP_DEBUG, ("| %2d | %2d | 0x%04x | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(IP_DEBUG, ("| %3ld | %3ld | %3ld | %3ld | (src)\n", + ntohl(iphdr->src.addr) >> 24 & 0xff, + ntohl(iphdr->src.addr) >> 16 & 0xff, + ntohl(iphdr->src.addr) >> 8 & 0xff, + ntohl(iphdr->src.addr) & 0xff)); + DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(IP_DEBUG, ("| %3ld | %3ld | %3ld | %3ld | (dest)\n", + ntohl(iphdr->dest.addr) >> 24 & 0xff, + ntohl(iphdr->dest.addr) >> 16 & 0xff, + ntohl(iphdr->dest.addr) >> 8 & 0xff, + ntohl(iphdr->dest.addr) & 0xff)); + DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ +/*-----------------------------------------------------------------------------------*/ + + + + + diff --git a/src/sys/net/core/ip_addr.c b/src/sys/net/core/ip_addr.c new file mode 100644 index 0000000..a955585 --- /dev/null +++ b/src/sys/net/core/ip_addr.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +#include + +#include "net/debug.h" +#include "net/ipv4/ip_addr.h" +#include "net/ipv4/inet.h" + +struct ip_addr ip_addr_broadcast = {0xffffffff}; + +/*-----------------------------------------------------------------------------------*/ + +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/mem.c b/src/sys/net/core/mem.c new file mode 100644 index 0000000..52cdf18 --- /dev/null +++ b/src/sys/net/core/mem.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* mem.c + * + * Memory manager. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/arch.h" +#include "net/opt.h" +#include "net/def.h" +#include "net/mem.h" + +#include "net/sys.h" + +#include "net/stats.h" + +#if MEM_RECLAIM +struct mem_reclaim_ { + struct mem_reclaim_ *next; + mem_reclaim_func f; + void *arg; +}; +#endif /* MEM_RECLAIM */ + +struct mem { + mem_size_t next, prev; + uInt8 used; +#if MEM_ALIGNMENT == 2 + uInt8 dummy; +#endif /* MEM_ALIGNEMNT == 2 */ +}; + +static struct mem *ram_end; +static uInt8 ram[MEM_ALIGN_SIZE(MEM_SIZE + sizeof(struct mem))]; + +#define MIN_SIZE 12 +#define SIZEOF_STRUCT_MEM MEM_ALIGN_SIZE(sizeof(struct mem)) +/*#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \ + (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \ + (4 - (sizeof(struct mem) % MEM_ALIGNMENT))))*/ + + +static struct mem *lfree; /* pointer to the lowest free block */ + +#if MEM_RECLAIM +static struct mem_reclaim_ *mrlist; +#endif /* MEM_RECLAIM */ + +static sys_sem_t mem_sem; + +/*-----------------------------------------------------------------------------------*/ +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + ASSERT("plug_holes: mem >= ram", (uInt8 *)mem >= ram); + ASSERT("plug_holes: mem < ram_end", (uInt8 *)mem < (uInt8 *)ram_end); + ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + ASSERT("plug_holes: mem->next <= MEM_SIZE", mem->next <= MEM_SIZE); + + nmem = (struct mem *)&ram[mem->next]; + if(mem != nmem && nmem->used == 0 && (uInt8 *)nmem != (uInt8 *)ram_end) { + if(lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)&ram[nmem->next])->prev = (uInt8 *)mem - ram; + } + + /* plug hole backward */ + pmem = (struct mem *)&ram[mem->prev]; + if(pmem != mem && pmem->used == 0) { + if(lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)&ram[mem->next])->prev = (uInt8 *)pmem - ram; + } + +} +/*-----------------------------------------------------------------------------------*/ +void +mem_init(void) +{ + struct mem *mem; + + bzero(ram, MEM_SIZE); + mem = (struct mem *)ram; + mem->next = MEM_SIZE; + mem->prev = 0; + mem->used = 0; + ram_end = (struct mem *)&ram[MEM_SIZE]; + ram_end->used = 1; + ram_end->next = MEM_SIZE; + ram_end->prev = MEM_SIZE; + + mem_sem = sys_sem_new(1); + + lfree = (struct mem *)ram; + +#if MEM_RECLAIM + mrlist = NULL; +#endif /* MEM_RECLAIM */ + +#ifdef MEM_STATS + stats.mem.avail = MEM_SIZE; +#endif /* MEM_STATS */ +} +/*-----------------------------------------------------------------------------------*/ +#if MEM_RECLAIM +void +mem_reclaim(unsigned int size) +{ + struct mem_reclaim_ *mr; + int rec; + + rec = 0; + + for(mr = mrlist; mr != NULL; mr = mr->next) { + DEBUGF(MEM_DEBUG, ("mem_malloc: calling reclaimer\n")); + rec += mr->f(mr->arg, size); + } +#ifdef MEM_STATS + stats.mem.reclaimed += rec; +#endif /* MEM_STATS */ +} +#endif /* MEM_RECLAIM */ +/*-----------------------------------------------------------------------------------*/ +void * +mem_malloc2(mem_size_t size) +{ + void *mem; + mem = mem_malloc(size); +#if MEM_RECLAIM + if(mem == NULL) { + mem_reclaim(size); + mem = mem_malloc(size); + } +#endif /* MEM_RECLAIM */ + return mem; +} +/*-----------------------------------------------------------------------------------*/ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + + if(size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + if((size % MEM_ALIGNMENT) != 0) { + size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT); + } + + if(size > MEM_SIZE) { + return NULL; + } + + sys_sem_wait(mem_sem); + + for(ptr = (uInt8 *)lfree - ram; ptr < MEM_SIZE; ptr = ((struct mem *)&ram[ptr])->next) { + mem = (struct mem *)&ram[ptr]; + if(!mem->used && + mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) { + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + mem2 = (struct mem *)&ram[ptr2]; + + mem2->prev = ptr; + mem2->next = mem->next; + mem->next = ptr2; + if(mem2->next != MEM_SIZE) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + + mem2->used = 0; + mem->used = 1; +#ifdef MEM_STATS + stats.mem.used += size; + /* if(stats.mem.max < stats.mem.used) { + stats.mem.max = stats.mem.used; + } */ + if(stats.mem.max < ptr2) { + stats.mem.max = ptr2; + } +#ifdef MEM_PERF + mem_perf_output(); +#endif /* MEM_PERF */ +#endif /* MEM_STATS */ + + if(mem == lfree) { + /* Find next free block after mem */ + while(lfree->used && lfree != ram_end) { + lfree = (struct mem *)&ram[lfree->next]; + } + ASSERT("mem_malloc: !lfree->used", !lfree->used); + } + sys_sem_signal(mem_sem); + ASSERT("mem_malloc: allocated memory not above ram_end.", + (uInt32)mem + SIZEOF_STRUCT_MEM + size <= (uInt32)ram_end); + ASSERT("mem_malloc: allocated memory properly aligned.", + (unsigned long)((uInt8 *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + return (uInt8 *)mem + SIZEOF_STRUCT_MEM; + } + } + DEBUGF(MEM_DEBUG, ("mem_malloc: could not allocate %d bytes\n", (int)size)); +#ifdef MEM_STATS + ++stats.mem.err; +#endif /* MEM_STATS */ + sys_sem_signal(mem_sem); + return NULL; +} +/*-----------------------------------------------------------------------------------*/ +void +mem_free(void *rmem) +{ + struct mem *mem; + + if(rmem == NULL) { + return; + } + + sys_sem_wait(mem_sem); + + ASSERT("mem_free: legal memory", (uInt8 *)rmem >= (uInt8 *)ram && + (uInt8 *)rmem < (uInt8 *)ram_end); + + + if((uInt8 *)rmem < (uInt8 *)ram || (uInt8 *)rmem >= (uInt8 *)ram_end) { + DEBUGF(MEM_DEBUG, ("mem_free: illegal memory\n")); +#ifdef MEM_STATS + ++stats.mem.err; +#endif /* MEM_STATS */ + return; + } + mem = (struct mem *)((uInt8 *)rmem - SIZEOF_STRUCT_MEM); + + ASSERT("mem_free: mem->used", mem->used); + + mem->used = 0; + + if(mem < lfree) { + lfree = mem; + } + +#ifdef MEM_STATS + stats.mem.used -= mem->next - ((uInt8 *)mem - ram) - SIZEOF_STRUCT_MEM; +#ifdef MEM_PERF + mem_perf_output(); +#endif /* MEM_PERF */ + +#endif /* MEM_STATS */ + plug_holes(mem); + sys_sem_signal(mem_sem); +} +/*-----------------------------------------------------------------------------------*/ +void * +mem_reallocm(void *rmem, mem_size_t newsize) +{ + void *nmem; + nmem = mem_malloc(newsize); + if(nmem == NULL) { + return mem_realloc(rmem, newsize); + } + bcopy(rmem, nmem, newsize); + mem_free(rmem); + return nmem; +} +/*-----------------------------------------------------------------------------------*/ +void * +mem_realloc(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + + sys_sem_wait(mem_sem); + + ASSERT("mem_realloc: legal memory", (uInt8 *)rmem >= (uInt8 *)ram && + (uInt8 *)rmem < (uInt8 *)ram_end); + + if((uInt8 *)rmem < (uInt8 *)ram || (uInt8 *)rmem >= (uInt8 *)ram_end) { + DEBUGF(MEM_DEBUG, ("mem_free: illegal memory\n")); + return rmem; + } + mem = (struct mem *)((uInt8 *)rmem - SIZEOF_STRUCT_MEM); + + ptr = (uInt8 *)mem - ram; + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; +#ifdef MEM_STATS + stats.mem.used -= (size - newsize); +#ifdef MEM_PERF + mem_perf_output(); +#endif /* MEM_PERF */ +#endif /* MEM_STATS */ + + if(newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) { + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if(mem2->next != MEM_SIZE) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + + plug_holes(mem2); + } + sys_sem_signal(mem_sem); + return rmem; +} +/*-----------------------------------------------------------------------------------*/ +#if MEM_RECLAIM +void +mem_register_reclaim(mem_reclaim_func f, void *arg) +{ + struct mem_reclaim_ *mr; + + mr = mem_malloc(sizeof(struct mem_reclaim_)); + if(mr == NULL) { + return; + } + mr->next = mrlist; + mrlist = mr; + mr->f = f; + mr->arg = arg; +} +#endif /* MEM_RECLAIM */ +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/memp.c b/src/sys/net/core/memp.c new file mode 100644 index 0000000..4d1b11b --- /dev/null +++ b/src/sys/net/core/memp.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +#include + +#include "net/lwipopts.h" + +#include "net/memp.h" + +#include "net/pbuf.h" +#include "net/udp.h" +#include "net/tcp.h" +#include "net/api.h" +#include "net/api_msg.h" +#include "net/tcpip.h" + +#include "net/sys.h" +#include "net/stats.h" + +struct memp { + struct memp *next; +}; + + + +static struct memp *memp_tab[MEMP_MAX]; + +static const uInt16 memp_sizes[MEMP_MAX] = { + sizeof(struct pbuf), + sizeof(struct udp_pcb), + sizeof(struct tcp_pcb), + sizeof(struct tcp_pcb_listen), + sizeof(struct tcp_seg), + sizeof(struct netbuf), + sizeof(struct netconn), + sizeof(struct api_msg), + sizeof(struct tcpip_msg), + sizeof(struct sys_timeout) +}; + +static const uInt16 memp_num[MEMP_MAX] = { + MEMP_NUM_PBUF, + MEMP_NUM_UDP_PCB, + MEMP_NUM_TCP_PCB, + MEMP_NUM_TCP_PCB_LISTEN, + MEMP_NUM_TCP_SEG, + MEMP_NUM_NETBUF, + MEMP_NUM_NETCONN, + MEMP_NUM_API_MSG, + MEMP_NUM_TCPIP_MSG, + MEMP_NUM_SYS_TIMEOUT +}; + +static uInt8 memp_memory[(MEMP_NUM_PBUF * + MEM_ALIGN_SIZE(sizeof(struct pbuf) + + sizeof(struct memp)) + + MEMP_NUM_UDP_PCB * + MEM_ALIGN_SIZE(sizeof(struct udp_pcb) + + sizeof(struct memp)) + + MEMP_NUM_TCP_PCB * + MEM_ALIGN_SIZE(sizeof(struct tcp_pcb) + + sizeof(struct memp)) + + MEMP_NUM_TCP_PCB_LISTEN * + MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen) + + sizeof(struct memp)) + + MEMP_NUM_TCP_SEG * + MEM_ALIGN_SIZE(sizeof(struct tcp_seg) + + sizeof(struct memp)) + + MEMP_NUM_NETBUF * + MEM_ALIGN_SIZE(sizeof(struct netbuf) + + sizeof(struct memp)) + + MEMP_NUM_NETCONN * + MEM_ALIGN_SIZE(sizeof(struct netconn) + + sizeof(struct memp)) + + MEMP_NUM_API_MSG * + MEM_ALIGN_SIZE(sizeof(struct api_msg) + + sizeof(struct memp)) + + MEMP_NUM_TCPIP_MSG * + MEM_ALIGN_SIZE(sizeof(struct tcpip_msg) + + sizeof(struct memp)) + + MEMP_NUM_SYS_TIMEOUT * + MEM_ALIGN_SIZE(sizeof(struct sys_timeout) + + sizeof(struct memp)))]; + +#if MEMP_RECLAIM +struct memp_reclaim_ { + struct memp_reclaim_ *next; + memp_reclaim_func f; + void *arg; +}; + +static struct memp_reclaim_ *memp_reclaim_funcs[MEMP_MAX]; + +static uInt8 memp_reclaim(memp_t type); +#endif /* MEMP_RECLAIM */ + +/*-----------------------------------------------------------------------------------*/ +static sys_sem_t mutex; +/*-----------------------------------------------------------------------------------*/ +#ifdef LWIP_DEBUG +static int +memp_sanity(void) +{ + int i, c; + struct memp *m, *n; + + for(i = 0; i < MEMP_MAX; i++) { + for(m = memp_tab[i]; m != NULL; m = m->next) { + c = 1; + for(n = memp_tab[i]; n != NULL; n = n->next) { + if(n == m) { + --c; + } + if(c < 0) + abort(); + } + } + } + return 1; +} +#endif /* LWIP_DEBUG */ +/*-----------------------------------------------------------------------------------*/ +void +memp_init(void) +{ + struct memp *m, *memp; + uInt16 i, j; + uInt16 size; + +#ifdef MEMP_STATS + for(i = 0; i < MEMP_MAX; ++i) { + stats.memp[i].used = stats.memp[i].max = + stats.memp[i].err = stats.memp[i].reclaimed = 0; + stats.memp[i].avail = memp_num[i]; + } +#endif /* MEMP_STATS */ + + memp = (struct memp *)&memp_memory[0]; + for(i = 0; i < MEMP_MAX; ++i) { + size = MEM_ALIGN_SIZE(memp_sizes[i] + sizeof(struct memp)); + if(memp_num[i] > 0) { + memp_tab[i] = memp; + m = memp; + + for(j = 0; j < memp_num[i]; ++j) { + m->next = (struct memp *)MEM_ALIGN((uInt8 *)m + size); + memp = m; + m = m->next; + } + memp->next = NULL; + memp = m; + } else { + memp_tab[i] = NULL; + } + } + + mutex = sys_sem_new(1); + +#if MEMP_RECLAIM + for(i = 0; i < MEMP_MAX; ++i) { + memp_reclaim_funcs[i] = NULL; + } +#endif /* MEMP_RECLAIM */ + +} +/*-----------------------------------------------------------------------------------*/ +void * +memp_malloc(memp_t type) +{ + struct memp *memp; + + ASSERT("memp_malloc: type < MEMP_MAX", type < MEMP_MAX); + + memp = memp_tab[type]; + + if(memp != NULL) { + memp_tab[type] = memp->next; + memp->next = NULL; +#ifdef MEMP_STATS + ++stats.memp[type].used; + if(stats.memp[type].used > stats.memp[type].max) { + stats.memp[type].max = stats.memp[type].used; + } +#endif /* MEMP_STATS */ + ASSERT("memp_malloc: memp properly aligned", + ((u32_t)MEM_ALIGN((uInt8 *)memp + sizeof(struct memp)) % MEM_ALIGNMENT) == 0); + + return MEM_ALIGN((uInt8 *)memp + sizeof(struct memp)); + } else { + DEBUGF(MEMP_DEBUG, ("memp_malloc: out of memory in pool %d\n", type)); +#ifdef MEMP_STATS + ++stats.memp[type].err; +#endif /* MEMP_STATS */ + return NULL; + } +} +/*-----------------------------------------------------------------------------------*/ +void * +memp_mallocp(memp_t type) +{ + void *mem; + sys_sem_wait(mutex); + mem = memp_malloc(type); + sys_sem_signal(mutex); + return mem; +} +/*-----------------------------------------------------------------------------------*/ +void * +memp_malloc2(memp_t type) +{ + void *mem; + + mem = memp_malloc(type); +#if MEMP_RECLAIM + if(mem == NULL) { + memp_reclaim(type); + mem = memp_malloc(type); + } +#endif /* MEMP_RECLAIM */ + return mem; +} +/*-----------------------------------------------------------------------------------*/ +void * +memp_realloc(memp_t fromtype, memp_t totype, void *mem) +{ + void *rmem; + uInt16 size; + + if(mem == NULL) { + return NULL; + } + + rmem = memp_malloc(totype); + if(rmem != NULL) { + size = memp_sizes[totype]; + if(memp_sizes[fromtype] < size) { + size = memp_sizes[fromtype]; + } + bcopy(mem, rmem, size); + memp_free(fromtype, mem); + } + return rmem; +} +/*-----------------------------------------------------------------------------------*/ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + + if(mem == NULL) { + return; + } + memp = (struct memp *)((uInt8 *)mem - sizeof(struct memp)); + +#ifdef MEMP_STATS + stats.memp[type].used--; +#endif /* MEMP_STATS */ + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + + ASSERT("memp sanity", memp_sanity()); + return; +} +/*-----------------------------------------------------------------------------------*/ +void +memp_freep(memp_t type, void *mem) +{ + sys_sem_wait(mutex); + memp_free(type, mem); + sys_sem_signal(mutex); +} +/*-----------------------------------------------------------------------------------*/ +#if MEMP_RECLAIM +static uInt8 +memp_reclaim(memp_t type) +{ + struct memp_reclaim_ *mr; + + for(mr = memp_reclaim_funcs[type]; mr != NULL; mr = mr->next) { + DEBUGF(MEMP_DEBUG, ("memp_reclaim: calling reclaimer for type %d\n", type)); + if(mr->f(mr->arg, type) != 0) { +#ifdef MEMP_STATS + ++stats.memp[type].reclaimed; +#endif /* MEMP_STATS */ + return 1; + } + } + return 0; +} +/*-----------------------------------------------------------------------------------*/ +void +memp_register_reclaim(memp_t type, memp_reclaim_func f, void *arg) +{ + struct memp_reclaim_ *mr; + + mr = mem_malloc(sizeof(struct memp_reclaim_)); + if(mr == NULL) { + return; + } + mr->next = memp_reclaim_funcs[type]; + memp_reclaim_funcs[type] = mr; + mr->f = f; + mr->arg = arg; + DEBUGF(MEMP_DEBUG, ("memp_register_reclaim: registering reclaimer for type %d\n", type)); +} +#endif /* MEMP_RECLAIM */ +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/netif.c b/src/sys/net/core/netif.c new file mode 100644 index 0000000..1c61ea1 --- /dev/null +++ b/src/sys/net/core/netif.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +#include +#include + +#include "net/debug.h" + +#include "net/def.h" +#include "net/mem.h" +#include "net/netif.h" + +struct netif *netif_list = NULL; +struct netif *netif_default = NULL; + +/*-----------------------------------------------------------------------------------*/ +struct netif * +netif_add(struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw, + void (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + struct netif *netif; + static int netifnum = 0; + + netif = mem_malloc(sizeof(struct netif)); + + if(netif == NULL) { + return NULL; + } + + netif->num = netifnum++; + netif->input = input; + ip_addr_set(&(netif->ip_addr), ipaddr); + ip_addr_set(&(netif->netmask), netmask); + ip_addr_set(&(netif->gw), gw); + + init(netif); + + netif->next = netif_list; + netif_list = netif; + kprintf("netif: added interface %c%c IP addr ",netif->name[0], netif->name[1]); + ip_addr_debug_print(ipaddr); + kprintf(" netmask "); + ip_addr_debug_print(netmask); + kprintf(" gw "); + ip_addr_debug_print(gw); + kprintf("\n"); + return netif; +} +/*-----------------------------------------------------------------------------------*/ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + uInt8 num; + + if(name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if(num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + kprintf("netif_find: found %s\n", name); + return netif; + } + } + kprintf("netif_find: didn't find %s\n", name); + return NULL; +} +/*-----------------------------------------------------------------------------------*/ +void +netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr) +{ + ip_addr_set(&(netif->ip_addr), ipaddr); + kprintf("netif: setting IP address of interface %c%c to %d.%d.%d.%d\n", + netif->name[0], netif->name[1], + (uInt8)(ntohl(ipaddr->addr) >> 24 & 0xff), + (uInt8)(ntohl(ipaddr->addr) >> 16 & 0xff), + (uInt8)(ntohl(ipaddr->addr) >> 8 & 0xff), + (uInt8)(ntohl(ipaddr->addr) & 0xff)); +} +/*-----------------------------------------------------------------------------------*/ +void +netif_set_gw(struct netif *netif, struct ip_addr *gw) +{ + ip_addr_set(&(netif->gw), gw); +} +/*-----------------------------------------------------------------------------------*/ +void +netif_set_netmask(struct netif *netif, struct ip_addr *netmask) +{ + ip_addr_set(&(netif->netmask), netmask); +} +/*-----------------------------------------------------------------------------------*/ +void +netif_set_default(struct netif *netif) +{ + netif_default = netif; + DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif->name[0], netif->name[1])); +} +/*-----------------------------------------------------------------------------------*/ +void +netif_init(void) +{ + netif_list = netif_default = NULL; +} +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/pbuf.c b/src/sys/net/core/pbuf.c new file mode 100644 index 0000000..add8e73 --- /dev/null +++ b/src/sys/net/core/pbuf.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* pbuf.c + * + * Functions for the manipulation of pbufs. The pbufs holds all packets in the + * system. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + + +#include "net/debug.h" + +#include "net/stats.h" + +#include "net/def.h" +#include "net/mem.h" +#include "net/memp.h" +#include "net/pbuf.h" + +#include "net/sys.h" + +#include "net/arch/perf.h" + +static uInt8 pbuf_pool_memory[(PBUF_POOL_SIZE * MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE + sizeof(struct pbuf)))]; +static volatile uInt8 pbuf_pool_free_lock, pbuf_pool_alloc_lock; +static sys_sem_t pbuf_pool_free_sem; +static struct pbuf *pbuf_pool = NULL; +static struct pbuf *pbuf_pool_alloc_cache = NULL; +static struct pbuf *pbuf_pool_free_cache = NULL; + +/*-----------------------------------------------------------------------------------*/ +/* pbuf_init(): + * + * Initializes the pbuf module. A large part of memory is allocated + * for holding the pool of pbufs. The size of the individual pbufs in + * the pool is given by the size parameter, and the number of pbufs in + * the pool by the num parameter. + * + * After the memory has been allocated, the pbufs are set up. The + * ->next pointer in each pbuf is set up to point to the next pbuf in + * the pool. + */ +/*-----------------------------------------------------------------------------------*/ +void +pbuf_init(void) +{ + struct pbuf *p, *q; + uInt8 i; + + pbuf_pool = (struct pbuf *)&pbuf_pool_memory[0]; + ASSERT("pbuf_init: pool aligned", (long)pbuf_pool % MEM_ALIGNMENT == 0); + +#ifdef PBUF_STATS + stats.pbuf.avail = PBUF_POOL_SIZE; +#endif /* PBUF_STATS */ + + /* Set up ->next pointers to link the pbufs of the pool together. */ + p = pbuf_pool; + + for(i = 0; i < PBUF_POOL_SIZE; ++i) { + p->next = (struct pbuf *)((uInt8 *)p + PBUF_POOL_BUFSIZE + sizeof(struct pbuf)); + p->len = p->tot_len = PBUF_POOL_BUFSIZE; + p->payload = MEM_ALIGN((void *)((uInt8 *)p + sizeof(struct pbuf))); + q = p; + p = p->next; + } + + /* The ->next pointer of last pbuf is NULL to indicate that there + are no more pbufs in the pool. */ + q->next = NULL; + + pbuf_pool_alloc_lock = 0; + pbuf_pool_free_lock = 0; + pbuf_pool_free_sem = sys_sem_new(1); + +} +/*-----------------------------------------------------------------------------------*/ +/* The following two functions are only called from pbuf_alloc(). */ +/*-----------------------------------------------------------------------------------*/ +static struct pbuf * +pbuf_pool_alloc(void) +{ + struct pbuf *p = NULL; + + /* First, see if there are pbufs in the cache. */ + if(pbuf_pool_alloc_cache) { + p = pbuf_pool_alloc_cache; + if(p) { + pbuf_pool_alloc_cache = p->next; + } + } else { + /* Next, check the actual pbuf pool, but if the pool is locked, we + pretend to be out of buffers and return NULL. */ + if(pbuf_pool_free_lock) { +#ifdef PBUF_STATS + ++stats.pbuf.alloc_locked; +#endif /* PBUF_STATS */ + return NULL; + } + ++pbuf_pool_alloc_lock; + if(!pbuf_pool_free_lock) { + p = pbuf_pool; + if(p) { + pbuf_pool = p->next; + } +#ifdef PBUF_STATS + } else { + ++stats.pbuf.alloc_locked; +#endif /* PBUF_STATS */ + } + --pbuf_pool_alloc_lock; + } + +#ifdef PBUF_STATS + if(p != NULL) { + ++stats.pbuf.used; + if(stats.pbuf.used > stats.pbuf.max) { + stats.pbuf.max = stats.pbuf.used; + } + } +#endif /* PBUF_STATS */ + + return p; +} +/*-----------------------------------------------------------------------------------*/ +static void +pbuf_pool_free(struct pbuf *p) +{ + struct pbuf *q; + +#ifdef PBUF_STATS + for(q = p; q != NULL; q = q->next) { + --stats.pbuf.used; + } +#endif /* PBUF_STATS */ + + if(pbuf_pool_alloc_cache == NULL) { + pbuf_pool_alloc_cache = p; + } else { + for(q = pbuf_pool_alloc_cache; q->next != NULL; q = q->next); + q->next = p; + } +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_alloc(): + * + * Allocates a pbuf at protocol layer l. The actual memory allocated + * for the pbuf is determined by the layer at which the pbuf is + * allocated and the requested size (from the size parameter). The + * flag parameter decides how and where the pbuf should be allocated + * as follows: + * + * * PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * * RBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. + * * PBUF_ROOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + */ +/*-----------------------------------------------------------------------------------*/ +struct pbuf * +pbuf_alloc(pbuf_layer l, uInt16 size, pbuf_flag flag) +{ + struct pbuf *p, *q, *r; + uInt16 offset; + Int32 rsize; + + offset = 0; + switch(l) { + case PBUF_TRANSPORT: + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + offset += PBUF_IP_HLEN; + offset += PBUF_LINK_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + break; + case PBUF_RAW: + break; + default: + ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch(flag) { + case PBUF_POOL: + /* Allocate head of pbuf chain into p. */ + p = pbuf_pool_alloc(); + if(p == NULL) { +#ifdef PBUF_STATS + ++stats.pbuf.err; +#endif /* PBUF_STATS */ + return NULL; + } + p->next = NULL; + + /* Set the payload pointer so that it points offset bytes into + pbuf data memory. */ + p->payload = MEM_ALIGN((void *)((uInt8 *)p + (sizeof(struct pbuf) + offset))); + + /* The total length of the pbuf is the requested size. */ + p->tot_len = size; + + /* Set the length of the first pbuf is the chain. */ + p->len = size > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: size; + + p->flags = PBUF_FLAG_POOL; + + /* Allocate the tail of the pbuf chain. */ + r = p; + rsize = size - p->len; + while(rsize > 0) { + q = pbuf_pool_alloc(); + if(q == NULL) { + DEBUGF(PBUF_DEBUG, ("pbuf_alloc: Out of pbufs in pool,\n")); +#ifdef PBUF_STATS + ++stats.pbuf.err; +#endif /* PBUF_STATS */ + pbuf_pool_free(p); + return NULL; + } + q->next = NULL; + r->next = q; + q->len = rsize > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rsize; + q->flags = PBUF_FLAG_POOL; + q->payload = (void *)((uInt8 *)q + sizeof(struct pbuf)); + r = q; + q->ref = 1; + q = q->next; + rsize -= PBUF_POOL_BUFSIZE; + } + r->next = NULL; + + ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((u32_t)p->payload % MEM_ALIGNMENT) == 0); + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + size + offset)); + if(p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = MEM_ALIGN((void *)((uInt8 *)p + sizeof(struct pbuf) + offset)); + p->len = p->tot_len = size; + p->next = NULL; + p->flags = PBUF_FLAG_RAM; + + ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((u32_t)p->payload % MEM_ALIGNMENT) == 0); + break; + case PBUF_ROM: + /* If the pbuf should point to ROM, we only need to allocate + memory for the pbuf structure. */ + p = memp_mallocp(MEMP_PBUF); + if(p == NULL) { + return NULL; + } + p->payload = NULL; + p->len = p->tot_len = size; + p->next = NULL; + p->flags = PBUF_FLAG_ROM; + break; + default: + ASSERT("pbuf_alloc: erroneous flag", 0); + return NULL; + } + p->ref = 1; + return p; +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_refresh(): + * + * Moves free buffers from the pbuf_pool_free_cache to the pbuf_pool + * list (if possible). + * + */ +/*-----------------------------------------------------------------------------------*/ +void +pbuf_refresh(void) +{ + struct pbuf *p; + + sys_sem_wait(pbuf_pool_free_sem); + + if(pbuf_pool_free_cache != NULL) { + ++pbuf_pool_free_lock; + if(!pbuf_pool_alloc_lock) { + if(pbuf_pool == NULL) { + pbuf_pool = pbuf_pool_free_cache; + } else { + for(p = pbuf_pool; p->next != NULL; p = p->next); + p->next = pbuf_pool_free_cache; + } + pbuf_pool_free_cache = NULL; +#ifdef PBUF_STATS + } else { + ++stats.pbuf.refresh_locked; +#endif /* PBUF_STATS */ + } + + --pbuf_pool_free_lock; + } + + sys_sem_signal(pbuf_pool_free_sem); +} +#define PBUF_POOL_FREE(p) do { \ + sys_sem_wait(pbuf_pool_free_sem); \ + p->next = pbuf_pool_free_cache; \ + pbuf_pool_free_cache = p; \ + sys_sem_signal(pbuf_pool_free_sem); \ + } while(0) +/*-----------------------------------------------------------------------------------*/ +/* pbuf_realloc: + * + * Reallocates the memory for a pbuf. If the pbuf is in ROM, this as + * simple as to adjust the ->tot_len and ->len fields. If the pbuf is + * a pbuf chain, as it might be with both pbufs in dynamically + * allocated RAM and for pbufs from the pbuf pool, we have to step + * through the chain until we find the new endpoint in the pbuf chain. + * Then the pbuf that is right on the endpoint is resized and any + * further pbufs on the chain are deallocated. + */ +/*-----------------------------------------------------------------------------------*/ +void +pbuf_realloc(struct pbuf *p, uInt16 size) +{ + struct pbuf *q, *r; + uInt16 rsize; + + ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL || + p->flags == PBUF_FLAG_ROM || + p->flags == PBUF_FLAG_RAM); + + + if(p->tot_len <= size) { + return; + } + + switch(p->flags) { + case PBUF_FLAG_POOL: + /* First, step over any pbufs that should still be in the chain. */ + rsize = size; + q = p; + while(rsize > q->len) { + rsize -= q->len; + q = q->next; + } + /* Adjust the length of the pbuf that will be halved. */ + q->len = rsize; + + /* And deallocate any left over pbufs. */ + r = q->next; + q->next = NULL; + q = r; + while(q != NULL) { + r = q->next; + PBUF_POOL_FREE(q); +#ifdef PBUF_STATS + --stats.pbuf.used; +#endif /* PBUF_STATS */ + q = r; + } + break; + case PBUF_FLAG_ROM: + p->len = size; + break; + case PBUF_FLAG_RAM: + /* First, step over the pbufs that should still be in the chain. */ + rsize = size; + q = p; + while(rsize > q->len) { + rsize -= q->len; + q = q->next; + } + if(q->flags == PBUF_FLAG_RAM) { + /* Reallocate and adjust the length of the pbuf that will be halved. */ + mem_realloc(q, (uInt8 *)q->payload - (uInt8 *)q + rsize); + } + + q->len = rsize; + + /* And deallocate any left over pbufs. */ + r = q->next; + q->next = NULL; + q = r; + while(q != NULL) { + r = q->next; + pbuf_free(q); + q = r; + } + break; + } + p->tot_len = size; + + pbuf_refresh(); +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_header(): + * + * Adjusts the ->payload pointer so that space for a header appears in + * the pbuf. Also, the ->tot_len and ->len fields are adjusted. + */ +/*-----------------------------------------------------------------------------------*/ +uInt8 +pbuf_header(struct pbuf *p, Int16 header_size) +{ + void *payload; + + payload = p->payload; + p->payload = (uInt8 *)p->payload - header_size/sizeof(uInt8); + + DEBUGF(PBUF_DEBUG, ("pbuf_header: old %p new %p (%d)\n", payload, p->payload, header_size)); + + if((uInt8 *)p->payload < (uInt8 *)p + sizeof(struct pbuf)) { + DEBUGF(PBUF_DEBUG, ("pbuf_header: failed %p %p\n", + (uInt8 *)p->payload, + (uInt8 *)p + sizeof(struct pbuf))); + p->payload = payload; + return -1; + } + p->len += header_size; + p->tot_len += header_size; + + return 0; +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_free(): + * + * Decrements the reference count and deallocates the pbuf if the + * reference count is zero. If the pbuf is a chain all pbufs in the + * chain are deallocated. + */ +/*-----------------------------------------------------------------------------------*/ +uInt8 +pbuf_free(struct pbuf *p) +{ + struct pbuf *q; + uInt8 count = 0; + + if(p == NULL) { + return 0; + } + + PERF_START; + + ASSERT("pbuf_free: sane flags", p->flags == PBUF_FLAG_POOL || + p->flags == PBUF_FLAG_ROM || + p->flags == PBUF_FLAG_RAM); + + ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + + /* Decrement reference count. */ + p->ref--; + + q = NULL; + /* If reference count == 0, actually deallocate pbuf. */ + if(p->ref == 0) { + + while(p != NULL) { + /* Check if this is a pbuf from the pool. */ + if(p->flags == PBUF_FLAG_POOL) { + p->len = p->tot_len = PBUF_POOL_BUFSIZE; + p->payload = (void *)((uInt8 *)p + sizeof(struct pbuf)); + q = p->next; + PBUF_POOL_FREE(p); +#ifdef PBUF_STATS + --stats.pbuf.used; +#endif /* PBUF_STATS */ + } else if(p->flags == PBUF_FLAG_ROM) { + q = p->next; + memp_freep(MEMP_PBUF, p); + } else { + q = p->next; + mem_free(p); + } + p = q; + count++; + } + pbuf_refresh(); + } + + PERF_STOP("pbuf_free"); + + return count; +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_clen(): + * + * Returns the length of the pbuf chain. + */ +/*-----------------------------------------------------------------------------------*/ +uInt8 +pbuf_clen(struct pbuf *p) +{ + uInt8 len; + + if(p == NULL) { + return 0; + } + + for(len = 0; p != NULL; p = p->next) { + ++len; + } + return len; +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_ref(): + * + * Increments the reference count of the pbuf. + */ +/*-----------------------------------------------------------------------------------*/ +void +pbuf_ref(struct pbuf *p) +{ + if(p == NULL) { + return; + } + ++(p->ref); +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_chain(): + * + * Chains the two pbufs h and t together. The ->tot_len field of the + * first pbuf (h) is adjusted. + */ +/*-----------------------------------------------------------------------------------*/ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + for(p = h; p->next != NULL; p = p->next); + p->next = t; + h->tot_len += t->tot_len; +} +/*-----------------------------------------------------------------------------------*/ +/* pbuf_dechain(): + * + * Adjusts the ->tot_len field of the pbuf and returns the tail (if + * any) of the pbuf chain. + */ +/*-----------------------------------------------------------------------------------*/ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + + q = p->next; + if (q != NULL) { + q->tot_len = p->tot_len - p->len; + } + p->tot_len = p->len; + p->next = NULL; + return q; +} +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/stats.c b/src/sys/net/core/stats.c new file mode 100644 index 0000000..1a5f9a0 --- /dev/null +++ b/src/sys/net/core/stats.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + + +#include "net/debug.h" +#include "net/opt.h" + +#include "net/def.h" + +#include "net/stats.h" +#include "net/mem.h" + + +#ifdef STATS +struct stats_ stats; +#endif /* STATS */ +/*-----------------------------------------------------------------------------------*/ +void +stats_init(void) +{ +#ifdef STATS + bzero(&stats, sizeof(struct stats_)); +#endif /* STATS */ +} +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/core/sys.c b/src/sys/net/core/sys.c new file mode 100644 index 0000000..143f176 --- /dev/null +++ b/src/sys/net/core/sys.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Log$ + * Revision 1.6 2004/04/13 19:40:50 reddawg + * Fixed typedefs + * + * + * $Id$ + */ + + +#include +#include +#include +#include +#include +#include + +/*-----------------------------------------------------------------------------------*/ +void +sys_mbox_fetch(sys_mbox_t mbox, void **msg) +{ + uInt16 time; + struct sys_timeouts *timeouts; + struct sys_timeout *tmptimeout; + sys_timeout_handler h; + void *arg; + + + again: + timeouts = sys_arch_timeouts(); + + if(timeouts->next == NULL) { + sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if(timeouts->next->time > 0) { + time = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + } else { + time = 0; + } + + if(time == 0) { + /* If time == 0, a timeout occured before a message could be + fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + h(arg); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time > 0, a message was received before the timeout + occured. The time variable is set to the number of + microseconds we waited for the message. */ + if(time <= timeouts->next->time) { + timeouts->next->time -= time; + } else { + timeouts->next->time = 0; + } + } + + } +} +/*-----------------------------------------------------------------------------------*/ +void +sys_sem_wait(sys_sem_t sem) +{ + uInt16 time; + struct sys_timeouts *timeouts; + struct sys_timeout *tmptimeout; + sys_timeout_handler h; + void *arg; + + /* while(sys_arch_sem_wait(sem, 1000) == 0); + return;*/ + + again: + + timeouts = sys_arch_timeouts(); + + if(timeouts->next == NULL) { + sys_arch_sem_wait(sem, 0); + } else { + if(timeouts->next->time > 0) { + time = sys_arch_sem_wait(sem, timeouts->next->time); + } else { + time = 0; + } + + if(time == 0) { + /* If time == 0, a timeout occured before a message could be + fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + h(arg); + + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time > 0, a message was received before the timeout + occured. The time variable is set to the number of + microseconds we waited for the message. */ + if(time <= timeouts->next->time) { + timeouts->next->time -= time; + } else { + timeouts->next->time = 0; + } + } + + } +} +/*-----------------------------------------------------------------------------------*/ +void +sys_timeout(uInt16 msecs, sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeout *timeout, *t; + + timeout = memp_malloc(MEMP_SYS_TIMEOUT); + if(timeout == NULL) { + return; + } + timeout->next = NULL; + timeout->h = h; + timeout->arg = arg; + timeout->time = msecs; + + timeouts = sys_arch_timeouts(); + + if(timeouts->next == NULL) { + timeouts->next = timeout; + return; + } + + if(timeouts->next->time > msecs) { + timeouts->next->time -= msecs; + timeout->next = timeouts->next; + timeouts->next = timeout; + } else { + for(t = timeouts->next; t != NULL; t = t->next) { + timeout->time -= t->time; + if(t->next == NULL || + t->next->time > timeout->time) { + if(t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } + +} +/*-----------------------------------------------------------------------------------*/ + diff --git a/src/sys/net/core/tcp.c b/src/sys/net/core/tcp.c new file mode 100644 index 0000000..d1f1121 --- /dev/null +++ b/src/sys/net/core/tcp.c @@ -0,0 +1,1160 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* tcp.c + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_input.c and tcp_output.c respectively. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/def.h" +#include "net/mem.h" +#include "net/memp.h" + +#include "net/tcp.h" + +/* Incremented every coarse grained timer shot + (typically every 500 ms, determined by TCP_COARSE_TIMEOUT). */ +uInt32 tcp_ticks; +const uInt8 tcp_backoff[13] = + { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; + +/* The TCP PCB lists. */ +struct tcp_pcb_listen *tcp_listen_pcbs; /* List of all TCP PCBs in LISTEN state. */ +struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +struct tcp_pcb *tcp_tmp_pcb; + +#define MIN(x,y) (x) < (y)? (x): (y) + +#if MEMP_RECLAIM +static uInt8 tcp_memp_reclaim(void *arg, memp_t type); +#endif +#if MEM_RECLAIM +static mem_size_t tcp_mem_reclaim(void *arg, mem_size_t size); +#endif + +static uInt8 tcp_timer; + +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_init(): + * + * Initializes the TCP layer. + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_init(void) +{ + /* Clear globals. */ + tcp_listen_pcbs = NULL; + tcp_active_pcbs = NULL; + tcp_tw_pcbs = NULL; + tcp_tmp_pcb = NULL; + + /* Register memory reclaim function */ +#if MEM_RECLAIM + mem_register_reclaim((mem_reclaim_func)tcp_mem_reclaim, NULL); +#endif /* MEM_RECLAIM */ + +#if MEMP_RECLAIM + memp_register_reclaim(MEMP_PBUF, (memp_reclaim_func)tcp_memp_reclaim, NULL); + memp_register_reclaim(MEMP_TCP_SEG, (memp_reclaim_func)tcp_memp_reclaim, NULL); + memp_register_reclaim(MEMP_TCP_PCB, (memp_reclaim_func)tcp_memp_reclaim, NULL); +#endif /* MEMP_RECLAIM */ + + /* initialize timer */ + tcp_ticks = 0; + tcp_timer = 0; + +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_tmr(): + * + * Called periodically to dispatch TCP timers. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_tmr() +{ + ++tcp_timer; + if(tcp_timer == 10) { + tcp_timer = 0; + } + + if(tcp_timer & 1) { + /* Call tcp_fasttmr() every 200 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_fasttmr(); + } + if(tcp_timer == 0 || tcp_timer == 5) { + /* Call tcp_slowtmr() every 500 ms, i.e., every fifth timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_close(): + * + * Closes the connection held by the PCB. + * + */ +/*-----------------------------------------------------------------------------------*/ +err_t +tcp_close(struct tcp_pcb *pcb) +{ + err_t err; + +#if TCP_DEBUG + DEBUGF(TCP_DEBUG, ("tcp_close: closing in state ")); + tcp_debug_print_state(pcb->state); + DEBUGF(TCP_DEBUG, ("\n")); +#endif /* TCP_DEBUG */ + switch(pcb->state) { + case LISTEN: + err = ERR_OK; + tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case SYN_RCVD: + err = tcp_send_ctrl(pcb, TCP_FIN); + if(err == ERR_OK) { + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_ctrl(pcb, TCP_FIN); + if(err == ERR_OK) { + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_ctrl(pcb, TCP_FIN); + if(err == ERR_OK) { + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if(pcb != NULL && err == ERR_OK) { + err = tcp_output(pcb); + } + return err; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_abort() + * + * Aborts a connection by sending a RST to the remote host and deletes + * the local protocol control block. This is done when a connection is + * killed because of shortage of memory. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_abort(struct tcp_pcb *pcb) +{ + uInt32 seqno, ackno; + uInt16 remote_port, local_port; + struct ip_addr remote_ip, local_ip; + void (* errf)(void *arg, err_t err); + void *errf_arg; + + + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if(pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else if(pcb->state == LISTEN) { + tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + } else { + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; + ip_addr_set(&local_ip, &(pcb->local_ip)); + ip_addr_set(&remote_ip, &(pcb->remote_ip)); + local_port = pcb->local_port; + remote_port = pcb->remote_port; + errf = pcb->errf; + errf_arg = pcb->callback_arg; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + if(errf != NULL) { + errf(errf_arg, ERR_ABRT); + } + DEBUGF(TCP_RST_DEBUG, ("tcp_abort: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_bind(): + * + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + */ +/*-----------------------------------------------------------------------------------*/ +err_t +tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, uInt16 port) +{ + struct tcp_pcb *cpcb; + + /* Check if the address already is in use. */ + for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if(cpcb->local_port == port) { + if(ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + for(cpcb = tcp_active_pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if(cpcb->local_port == port) { + if(ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + if(!ip_addr_isany(ipaddr)) { + pcb->local_ip = *ipaddr; + } + pcb->local_port = port; + DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %d\n", port)); + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_listen(): + * + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + */ +/*-----------------------------------------------------------------------------------*/ +struct tcp_pcb * +tcp_listen(struct tcp_pcb *pcb) +{ + pcb->state = LISTEN; + pcb = memp_realloc(MEMP_TCP_PCB, MEMP_TCP_PCB_LISTEN, pcb); + if(pcb == NULL) { + return NULL; + } + TCP_REG((struct tcp_pcb **)&tcp_listen_pcbs, pcb); + return pcb; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_recved(): + * + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_recved(struct tcp_pcb *pcb, uInt16 len) +{ + pcb->rcv_wnd += len; + if(pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } + if(!(pcb->flags & TF_ACK_DELAY) || + !(pcb->flags & TF_ACK_NOW)) { + tcp_ack(pcb); + } + DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %d bytes, wnd %u (%u).\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_new_port(): + * + * A nastly hack featuring 'goto' statements that allocates a + * new TCP local port. + */ +/*-----------------------------------------------------------------------------------*/ +static uInt16 +tcp_new_port(void) +{ + struct tcp_pcb *pcb; + static uInt16 port = 4096; + + again: + if(++port > 0x7fff) { + port = 4096; + } + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->local_port == port) { + goto again; + } + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->local_port == port) { + goto again; + } + } + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->local_port == port) { + goto again; + } + } + return port; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_connect(): + * + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + */ +/*-----------------------------------------------------------------------------------*/ +err_t +tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, uInt16 port, + err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) +{ + uInt32 optdata; + err_t ret; + uInt32 iss; + + DEBUGF(TCP_DEBUG, ("tcp_connect to port %d\n", port)); + if(ipaddr != NULL) { + pcb->remote_ip = *ipaddr; + } else { + return ERR_VAL; + } + pcb->remote_port = port; + if(pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + } + iss = tcp_next_iss(); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND; + pcb->snd_wnd = TCP_WND; + pcb->mss = TCP_MSS; + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; + pcb->state = SYN_SENT; + pcb->connected = connected; + TCP_REG(&tcp_active_pcbs, pcb); + + /* Build an MSS option */ + optdata = HTONL(((uInt32)2 << 24) | + ((uInt32)4 << 16) | + (((uInt32)pcb->mss / 256) << 8) | + (pcb->mss & 255)); + + ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (uInt8 *)&optdata, 4); + if(ret == ERR_OK) { + tcp_output(pcb); + } + return ret; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_slowtmr(): + * + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_slowtmr(void) +{ + static struct tcp_pcb *pcb, *pcb2, *prev; + static struct tcp_seg *seg, *useg; + static uInt32 eff_wnd; + static uInt8 pcb_remove; /* flag if a PCB should be removed */ + + ++tcp_ticks; + + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + while(pcb != NULL) { + ASSERT("tcp_timer_coarse: active pcb->state != CLOSED", pcb->state != CLOSED); + ASSERT("tcp_timer_coarse: active pcb->state != LISTEN", pcb->state != LISTEN); + ASSERT("tcp_timer_coarse: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + + pcb_remove = 0; + + if(pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + } else if(pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + } else { + ++pcb->rtime; + seg = pcb->unacked; + if(seg != NULL && pcb->rtime >= pcb->rto) { + + DEBUGF(TCP_RTO_DEBUG, ("tcp_timer_coarse: rtime %ld pcb->rto %d\n", + tcp_ticks - pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + connect to somebody (i.e., we are in SYN_SENT). */ + if(pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + + /* Move all other unacked segments to the unsent queue. */ + if(seg->next != NULL) { + for(useg = seg->next; useg->next != NULL; useg = useg->next); + /* useg now points to the last segment on the unacked queue. */ + useg->next = pcb->unsent; + pcb->unsent = seg->next; + seg->next = NULL; + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + } + + /* Do the actual retransmission. */ + tcp_rexmit_seg(pcb, seg); + + /* Reduce congestion window and ssthresh. */ + eff_wnd = MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if(pcb->ssthresh < pcb->mss) { + pcb->ssthresh = pcb->mss * 2; + } + pcb->cwnd = pcb->mss; + + DEBUGF(TCP_CWND_DEBUG, ("tcp_rexmit_seg: cwnd %u ssthresh %u\n", + pcb->cwnd, pcb->ssthresh)); + } + } + + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if(pcb->state == FIN_WAIT_2) { + if((uInt32)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if(pcb->ooseq != NULL && + (uInt32)tcp_ticks - pcb->tmr >= + pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if(pcb->state == SYN_RCVD) { + if((uInt32)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + } + + + /* If the PCB should be removed, do it. */ + if(pcb_remove) { + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if(prev != NULL) { + ASSERT("tcp_timer_coarse: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + ASSERT("tcp_timer_coarse: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if(pcb->errf != NULL) { + pcb->errf(pcb->callback_arg, ERR_ABRT); + } + + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + + /* We check if we should poll the connection. */ + ++pcb->polltmr; + if(pcb->polltmr >= pcb->pollinterval && + pcb->poll != NULL) { + pcb->polltmr = 0; + pcb->poll(pcb->callback_arg, pcb); + tcp_output(pcb); + } + + prev = pcb; + pcb = pcb->next; + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while(pcb != NULL) { + ASSERT("tcp_timer_coarse: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if((uInt32)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if(pcb_remove) { + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if(prev != NULL) { + ASSERT("tcp_timer_coarse: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + ASSERT("tcp_timer_coarse: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + prev = pcb; + pcb = pcb->next; + } + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_fasttmr(): + * + * Is called every TCP_FINE_TIMEOUT (100 ms) and sends delayed ACKs. + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + /* send delayed ACKs */ + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->flags & TF_ACK_DELAY) { + DEBUGF(TCP_DEBUG, ("tcp_timer_fine: delayed ACK\n")); + tcp_ack_now(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_segs_free(): + * + * Deallocates a list of TCP segments (tcp_seg structures). + * + */ +/*-----------------------------------------------------------------------------------*/ +uInt8 +tcp_segs_free(struct tcp_seg *seg) +{ + uInt8 count = 0; + struct tcp_seg *next; + again: + if(seg != NULL) { + next = seg->next; + count += tcp_seg_free(seg); + seg = next; + goto again; + } + return count; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_seg_free(): + * + * Frees a TCP segment. + * + */ +/*-----------------------------------------------------------------------------------*/ +uInt8 +tcp_seg_free(struct tcp_seg *seg) +{ + uInt8 count = 0; + + if(seg != NULL) { + if(seg->p == NULL) { + memp_free(MEMP_TCP_SEG, seg); + } else { + count = pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + memp_free(MEMP_TCP_SEG, seg); + } + } + return count; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_seg_copy(): + * + * Returns a copy of the given TCP segment. + * + */ +/*-----------------------------------------------------------------------------------*/ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = memp_malloc(MEMP_TCP_SEG); + if(cseg == NULL) { + return NULL; + } + bcopy(seg, cseg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_new(): + * + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * + */ +/*-----------------------------------------------------------------------------------*/ +struct tcp_pcb * +tcp_new(void) +{ + struct tcp_pcb *pcb; + uInt32 iss; + + pcb = memp_malloc2(MEMP_TCP_PCB); + if(pcb != NULL) { + bzero(pcb, sizeof(struct tcp_pcb)); + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->mss = TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = 0; + pcb->cwnd = 1; + iss = tcp_next_iss(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->snd_max = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; + + pcb->polltmr = 0; + + return pcb; + } + return NULL; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_mem_reclaim(): + * + * Tries to free up TCP memory. This function is called from the memory manager + * when memory is scarce. + * + */ +/*-----------------------------------------------------------------------------------*/ +#if MEM_RECLAIM +static mem_size_t +tcp_mem_reclaim(void *arg, mem_size_t size) +{ + static struct tcp_pcb *pcb, *inactive; + static uInt32 inactivity; + static mem_size_t reclaimed; + + reclaimed = 0; + + /* Kill the oldest active connection, hoping that there may be + memory associated with it. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if((uInt32)(tcp_ticks - pcb->tmr) > inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if(inactive != NULL) { + DEBUGF(TCP_DEBUG, ("tcp_mem_reclaim: killing oldest PCB 0x%p (%ld)\n", + inactive, inactivity)); + tcp_abort(inactive); + } + return reclaimed; +} +#endif /* MEM_RECLAIM */ +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_memp_reclaim(): + * + * Tries to free up TCP memory. This function is called from the + * memory pool manager when memory is scarce. + * + */ +/*-----------------------------------------------------------------------------------*/ +#if MEMP_RECLAIM +static uInt8 +tcp_memp_reclaim(void *arg, memp_t type) +{ + struct tcp_pcb *pcb, *inactive; + uInt32 inactivity; + + switch(type) { + case MEMP_TCP_SEG: +#if TCP_QUEUE_OOSEQ + /* Try to find any buffered out of sequence data. */ + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->ooseq) { + DEBUGF(TCP_DEBUG, ("tcp_memp_reclaim: reclaiming memory from PCB 0x%lx\n", (long)pcb)); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return 1; + } + } + +#else /* TCP_QUEUE_OOSEQ */ + return 0; +#endif /* TCP_QUEUE_OOSEQ */ + break; + + case MEMP_PBUF: + return tcp_memp_reclaim(arg, MEMP_TCP_SEG); + + case MEMP_TCP_PCB: + /* We either kill off a connection in TIME-WAIT, or the oldest + active connection. */ + pcb = tcp_tw_pcbs; + if(pcb != NULL) { + tcp_tw_pcbs = tcp_tw_pcbs->next; + memp_free(MEMP_TCP_PCB, pcb); + return 1; + } else { + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if((uInt32)(tcp_ticks - pcb->tmr) > inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if(inactive != NULL) { + DEBUGF(TCP_DEBUG, ("tcp_mem_reclaim: killing oldest PCB 0x%p (%ld)\n", + inactive, inactivity)); + tcp_abort(inactive); + return 1; + } + } + break; + + default: + ASSERT("tcp_memp_reclaim: called with wrong type", 0); + break; + } + return 0; +} +#endif /* MEM_RECLAIM */ +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_arg(): + * + * Used to specify the argument that should be passed callback + * functions. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_recv(): + * + * Used to specify the function that should be called when a TCP + * connection receives data. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)) +{ + pcb->recv = recv; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_sent(): + * + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, uInt16 len)) +{ + pcb->sent = sent; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_err(): + * + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_err(struct tcp_pcb *pcb, + void (* errf)(void *arg, err_t err)) +{ + pcb->errf = errf; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_poll(): + * + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), uInt8 interval) +{ + pcb->poll = poll; + pcb->pollinterval = interval; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_accept(): + * + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) +{ + pcb->accept = accept; +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_pcb_purge(): + * + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if(pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + +#if TCP_DEBUG + if(pcb->unsent != NULL) { + DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if(pcb->unacked != NULL) { + DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } + if(pcb->ooseq != NULL) { + DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } +#endif /* TCP_DEBUG */ + tcp_segs_free(pcb->unsent); +#if TCP_QUEUE_OOSEQ + tcp_segs_free(pcb->ooseq); +#endif /* TCP_QUEUE_OOSEQ */ + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = +#if TCP_QUEUE_OOSEQ + pcb->ooseq = +#endif /* TCP_QUEUE_OOSEQ */ + NULL; + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_pcb_remove(): + * + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if(pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + pcb->state = CLOSED; + + ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_next_iss(): + * + * Calculates a new initial sequence number for new connections. + * + */ +/*-----------------------------------------------------------------------------------*/ +uInt32 +tcp_next_iss(void) +{ + static uInt32 iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} +/*-----------------------------------------------------------------------------------*/ +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + DEBUGF(TCP_DEBUG, ("TCP header:\n")); + DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(TCP_DEBUG, ("| %04x | %04x | (src port, dest port)\n", + tcphdr->src, tcphdr->dest)); + DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(TCP_DEBUG, ("| %08lu | (seq no)\n", + tcphdr->seqno)); + DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(TCP_DEBUG, ("| %08lu | (ack no)\n", + tcphdr->ackno)); + DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(TCP_DEBUG, ("| %2d | |%d%d%d%d%d| %5d | (offset, flags (", + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + tcphdr->wnd)); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + DEBUGF(TCP_DEBUG, ("), win)\n")); + DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(TCP_DEBUG, ("| 0x%04x | %5d | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} +/*-----------------------------------------------------------------------------------*/ +void +tcp_debug_print_state(enum tcp_state s) +{ + DEBUGF(TCP_DEBUG, ("State: ")); + switch(s) { + case CLOSED: + DEBUGF(TCP_DEBUG, ("CLOSED\n")); + break; + case LISTEN: + DEBUGF(TCP_DEBUG, ("LISTEN\n")); + break; + case SYN_SENT: + DEBUGF(TCP_DEBUG, ("SYN_SENT\n")); + break; + case SYN_RCVD: + DEBUGF(TCP_DEBUG, ("SYN_RCVD\n")); + break; + case ESTABLISHED: + DEBUGF(TCP_DEBUG, ("ESTABLISHED\n")); + break; + case FIN_WAIT_1: + DEBUGF(TCP_DEBUG, ("FIN_WAIT_1\n")); + break; + case FIN_WAIT_2: + DEBUGF(TCP_DEBUG, ("FIN_WAIT_2\n")); + break; + case CLOSE_WAIT: + DEBUGF(TCP_DEBUG, ("CLOSE_WAIT\n")); + break; + case CLOSING: + DEBUGF(TCP_DEBUG, ("CLOSING\n")); + break; + case LAST_ACK: + DEBUGF(TCP_DEBUG, ("LAST_ACK\n")); + break; + case TIME_WAIT: + DEBUGF(TCP_DEBUG, ("TIME_WAIT\n")); + break; + } +} +/*-----------------------------------------------------------------------------------*/ +void +tcp_debug_print_flags(uInt8 flags) +{ + if(flags & TCP_FIN) { + DEBUGF(TCP_DEBUG, ("FIN ")); + } + if(flags & TCP_SYN) { + DEBUGF(TCP_DEBUG, ("SYN ")); + } + if(flags & TCP_RST) { + DEBUGF(TCP_DEBUG, ("RST ")); + } + if(flags & TCP_PSH) { + DEBUGF(TCP_DEBUG, ("PSH ")); + } + if(flags & TCP_ACK) { + DEBUGF(TCP_DEBUG, ("ACK ")); + } + if(flags & TCP_URG) { + DEBUGF(TCP_DEBUG, ("URG ")); + } +} +/*-----------------------------------------------------------------------------------*/ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + DEBUGF(TCP_DEBUG, ("Local port %d, foreign port %d snd_nxt %lu rcv_nxt %lu ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) { + DEBUGF(TCP_DEBUG, ("Local port %d, foreign port %d snd_nxt %lu rcv_nxt %lu ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + DEBUGF(TCP_DEBUG, ("Local port %d, foreign port %d snd_nxt %lu rcv_nxt %lu ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} +/*-----------------------------------------------------------------------------------*/ +int +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) { + ASSERT("tcp_pcbs_sane: listen pcb->state == LISTEN", pcb->state == LISTEN); + } + return 1; +} +#endif /* TCP_DEBUG */ +/*-----------------------------------------------------------------------------------*/ + + + + + + + + + diff --git a/src/sys/net/core/tcp_input.c b/src/sys/net/core/tcp_input.c new file mode 100644 index 0000000..a55da7e --- /dev/null +++ b/src/sys/net/core/tcp_input.c @@ -0,0 +1,1121 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* tcp_input.c + * + * The input processing functions of TCP. + * + * These functions are generally called in the order (ip_input() ->) tcp_input() -> + * tcp_process() -> tcp_receive() (-> application). + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/def.h" +#include "net/opt.h" + +#include "net/netif.h" +#include "net/mem.h" +#include "net/memp.h" + +#include "net/ipv4/inet.h" +#include "net/tcp.h" + +#include "net/stats.h" + +#include "net/arch/perf.h" + +static struct tcp_seg inseg; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +/*-----------------------------------------------------------------------------------*/ +/* tcp_input: + * + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + */ +/*-----------------------------------------------------------------------------------*/ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_hdr *tcphdr; + struct tcp_pcb *pcb, *prev; + struct ip_hdr *iphdr; + uInt8 offset; + err_t err; + + + PERF_START; + +#ifdef TCP_STATS + ++stats.tcp.recv; +#endif /* TCP_STATS */ + + iphdr = p->payload; + tcphdr = (struct tcp_hdr *)((uInt8 *)p->payload + IPH_HL(iphdr) * 4/sizeof(uInt8)); + + pbuf_header(p, -(IPH_HL(iphdr) * 4/sizeof(uInt8))); + + /* Don't even process incoming broadcasts/multicasts. */ + if(ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask)) || + ip_addr_ismulticast(&(iphdr->dest))) { + pbuf_free(p); + return; + } + + + /* Verify TCP checksum. */ + if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_TCP, p->tot_len) != 0) { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04x\n", inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_TCP, p->tot_len))); +#if TCP_DEBUG + tcp_debug_print(tcphdr); +#endif /* TCP_DEBUG */ +#ifdef TCP_STATS + ++stats.tcp.chkerr; + ++stats.tcp.drop; +#endif /* TCP_STATS */ + + pbuf_free(p); + return; + } + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + offset = TCPH_OFFSET(tcphdr) >> 4; + + pbuf_header(p, -(offset * 4)); + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); + tcphdr->dest = ntohs(tcphdr->dest); + tcphdr->seqno = ntohl(tcphdr->seqno); + tcphdr->ackno = ntohl(tcphdr->ackno); + tcphdr->wnd = ntohs(tcphdr->wnd); + + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if(pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { + + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if(prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } + ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + if(pcb == NULL) { + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if(pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + break; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incomming connections. */ + prev = NULL; + if(pcb == NULL) { + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) { + ASSERT("tcp_input: LISTEN pcb->state == LISTEN", pcb->state == LISTEN); + if((ip_addr_isany(&(pcb->local_ip)) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) && + pcb->local_port == tcphdr->dest) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if(prev != NULL) { + prev->next = pcb->next; + pcb->next = (struct tcp_pcb *)tcp_listen_pcbs; + tcp_listen_pcbs = (struct tcp_pcb_listen *)pcb; + } + break; + } + prev = pcb; + } + } + } + +#if TCP_INPUT_DEBUG + DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + /* seg = memp_malloc2(MEMP_TCP_SEG); + if(seg != NULL && pcb != NULL) {*/ + if(pcb != NULL) { + +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.dataptr = p->payload; + inseg.p = p; + inseg.tcphdr = tcphdr; + + /* The len field in the tcp_seg structure is the segment length + in TCP terms. In TCP, the SYN and FIN segments are treated as + one byte, hence increment the len field. */ + /* if(TCPH_FLAGS(tcphdr) & TCP_FIN || TCPH_FLAGS(tcphdr) & TCP_SYN) { + ++inseg.len; + } */ + + if(pcb->state != LISTEN && pcb->state != TIME_WAIT) { + pcb->recv_data = NULL; + } + err = tcp_process(pcb); + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. */ + if(err != ERR_ABRT) { + if(pcb->state != LISTEN) { + if(pcb->flags & TF_RESET) { + if(pcb->state != LISTEN) { + if(pcb->errf != NULL) { + pcb->errf(pcb->callback_arg, ERR_RST); + } + } + if(pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + } else { + tcp_pcb_remove(&tcp_active_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + } else if(pcb->flags & TF_CLOSED) { + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + if(pcb->state < TIME_WAIT) { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is avaliable, we call it + now. */ + if(pcb->acked > 0 && pcb->sent != NULL) { + err = pcb->sent(pcb->callback_arg, pcb, pcb->acked); + } + if(pcb->recv != NULL) { + if(pcb->recv_data != NULL) { + err = pcb->recv(pcb->callback_arg, pcb, pcb->recv_data, ERR_OK); + } + if(pcb->flags & TF_GOT_FIN) { + err = pcb->recv(pcb->callback_arg, pcb, NULL, ERR_OK); + } + } else { + err = ERR_OK; + pbuf_free(pcb->recv_data); + if(pcb->flags & TF_GOT_FIN) { + tcp_close(pcb); + } + } + if(err == ERR_OK) { + tcp_output(pcb); + } + } else if(pcb->state == TIME_WAIT) { + pbuf_free(pcb->recv_data); + tcp_output(pcb); + } + } + } + } + + pbuf_free(inseg.p); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + } else { + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if(!(TCPH_FLAGS(tcphdr) & TCP_RST)) { +#ifdef TCP_STATS + ++stats.tcp.proterr; + ++stats.tcp.drop; +#endif /* TCP_STATS */ + tcp_rst(tcphdr->ackno, tcphdr->seqno + p->tot_len + + ((TCPH_FLAGS(tcphdr) & TCP_FIN || TCPH_FLAGS(tcphdr) & TCP_SYN)? 1: 0), + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + pbuf_free(p); + } + + ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); +} +/*-----------------------------------------------------------------------------------*/ +/* tcp_process + * + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + */ +/*-----------------------------------------------------------------------------------*/ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_pcb *npcb; + struct ip_hdr *iphdr; + struct tcp_hdr *tcphdr; + uInt32 seqno, ackno; + uInt8 flags; + uInt32 optdata; + struct tcp_seg *rseg; + uInt8 acceptable = 0; + + iphdr = (struct ip_hdr *)((uInt8 *)inseg.tcphdr - IP_HLEN/sizeof(uInt8)); + tcphdr = inseg.tcphdr; + flags = TCPH_FLAGS(tcphdr); + seqno = tcphdr->seqno; + ackno = tcphdr->ackno; + + + + /* Process incoming RST segments. */ + if(flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if(pcb->state != LISTEN) { + if(pcb->state == SYN_SENT) { + if(ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if(TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && + TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { + acceptable = 1; + } + } + } + if(acceptable) { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + pcb->flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + } else { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %lu rcv_nxt %lu\n", + seqno, pcb->rcv_nxt)); + DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %lu rcv_nxt %lu\n", + seqno, pcb->rcv_nxt)); + } + + return ERR_RST; + } + + /* Update the PCB timer unless we are in the LISTEN state, in + which case we don't even have memory allocated for the timer, + much less use it. */ + if(pcb->state != LISTEN) { + pcb->tmr = tcp_ticks; + } + + /* Do different things depending on the TCP state. */ + switch(pcb->state) { + case CLOSED: + /* Do nothing in the CLOSED state. In fact, this case should never occur + since PCBs in the CLOSED state are never found in the list of + active PCBs. */ + break; + case LISTEN: + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if(flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + DEBUGF(TCP_RST_DEBUG, ("tcp_process: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno + 1, seqno + TCP_TCPLEN(&inseg), + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } else if(flags & TCP_SYN) { + DEBUGF(DEMO_DEBUG, ("TCP connection request %d -> %d.\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + npcb = tcp_new(); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory avaliable. */ + if(npcb == NULL) { +#ifdef TCP_STATS + ++stats.tcp.memerr; +#endif /* TCP_STATS */ + break; + } + /* Set up the new PCB. */ + ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); + npcb->local_port = pcb->local_port; + ip_addr_set(&(npcb->remote_ip), &(iphdr->src)); + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->snd_wnd = tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = tcphdr->seqno; + npcb->accept = pcb->accept; + npcb->callback_arg = pcb->callback_arg; + + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG(&tcp_active_pcbs, npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); + + /* Build an MSS option. */ + optdata = HTONL(((uInt32)2 << 24) | + ((uInt32)4 << 16) | + (((uInt32)npcb->mss / 256) << 8) | + (npcb->mss & 255)); + /* Send a SYN|ACK together with the MSS option. */ + tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (uInt8 *)&optdata, 4); + return tcp_output(npcb); + } + break; + case SYN_SENT: + DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %lu pcb->snd_nxt %lu unacked %lu\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + if(flags & TCP_ACK && + flags & TCP_SYN && + ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->rcv_nxt = seqno + 1; + pcb->lastack = ackno; + pcb->rcv_wnd = tcphdr->wnd; + pcb->state = ESTABLISHED; + pcb->cwnd = pcb->mss; + --pcb->snd_queuelen; + DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %d\n", pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + tcp_seg_free(rseg); + + /* Parse any options in the SYNACK. */ + tcp_parseopt(pcb); + + /* Call the user specified function to call when sucessfully + connected. */ + if(pcb->connected != NULL) { + pcb->connected(pcb->callback_arg, pcb, ERR_OK); + } + tcp_ack(pcb); + } + break; + case SYN_RCVD: + if(flags & TCP_ACK && + !(flags & TCP_RST)) { + if(TCP_SEQ_LT(pcb->lastack, ackno) && + TCP_SEQ_LEQ(ackno, pcb->snd_nxt)) { + pcb->state = ESTABLISHED; + DEBUGF(DEMO_DEBUG, ("TCP connection established %d -> %d.\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* Call the accept function. */ + if(pcb->accept != NULL) { + if(pcb->accept(pcb->callback_arg, pcb, ERR_OK) != ERR_OK) { + /* If the accept function returns with an error, we abort + the connection. */ + tcp_abort(pcb); + break; + } + } else { + /* If a PCB does not have an accept function (i.e., no + application is connected to it), the connection would + linger in memory until the connection reset by the remote + peer (which might never happen). Therefore, we abort the + connection before it is too late. */ + tcp_abort(pcb); + break; + } + /* If there was any data contained within this ACK, + we'd better pass it on to the application as well. */ + tcp_receive(pcb); + pcb->cwnd = pcb->mss; + } + } + break; + case CLOSE_WAIT: + case ESTABLISHED: + tcp_receive(pcb); + if(flags & TCP_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if(flags & TCP_FIN) { + if(flags & TCP_ACK && ackno == pcb->snd_nxt) { + DEBUGF(DEMO_DEBUG, + ("TCP connection closed %d -> %d.\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + /* pcb = memp_realloc(MEMP_TCP_PCB, MEMP_TCP_PCB_TW, pcb);*/ + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if(flags & TCP_ACK && ackno == pcb->snd_nxt) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if(flags & TCP_FIN) { + DEBUGF(DEMO_DEBUG, ("TCP connection closed %d -> %d.\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + /* pcb = memp_realloc(MEMP_TCP_PCB, MEMP_TCP_PCB_TW, pcb); */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if(flags & TCP_ACK && ackno == pcb->snd_nxt) { + DEBUGF(DEMO_DEBUG, ("TCP connection closed %d -> %d.\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + /* pcb = memp_realloc(MEMP_TCP_PCB, MEMP_TCP_PCB_TW, pcb); */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if(flags & TCP_ACK && ackno == pcb->snd_nxt) { + DEBUGF(DEMO_DEBUG, ("TCP connection closed %d -> %d.\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + pcb->state = CLOSED; + pcb->flags |= TF_CLOSED; + } + break; + case TIME_WAIT: + if(TCP_SEQ_GT(seqno + TCP_TCPLEN(&inseg), pcb->rcv_nxt)) { + pcb->rcv_nxt = seqno + TCP_TCPLEN(&inseg); + } + if(TCP_TCPLEN(&inseg) > 0) { + tcp_ack_now(pcb); + } + break; + } + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* tcp_receive: + * + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * i it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + */ +/*-----------------------------------------------------------------------------------*/ +/* static void + tcp_receive(struct tcp_seg *seg, struct tcp_pcb *pcb) */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next, *prev, *cseg; + struct pbuf *p; + uInt32 ackno, seqno; + Int32 off; + int m; + + ackno = inseg.tcphdr->ackno; + seqno = inseg.tcphdr->seqno; + + + if(TCPH_FLAGS(inseg.tcphdr) & TCP_ACK) { + /* Update window. */ + if(TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && inseg.tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = inseg.tcphdr->wnd; + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %lu\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if(pcb->snd_wnd != inseg.tcphdr->wnd) { + DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %lu snd_max %lu ackno %lu wl1 %lu seqno %lu wl2 %lu\n", + pcb->lastack, pcb->snd_max, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + + if(pcb->lastack == ackno) { + ++pcb->dupacks; + if(pcb->dupacks >= 3 && pcb->unacked != NULL) { + if(!(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %d (%lu), fast retransmit %lu\n", + pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit_seg(pcb, pcb->unacked); + /* Set ssthresh to max (FlightSize / 2, 2*SMSS) */ + pcb->ssthresh = UMAX((pcb->snd_max - + pcb->lastack) / 2, + 2 * pcb->mss); + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } else { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if(pcb->cwnd + pcb->mss > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + + } + } + } else if(TCP_SEQ_LT(pcb->lastack, ackno) && + TCP_SEQ_LEQ(ackno, pcb->snd_max)) { + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if(pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. */ + pcb->acked = ackno - pcb->lastack; + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if(pcb->state >= ESTABLISHED) { + if(pcb->cwnd < pcb->ssthresh) { + if(pcb->cwnd + pcb->mss > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %u\n", pcb->cwnd)); + } else { + if(pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd > pcb->cwnd) { + pcb->cwnd += pcb->mss * pcb->mss / pcb->cwnd; + } + DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %u\n", pcb->cwnd)); + } + } + DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %lu, unacked->seqno %lu:%lu\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact be sent once. */ + while(pcb->unsent != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), + ackno)) { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %lu:%lu from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), + ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; + DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %d ... ", pcb->snd_queuelen)); + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + DEBUGF(TCP_QLEN_DEBUG, ("%d (after freeing unsent)\n", pcb->snd_queuelen)); +#ifdef LWIP_DEBUG + if(pcb->snd_queuelen != 0) { + ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } +#endif /* LWIP_DEBUG */ + + if(pcb->unsent != NULL) { + pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno); + } + } + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. */ + while(pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %lu:%lu from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %d ... ", pcb->snd_queuelen)); + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + + DEBUGF(TCP_QLEN_DEBUG, ("%d (after freeing unacked)\n", pcb->snd_queuelen)); +#ifdef LWIP_DEBUG + if(pcb->snd_queuelen != 0) { + ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } +#endif /* LWIP_DEBUG */ + } + pcb->polltmr = 0; + } + /* End of ACK for new data processing. */ + + DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %d rtseq %lu ackno %lu\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if(pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + m = tcp_ticks - pcb->rttest; + + DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %d ticks (%d msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if(m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %d (%d miliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further. */ + if(TCP_TCPLEN(&inseg) > 0) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + if(TCP_SEQ_LT(seqno, pcb->rcv_nxt) && + TCP_SEQ_LT(pcb->rcv_nxt, seqno + inseg.len)) { + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + off = pcb->rcv_nxt - seqno; + if(inseg.p->len < off) { + p = inseg.p; + while(p->len < off) { + off -= p->len; + inseg.p->tot_len -= p->len; + p->len = 0; + p = p->next; + } + pbuf_header(p, -off); + /*inseg.p->tot_len -= off;*/ + /* inseg.p->payload = p->payload;*/ + } else { + pbuf_header(inseg.p, -off); + } + inseg.dataptr = inseg.p->payload; + inseg.len -= pcb->rcv_nxt - seqno; + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if(TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && + TCP_SEQ_LT(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { + if(pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ +#if TCP_QUEUE_OOSEQ + if(pcb->ooseq != NULL && + TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + inseg.len)) { + /* We have to trim the second edge of the incoming + segment. */ + inseg.len = pcb->ooseq->tcphdr->seqno - seqno; + pbuf_realloc(inseg.p, inseg.len); + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt += TCP_TCPLEN(&inseg); + + /* Update the receiver's (our) window. */ + if(pcb->rcv_wnd < TCP_TCPLEN(&inseg)) { + pcb->rcv_wnd = 0; + } else { + pcb->rcv_wnd -= TCP_TCPLEN(&inseg); + } + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if(inseg.p->tot_len > 0) { + pcb->recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if(TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.")); + pcb->flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + is now in sequence. */ + while(pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + if(pcb->rcv_wnd < TCP_TCPLEN(cseg)) { + pcb->rcv_wnd = 0; + } else { + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + } + if(cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + pbuf_chain(pcb->recv_data, cseg->p); + cseg->p = NULL; + } + if(TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.")); + pcb->flags |= TF_GOT_FIN; + } + + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_ack_now(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if(pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) { + if(seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if(inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace the old segment with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if(cseg != NULL) { + cseg->next = next->next; + if(prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if(prev == NULL) { + if(TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + + if(TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + inseg.len = next->tcphdr->seqno - seqno; + pbuf_realloc(inseg.p, inseg.len); + } + cseg = tcp_seg_copy(&inseg); + if(cseg != NULL) { + cseg->next = next; + pcb->ooseq = cseg; + } + break; + } + } else if(TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim and insert the + incoming segment and trim the previous segment, if + needed. */ + if(TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + inseg.len = next->tcphdr->seqno - seqno; + pbuf_realloc(inseg.p, inseg.len); + } + + cseg = tcp_seg_copy(&inseg); + if(cseg != NULL) { + cseg->next = next; + prev->next = cseg; + if(TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = seqno - prev->tcphdr->seqno; + pbuf_realloc(prev->p, prev->len); + } + } + break; + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if(next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + next->next = tcp_seg_copy(&inseg); + if(next->next != NULL) { + if(TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = seqno - next->tcphdr->seqno; + pbuf_realloc(next->p, next->len); + } + } + break; + } + } + prev = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + } + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + if(TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { + tcp_ack_now(pcb); + } + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * tcp_parseopt: + * + * Parses the options contained in the incoming segment. (Code taken + * from uIP with only small changes.) + * + */ +/*-----------------------------------------------------------------------------------*/ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + uInt8 c; + uInt8 *opts, opt; + uInt16 mss; + + opts = (uInt8 *)inseg.tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if((TCPH_OFFSET(inseg.tcphdr) & 0xf0) > 0x50) { + for(c = 0; c < ((TCPH_OFFSET(inseg.tcphdr) >> 4) - 5) << 2 ;) { + opt = opts[c]; + if(opt == 0x00) { + /* End of options. */ + break; + } else if(opt == 0x01) { + ++c; + /* NOP option. */ + } else if(opt == 0x02 && + opts[c + 1] == 0x04) { + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + pcb->mss = mss > TCP_MSS? TCP_MSS: mss; + + /* And we are done processing options. */ + break; + } else { + if(opts[c + 1] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} +/*-----------------------------------------------------------------------------------*/ + diff --git a/src/sys/net/core/tcp_output.c b/src/sys/net/core/tcp_output.c new file mode 100644 index 0000000..968e36e --- /dev/null +++ b/src/sys/net/core/tcp_output.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* tcp_output.c + * + * The output functions of TCP. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/def.h" +#include "net/opt.h" + +#include "net/arch/lib.h" + +#include "net/mem.h" +#include "net/memp.h" +#include "net/sys.h" + +#include "net/netif.h" + +#include "net/ipv4/inet.h" +#include "net/tcp.h" + +#include "net/stats.h" + + +#define MIN(x,y) (x) < (y)? (x): (y) + + + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + + +/*-----------------------------------------------------------------------------------*/ +err_t +tcp_send_ctrl(struct tcp_pcb *pcb, uInt8 flags) +{ + return tcp_enqueue(pcb, NULL, 0, flags, 1, NULL, 0); + +} +/*-----------------------------------------------------------------------------------*/ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, uInt16 len, uInt8 copy) +{ + if(pcb->state == SYN_SENT || + pcb->state == SYN_RCVD || + pcb->state == ESTABLISHED || + pcb->state == CLOSE_WAIT) { + if(len > 0) { + return tcp_enqueue(pcb, (void *)arg, len, 0, copy, NULL, 0); + } + return ERR_OK; + } else { + return ERR_CONN; + } +} +/*-----------------------------------------------------------------------------------*/ +err_t +tcp_enqueue(struct tcp_pcb *pcb, void *arg, uInt16 len, + uInt8 flags, uInt8 copy, + uInt8 *optdata, uInt8 optlen) +{ + struct pbuf *p; + struct tcp_seg *seg, *useg, *queue; + uInt32 left, seqno; + uInt16 seglen; + void *ptr; + uInt8 queuelen; + + left = len; + ptr = arg; + + if(len > pcb->snd_buf) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too much data %d\n", len)); + return ERR_MEM; + } + + seqno = pcb->snd_lbb; + + queue = NULL; + DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d\n", pcb->snd_queuelen)); + queuelen = pcb->snd_queuelen; + if(queuelen >= TCP_SND_QUEUELEN) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN)); + goto memerr; + } + +#ifdef LWIP_DEBUG + if(pcb->snd_queuelen != 0) { + ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } +#endif /* LWIP_DEBUG */ + + seg = NULL; + seglen = 0; + + while(queue == NULL || left > 0) { + + seglen = left > pcb->mss? pcb->mss: left; + + /* allocate memory for tcp_seg, and fill in fields */ + seg = memp_malloc(MEMP_TCP_SEG); + if(seg == NULL) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for tcp_seg\n")); + goto memerr; + } + seg->next = NULL; + seg->p = NULL; + + + if(queue == NULL) { + queue = seg; + } else { + for(useg = queue; useg->next != NULL; useg = useg->next); + useg->next = seg; + } + + /* If copy is set, memory should be allocated + and data copied into pbuf, otherwise data comes from + ROM or other static memory, and need not be copied. If + optdata is != NULL, we have options instead of data. */ + if(optdata != NULL) { + if((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + goto memerr; + } + ++queuelen; + seg->dataptr = seg->p->payload; + } else if(copy) { + if((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf copy\n")); + goto memerr; + } + ++queuelen; + if(arg != NULL) { + bcopy(ptr, seg->p->payload, seglen); + } + seg->dataptr = seg->p->payload; + } else { + /* Do not copy the data. */ + if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n")); + goto memerr; + } + ++queuelen; + p->payload = ptr; + seg->dataptr = ptr; + if((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { + pbuf_free(p); + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for header pbuf\n")); + goto memerr; + } + ++queuelen; + pbuf_chain(seg->p, p); + } + if(queuelen > TCP_SND_QUEUELEN) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); + goto memerr; + } + + seg->len = seglen; + /* if((flags & TCP_SYN) || (flags & TCP_FIN)) { + ++seg->len; + }*/ + + /* build TCP header */ + if(pbuf_header(seg->p, TCP_HLEN)) { + + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: no room for TCP header in pbuf.\n")); + +#ifdef TCP_STATS + ++stats.tcp.err; +#endif /* TCP_STATS */ + goto memerr; + } + seg->tcphdr = seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + seg->tcphdr->urgp = 0; + TCPH_FLAGS_SET(seg->tcphdr, flags); + /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ + + if(optdata == NULL) { + TCPH_OFFSET_SET(seg->tcphdr, 5 << 4); + } else { + TCPH_OFFSET_SET(seg->tcphdr, (5 + optlen / 4) << 4); + /* Copy options into data portion of segment. + Options can thus only be sent in non data carrying + segments such as SYN|ACK. */ + bcopy(optdata, seg->dataptr, optlen); + } + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queueing %lu:%lu (0x%x)\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + flags)); + + left -= seglen; + seqno += seglen; + ptr = (void *)((char *)ptr + seglen); + } + + + /* Go to the last segment on the ->unsent queue. */ + if(pcb->unsent == NULL) { + useg = NULL; + } else { + for(useg = pcb->unsent; useg->next != NULL; useg = useg->next); + } + + /* If there is room in the last pbuf on the unsent queue, + chain the first pbuf on the queue together with that. */ + if(useg != NULL && + TCP_TCPLEN(useg) != 0 && + !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && + !(flags & (TCP_SYN | TCP_FIN)) && + useg->len + queue->len <= pcb->mss) { + /* Remove TCP header from first segment. */ + pbuf_header(queue->p, -TCP_HLEN); + pbuf_chain(useg->p, queue->p); + useg->len += queue->len; + useg->next = queue->next; + + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: chaining, new len %u\n", useg->len)); + if(seg == queue) { + seg = NULL; + } + memp_free(MEMP_TCP_SEG, queue); + } else { + if(useg == NULL) { + pcb->unsent = queue; + } else { + useg->next = queue; + } + } + if((flags & TCP_SYN) || (flags & TCP_FIN)) { + ++len; + } + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queuelen; + DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen)); +#ifdef LWIP_DEBUG + if(pcb->snd_queuelen != 0) { + ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + + } +#endif /* LWIP_DEBUG */ + + /* Set the PSH flag in the last segment that we enqueued, but only + if the segment has data (indicated by seglen > 0). */ + if(seg != NULL && seglen > 0 && seg->tcphdr != NULL) { + TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_PSH); + } + + return ERR_OK; + memerr: +#ifdef TCP_STATS + ++stats.tcp.memerr; +#endif /* TCP_STATS */ + + if(queue != NULL) { + tcp_segs_free(queue); + } +#ifdef LWIP_DEBUG + if(pcb->snd_queuelen != 0) { + ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + + } +#endif /* LWIP_DEBUG */ + DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} +/*-----------------------------------------------------------------------------------*/ +/* find out what we can send and send it */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg, *useg; + uInt32 wnd; +#if TCP_CWND_DEBUG + int i = 0; +#endif /* TCP_CWND_DEBUG */ + + wnd = MIN(pcb->snd_wnd, pcb->cwnd); + + + seg = pcb->unsent; + + if(pcb->flags & TF_ACK_NOW) { + /* If no segments are enqueued but we should send an ACK, we + construct the ACK and send it. */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM); + if(p == NULL) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: sending ACK for %lu\n", pcb->rcv_nxt)); + if(pbuf_header(p, TCP_HLEN)) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: (ACK) no room for TCP header in pbuf.\n")); + +#ifdef TCP_STATS + ++stats.tcp.err; +#endif /* TCP_STATS */ + pbuf_free(p); + return ERR_BUF; + } + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt); + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->urgp = 0; + TCPH_OFFSET_SET(tcphdr, 5 << 4); + + tcphdr->chksum = 0; + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), + IP_PROTO_TCP, p->tot_len); + + ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), TCP_TTL, + IP_PROTO_TCP); + pbuf_free(p); + + return ERR_OK; + } + +#if TCP_OUTPUT_DEBUG + if(seg == NULL) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send\n")); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if(seg == NULL) { + DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, seg == NULL, ack %lu\n", + pcb->snd_wnd, pcb->cwnd, wnd, + pcb->lastack)); + } else { + DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, effwnd %lu, seq %lu, ack %lu\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + + while(seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + pcb->rtime = 0; +#if TCP_CWND_DEBUG + DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %lu, cwnd %lu, wnd %lu, effwnd %lu, seq %lu, ack %lu, i%d\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + + if(pcb->state != SYN_SENT) { + TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_ACK); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + tcp_output_segment(seg, pcb); + pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if(TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) { + pcb->snd_max = pcb->snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if(TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + if(pcb->unacked == NULL) { + pcb->unacked = seg; + } else { + for(useg = pcb->unacked; useg->next != NULL; useg = useg->next); + useg->next = seg; + } + /* seg->rtime = 0;*/ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + uInt16 len, tot_len; + struct netif *netif; + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* silly window avoidance */ + if(pcb->rcv_wnd < pcb->mss) { + seg->tcphdr->wnd = 0; + } else { + seg->tcphdr->wnd = htons(pcb->rcv_wnd); + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if(ip_addr_isany(&(pcb->local_ip))) { + netif = ip_route(&(pcb->remote_ip)); + if(netif == NULL) { + return; + } + ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); + } + + pcb->rtime = 0; + + if(pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %lu\n", pcb->rtseq)); + } + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %lu:%lu\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + seg->tcphdr->chksum = 0; + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, + &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#ifdef TCP_STATS + ++stats.tcp.xmit; +#endif /* TCP_STATS */ + + len = seg->p->len; + tot_len = seg->p->tot_len; + ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), TCP_TTL, + IP_PROTO_TCP); + seg->p->len = len; + seg->p->tot_len = tot_len; + seg->p->payload = seg->tcphdr; + +} +/*-----------------------------------------------------------------------------------*/ +void +tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg) +{ + uInt32 wnd; + uInt16 len, tot_len; + struct netif *netif; + + DEBUGF(TCP_REXMIT_DEBUG, ("tcp_rexmit_seg: skickar %ld:%ld\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + wnd = MIN(pcb->snd_wnd, pcb->cwnd); + + if(ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + + /* Count the number of retranmissions. */ + ++pcb->nrtx; + + if((netif = ip_route((struct ip_addr *)&(pcb->remote_ip))) == NULL) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_rexmit_segment: No route to 0x%lx\n", pcb->remote_ip.addr)); +#ifdef TCP_STATS + ++stats.tcp.rterr; +#endif /* TCP_STATS */ + return; + } + + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + seg->tcphdr->wnd = htons(pcb->rcv_wnd); + + /* Recalculate checksum. */ + seg->tcphdr->chksum = 0; + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, + &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); + + len = seg->p->len; + tot_len = seg->p->tot_len; + pbuf_header(seg->p, IP_HLEN); + ip_output_if(seg->p, NULL, IP_HDRINCL, TCP_TTL, IP_PROTO_TCP, netif); + seg->p->len = len; + seg->p->tot_len = tot_len; + seg->p->payload = seg->tcphdr; + +#ifdef TCP_STATS + ++stats.tcp.xmit; + ++stats.tcp.rexmit; +#endif /* TCP_STATS */ + + pcb->rtime = 0; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + } else { + DEBUGF(TCP_REXMIT_DEBUG, ("tcp_rexmit_seg: no room in window %lu to send %lu (ack %lu)\n", + wnd, ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +} +/*-----------------------------------------------------------------------------------*/ +void +tcp_rst(uInt32 seqno, uInt32 ackno, + struct ip_addr *local_ip, struct ip_addr *remote_ip, + uInt16 local_port, uInt16 remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM); + if(p == NULL) { +#if MEM_RECLAIM + mem_reclaim(sizeof(struct pbuf)); + p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM); +#endif /* MEM_RECLAIM */ + if(p == NULL) { + DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + } + if(pbuf_header(p, TCP_HLEN)) { + DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_send_data: no room for TCP header in pbuf.\n")); + +#ifdef TCP_STATS + ++stats.tcp.err; +#endif /* TCP_STATS */ + return; + } + + tcphdr = p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK); + tcphdr->wnd = 0; + tcphdr->urgp = 0; + TCPH_OFFSET_SET(tcphdr, 5 << 4); + + tcphdr->chksum = 0; + tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, + IP_PROTO_TCP, p->tot_len); + +#ifdef TCP_STATS + ++stats.tcp.xmit; +#endif /* TCP_STATS */ + ip_output(p, local_ip, remote_ip, TCP_TTL, IP_PROTO_TCP); + pbuf_free(p); + DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %lu ackno %lu.\n", seqno, ackno)); +} +/*-----------------------------------------------------------------------------------*/ + + + + + + + + + + + diff --git a/src/sys/net/core/udp.c b/src/sys/net/core/udp.c new file mode 100644 index 0000000..776324a --- /dev/null +++ b/src/sys/net/core/udp.c @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * $Id$ + */ + +/*-----------------------------------------------------------------------------------*/ +/* udp.c + * + * The code for the User Datagram Protocol UDP. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include + +#include "net/debug.h" + +#include "net/def.h" +#include "net/memp.h" +#include "net/ipv4/inet.h" +#include "net/netif.h" +#include "net/udp.h" +#include "net/ipv4/icmp.h" + +#include "net/stats.h" + +#include "net/arch/perf.h" + +/*-----------------------------------------------------------------------------------*/ + +/* The list of UDP PCBs. */ +static struct udp_pcb *udp_pcbs = NULL; + +static struct udp_pcb *pcb_cache = NULL; + +#if UDP_DEBUG +int udp_debug_print(struct udp_hdr *udphdr); +#endif /* UDP_DEBUG */ + +/*-----------------------------------------------------------------------------------*/ +void +udp_init(void) +{ +} +/*-----------------------------------------------------------------------------------*/ +/* udp_lookup: + * + * An experimental feature that will be changed in future versions. Do + * not depend on it yet... + */ +/*-----------------------------------------------------------------------------------*/ +#ifdef LWIP_DEBUG +uInt8 +udp_lookup(struct ip_hdr *iphdr, struct netif *inp) +{ + struct udp_pcb *pcb; + struct udp_hdr *udphdr; + uInt16 src, dest; + + PERF_START; + + udphdr = (struct udp_hdr *)(uInt8 *)iphdr + IPH_HL(iphdr) * 4/sizeof(uInt8); + + src = NTOHS(udphdr->src); + dest = NTOHS(udphdr->dest); + + pcb = pcb_cache; + if(pcb != NULL && + pcb->remote_port == src && + pcb->local_port == dest && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { + return 1; + } else { + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->remote_port == src && + pcb->local_port == dest && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { + pcb_cache = pcb; + break; + } + } + + if(pcb == NULL) { + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->local_port == dest && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { + break; + } + } + } + } + + PERF_STOP("udp_lookup"); + + if(pcb != NULL) { + return 1; + } else { + return 1; + } +} +#endif /* LWIP_DEBUG */ +/*-----------------------------------------------------------------------------------*/ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb; + struct ip_hdr *iphdr; + uInt16 src, dest; + + PERF_START; + +#ifdef UDP_STATS + ++stats.udp.recv; +#endif /* UDP_STATS */ + + iphdr = p->payload; + + pbuf_header(p, -(UDP_HLEN + IPH_HL(iphdr) * 4)); + + udphdr = (struct udp_hdr *)((uInt8 *)p->payload - UDP_HLEN); + + DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %d\n", p->tot_len)); + + src = NTOHS(udphdr->src); + dest = NTOHS(udphdr->dest); + +#if UDP_DEBUG + udp_debug_print(udphdr); +#endif /* UDP_DEBUG */ + + /* Demultiplex packet. First, go for a perfect match. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + DEBUGF(UDP_DEBUG, ("udp_input: pcb local port %d (dgram %d)\n", + pcb->local_port, ntohs(udphdr->dest))); + if(pcb->remote_port == src && + pcb->local_port == dest && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { + break; + } + } + + if(pcb == NULL) { + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + DEBUGF(UDP_DEBUG, ("udp_input: pcb local port %d (dgram %d)\n", + pcb->local_port, dest)); + if(pcb->local_port == dest && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { + break; + } + } + } + + + /* Check checksum if this is a match or if it was directed at us. */ + /* if(pcb != NULL || + ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) {*/ + if(pcb != NULL) { + DEBUGF(UDP_DEBUG, ("udp_input: calculating checksum\n")); + pbuf_header(p, UDP_HLEN); +#ifdef IPv6 + if(iphdr->nexthdr == IP_PROTO_UDPLITE) { +#else + if(IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { +#endif /* IPv4 */ + /* Do the UDP Lite checksum */ + if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDPLITE, ntohs(udphdr->len)) != 0) { + DEBUGF(UDP_DEBUG, ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); +#ifdef UDP_STATS + ++stats.udp.chkerr; + ++stats.udp.drop; +#endif /* UDP_STATS */ + pbuf_free(p); + goto end; + } + } else { + if(udphdr->chksum != 0) { + if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDP, p->tot_len) != 0) { + DEBUGF(UDP_DEBUG, ("udp_input: UDP datagram discarded due to failing checksum\n")); + +#ifdef UDP_STATS + ++stats.udp.chkerr; + ++stats.udp.drop; +#endif /* UDP_STATS */ + pbuf_free(p); + goto end; + } + } + } + pbuf_header(p, -UDP_HLEN); + if(pcb != NULL) { + pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src); + } else { + DEBUGF(UDP_DEBUG, ("udp_input: not for us.\n")); + + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + + if(!ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) && + !ip_addr_ismulticast(&iphdr->dest)) { + + /* deconvert from host to network byte order */ + udphdr->src = htons(udphdr->src); + udphdr->dest = htons(udphdr->dest); + + /* adjust pbuf pointer */ + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PORT); + } +#ifdef UDP_STATS + ++stats.udp.proterr; + ++stats.udp.drop; +#endif /* UDP_STATS */ + pbuf_free(p); + } + } else { + pbuf_free(p); + } + end: + + PERF_STOP("udp_input"); +} +/*-----------------------------------------------------------------------------------*/ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + struct udp_hdr *udphdr; + struct netif *netif; + struct ip_addr *src_ip; + err_t err; + struct pbuf *q; + + if(pbuf_header(p, UDP_HLEN)) { + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + if(q == NULL) { + return ERR_MEM; + } + pbuf_chain(q, p); + p = q; + } + + udphdr = p->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(pcb->remote_port); + udphdr->chksum = 0x0000; + + if((netif = ip_route(&(pcb->remote_ip))) == NULL) { + DEBUGF(UDP_DEBUG, ("udp_send: No route to 0x%lx\n", pcb->remote_ip.addr)); +#ifdef UDP_STATS + ++stats.udp.rterr; +#endif /* UDP_STATS */ + return ERR_RTE; + } + + if(ip_addr_isany(&pcb->local_ip)) { + src_ip = &(netif->ip_addr); + } else { + src_ip = &(pcb->local_ip); + } + + DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %d\n", p->tot_len)); + + if(pcb->flags & UDP_FLAGS_UDPLITE) { + udphdr->len = htons(pcb->chksum_len); + /* calculate checksum */ + udphdr->chksum = inet_chksum_pseudo(p, src_ip, &(pcb->remote_ip), + IP_PROTO_UDP, pcb->chksum_len); + if(udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } + err = ip_output_if(p, src_ip, &pcb->remote_ip, UDP_TTL, IP_PROTO_UDPLITE, netif); + } else { + udphdr->len = htons(p->tot_len); + /* calculate checksum */ + if((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + udphdr->chksum = inet_chksum_pseudo(p, src_ip, &pcb->remote_ip, + IP_PROTO_UDP, p->tot_len); + if(udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } + } + err = ip_output_if(p, src_ip, &pcb->remote_ip, UDP_TTL, IP_PROTO_UDP, netif); + } + +#ifdef UDP_STATS + ++stats.udp.xmit; +#endif /* UDP_STATS */ + return err; +} +/*-----------------------------------------------------------------------------------*/ +err_t +udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, uInt16 port) +{ + struct udp_pcb *ipcb; + ip_addr_set(&pcb->local_ip, ipaddr); + pcb->local_port = port; + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if(pcb == ipcb) { + /* Already on the list, just return. */ + return ERR_OK; + } + } + /* We need to place the PCB on the list. */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + + DEBUGF(UDP_DEBUG, ("udp_bind: bound to port %d\n", port)); + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +err_t +udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, uInt16 port) +{ + struct udp_pcb *ipcb; + ip_addr_set(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if(pcb == ipcb) { + /* Already on the list, just return. */ + return ERR_OK; + } + } + /* We need to place the PCB on the list. */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +void +udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, uInt16 port), + void *recv_arg) +{ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} +/*-----------------------------------------------------------------------------------*/ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + if(udp_pcbs == pcb) { + udp_pcbs = udp_pcbs->next; + } else for(pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + if(pcb2->next != NULL && pcb2->next == pcb) { + pcb2->next = pcb->next; + } + } + + memp_free(MEMP_UDP_PCB, pcb); +} +/*-----------------------------------------------------------------------------------*/ +struct udp_pcb * +udp_new(void) { + struct udp_pcb *pcb; + pcb = memp_malloc(MEMP_UDP_PCB); + if(pcb != NULL) { + bzero(pcb, sizeof(struct udp_pcb)); + return pcb; + } + return NULL; + +} +/*-----------------------------------------------------------------------------------*/ +#if UDP_DEBUG +int +udp_debug_print(struct udp_hdr *udphdr) +{ + DEBUGF(UDP_DEBUG, ("UDP header:\n")); + DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(UDP_DEBUG, ("| %5d | %5d | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + DEBUGF(UDP_DEBUG, ("| %5d | 0x%04x | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + return 0; +} +#endif /* UDP_DEBUG */ +/*-----------------------------------------------------------------------------------*/ + + + + + + + + + +