diff --git a/src/Makefile b/src/Makefile index 07150ab..178e963 100644 --- a/src/Makefile +++ b/src/Makefile @@ -92,9 +92,9 @@ install: install-world install-kernel clean: - (cd sys;${KMAKE} clean) - (cd bin;${WMAKE} clean) - (cd lib;${WMAKE} clean) + (cd sys;${MAKE} clean) + (cd bin;${MAKE} clean) + (cd lib;${MAKE} clean) # (cd src/lib/ubix;make clean) # (cd src/lib/objgfx40;make clean) # (cd src/lib/libcpp;make clean) diff --git a/src/bin/init/main.c b/src/bin/init/main.c index f098cb7..dcca8f0 100644 --- a/src/bin/init/main.c +++ b/src/bin/init/main.c @@ -40,12 +40,12 @@ /* Create a mailbox for this task */ -/* + /* if (mpi_createMbox("init") != 0x0) { printf("Error: Failed to creating mail box: init\n"); exit(0x1); } -*/ + */ /* Make sure we have superuser permissions if not exit */ if ((getuid() != 0x0) || (getgid() != 0x0)) { @@ -56,25 +56,19 @@ printf("Initializing UbixOS\n"); -#if 0 - /* Start TTYD */ - printf("About To Fork!\n"); +/* i = fork(); - printf("FORKED: %i!\n", i); if (0x0 == i) { printf("Error: Could not start TTYD\n"); - exec("sys:/ttyd",0x0); + execve("sys:/bin/ttyd",0x0,0x0); printf("Error: Could not start TTYD\n"); exit(0x0); - } + } +*/ - while (pidStatus(i) > 0x0) - sched_yield(); - -#endif /* i = fork(); @@ -101,10 +95,8 @@ exit(0x0); } -while (1) -; - while (pidStatus(i) > 0x0) { + /* fetchAgain: if (mpi_fetchMessage("init",&myMsg) == 0x0) { switch (myMsg.header) { @@ -117,6 +109,7 @@ } goto fetchAgain; } + */ sched_yield(); } printf("login exited?"); diff --git a/src/sys/Makefile b/src/sys/Makefile index adc55be..90a5eb4 100644 --- a/src/sys/Makefile +++ b/src/sys/Makefile @@ -29,7 +29,7 @@ @echo "Kernel Build For ${_ARCH} Completed On `LC_ALL=C date`" @echo "***************************************************************" -kernel-build: init-code sys-code arch-code kernel-code vmm-code mpi-code fs-code lib-code isa-code pci-code kernel-img +kernel-build: init-code sys-code arch-code kernel-code vmm-code mpi-code fs-code lib-code isa-code pci-code net-code kernel-img generic-code: generic (cd generic;make) diff --git a/src/sys/compile/Makefile b/src/sys/compile/Makefile index 3723724..5411053 100644 --- a/src/sys/compile/Makefile +++ b/src/sys/compile/Makefile @@ -9,7 +9,7 @@ OBJS = null.o #Kernel Parts -KPARTS = ../${_ARCH}/*.o ../fs/ubixfs/*.o ../init/*.o ../sys/*.o ../vmm/*.o ../lib/*.o ../kernel/*.o ../isa/*.o ../fs/vfs/*.o ../pci/*.o ../fs/devfs/*.o ../mpi/*.o ../fs/ufs/*.o ../fs/common/*.o +KPARTS = ../${_ARCH}/*.o ../fs/ubixfs/*.o ../init/*.o ../sys/*.o ../vmm/*.o ../lib/*.o ../kernel/*.o ../isa/*.o ../fs/vfs/*.o ../pci/*.o ../fs/devfs/*.o ../mpi/*.o ../fs/ufs/*.o ../fs/common/*.o ../net/net/*.o ../net/netif/*.o ../net/api/*.o ../net/core/*.o # ../sde/*.o ../graphics/*.o ../ld/*.o -Ttext 0x30000 -Tdata 0x34000 ../ubixfs/*.o # Link the kernel statically with fixed text+data address @1M diff --git a/src/sys/include/net/mem.h b/src/sys/include/net/mem.h index 0b84093..cad5c55 100644 --- a/src/sys/include/net/mem.h +++ b/src/sys/include/net/mem.h @@ -35,7 +35,7 @@ #ifndef __LWIP_MEM_H__ #define __LWIP_MEM_H__ -#include +#include #include "net/debug.h" #include "net/opt.h" diff --git a/src/sys/include/net/memp.h b/src/sys/include/net/memp.h index 8acd119..d88ad82 100644 --- a/src/sys/include/net/memp.h +++ b/src/sys/include/net/memp.h @@ -36,7 +36,7 @@ #ifndef __LWIP_MEMP_H__ #define __LWIP_MEMP_H__ -#include +#include #include "net/debug.h" #include "net/arch/cc.h" diff --git a/src/sys/include/pci/lnc.h b/src/sys/include/pci/lnc.h index 248c7b7..9f0cef2 100644 --- a/src/sys/include/pci/lnc.h +++ b/src/sys/include/pci/lnc.h @@ -30,7 +30,7 @@ #ifndef _LNC_H #define _LNC_H -#include +#include #define NDESC(len2) (1 << len2) #define NORMAL 0 diff --git a/src/sys/include/ubixos/init.h b/src/sys/include/ubixos/init.h index bc8ed23..a2259e4 100644 --- a/src/sys/include/ubixos/init.h +++ b/src/sys/include/ubixos/init.h @@ -51,16 +51,18 @@ #include #include #include +#include typedef int (*intFunctionPTR)( void ); intFunctionPTR init_tasks[] = { vmm_init, static_constructors, i8259_init, idt_init, vitals_init, sysctl_init, vfs_init, sched_init, pit_init, atkbd_init, time_init, -//net_init, +net_init, //ne2k_init, devfs_init, - //pci_init, + pci_init, //ubixfs_init, //fdc_init, + initLNC, tty_init, ufs_init, initHardDisk, }; int init_tasksTotal = sizeof(init_tasks) / sizeof(intFunctionPTR); diff --git a/src/sys/isa/Makefile b/src/sys/isa/Makefile index 75940e5..c429e79 100644 --- a/src/sys/isa/Makefile +++ b/src/sys/isa/Makefile @@ -6,8 +6,7 @@ include ../Makefile.incl # Objects -OBJS = mouse.o atkbd.o fdc.o 8259.o pit.o -#ne2k.o +OBJS = mouse.o atkbd.o fdc.o 8259.o pit.o ne2k.o all: $(OBJS) diff --git a/src/sys/net/Makefile b/src/sys/net/Makefile index f8e845e..89142d0 100644 --- a/src/sys/net/Makefile +++ b/src/sys/net/Makefile @@ -3,7 +3,7 @@ all: core-code net-code api-code netif-code -core-code: core +core-code: core (cd core;make) net-code: net diff --git a/src/sys/net/api/Makefile b/src/sys/net/api/Makefile index e128f03..c27d281 100644 --- a/src/sys/net/api/Makefile +++ b/src/sys/net/api/Makefile @@ -3,7 +3,7 @@ # $Id: Makefile 54 2016-01-11 01:29:55Z reddawg $ # Include Global 'Source' Options -Makefile.incl +include ../../../Makefile.incl include ../../Makefile.incl # Objects diff --git a/src/sys/net/api/api_lib.c b/src/sys/net/api/api_lib.c index 1f01802..1117c5c 100644 --- a/src/sys/net/api/api_lib.c +++ b/src/sys/net/api/api_lib.c @@ -36,7 +36,7 @@ /* This is the part of the API that is linked with the application */ -#include +#include #include "net/debug.h" #include "net/api.h" diff --git a/src/sys/net/api/api_msg.c b/src/sys/net/api/api_msg.c index eb45f64..52fa1f8 100644 --- a/src/sys/net/api/api_msg.c +++ b/src/sys/net/api/api_msg.c @@ -33,7 +33,7 @@ * $Id: api_msg.c 54 2016-01-11 01:29:55Z reddawg $ */ -#include +#include #include "net/debug.h" #include "net/arch.h" diff --git a/src/sys/net/api/sockets.c b/src/sys/net/api/sockets.c index 615f025..33d81bb 100644 --- a/src/sys/net/api/sockets.c +++ b/src/sys/net/api/sockets.c @@ -33,7 +33,7 @@ * $Id: sockets.c 54 2016-01-11 01:29:55Z reddawg $ */ -#include +#include #include "net/debug.h" #include "net/api.h" diff --git a/src/sys/net/core/icmp.c b/src/sys/net/core/icmp.c index fb5d670..2f8a629 100644 --- a/src/sys/net/core/icmp.c +++ b/src/sys/net/core/icmp.c @@ -1,202 +1,194 @@ -/* - * 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: icmp.c,v 1.3 2004/05/19 01:23:43 reddawg Exp $ - */ - -/* 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); -} - - - - - - - - +/* + * 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: icmp.c,v 1.3 2004/05/19 01:23:43 reddawg Exp $ + */ + +/* 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 index 3519526..a9722f6 100644 --- a/src/sys/net/core/inet.c +++ b/src/sys/net/core/inet.c @@ -1,172 +1,174 @@ -/* - * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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); -} - - -/*-----------------------------------------------------------------------------------*/ +/* + * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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; + uInt32 *ptr = dataptr; + + for(acc = 0; len > 1; len -= 2) { + //acc += *((uInt16 *)dataptr)++; + acc += *ptr++; + } + + /* 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/ip.c b/src/sys/net/core/ip.c index 12040d6..44fff09 100644 --- a/src/sys/net/core/ip.c +++ b/src/sys/net/core/ip.c @@ -1,705 +1,700 @@ -/* - * 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,v 1.6 2004/10/07 21:55:44 kurlon Exp $ - */ - - -/*-----------------------------------------------------------------------------------*/ -/* 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 */ -/*-----------------------------------------------------------------------------------*/ - - - - - +/* + * 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,v 1.6 2004/10/07 21:55:44 kurlon Exp $ + */ + + +/*-----------------------------------------------------------------------------------*/ +/* 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 index 558c875..74029f7 100644 --- a/src/sys/net/core/ip_addr.c +++ b/src/sys/net/core/ip_addr.c @@ -1,46 +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: ip_addr.c,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -#include - -#include "net/debug.h" -#include "net/ipv4/ip_addr.h" -#include "net/ipv4/inet.h" - -struct ip_addr ip_addr_broadcast = {0xffffffff}; - -/*-----------------------------------------------------------------------------------*/ - -/*-----------------------------------------------------------------------------------*/ +/* + * 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_addr.c,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +#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 index 38c3834..4f2e20d 100644 --- a/src/sys/net/core/mem.c +++ b/src/sys/net/core/mem.c @@ -1,378 +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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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 */ -/*-----------------------------------------------------------------------------------*/ +/* + * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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 index f44a4ac..8a9338d 100644 --- a/src/sys/net/core/memp.c +++ b/src/sys/net/core/memp.c @@ -1,340 +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: memp.c,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -#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 */ -/*-----------------------------------------------------------------------------------*/ +/* + * 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: memp.c,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +#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 index c27f39a..b189824 100644 --- a/src/sys/net/core/netif.c +++ b/src/sys/net/core/netif.c @@ -1,145 +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: netif.c,v 1.7 2004/10/07 21:55:44 kurlon Exp $ - */ - -#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; -} -/*-----------------------------------------------------------------------------------*/ +/* + * 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: netif.c,v 1.7 2004/10/07 21:55:44 kurlon Exp $ + */ + +#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 index 85d1244..448bafe 100644 --- a/src/sys/net/core/pbuf.c +++ b/src/sys/net/core/pbuf.c @@ -1,597 +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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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; -} -/*-----------------------------------------------------------------------------------*/ +/* + * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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/sys.c b/src/sys/net/core/sys.c index a8088ee..0994a5c 100644 --- a/src/sys/net/core/sys.c +++ b/src/sys/net/core/sys.c @@ -1,198 +1,198 @@ -/* - * 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: sys.c,v $ - * Revision 1.1 2004/04/15 12:38:25 reddawg - * Fixed to compile - * - * Revision 1.6 2004/04/13 19:40:50 reddawg - * Fixed typedefs - * - * - * $Id: sys.c,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - - -#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; - } - } - } - -} -/*-----------------------------------------------------------------------------------*/ - +/* + * 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: sys.c,v $ + * Revision 1.1 2004/04/15 12:38:25 reddawg + * Fixed to compile + * + * Revision 1.6 2004/04/13 19:40:50 reddawg + * Fixed typedefs + * + * + * $Id: sys.c,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + + +#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 index a801ea9..e084aac 100644 --- a/src/sys/net/core/tcp.c +++ b/src/sys/net/core/tcp.c @@ -1,1160 +1,1151 @@ -/* - * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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 */ -/*-----------------------------------------------------------------------------------*/ - - - - - - - - - +/* + * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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 index 7de0625..74caa6c 100644 --- a/src/sys/net/core/tcp_input.c +++ b/src/sys/net/core/tcp_input.c @@ -1,1121 +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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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]; - } - } - } -} -/*-----------------------------------------------------------------------------------*/ - +/* + * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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 index 5a56647..676e484 100644 --- a/src/sys/net/core/tcp_output.c +++ b/src/sys/net/core/tcp_output.c @@ -1,611 +1,600 @@ -/* - * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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)); -} -/*-----------------------------------------------------------------------------------*/ - - - - - - - - - - - +/* + * 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,v 1.1 2004/04/15 12:38:25 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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 index 62b9b33..4de5893 100644 --- a/src/sys/net/core/udp.c +++ b/src/sys/net/core/udp.c @@ -1,445 +1,435 @@ -/* - * 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,v 1.2 2004/08/26 22:51:18 reddawg Exp $ - */ - -/*-----------------------------------------------------------------------------------*/ -/* 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; - -// UBU 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 */ -/*-----------------------------------------------------------------------------------*/ - - - - - - - - - - +/* + * 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,v 1.2 2004/08/26 22:51:18 reddawg Exp $ + */ + +/*-----------------------------------------------------------------------------------*/ +/* 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; + +// UBU 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 */ +/*-----------------------------------------------------------------------------------*/ diff --git a/src/sys/net/net/Makefile b/src/sys/net/net/Makefile index 58a9974..856fd96 100644 --- a/src/sys/net/net/Makefile +++ b/src/sys/net/net/Makefile @@ -2,7 +2,7 @@ # $Id: Makefile 54 2016-01-11 01:29:55Z reddawg $ # Include Global 'Source' Options -Makefile.incl +include ../../../Makefile.incl include ../../Makefile.incl # Objects diff --git a/src/sys/net/net/init.c b/src/sys/net/net/init.c index 921eed4..e6d77fb 100644 --- a/src/sys/net/net/init.c +++ b/src/sys/net/net/init.c @@ -27,7 +27,7 @@ *****************************************************************************************/ -#include +#include #include #include @@ -75,9 +75,9 @@ kprintf("TCP/IP initialized.\n"); - IP4_ADDR(&gw, 10,4,0,1); - IP4_ADDR(&ipaddr, 10,4,0,69); - IP4_ADDR(&netmask, 255,255,255,0); + IP4_ADDR(&gw, 10,50,0,7); + IP4_ADDR(&ipaddr, 10,50,0,1); + IP4_ADDR(&netmask, 255,255,0,0); netif_set_default(netif_add(&ipaddr, &netmask, &gw, ethernetif_init, tcpip_input)); IP4_ADDR(&gw, 127,0,0,1); diff --git a/src/sys/net/net/sys_arch.c b/src/sys/net/net/sys_arch.c index f886e5e..9fdceba 100644 --- a/src/sys/net/net/sys_arch.c +++ b/src/sys/net/net/sys_arch.c @@ -79,7 +79,7 @@ * $Id: sys_arch.c 54 2016-01-11 01:29:55Z reddawg $ */ -#include +#include #include #include #include diff --git a/src/sys/net/net/udpecho.c b/src/sys/net/net/udpecho.c index f1a6673..285db42 100644 --- a/src/sys/net/net/udpecho.c +++ b/src/sys/net/net/udpecho.c @@ -33,7 +33,7 @@ * $Id: udpecho.c 54 2016-01-11 01:29:55Z reddawg $ */ -#include +#include #include #include "net/api.h" diff --git a/src/sys/net/netif/Makefile b/src/sys/net/netif/Makefile index 694f69f..693365d 100755 --- a/src/sys/net/netif/Makefile +++ b/src/sys/net/netif/Makefile @@ -2,7 +2,7 @@ # $Id: Makefile 54 2016-01-11 01:29:55Z reddawg $ # Include Global 'Source' Options -Makefile.incl +include ../../../Makefile.incl include ../../Makefile.incl # Objects diff --git a/src/sys/net/netif/arp.c b/src/sys/net/netif/arp.c index e0d57d1..ed1c4f5 100644 --- a/src/sys/net/netif/arp.c +++ b/src/sys/net/netif/arp.c @@ -34,7 +34,7 @@ * */ -#include +#include #include #include "net/debug.h" diff --git a/src/sys/net/netif/ethernetif.c b/src/sys/net/netif/ethernetif.c index bbe621c..1d3969f 100644 --- a/src/sys/net/netif/ethernetif.c +++ b/src/sys/net/netif/ethernetif.c @@ -40,7 +40,7 @@ * something that better describes your network interface. */ -#include +#include #include #include #include diff --git a/src/sys/pci/Makefile b/src/sys/pci/Makefile index ce76ae6..2647058 100644 --- a/src/sys/pci/Makefile +++ b/src/sys/pci/Makefile @@ -6,7 +6,7 @@ include ../Makefile.incl # Objects -OBJS = hd.o pci.o +OBJS = hd.o pci.o lnc.o all: $(OBJS) diff --git a/src/sys/pci/lnc.c b/src/sys/pci/lnc.c index b136baf..bde2327 100644 --- a/src/sys/pci/lnc.c +++ b/src/sys/pci/lnc.c @@ -47,7 +47,7 @@ #include #include -#include +#include #include #include #include @@ -103,13 +103,22 @@ void initLNC() { int i = 0x0; - lnc = kmalloc(sizeof(struct lncInfo),-2); + lnc = kmalloc(sizeof(struct lncInfo)); - lnc->rap = 0x1000 + PCNET_RAP; - lnc->rdp = 0x1000 + PCNET_RDP; - lnc->bdp = 0x1000 + PCNET_BDP; + lnc->rap = 0xE2000000 + PCNET_RAP; + lnc->rdp = 0xE2000000 + PCNET_RDP; + lnc->bdp = 0xE2000000 + PCNET_BDP; + kprintf("[0x%X]", inportDWord(0xE2000000 + 0x18)); + kprintf("[0x%X]", inportWord(0xE2000000 + 0x14)); + + outportDWord(0xE2000000 + 0x10, 0); + for (i = 0; i < 4;i++) + kprintf("[0x%X]", inportDWord(0xE2000000 + (0x4 * i))); + + kprintf("[0x%X]", inportDWord(0xE2000000 + 0x00)); lnc->nic.ic = probe(lnc); +kprintf("[0x%X]\n", lnc->nic.ic); if ((lnc->nic.ic > 0) && (lnc->nic.ic >= PCnet_32)) { lnc->nic.ident = NE2100; lnc->nic.memMode = DMA_FIXED; @@ -119,7 +128,7 @@ /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) { - lnc->arpcom.ac_enaddr[i] = inportByte(0x1000 + i); + lnc->arpcom.ac_enaddr[i] = inportByte(0xE2000000 + i); kprintf("[0x%X]",lnc->arpcom.ac_enaddr[i]); } } @@ -137,7 +146,7 @@ if (readCsr(lnc, CSR0) & IDON) { writeCsr(lnc, CSR0, STRT | INEA); setVector(_lncInt,mVec+9, (dInt + dPresent + dDpl3)); - enableIrq(9); + irqEnable(9); /* * sc->arpcom.ac_if.if_flags |= IFF_RUNNING; * sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; @@ -157,6 +166,7 @@ if ((type = lanceProbe(lnc))) { chipId = readCsr(lnc, CSR89); +kprintf("chipId: %i", chipId); chipId <<= 16; chipId |= readCsr(lnc, CSR88); if (chipId & AMD_MASK) { @@ -190,8 +200,11 @@ } int lanceProbe(struct lncInfo *lnc) { + uInt16 inW = 0; writeCsr(lnc, CSR0, STOP); - if ((inportWord(lnc->rdp) & STOP) && !(readCsr(lnc, CSR3))) { + inW = inportWord(lnc->rdp); + kprintf("[inW: 0x%X - 0x%X - 0x%X - (0x%X)]",inW, STOP, inW & STOP, readCsr(lnc, CSR3)); + if ((inW & STOP) && !(readCsr(lnc, CSR3))) { writeCsr(lnc, CSR0, INEA); if (readCsr(lnc, CSR0) & INEA) { return(C_LANCE); @@ -266,7 +279,7 @@ * descriptor's can only hold 16 bit addresses. */ /* sc->recv_ring = contigmalloc(lnc_mem_size, M_DEVBUF, M_NOWAIT,0ul, 0xfffffful, 4ul, 0x1000000); */ - lnc->recvRing = kmalloc(lncMemSize,-2); + lnc->recvRing = kmalloc(lncMemSize); kprintf("PCI Board\n"); } } diff --git a/src/sys/pci/pci.c b/src/sys/pci/pci.c index 426b69d..a96ca05 100644 --- a/src/sys/pci/pci.c +++ b/src/sys/pci/pci.c @@ -238,7 +238,14 @@ int i; for(i=0;i<4;i++) { word[i] = pciRead(bus,dev,func,4*i,4); + if (cfg->vendorId == 0x1022 && i == 1) { + kprintf("got it: 0x%X", word[i]); + word[i] &= 0xffff0000; + word[i] |= 0x5; + pciWrite(bus,dev,func,4*i,word[i],4); + kprintf("set it: 0x%X[0x%X]", word[i], pciRead(bus,dev,func,0x10,4)); } + } if(cfg->vendorId == 0xffff) return FALSE; if(cfg->vendorId == 0x0) return FALSE; /* Quick Hack */ @@ -247,9 +254,11 @@ cfg->func = func; cfg->subsysVendor = pciRead(bus, dev, func, 0x2c, 2); cfg->subsys = pciRead(bus, dev, func, 0x2e, 2); +if (cfg->vendorId == 0x1022) { kprintf("Device Info: /bus/pci/%d/%d/%d\n",bus,dev,func); kprintf(" * Vendor: %X Device: %X Class/SubClass/Interface %X/%X/%X\n",cfg->vendorId,cfg->deviceId,cfg->baseClass,cfg->subClass,cfg->interface); kprintf(" * Status: %X Command: %X BIST/Type/Lat/CLS: %X/%X/%X/%X\n",cfg->status, cfg->command, cfg->bist, cfg->headerType,cfg->latencyTimer, cfg->cacheLineSize); +} switch(cfg->headerType & 0x7F){ case 0: /* normal device */ for(i=0;i<6;i++) { @@ -299,7 +308,9 @@ /* kprintf(" * Vendor: %X Device: %X Class/SubClass/Interface %X/%X/%X\n",pcfg.vendorId,pcfg.deviceId,pcfg.baseClass,pcfg.subClass,pcfg.interface); */ for (i=0x0;i